ebiten/graphics/opengl/shader/program.go
2013-10-30 22:26:10 +09:00

162 lines
4.1 KiB
Go

package shader
// #cgo LDFLAGS: -framework OpenGL
//
// #include <OpenGL/gl.h>
// #include <stdlib.h>
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
}