package shader // #cgo LDFLAGS: -framework OpenGL // // #include // #include import "C" import ( "github.com/hajimehoshi/go-ebiten/graphics/matrix" "unsafe" ) type program struct { native C.GLuint shaderIds []shaderId } type programId int const ( programRegular programId = iota programColorMatrix ) var programs = map[programId]*program{ programRegular: &program{ shaderIds: []shaderId{shaderVertex, shaderFragment}, }, programColorMatrix: &program{ shaderIds: []shaderId{shaderVertex, shaderColorMatrix}, }, } func (p *program) create() { p.native = C.glCreateProgram() if p.native == 0 { panic("glCreateProgram failed") } for _, shaderId := range p.shaderIds { C.glAttachShader(p.native, shaders[shaderId].native) } C.glLinkProgram(p.native) linked := C.GLint(C.GL_FALSE) C.glGetProgramiv(p.native, C.GL_LINK_STATUS, &linked) if linked == C.GL_FALSE { panic("program error") } } var initialized = false func initialize() { for _, shader := range shaders { shader.compile() } defer func() { for _, shader := range shaders { shader.delete() } }() for _, program := range programs { program.create() } 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 { programId := programRegular if !colorMatrix.IsIdentity() { programId = programColorMatrix } program := programs[programId] C.glUseProgram(program.native) C.glUniformMatrix4fv(C.GLint(getUniformLocation(program.native, "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.native, "modelview_matrix"), 1, C.GL_FALSE, (*C.GLfloat)(&glModelviewMatrix[0])) C.glUniform1i(getUniformLocation(program.native, "texture"), 0) if programId != programColorMatrix { return program.native } 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.native, "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.native, "color_matrix_translation"), 1, (*C.GLfloat)(&glColorMatrixTranslation[0])) return program.native }