package shader // #cgo LDFLAGS: -framework OpenGL // // #include // #include import "C" import ( "github.com/hajimehoshi/go-ebiten/graphics/matrix" "unsafe" ) func createProgram(shaders ...*shader) C.GLuint { program := C.glCreateProgram() for _, shader := range shaders { C.glAttachShader(program, shader.id) } C.glLinkProgram(program) linked := C.GLint(C.GL_FALSE) C.glGetProgramiv(program, C.GL_LINK_STATUS, &linked) if linked == C.GL_FALSE { panic("program error") } return program } var ( initialized = false ) func initialize() { // TODO: when should this function be called? vertexShader.id = C.glCreateShader(C.GL_VERTEX_SHADER) if vertexShader.id == 0 { panic("creating shader failed") } fragmentShader.id = C.glCreateShader(C.GL_FRAGMENT_SHADER) if fragmentShader.id == 0 { panic("creating shader failed") } colorMatrixShader.id = C.glCreateShader(C.GL_FRAGMENT_SHADER) if colorMatrixShader.id == 0 { panic("creating shader failed") } vertexShader.compile() fragmentShader.compile() colorMatrixShader.compile() programRegular = createProgram(vertexShader, fragmentShader) programColorMatrix = createProgram(vertexShader, colorMatrixShader) C.glDeleteShader(vertexShader.id) C.glDeleteShader(fragmentShader.id) C.glDeleteShader(colorMatrixShader.id) initialized = true } const ( qualifierVariableTypeAttribute = iota qualifierVariableTypeUniform ) var ( shaderLocationCache = map[int]map[string]C.GLint{ qualifierVariableTypeAttribute: map[string]C.GLint{}, qualifierVariableTypeUniform: map[string]C.GLint{}, } ) func getLocation(program C.GLuint, name string, qualifierVariableType int) C.GLint { if location, ok := shaderLocationCache[qualifierVariableType][name]; ok { return location } locationName := C.CString(name) defer C.free(unsafe.Pointer(locationName)) location := C.GLint(-1) switch qualifierVariableType { case qualifierVariableTypeAttribute: location = C.glGetAttribLocation(program, (*C.GLchar)(locationName)) case qualifierVariableTypeUniform: location = C.glGetUniformLocation(program, (*C.GLchar)(locationName)) default: panic("no reach") } if location == -1 { panic("glGetUniformLocation failed") } shaderLocationCache[qualifierVariableType][name] = location return location } func getAttributeLocation(program C.GLuint, name string) C.GLint { return getLocation(program, name, qualifierVariableTypeAttribute) } func getUniformLocation(program C.GLuint, name string) C.GLint { return getLocation(program, name, qualifierVariableTypeUniform) } func use(projectionMatrix [16]float32, geometryMatrix matrix.Geometry, colorMatrix matrix.Color) C.GLuint { program := programRegular if !colorMatrix.IsIdentity() { program = programColorMatrix } C.glUseProgram(program) C.glUniformMatrix4fv(C.GLint(getUniformLocation(program, "projection_matrix")), 1, C.GL_FALSE, (*C.GLfloat)(&projectionMatrix[0])) a := float32(geometryMatrix.Elements[0][0]) b := float32(geometryMatrix.Elements[0][1]) c := float32(geometryMatrix.Elements[1][0]) d := float32(geometryMatrix.Elements[1][1]) tx := float32(geometryMatrix.Elements[0][2]) ty := float32(geometryMatrix.Elements[1][2]) glModelviewMatrix := [...]float32{ a, c, 0, 0, b, d, 0, 0, 0, 0, 1, 0, tx, ty, 0, 1, } C.glUniformMatrix4fv(getUniformLocation(program, "modelview_matrix"), 1, C.GL_FALSE, (*C.GLfloat)(&glModelviewMatrix[0])) C.glUniform1i(getUniformLocation(program, "texture"), 0) if program != programColorMatrix { return program } e := [4][5]float32{} for i := 0; i < 4; i++ { for j := 0; j < 5; j++ { e[i][j] = float32(colorMatrix.Elements[i][j]) } } glColorMatrix := [...]float32{ e[0][0], e[1][0], e[2][0], e[3][0], e[0][1], e[1][1], e[2][1], e[3][1], e[0][2], e[1][2], e[2][2], e[3][2], e[0][3], e[1][3], e[2][3], e[3][3], } C.glUniformMatrix4fv(getUniformLocation(program, "color_matrix"), 1, C.GL_FALSE, (*C.GLfloat)(&glColorMatrix[0])) glColorMatrixTranslation := [...]float32{ e[0][4], e[1][4], e[2][4], e[3][4], } C.glUniform4fv(getUniformLocation(program, "color_matrix_translation"), 1, (*C.GLfloat)(&glColorMatrixTranslation[0])) return program }