Add TextureFactory

This commit is contained in:
Hajime Hoshi 2013-06-19 23:08:24 +09:00
parent eda572d03e
commit 847b6b80f6
5 changed files with 97 additions and 90 deletions

View File

@ -7,8 +7,9 @@ import (
) )
type Game interface { type Game interface {
Init(tf graphics.TextureFactory)
Update() Update()
Draw(g graphics.GraphicsContext, offscreen *graphics.Texture) Draw(g graphics.GraphicsContext, offscreen graphics.TextureID)
} }
type UI interface { type UI interface {
@ -22,7 +23,7 @@ func OpenGLRun(game Game, ui UI) {
ch := make(chan bool, 1) ch := make(chan bool, 1)
device := opengl.NewDevice( device := opengl.NewDevice(
ui.ScreenWidth(), ui.ScreenHeight(), ui.ScreenScale(), ui.ScreenWidth(), ui.ScreenHeight(), ui.ScreenScale(),
func(g graphics.GraphicsContext, offscreen *graphics.Texture) { func(g graphics.GraphicsContext, offscreen graphics.TextureID) {
ticket := <-ch ticket := <-ch
game.Draw(g, offscreen) game.Draw(g, offscreen)
ch<- ticket ch<- ticket
@ -38,7 +39,8 @@ func OpenGLRun(game Game, ui UI) {
ch<- ticket ch<- ticket
} }
}() }()
ch<- true
game.Init(device.TextureFactory())
ch<- true
ui.Run(device) ui.Run(device)
} }

View File

@ -91,37 +91,34 @@ func (ui *GlutUI) Run(device graphics.Device) {
} }
type DemoGame struct { type DemoGame struct {
ebitenTexture *graphics.Texture ebitenTexture graphics.Texture
x int x int
} }
func (game *DemoGame) Update() { func (game *DemoGame) Init(tf graphics.TextureFactory) {
if game.ebitenTexture == nil { file, err := os.Open("ebiten.png")
file, err := os.Open("ebiten.png") if err != nil {
if err != nil { panic(err)
panic(err)
}
defer file.Close()
img, _, err := image.Decode(file)
if err != nil {
panic(err)
}
game.ebitenTexture = graphics.NewTextureFromImage(img)
} }
defer file.Close()
img, _, err := image.Decode(file)
if err != nil {
panic(err)
}
game.ebitenTexture = tf.NewTextureFromImage(img)
}
func (game *DemoGame) Update() {
game.x++ game.x++
} }
func (game *DemoGame) Draw(g graphics.GraphicsContext, offscreen *graphics.Texture) { func (game *DemoGame) Draw(g graphics.GraphicsContext, offscreen graphics.TextureID) {
g.Fill(&color.RGBA{R: 128, G: 128, B: 255, A: 255}) g.Fill(&color.RGBA{R: 128, G: 128, B: 255, A: 255})
if game.ebitenTexture == nil {
return
}
geometryMatrix := graphics.IdentityGeometryMatrix() geometryMatrix := graphics.IdentityGeometryMatrix()
geometryMatrix.SetTx(float64(game.x)) geometryMatrix.SetTx(float64(game.x))
geometryMatrix.SetTy(float64(game.x)) geometryMatrix.SetTy(float64(game.x))
g.DrawTexture(game.ebitenTexture, g.DrawTexture(game.ebitenTexture.ID,
0, 0, game.ebitenTexture.Width, game.ebitenTexture.Height, 0, 0, game.ebitenTexture.Width, game.ebitenTexture.Height,
geometryMatrix, geometryMatrix,
graphics.IdentityColorMatrix()) graphics.IdentityColorMatrix())

View File

@ -7,28 +7,27 @@ import (
type Device interface { type Device interface {
Update() Update()
TextureFactory() TextureFactory
} }
type GraphicsContext interface { type GraphicsContext interface {
Clear() Clear()
Fill(color color.Color) Fill(color color.Color)
DrawTexture(texture *Texture, DrawTexture(textureId TextureID,
srcX, srcY, srcWidth, srcHeight int, srcX, srcY, srcWidth, srcHeight int,
geometryMatrix *GeometryMatrix, colorMatrix *ColorMatrix) geometryMatrix *GeometryMatrix, colorMatrix *ColorMatrix)
SetOffscreen(texture *Texture) SetOffscreen(textureId TextureID)
}
type TextureFactory interface {
NewTexture(width, height int) Texture
NewTextureFromImage(img image.Image) Texture
} }
type Texture struct { type Texture struct {
ID TextureID
Width int Width int
Height int Height int
Image image.Image
} }
func NewTexture(width, height int) *Texture { type TextureID int
return &Texture{width, height, nil}
}
func NewTextureFromImage(img image.Image) *Texture {
size := img.Bounds().Size()
return &Texture{size.X, size.Y, img}
}

View File

@ -14,22 +14,21 @@ type Device struct {
screenHeight int screenHeight int
screenScale int screenScale int
graphicsContext *GraphicsContext graphicsContext *GraphicsContext
offscreenTexture *graphics.Texture offscreenTexture graphics.Texture
drawFunc func(graphics.GraphicsContext, *graphics.Texture) drawFunc func(graphics.GraphicsContext, graphics.TextureID)
funcs []func()
} }
func NewDevice(screenWidth, screenHeight, screenScale int, func NewDevice(screenWidth, screenHeight, screenScale int,
drawFunc func(graphics.GraphicsContext, *graphics.Texture)) *Device { drawFunc func(graphics.GraphicsContext, graphics.TextureID)) *Device {
device := &Device{ device := &Device{
screenWidth: screenWidth, screenWidth: screenWidth,
screenHeight: screenHeight, screenHeight: screenHeight,
screenScale: screenScale, screenScale: screenScale,
graphicsContext: newGraphicsContext(screenWidth, screenHeight, screenScale), graphicsContext: newGraphicsContext(screenWidth, screenHeight, screenScale),
drawFunc: drawFunc, drawFunc: drawFunc,
funcs: []func(){},
} }
device.offscreenTexture = graphics.NewTexture(screenWidth, screenHeight) device.offscreenTexture =
device.graphicsContext.NewTexture(screenWidth, screenHeight)
return device return device
} }
@ -38,9 +37,9 @@ func (device *Device) Update() {
C.glEnable(C.GL_TEXTURE_2D) C.glEnable(C.GL_TEXTURE_2D)
C.glTexParameteri(C.GL_TEXTURE_2D, C.GL_TEXTURE_MIN_FILTER, C.GL_NEAREST) C.glTexParameteri(C.GL_TEXTURE_2D, C.GL_TEXTURE_MIN_FILTER, C.GL_NEAREST)
C.glTexParameteri(C.GL_TEXTURE_2D, C.GL_TEXTURE_MAG_FILTER, C.GL_NEAREST) C.glTexParameteri(C.GL_TEXTURE_2D, C.GL_TEXTURE_MAG_FILTER, C.GL_NEAREST)
g.SetOffscreen(device.offscreenTexture) g.SetOffscreen(device.offscreenTexture.ID)
g.Clear() g.Clear()
device.drawFunc(g, device.offscreenTexture) device.drawFunc(g, device.offscreenTexture.ID)
g.flush() g.flush()
C.glTexParameteri(C.GL_TEXTURE_2D, C.GL_TEXTURE_MIN_FILTER, C.GL_LINEAR) C.glTexParameteri(C.GL_TEXTURE_2D, C.GL_TEXTURE_MIN_FILTER, C.GL_LINEAR)
@ -50,8 +49,12 @@ func (device *Device) Update() {
geometryMatrix := graphics.IdentityGeometryMatrix() geometryMatrix := graphics.IdentityGeometryMatrix()
geometryMatrix.SetA(float64(g.screenScale)) geometryMatrix.SetA(float64(g.screenScale))
geometryMatrix.SetD(float64(g.screenScale)) geometryMatrix.SetD(float64(g.screenScale))
g.DrawTexture(device.offscreenTexture, g.DrawTexture(device.offscreenTexture.ID,
0, 0, device.screenWidth, device.screenHeight, 0, 0, device.screenWidth, device.screenHeight,
geometryMatrix, graphics.IdentityColorMatrix()) geometryMatrix, graphics.IdentityColorMatrix())
g.flush() g.flush()
} }
func (device *Device) TextureFactory() graphics.TextureFactory {
return device.graphicsContext
}

View File

@ -7,6 +7,7 @@ package opengl
import "C" import "C"
import ( import (
"fmt" "fmt"
"image"
"image/color" "image/color"
"unsafe" "unsafe"
"github.com/hajimehoshi/go-ebiten/graphics" "github.com/hajimehoshi/go-ebiten/graphics"
@ -16,9 +17,10 @@ type GraphicsContext struct {
screenWidth int screenWidth int
screenHeight int screenHeight int
screenScale int screenScale int
mainFramebuffer C.GLuint textures map[graphics.TextureID]*Texture
projectionMatrix [16]float32 projectionMatrix [16]float32
currentShaderProgram C.GLuint currentShaderProgram C.GLuint
mainFramebuffer C.GLuint
framebuffers map[C.GLuint]C.GLuint framebuffers map[C.GLuint]C.GLuint
} }
@ -28,6 +30,7 @@ func newGraphicsContext(screenWidth, screenHeight, screenScale int) *GraphicsCon
screenWidth: screenWidth, screenWidth: screenWidth,
screenHeight: screenHeight, screenHeight: screenHeight,
screenScale: screenScale, screenScale: screenScale,
textures: map[graphics.TextureID]*Texture{},
mainFramebuffer: 0, mainFramebuffer: 0,
framebuffers: map[C.GLuint]C.GLuint{}, framebuffers: map[C.GLuint]C.GLuint{},
} }
@ -41,23 +44,6 @@ func newGraphicsContext(screenWidth, screenHeight, screenScale int) *GraphicsCon
return context return context
} }
var glTextureCache = map[*graphics.Texture]*Texture{}
func glTexture(tex *graphics.Texture) *Texture {
if glTex, ok := glTextureCache[tex]; ok {
return glTex
}
var glTex *Texture = nil
if tex.Image != nil {
glTex = newTextureFromImage(tex.Image)
} else {
glTex = newTexture(tex.Width, tex.Height)
}
glTextureCache[tex] = glTex
return glTex
}
func (context *GraphicsContext) Clear() { func (context *GraphicsContext) Clear() {
C.glClearColor(0, 0, 0, 1) C.glClearColor(0, 0, 0, 1)
C.glClear(C.GL_COLOR_BUFFER_BIT) C.glClear(C.GL_COLOR_BUFFER_BIT)
@ -78,13 +64,14 @@ func (context *GraphicsContext) DrawRect(x, y, width, height int, clr color.Colo
// TODO: implement! // TODO: implement!
} }
func (context *GraphicsContext) DrawTexture(tex *graphics.Texture, func (context *GraphicsContext) DrawTexture(
textureID graphics.TextureID,
srcX, srcY, srcWidth, srcHeight int, srcX, srcY, srcWidth, srcHeight int,
geometryMatrix *graphics.GeometryMatrix, colorMatrix *graphics.ColorMatrix) { geometryMatrix *graphics.GeometryMatrix, colorMatrix *graphics.ColorMatrix) {
geometryMatrix = geometryMatrix.Clone() geometryMatrix = geometryMatrix.Clone()
colorMatrix = colorMatrix.Clone() colorMatrix = colorMatrix.Clone()
texture := glTexture(tex) texture := context.textures[textureID]
context.setShaderProgram(geometryMatrix, colorMatrix) context.setShaderProgram(geometryMatrix, colorMatrix)
C.glBindTexture(C.GL_TEXTURE_2D, texture.id) C.glBindTexture(C.GL_TEXTURE_2D, texture.id)
@ -136,24 +123,23 @@ func abs(x int) int {
return x return x
} }
func (context *GraphicsContext) SetOffscreen(tex *graphics.Texture) { func (context *GraphicsContext) SetOffscreen(textureID graphics.TextureID) {
texture := context.textures[textureID]
framebuffer := context.getFramebuffer(texture.id)
if framebuffer == context.mainFramebuffer {
panic("invalid framebuffer")
}
context.setOffscreenFramebuffer(framebuffer,
texture.textureWidth, texture.textureHeight)
}
func (context *GraphicsContext) setOffscreenFramebuffer(framebuffer C.GLuint,
textureWidth, textureHeight int) {
C.glFlush() C.glFlush()
var texture *Texture = nil
if tex != nil {
texture = glTexture(tex)
}
framebuffer := C.GLuint(0)
if texture != nil {
framebuffer = context.getFramebuffer(texture)
if framebuffer == context.mainFramebuffer {
panic("invalid framebuffer")
}
} else {
framebuffer = context.mainFramebuffer
}
C.glBindFramebuffer(C.GL_FRAMEBUFFER, framebuffer) C.glBindFramebuffer(C.GL_FRAMEBUFFER, framebuffer)
if err := C.glCheckFramebufferStatus(C.GL_FRAMEBUFFER); err != C.GL_FRAMEBUFFER_COMPLETE { if err := C.glCheckFramebufferStatus(C.GL_FRAMEBUFFER);
err != C.GL_FRAMEBUFFER_COMPLETE {
panic(fmt.Sprintf("glBindFramebuffer failed: %d", err)) panic(fmt.Sprintf("glBindFramebuffer failed: %d", err))
} }
C.glEnable(C.GL_BLEND) C.glEnable(C.GL_BLEND)
@ -161,8 +147,8 @@ func (context *GraphicsContext) SetOffscreen(tex *graphics.Texture) {
width, height, tx, ty := 0, 0, 0, 0 width, height, tx, ty := 0, 0, 0, 0
if framebuffer != context.mainFramebuffer { if framebuffer != context.mainFramebuffer {
width = texture.textureWidth width = textureWidth
height = texture.textureHeight height = textureHeight
tx = -1 tx = -1
ty = -1 ty = -1
} else { } else {
@ -185,7 +171,7 @@ func (context *GraphicsContext) SetOffscreen(tex *graphics.Texture) {
} }
func (context *GraphicsContext) resetOffscreen() { func (context *GraphicsContext) resetOffscreen() {
context.SetOffscreen(nil) context.setOffscreenFramebuffer(context.mainFramebuffer, 0, 0)
} }
// This method should be called on the UI thread. // This method should be called on the UI thread.
@ -255,9 +241,8 @@ func (context *GraphicsContext) setShaderProgram(
1, (*C.GLfloat)(&glColorMatrixTranslation[0])) 1, (*C.GLfloat)(&glColorMatrixTranslation[0]))
} }
// This method should be called on the UI thread. func (context *GraphicsContext) getFramebuffer(textureID C.GLuint) C.GLuint{
func (context *GraphicsContext) getFramebuffer(texture *Texture) C.GLuint{ framebuffer, ok := context.framebuffers[textureID]
framebuffer, ok := context.framebuffers[texture.id]
if ok { if ok {
return framebuffer return framebuffer
} }
@ -269,23 +254,44 @@ func (context *GraphicsContext) getFramebuffer(texture *Texture) C.GLuint{
C.glGetIntegerv(C.GL_FRAMEBUFFER_BINDING, &origFramebuffer) C.glGetIntegerv(C.GL_FRAMEBUFFER_BINDING, &origFramebuffer)
C.glBindFramebuffer(C.GL_FRAMEBUFFER, newFramebuffer) C.glBindFramebuffer(C.GL_FRAMEBUFFER, newFramebuffer)
C.glFramebufferTexture2D(C.GL_FRAMEBUFFER, C.GL_COLOR_ATTACHMENT0, C.glFramebufferTexture2D(C.GL_FRAMEBUFFER, C.GL_COLOR_ATTACHMENT0,
C.GL_TEXTURE_2D, texture.id, 0) C.GL_TEXTURE_2D, textureID, 0)
C.glBindFramebuffer(C.GL_FRAMEBUFFER, C.GLuint(origFramebuffer)) C.glBindFramebuffer(C.GL_FRAMEBUFFER, C.GLuint(origFramebuffer))
if C.glCheckFramebufferStatus(C.GL_FRAMEBUFFER) != C.GL_FRAMEBUFFER_COMPLETE { if C.glCheckFramebufferStatus(C.GL_FRAMEBUFFER) != C.GL_FRAMEBUFFER_COMPLETE {
panic("creating framebuffer failed") panic("creating framebuffer failed")
} }
context.framebuffers[texture.id] = newFramebuffer context.framebuffers[textureID] = newFramebuffer
return newFramebuffer return newFramebuffer
} }
// This method should be called on the UI thread. func (context *GraphicsContext) deleteFramebuffer(textureID C.GLuint) {
func (context *GraphicsContext) deleteFramebuffer(texture *Texture) { framebuffer, ok := context.framebuffers[textureID]
framebuffer, ok := context.framebuffers[texture.id]
if !ok { if !ok {
// TODO: panic? // TODO: panic?
return return
} }
C.glDeleteFramebuffers(1, &framebuffer) C.glDeleteFramebuffers(1, &framebuffer)
delete(context.framebuffers, texture.id) delete(context.framebuffers, textureID)
}
func (context *GraphicsContext) NewTexture(width, height int) graphics.Texture {
texture := newTexture(width, height)
id := graphics.TextureID(texture.id)
context.textures[id] = texture
return graphics.Texture{
ID: id,
Width: texture.width,
Height: texture.height,
}
}
func (context *GraphicsContext) NewTextureFromImage(img image.Image) graphics.Texture {
texture := newTextureFromImage(img)
id := graphics.TextureID(texture.id)
context.textures[id] = texture
return graphics.Texture{
ID: id,
Width: texture.width,
Height: texture.height,
}
} }