Refactoring

This commit is contained in:
Hajime Hoshi 2013-10-26 04:15:27 +09:00
parent ac572a5554
commit c5f8738ae9
7 changed files with 253 additions and 161 deletions

View File

@ -9,6 +9,8 @@ import (
"fmt"
"github.com/hajimehoshi/go-ebiten/graphics"
"github.com/hajimehoshi/go-ebiten/graphics/matrix"
"github.com/hajimehoshi/go-ebiten/graphics/opengl/shader"
"github.com/hajimehoshi/go-ebiten/graphics/rendertarget"
"github.com/hajimehoshi/go-ebiten/graphics/texture"
"image"
"math"
@ -21,10 +23,10 @@ type Context struct {
screenHeight int
screenScale int
textures map[graphics.TextureId]*texture.Texture
renderTargets map[graphics.RenderTargetId]*RenderTarget
renderTargets map[graphics.RenderTargetId]*rendertarget.RenderTarget
renderTargetToTexture map[graphics.RenderTargetId]graphics.TextureId
currentOffscreen *RenderTarget
mainFramebufferTexture *RenderTarget
currentOffscreen *rendertarget.RenderTarget
mainFramebufferTexture *rendertarget.RenderTarget
projectionMatrix [16]float32
}
@ -34,7 +36,7 @@ func newContext(screenWidth, screenHeight, screenScale int) *Context {
screenHeight: screenHeight,
screenScale: screenScale,
textures: map[graphics.TextureId]*texture.Texture{},
renderTargets: map[graphics.RenderTargetId]*RenderTarget{},
renderTargets: map[graphics.RenderTargetId]*rendertarget.RenderTarget{},
renderTargetToTexture: map[graphics.RenderTargetId]graphics.TextureId{},
}
return context
@ -55,7 +57,7 @@ func (context *Context) Init() {
panic("creating main framebuffer failed: " + err.Error())
}
initializeShaders()
shader.Initialize()
context.screenId, err = context.NewRenderTarget(
context.screenWidth, context.screenHeight)
@ -82,37 +84,21 @@ func (context *Context) Fill(r, g, b uint8) {
C.glClear(C.GL_COLOR_BUFFER_BIT)
}
func (context *Context) DrawTexture(
textureId graphics.TextureId,
geometryMatrix matrix.Geometry, colorMatrix matrix.Color) {
texture, ok := context.textures[textureId]
if !ok {
panic("invalid texture ID")
}
// TODO: fix this
source := graphics.Rect{0, 0, texture.Width(), texture.Height()}
locations := []graphics.TexturePart{{0, 0, source}}
context.DrawTextureParts(textureId, locations,
geometryMatrix, colorMatrix)
type TextureDrawing struct {
context *Context
geometryMatrix matrix.Geometry
colorMatrix matrix.Color
}
func (context *Context) DrawTextureParts(
textureId graphics.TextureId, parts []graphics.TexturePart,
geometryMatrix matrix.Geometry, colorMatrix matrix.Color) {
texture, ok := context.textures[textureId]
if !ok {
panic("invalid texture ID")
}
if len(parts) == 0 {
func (t *TextureDrawing) Draw(native interface{}, quads []texture.Quad) {
if len(quads) == 0 {
return
}
shaderProgram := t.context.setShaderProgram(t.geometryMatrix, t.colorMatrix)
C.glBindTexture(C.GL_TEXTURE_2D, native.(C.GLuint))
shaderProgram := context.setShaderProgram(geometryMatrix, colorMatrix)
C.glBindTexture(C.GL_TEXTURE_2D, texture.Native().(C.GLuint))
vertexAttrLocation := getAttributeLocation(shaderProgram, "vertex")
textureAttrLocation := getAttributeLocation(shaderProgram, "texture")
vertexAttrLocation := shader.GetAttributeLocation(shaderProgram, "vertex")
textureAttrLocation := shader.GetAttributeLocation(shaderProgram, "texture")
C.glEnableClientState(C.GL_VERTEX_ARRAY)
C.glEnableClientState(C.GL_TEXTURE_COORD_ARRAY)
@ -122,29 +108,28 @@ func (context *Context) DrawTextureParts(
texCoords := []float32{}
indicies := []uint32{}
// TODO: Check len(parts) and GL_MAX_ELEMENTS_INDICES
for i, part := range parts {
x1 := float32(part.LocationX)
x2 := float32(part.LocationX + part.Source.Width)
y1 := float32(part.LocationY)
y2 := float32(part.LocationY + part.Source.Height)
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,
)
src := part.Source
tu1 := float32(texture.U(src.X))
tu2 := float32(texture.U(src.X + src.Width))
tv1 := float32(texture.V(src.Y))
tv2 := float32(texture.V(src.Y + src.Height))
u1 := quad.TextureCoordU1
u2 := quad.TextureCoordU2
v1 := quad.TextureCoordV1
v2 := quad.TextureCoordV2
texCoords = append(texCoords,
tu1, tv1,
tu2, tv1,
tu1, tv2,
tu2, tv2,
u1, v1,
u2, v1,
u1, v2,
u2, v2,
)
base := uint32(i*4)
base := uint32(i * 4)
indicies = append(indicies,
base, base+1, base+2,
base+1, base+2, base+3,
@ -164,6 +149,28 @@ func (context *Context) DrawTextureParts(
C.glDisableClientState(C.GL_VERTEX_ARRAY)
}
func (context *Context) DrawTexture(
textureId graphics.TextureId,
geometryMatrix matrix.Geometry, colorMatrix matrix.Color) {
texture, ok := context.textures[textureId]
if !ok {
panic("invalid texture ID")
}
drawing := &TextureDrawing{context, geometryMatrix, colorMatrix}
texture.Draw(drawing.Draw)
}
func (context *Context) DrawTextureParts(
textureId graphics.TextureId, parts []graphics.TexturePart,
geometryMatrix matrix.Geometry, colorMatrix matrix.Color) {
texture, ok := context.textures[textureId]
if !ok {
panic("invalid texture ID")
}
drawing := &TextureDrawing{context, geometryMatrix, colorMatrix}
texture.DrawParts(parts, drawing.Draw)
}
func (context *Context) ResetOffscreen() {
context.SetOffscreen(context.screenId)
}
@ -173,12 +180,13 @@ func (context *Context) SetOffscreen(renderTargetId graphics.RenderTargetId) {
context.setOffscreen(renderTarget)
}
func (context *Context) setOffscreen(renderTarget *RenderTarget) {
func (context *Context) setOffscreen(renderTarget *rendertarget.RenderTarget) {
context.currentOffscreen = renderTarget
C.glFlush()
C.glBindFramebuffer(C.GL_FRAMEBUFFER, renderTarget.framebuffer)
framebuffer := renderTarget.Framebuffer().(C.GLuint)
C.glBindFramebuffer(C.GL_FRAMEBUFFER, framebuffer)
err := C.glCheckFramebufferStatus(C.GL_FRAMEBUFFER)
if err != C.GL_FRAMEBUFFER_COMPLETE {
panic(fmt.Sprintf("glBindFramebuffer failed: %d", err))
@ -188,23 +196,34 @@ func (context *Context) setOffscreen(renderTarget *RenderTarget) {
C.glBlendFuncSeparate(C.GL_SRC_ALPHA, C.GL_ONE_MINUS_SRC_ALPHA,
C.GL_ZERO, C.GL_ONE)
context.currentOffscreen.SetAsViewport(context)
isUsingMainFramebuffer := context.currentOffscreen == context.mainFramebufferTexture
setter := &viewportSetter{
isUsingMainFramebuffer,
context.screenHeight * context.screenScale,
context,
}
context.currentOffscreen.SetAsViewport(setter.Set)
}
func (context *Context) SetViewport(x, y, width, height int) {
type viewportSetter struct {
isUsingMainFramebuffer bool
actualScreenHeight int
context *Context
}
func (v *viewportSetter) Set(x, y, width, height int) {
C.glViewport(C.GLint(x), C.GLint(y), C.GLsizei(width), C.GLsizei(height))
matrix := graphics.OrthoProjectionMatrix(x, width, y, height)
if context.currentOffscreen == context.mainFramebufferTexture {
if v.isUsingMainFramebuffer {
// Flip Y and move to fit with the top of the window.
matrix[1][1] *= -1
actualHeight := context.screenHeight * context.screenScale
matrix[1][3] += float64(actualHeight) / float64(height) * 2
matrix[1][3] += float64(v.actualScreenHeight) / float64(height) * 2
}
for j := 0; j < 4; j++ {
for i := 0; i < 4; i++ {
context.projectionMatrix[i+j*4] = float32(matrix[i][j])
v.context.projectionMatrix[i+j*4] = float32(matrix[i][j])
}
}
@ -220,64 +239,8 @@ func (context *Context) flush() {
}
func (context *Context) setShaderProgram(
geometryMatrix matrix.Geometry, colorMatrix matrix.Color) (program C.GLuint) {
if colorMatrix.IsIdentity() {
program = regularShaderProgram
} else {
program = colorMatrixShaderProgram
}
// TODO: cache and skip?
C.glUseProgram(program)
C.glUniformMatrix4fv(getUniformLocation(program, "projection_matrix"),
1, C.GL_FALSE,
(*C.GLfloat)(&context.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 != colorMatrixShaderProgram {
return
}
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
geometryMatrix matrix.Geometry, colorMatrix matrix.Color) (program shader.Program) {
return shader.Use(context.projectionMatrix, geometryMatrix, colorMatrix)
}
func (context *Context) NewRenderTarget(width, height int) (
@ -289,7 +252,7 @@ func (context *Context) NewRenderTarget(width, height int) (
renderTargetId := graphics.RenderTargetId(<-newId)
textureId := graphics.TextureId(<-newId)
context.renderTargets[renderTargetId] = renderTarget
context.textures[textureId] = renderTarget.texture
context.textures[textureId] = renderTarget.Texture()
context.renderTargetToTexture[renderTargetId] = textureId
context.setOffscreen(renderTarget)

View File

@ -1,4 +1,4 @@
package opengl
package shader
// #cgo LDFLAGS: -framework OpenGL
//
@ -6,9 +6,19 @@ package opengl
// #include <stdlib.h>
import "C"
import (
"github.com/hajimehoshi/go-ebiten/graphics/matrix"
"unsafe"
)
type Program int
const (
ProgramRegular Program = iota
ProgramColorMatrix
)
type Location int
type shader struct {
id C.GLuint
name string
@ -110,7 +120,7 @@ func createProgram(shaders ...*shader) C.GLuint {
return program
}
func initializeShaders() {
func Initialize() {
// TODO: when should this function be called?
vertexShader.id = C.glCreateShader(C.GL_VERTEX_SHADER)
if vertexShader.id == 0 {
@ -149,20 +159,34 @@ var (
}
)
func getLocation(program C.GLuint, name string, qualifierVariableType int) C.GLint {
func toInnerProgram(program Program) C.GLuint {
switch program {
case ProgramRegular:
return regularShaderProgram
case ProgramColorMatrix:
return colorMatrixShaderProgram
default:
panic("no reach")
}
return C.GLuint(0)
}
func getLocation(program Program, name string, qualifierVariableType int) int {
if location, ok := shaderLocationCache[qualifierVariableType][name]; ok {
return location
return int(location)
}
locationName := C.CString(name)
defer C.free(unsafe.Pointer(locationName))
location := C.GLint(-1)
innerProgram := toInnerProgram(program)
switch qualifierVariableType {
case qualifierVariableTypeAttribute:
location = C.glGetAttribLocation(program, (*C.GLchar)(locationName))
location = C.glGetAttribLocation(innerProgram, (*C.GLchar)(locationName))
case qualifierVariableTypeUniform:
location = C.glGetUniformLocation(program, (*C.GLchar)(locationName))
location = C.glGetUniformLocation(innerProgram, (*C.GLchar)(locationName))
default:
panic("no reach")
}
@ -171,13 +195,70 @@ func getLocation(program C.GLuint, name string, qualifierVariableType int) C.GLi
}
shaderLocationCache[qualifierVariableType][name] = location
return location
return int(location)
}
func getAttributeLocation(program C.GLuint, name string) C.GLint {
func GetAttributeLocation(program Program, name string) int {
return getLocation(program, name, qualifierVariableTypeAttribute)
}
func getUniformLocation(program C.GLuint, name string) C.GLint {
func getUniformLocation(program Program, name string) int {
return getLocation(program, name, qualifierVariableTypeUniform)
}
func Use(projectionMatrix [16]float32, geometryMatrix matrix.Geometry, colorMatrix matrix.Color) Program {
program := ProgramRegular
if !colorMatrix.IsIdentity() {
program = ProgramColorMatrix
}
C.glUseProgram(toInnerProgram(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(C.GLint(getUniformLocation(program, "modelview_matrix")),
1, C.GL_FALSE,
(*C.GLfloat)(&glModelviewMatrix[0]))
C.glUniform1i(C.GLint(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(C.GLint(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(C.GLint(getUniformLocation(program, "color_matrix_translation")),
1, (*C.GLfloat)(&glColorMatrixTranslation[0]))
return program
}

View File

@ -5,22 +5,12 @@ package opengl
// #include <OpenGL/gl.h>
import "C"
import (
"github.com/hajimehoshi/go-ebiten/graphics/rendertarget"
"github.com/hajimehoshi/go-ebiten/graphics/texture"
"image"
"unsafe"
)
type RenderTarget struct {
texture *texture.Texture
framebuffer C.GLuint
}
func (renderTarget *RenderTarget) SetAsViewport(setter interface{
SetViewport(x, y, width, height int)
}) {
renderTarget.texture.SetAsViewport(0, 0, setter)
}
func createNativeTexture(textureWidth, textureHeight int, pixels []uint8) C.GLuint {
nativeTexture := C.GLuint(0)
@ -57,27 +47,21 @@ func (creator *NativeTextureCreator) CreateFromImage(img *image.NRGBA) (interfac
return createNativeTexture(size.X, size.Y, img.Pix), nil
}
func newRenderTarget(width, height int) (*RenderTarget, error) {
func newRenderTarget(width, height int) (*rendertarget.RenderTarget, error) {
texture, err := texture.New(width, height, &NativeTextureCreator{})
if err != nil {
return nil, err
}
framebuffer := createFramebuffer(texture.Native().(C.GLuint))
return &RenderTarget{
texture: texture,
framebuffer: framebuffer,
}, nil
return rendertarget.NewWithFramebuffer(texture, framebuffer), nil
}
func newRenderTargetWithFramebuffer(width, height int, framebuffer C.GLuint) (*RenderTarget, error) {
func newRenderTargetWithFramebuffer(width, height int, framebuffer C.GLuint) (*rendertarget.RenderTarget, error) {
texture, err := texture.New(width, height, &NativeTextureCreator{})
if err != nil {
return nil, err
}
return &RenderTarget{
texture: texture,
framebuffer: framebuffer,
}, nil
return rendertarget.NewWithFramebuffer(texture, framebuffer), nil
}
func createFramebuffer(nativeTexture C.GLuint) C.GLuint {

View File

@ -0,0 +1,31 @@
package rendertarget
import (
"github.com/hajimehoshi/go-ebiten/graphics/texture"
)
type RenderTarget struct {
texture *texture.Texture
framebuffer interface{}
}
func NewWithFramebuffer(texture *texture.Texture, framebuffer interface{}) *RenderTarget {
return &RenderTarget{
texture: texture,
framebuffer: framebuffer,
}
}
// TODO: Remove this
func (renderTarget *RenderTarget) Texture() *texture.Texture {
return renderTarget.texture
}
// TODO: Remove this
func (renderTarget *RenderTarget) Framebuffer() interface{} {
return renderTarget.framebuffer
}
func (renderTarget *RenderTarget) SetAsViewport(setter func(x, y, width, height int)) {
renderTarget.texture.SetAsViewport(setter)
}

View File

@ -1,3 +1,3 @@
package opengl
package texture
var NextPowerOf2 = nextPowerOf2

View File

@ -1,6 +1,7 @@
package texture
import (
"github.com/hajimehoshi/go-ebiten/graphics"
"image"
"image/draw"
)
@ -64,14 +65,6 @@ func NewFromImage(img image.Image, creator interface {
return texture, nil
}
func (texture *Texture) Width() int {
return texture.width
}
func (texture *Texture) Height() int {
return texture.height
}
func (texture *Texture) textureWidth() int {
return int(nextPowerOf2(uint64(texture.width)))
}
@ -80,20 +73,60 @@ func (texture *Texture) textureHeight() int {
return int(nextPowerOf2(uint64(texture.height)))
}
// TODO: Remove this
func (texture *Texture) Native() interface{} {
return texture.native
}
func (texture *Texture) U(x int) float64 {
return float64(x) / float64(texture.textureWidth())
func (texture *Texture) u(x int) float32 {
return float32(x) / float32(texture.textureWidth())
}
func (texture *Texture) V(y int) float64 {
return float64(y) / float64(texture.textureHeight())
func (texture *Texture) v(y int) float32 {
return float32(y) / float32(texture.textureHeight())
}
func (texture *Texture) SetAsViewport(x, y int, setter interface{
SetViewport(x, y, width, height int)
}) {
setter.SetViewport(x, y, texture.textureWidth(), texture.textureHeight())
func (texture *Texture) SetAsViewport(setter func(x, y, width, height int)) {
setter(0, 0, texture.textureWidth(), texture.textureHeight())
}
type Quad struct {
VertexX1 float32
VertexX2 float32
VertexY1 float32
VertexY2 float32
TextureCoordU1 float32
TextureCoordU2 float32
TextureCoordV1 float32
TextureCoordV2 float32
}
func (texture *Texture) Draw(draw func(native interface{}, quads []Quad)) {
x1 := float32(0)
x2 := float32(texture.width)
y1 := float32(0)
y2 := float32(texture.height)
u1 := texture.u(0)
u2 := texture.u(texture.width)
v1 := texture.v(0)
v2 := texture.v(texture.height)
quad := Quad{x1, x2, y1, y2, u1, u2, v1, v2}
draw(texture.native, []Quad{quad})
}
func (texture *Texture) DrawParts(parts []graphics.TexturePart, draw func(native interface{}, quads []Quad)) {
quads := []Quad{}
for _, part := range parts {
x1 := float32(part.LocationX)
x2 := float32(part.LocationX + part.Source.Width)
y1 := float32(part.LocationY)
y2 := float32(part.LocationY + part.Source.Height)
u1 := texture.u(part.Source.X)
u2 := texture.u(part.Source.X + part.Source.Width)
v1 := texture.v(part.Source.Y)
v2 := texture.v(part.Source.Y + part.Source.Height)
quad := Quad{x1, x2, y1, y2, u1, u2, v1, v2}
quads = append(quads, quad)
}
draw(texture.native, quads)
}

View File

@ -1,4 +1,4 @@
package opengl_test
package texture_test
import (
. "."