diff --git a/graphics/opengl/context.go b/graphics/opengl/context.go index ae2ea5cc8..7df54f803 100644 --- a/graphics/opengl/context.go +++ b/graphics/opengl/context.go @@ -24,7 +24,6 @@ type Context struct { textures map[graphics.TextureId]*texture.Texture renderTargets map[graphics.RenderTargetId]*rendertarget.RenderTarget renderTargetToTexture map[graphics.RenderTargetId]graphics.TextureId - currentOffscreen *rendertarget.RenderTarget mainFramebufferTexture *rendertarget.RenderTarget projectionMatrix [16]float32 } @@ -89,7 +88,8 @@ func (context *Context) DrawTexture( panic("invalid texture ID") } tex.Draw(func(native interface{}, quads []texture.Quad) { - shader.DrawTexture(uint(native.(C.GLuint)), context.projectionMatrix, quads, + shader.DrawTexture(shader.Texture(native.(C.GLuint)), + context.projectionMatrix, quads, geometryMatrix, colorMatrix) }) } @@ -102,7 +102,8 @@ func (context *Context) DrawTextureParts( panic("invalid texture ID") } tex.DrawParts(parts, func(native interface{}, quads []texture.Quad) { - shader.DrawTexture(uint(native.(C.GLuint)), context.projectionMatrix, quads, + shader.DrawTexture(shader.Texture(native.(C.GLuint)), + context.projectionMatrix, quads, geometryMatrix, colorMatrix) }) } @@ -117,8 +118,6 @@ func (context *Context) SetOffscreen(renderTargetId graphics.RenderTargetId) { } func (context *Context) setOffscreen(renderTarget *rendertarget.RenderTarget) { - context.currentOffscreen = renderTarget - C.glFlush() framebuffer := renderTarget.Framebuffer().(C.GLuint) @@ -132,13 +131,13 @@ func (context *Context) setOffscreen(renderTarget *rendertarget.RenderTarget) { C.glBlendFuncSeparate(C.GL_SRC_ALPHA, C.GL_ONE_MINUS_SRC_ALPHA, C.GL_ZERO, C.GL_ONE) - isUsingMainFramebuffer := context.currentOffscreen == context.mainFramebufferTexture + isUsingMainFramebuffer := renderTarget == context.mainFramebufferTexture setter := &viewportSetter{ isUsingMainFramebuffer, context.screenHeight * context.screenScale, context, } - context.currentOffscreen.SetAsViewport(setter.Set) + renderTarget.SetAsViewport(setter.Set) } type viewportSetter struct { diff --git a/graphics/opengl/shader/program.go b/graphics/opengl/shader/program.go new file mode 100644 index 000000000..ff459636d --- /dev/null +++ b/graphics/opengl/shader/program.go @@ -0,0 +1,233 @@ +package shader + +// #cgo LDFLAGS: -framework OpenGL +// +// #include +// #include +import "C" +import ( + "github.com/hajimehoshi/go-ebiten/graphics/matrix" + "github.com/hajimehoshi/go-ebiten/graphics/texture" + "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 +} + +type Texture C.GLuint + +func DrawTexture(native Texture, projectionMatrix [16]float32, quads []texture.Quad, + geometryMatrix matrix.Geometry, colorMatrix matrix.Color) { + if !initialized { + initialize() + } + + if len(quads) == 0 { + return + } + shaderProgram := use(projectionMatrix, geometryMatrix, colorMatrix) + // This state affects the other functions, so can't disable here... + C.glBindTexture(C.GL_TEXTURE_2D, C.GLuint(native)) + //defer C.glBindTexture(C.GL_TEXTURE_2D, 0) + + vertexAttrLocation := getAttributeLocation(shaderProgram, "vertex") + textureAttrLocation := getAttributeLocation(shaderProgram, "texture") + + C.glEnableClientState(C.GL_VERTEX_ARRAY) + C.glEnableClientState(C.GL_TEXTURE_COORD_ARRAY) + C.glEnableVertexAttribArray(C.GLuint(vertexAttrLocation)) + C.glEnableVertexAttribArray(C.GLuint(textureAttrLocation)) + defer func() { + C.glDisableVertexAttribArray(C.GLuint(textureAttrLocation)) + C.glDisableVertexAttribArray(C.GLuint(vertexAttrLocation)) + C.glDisableClientState(C.GL_TEXTURE_COORD_ARRAY) + C.glDisableClientState(C.GL_VERTEX_ARRAY) + }() + + vertices := []float32{} + texCoords := []float32{} + indicies := []uint32{} + // TODO: Check len(parts) and GL_MAX_ELEMENTS_INDICES + for i, quad := range quads { + x1 := quad.VertexX1 + x2 := quad.VertexX2 + y1 := quad.VertexY1 + y2 := quad.VertexY2 + vertices = append(vertices, + x1, y1, + x2, y1, + x1, y2, + x2, y2, + ) + u1 := quad.TextureCoordU1 + u2 := quad.TextureCoordU2 + v1 := quad.TextureCoordV1 + v2 := quad.TextureCoordV2 + texCoords = append(texCoords, + u1, v1, + u2, v1, + u1, v2, + u2, v2, + ) + base := uint32(i * 4) + indicies = append(indicies, + base, base+1, base+2, + base+1, base+2, base+3, + ) + } + C.glVertexAttribPointer(C.GLuint(vertexAttrLocation), 2, + C.GL_FLOAT, C.GL_FALSE, + 0, unsafe.Pointer(&vertices[0])) + C.glVertexAttribPointer(C.GLuint(textureAttrLocation), 2, + C.GL_FLOAT, C.GL_FALSE, + 0, unsafe.Pointer(&texCoords[0])) + C.glDrawElements(C.GL_TRIANGLES, C.GLsizei(len(indicies)), + C.GL_UNSIGNED_INT, unsafe.Pointer(&indicies[0])) +} diff --git a/graphics/opengl/shader/shader.go b/graphics/opengl/shader/shader.go index ff66c7138..8751ba49b 100644 --- a/graphics/opengl/shader/shader.go +++ b/graphics/opengl/shader/shader.go @@ -6,13 +6,9 @@ package shader // #include import "C" import ( - "github.com/hajimehoshi/go-ebiten/graphics/matrix" - "github.com/hajimehoshi/go-ebiten/graphics/texture" "unsafe" ) -type Location int - type shader struct { id C.GLuint name string @@ -99,217 +95,3 @@ func (s *shader) showShaderLog() { message := string(C.GoBytes(unsafe.Pointer(&buffer[0]), C.int(length))) print("shader error (", s.name, "):\n", message) } - -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 -} - -func DrawTexture(native uint, projectionMatrix [16]float32, quads []texture.Quad, - geometryMatrix matrix.Geometry, colorMatrix matrix.Color) { - if !initialized { - initialize() - } - - if len(quads) == 0 { - return - } - shaderProgram := use(projectionMatrix, geometryMatrix, colorMatrix) - C.glBindTexture(C.GL_TEXTURE_2D, C.GLuint(native)) - - vertexAttrLocation := getAttributeLocation(shaderProgram, "vertex") - textureAttrLocation := getAttributeLocation(shaderProgram, "texture") - - C.glEnableClientState(C.GL_VERTEX_ARRAY) - C.glEnableClientState(C.GL_TEXTURE_COORD_ARRAY) - C.glEnableVertexAttribArray(C.GLuint(vertexAttrLocation)) - C.glEnableVertexAttribArray(C.GLuint(textureAttrLocation)) - vertices := []float32{} - texCoords := []float32{} - indicies := []uint32{} - // TODO: Check len(parts) and GL_MAX_ELEMENTS_INDICES - for i, quad := range quads { - x1 := quad.VertexX1 - x2 := quad.VertexX2 - y1 := quad.VertexY1 - y2 := quad.VertexY2 - vertices = append(vertices, - x1, y1, - x2, y1, - x1, y2, - x2, y2, - ) - u1 := quad.TextureCoordU1 - u2 := quad.TextureCoordU2 - v1 := quad.TextureCoordV1 - v2 := quad.TextureCoordV2 - texCoords = append(texCoords, - u1, v1, - u2, v1, - u1, v2, - u2, v2, - ) - base := uint32(i * 4) - indicies = append(indicies, - base, base+1, base+2, - base+1, base+2, base+3, - ) - } - C.glVertexAttribPointer(C.GLuint(vertexAttrLocation), 2, - C.GL_FLOAT, C.GL_FALSE, - 0, unsafe.Pointer(&vertices[0])) - C.glVertexAttribPointer(C.GLuint(textureAttrLocation), 2, - C.GL_FLOAT, C.GL_FALSE, - 0, unsafe.Pointer(&texCoords[0])) - C.glDrawElements(C.GL_TRIANGLES, C.GLsizei(len(indicies)), - C.GL_UNSIGNED_INT, unsafe.Pointer(&indicies[0])) - C.glDisableVertexAttribArray(C.GLuint(textureAttrLocation)) - C.glDisableVertexAttribArray(C.GLuint(vertexAttrLocation)) - C.glDisableClientState(C.GL_TEXTURE_COORD_ARRAY) - C.glDisableClientState(C.GL_VERTEX_ARRAY) -}