Refactoring

This commit is contained in:
Hajime Hoshi 2013-11-17 22:42:12 +09:00
parent 00fd9fc8ba
commit f72076641a
6 changed files with 117 additions and 307 deletions

View File

@ -11,7 +11,7 @@ import (
"github.com/hajimehoshi/go-ebiten/example/game/testpattern" "github.com/hajimehoshi/go-ebiten/example/game/testpattern"
"github.com/hajimehoshi/go-ebiten/ui" "github.com/hajimehoshi/go-ebiten/ui"
"github.com/hajimehoshi/go-ebiten/ui/cocoa" "github.com/hajimehoshi/go-ebiten/ui/cocoa"
"github.com/hajimehoshi/go-ebiten/ui/glut" // "github.com/hajimehoshi/go-ebiten/ui/glut"
"os" "os"
"runtime" "runtime"
) )
@ -58,8 +58,6 @@ func main() {
fallthrough fallthrough
case "cocoa": case "cocoa":
u = cocoa.New(screenWidth, screenHeight, screenScale, title) u = cocoa.New(screenWidth, screenHeight, screenScale, title)
case "glut":
u = glut.New(screenWidth, screenHeight, screenScale, title)
} }
ui.Run(u, game) u.Run(game)
} }

View File

@ -111,10 +111,9 @@ func (context *Context) SetOffscreen(renderTargetId graphics.RenderTargetId) {
context.setOffscreen(renderTarget) context.setOffscreen(renderTarget)
} }
func (context *Context) setOffscreen(rt *grendertarget.RenderTarget) { func (context *Context) doSetOffscreen(
C.glFlush() usingMainFramebuffer bool,
framebuffer interface{}, x, y, width, height int) {
rt.SetAsOffscreen(func(framebuffer interface{}, x, y, width, height int) {
f := framebuffer.(rendertarget.Framebuffer) f := framebuffer.(rendertarget.Framebuffer)
C.glBindFramebuffer(C.GL_FRAMEBUFFER, C.GLuint(f)) C.glBindFramebuffer(C.GL_FRAMEBUFFER, C.GLuint(f))
err := C.glCheckFramebufferStatus(C.GL_FRAMEBUFFER) err := C.glCheckFramebufferStatus(C.GL_FRAMEBUFFER)
@ -125,10 +124,11 @@ func (context *Context) setOffscreen(rt *grendertarget.RenderTarget) {
C.glBlendFuncSeparate(C.GL_SRC_ALPHA, C.GL_ONE_MINUS_SRC_ALPHA, C.glBlendFuncSeparate(C.GL_SRC_ALPHA, C.GL_ONE_MINUS_SRC_ALPHA,
C.GL_ZERO, C.GL_ONE) C.GL_ZERO, C.GL_ONE)
C.glViewport(C.GLint(x), C.GLint(y), C.GLsizei(width), C.GLsizei(height)) C.glViewport(C.GLint(x), C.GLint(y),
C.GLsizei(width), C.GLsizei(height))
matrix := graphics.OrthoProjectionMatrix(x, width, y, height) matrix := graphics.OrthoProjectionMatrix(x, width, y, height)
if rt == context.mainFramebufferTexture { if usingMainFramebuffer {
actualScreenHeight := context.screenHeight * context.screenScale actualScreenHeight := context.screenHeight * context.screenScale
// Flip Y and move to fit with the top of the window. // Flip Y and move to fit with the top of the window.
matrix[1][1] *= -1 matrix[1][1] *= -1
@ -140,6 +140,16 @@ func (context *Context) setOffscreen(rt *grendertarget.RenderTarget) {
context.projectionMatrix[i+j*4] = float32(matrix[i][j]) context.projectionMatrix[i+j*4] = float32(matrix[i][j])
} }
} }
}
func (context *Context) setOffscreen(rt *grendertarget.RenderTarget) {
C.glFlush()
usingMainFramebuffer := rt == context.mainFramebufferTexture
rt.SetAsOffscreen(func(framebuffer interface{},
x, y, width, height int) {
context.doSetOffscreen(usingMainFramebuffer, framebuffer,
x, y, width, height)
}) })
} }

View File

@ -6,7 +6,7 @@ package rendertarget
import "C" import "C"
import ( import (
"github.com/hajimehoshi/go-ebiten/graphics/opengl/texture" "github.com/hajimehoshi/go-ebiten/graphics/opengl/texture"
"github.com/hajimehoshi/go-ebiten/graphics/rendertarget" grendertarget "github.com/hajimehoshi/go-ebiten/graphics/rendertarget"
gtexture "github.com/hajimehoshi/go-ebiten/graphics/texture" gtexture "github.com/hajimehoshi/go-ebiten/graphics/texture"
) )
@ -38,7 +38,7 @@ func createFramebuffer(nativeTexture C.GLuint) C.GLuint {
} }
func New(width, height int, filter texture.Filter) ( func New(width, height int, filter texture.Filter) (
*rendertarget.RenderTarget, *gtexture.Texture, error) { *grendertarget.RenderTarget, *gtexture.Texture, error) {
tex, err := texture.New(width, height, filter) tex, err := texture.New(width, height, filter)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
@ -47,15 +47,15 @@ func New(width, height int, filter texture.Filter) (
return createFramebuffer(C.GLuint(native.(texture.Native))) return createFramebuffer(C.GLuint(native.(texture.Native)))
} }
framebuffer := tex.CreateFramebuffer(f) framebuffer := tex.CreateFramebuffer(f)
return rendertarget.NewWithFramebuffer(tex, return grendertarget.NewWithFramebuffer(tex,
Framebuffer(framebuffer.(C.GLuint))), tex, nil Framebuffer(framebuffer.(C.GLuint))), tex, nil
} }
func NewWithFramebuffer(width, height int, framebuffer Framebuffer) ( func NewWithFramebuffer(width, height int, framebuffer Framebuffer) (
*rendertarget.RenderTarget, error) { *grendertarget.RenderTarget, error) {
tex, err := texture.NewEmpty(width, height) tex, err := texture.NewEmpty(width, height)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return rendertarget.NewWithFramebuffer(tex, framebuffer), nil return grendertarget.NewWithFramebuffer(tex, framebuffer), nil
} }

View File

@ -12,20 +12,40 @@ import "C"
import ( import (
"github.com/hajimehoshi/go-ebiten" "github.com/hajimehoshi/go-ebiten"
"github.com/hajimehoshi/go-ebiten/graphics/opengl" "github.com/hajimehoshi/go-ebiten/graphics/opengl"
"sync"
"time"
"unsafe" "unsafe"
) )
type GameContext struct {
screenWidth int
screenHeight int
inputState ebiten.InputState
}
func (context *GameContext) ScreenWidth() int {
return context.screenWidth
}
func (context *GameContext) ScreenHeight() int {
return context.screenHeight
}
func (context *GameContext) InputState() ebiten.InputState {
return context.inputState
}
type UI struct { type UI struct {
screenWidth int screenWidth int
screenHeight int screenHeight int
screenScale int screenScale int
title string title string
initializing chan ebiten.Game
initialized chan ebiten.Game
updating chan ebiten.Game updating chan ebiten.Game
updated chan ebiten.Game updated chan ebiten.Game
input chan ebiten.InputState input chan ebiten.InputState
graphicsDevice *opengl.Device graphicsDevice *opengl.Device
funcsExecutedOnMainThread []func() // TODO: map?
lock sync.Mutex
} }
var currentUI *UI var currentUI *UI
@ -39,17 +59,38 @@ func New(screenWidth, screenHeight, screenScale int, title string) *UI {
screenHeight: screenHeight, screenHeight: screenHeight,
screenScale: screenScale, screenScale: screenScale,
title: title, title: title,
initializing: make(chan ebiten.Game),
initialized: make(chan ebiten.Game),
updating: make(chan ebiten.Game), updating: make(chan ebiten.Game),
updated: make(chan ebiten.Game), updated: make(chan ebiten.Game),
input: make(chan ebiten.InputState), input: make(chan ebiten.InputState),
funcsExecutedOnMainThread: []func(){},
} }
currentUI = ui currentUI = ui
return ui return ui
} }
func (ui *UI) MainLoop() { func (ui *UI) gameMainLoop(game ebiten.Game) {
frameTime := time.Duration(int64(time.Second) / int64(ebiten.FPS))
tick := time.Tick(frameTime)
gameContext := &GameContext{
screenWidth: ui.screenWidth,
screenHeight: ui.screenHeight,
inputState: ebiten.InputState{-1, -1},
}
ui.InitializeGame(game)
for {
select {
case gameContext.inputState = <-ui.input:
case <-tick:
game.Update(gameContext)
case ui.updating <- game:
game = <-ui.updated
}
}
}
func (ui *UI) Run(game ebiten.Game) {
go ui.gameMainLoop(game)
cTitle := C.CString(ui.title) cTitle := C.CString(ui.title)
defer C.free(unsafe.Pointer(cTitle)) defer C.free(unsafe.Pointer(cTitle))
@ -59,32 +100,20 @@ func (ui *UI) MainLoop() {
cTitle) cTitle)
} }
func (ui *UI) ScreenWidth() int { func (ui *UI) InitializeGame(game ebiten.Game) {
return ui.screenWidth ui.lock.Lock()
defer ui.lock.Unlock()
ui.funcsExecutedOnMainThread = append(ui.funcsExecutedOnMainThread, func() {
game.InitTextures(ui.graphicsDevice.TextureFactory())
})
} }
func (ui *UI) ScreenHeight() int { func (ui *UI) DrawGame(game ebiten.Game) {
return ui.screenHeight ui.lock.Lock()
} defer ui.lock.Unlock()
ui.funcsExecutedOnMainThread = append(ui.funcsExecutedOnMainThread, func() {
func (ui *UI) Initializing() chan<- ebiten.Game { ui.graphicsDevice.Update(game.Draw)
return ui.initializing })
}
func (ui *UI) Initialized() <-chan ebiten.Game {
return ui.initialized
}
func (ui *UI) Updating() chan<- ebiten.Game {
return ui.updating
}
func (ui *UI) Updated() <-chan ebiten.Game {
return ui.updated
}
func (ui *UI) Input() <-chan ebiten.InputState {
return ui.input
} }
//export ebiten_EbitenOpenGLView_Initialized //export ebiten_EbitenOpenGLView_Initialized
@ -98,14 +127,17 @@ func ebiten_EbitenOpenGLView_Initialized() {
currentUI.screenHeight, currentUI.screenHeight,
currentUI.screenScale) currentUI.screenScale)
currentUI.graphicsDevice.Init() currentUI.graphicsDevice.Init()
game := <-currentUI.initializing
game.InitTextures(currentUI.graphicsDevice.TextureFactory())
currentUI.initialized <- game
} }
//export ebiten_EbitenOpenGLView_Updating //export ebiten_EbitenOpenGLView_Updating
func ebiten_EbitenOpenGLView_Updating() { func ebiten_EbitenOpenGLView_Updating() {
currentUI.lock.Lock()
defer currentUI.lock.Unlock()
for _, f := range currentUI.funcsExecutedOnMainThread {
f()
}
currentUI.funcsExecutedOnMainThread = currentUI.funcsExecutedOnMainThread[0:0]
game := <-currentUI.updating game := <-currentUI.updating
currentUI.graphicsDevice.Update(game.Draw) currentUI.graphicsDevice.Update(game.Draw)
currentUI.updated <- game currentUI.updated <- game

View File

@ -1,177 +0,0 @@
// This package is experimental.
package glut
// #cgo CFLAGS: -Wno-deprecated-declarations
// #cgo LDFLAGS: -framework GLUT -framework OpenGL
//
// #include <stdlib.h>
// #include <GLUT/glut.h>
//
// void display(void);
// void mouse(int button, int state, int x, int y);
// void motion(int x, int y);
// void idle(void);
//
// static void setGlutFuncs(void) {
// glutDisplayFunc(display);
// glutMouseFunc(mouse);
// glutMotionFunc(motion);
// glutIdleFunc(idle);
// }
//
import "C"
import (
"github.com/hajimehoshi/go-ebiten"
"github.com/hajimehoshi/go-ebiten/graphics/opengl"
"os"
"unsafe"
)
type glutInputEvent struct {
IsActive bool
X int
Y int
}
type UI struct {
screenWidth int
screenHeight int
screenScale int
title string
initializing chan ebiten.Game
initialized chan ebiten.Game
updating chan ebiten.Game
updated chan ebiten.Game
input chan ebiten.InputState
graphicsDevice *opengl.Device
glutInputting chan glutInputEvent
}
var currentUI *UI
func New(screenWidth, screenHeight, screenScale int, title string) *UI {
if currentUI != nil {
panic("UI can't be duplicated.")
}
ui := &UI{
screenWidth: screenWidth,
screenHeight: screenHeight,
screenScale: screenScale,
title: title,
initializing: make(chan ebiten.Game),
initialized: make(chan ebiten.Game),
updating: make(chan ebiten.Game),
updated: make(chan ebiten.Game),
input: make(chan ebiten.InputState),
glutInputting: make(chan glutInputEvent),
}
currentUI = ui
return ui
}
func (ui *UI) MainLoop() {
cargs := []*C.char{C.CString(os.Args[0])}
defer func() {
for _, carg := range cargs {
C.free(unsafe.Pointer(carg))
}
}()
cargc := C.int(len(cargs))
// Initialize OpenGL
C.glutInit(&cargc, &cargs[0])
C.glutInitDisplayMode(C.GLUT_RGBA)
C.glutInitWindowSize(
C.int(ui.screenWidth*ui.screenScale),
C.int(ui.screenHeight*ui.screenScale))
cTitle := C.CString(ui.title)
defer C.free(unsafe.Pointer(cTitle))
C.glutCreateWindow(cTitle)
ui.graphicsDevice = opengl.NewDevice(
ui.screenWidth, ui.screenHeight, ui.screenScale)
ui.graphicsDevice.Init()
game := <-ui.initializing
game.InitTextures(ui.graphicsDevice.TextureFactory())
ui.initialized <- game
// Set the callbacks
C.setGlutFuncs()
C.glutMainLoop()
}
func (ui *UI) ScreenWidth() int {
return ui.screenWidth
}
func (ui *UI) ScreenHeight() int {
return ui.screenHeight
}
func (ui *UI) Initializing() chan<- ebiten.Game {
return ui.initializing
}
func (ui *UI) Initialized() <-chan ebiten.Game {
return ui.initialized
}
func (ui *UI) Updating() chan<- ebiten.Game {
return ui.updating
}
func (ui *UI) Updated() <-chan ebiten.Game {
return ui.updated
}
func (ui *UI) Input() <-chan ebiten.InputState {
return ui.input
}
func (ui *UI) normalizePoint(x, y int) (newX, newY int) {
x /= ui.screenScale
y /= ui.screenScale
if x < 0 {
x = 0
} else if ui.screenWidth <= x {
x = ui.screenWidth - 1
}
if y < 0 {
y = 0
} else if ui.screenHeight <= y {
y = ui.screenHeight - 1
}
return x, y
}
//export display
func display() {
game := <-currentUI.updating
currentUI.graphicsDevice.Update(game.Draw)
currentUI.updated <- game
C.glutSwapBuffers()
}
//export mouse
func mouse(button, state, x, y C.int) {
if state != C.GLUT_DOWN {
currentUI.input <- ebiten.InputState{-1, -1}
return
}
newX, newY := currentUI.normalizePoint(int(x), int(y))
currentUI.input <- ebiten.InputState{newX, newY}
}
//export motion
func motion(x, y C.int) {
newX, newY := currentUI.normalizePoint(int(x), int(y))
currentUI.input <- ebiten.InputState{newX, newY}
}
//export idle
func idle() {
C.glutPostRedisplay()
}

View File

@ -2,61 +2,8 @@ package ui
import ( import (
"github.com/hajimehoshi/go-ebiten" "github.com/hajimehoshi/go-ebiten"
"time"
) )
type UI interface { type UI interface {
MainLoop() Run(game ebiten.Game)
ScreenWidth() int
ScreenHeight() int
Initializing() chan<- ebiten.Game
Initialized() <-chan ebiten.Game
Updating() chan<- ebiten.Game
Updated() <-chan ebiten.Game
Input() <-chan ebiten.InputState
}
func mainLoop(ui UI, game ebiten.Game) {
ui.Initializing() <- game
game = <-ui.Initialized()
frameTime := time.Duration(int64(time.Second) / int64(ebiten.FPS))
tick := time.Tick(frameTime)
gameContext := &GameContext{
screenWidth: ui.ScreenWidth(),
screenHeight: ui.ScreenHeight(),
inputState: ebiten.InputState{-1, -1},
}
for {
select {
case gameContext.inputState = <-ui.Input():
case <-tick:
game.Update(gameContext)
case ui.Updating() <- game:
game = <-ui.Updated()
}
}
}
func Run(ui UI, game ebiten.Game) {
go mainLoop(ui, game)
ui.MainLoop()
}
type GameContext struct {
screenWidth int
screenHeight int
inputState ebiten.InputState
}
func (context *GameContext) ScreenWidth() int {
return context.screenWidth
}
func (context *GameContext) ScreenHeight() int {
return context.screenHeight
}
func (context *GameContext) InputState() ebiten.InputState {
return context.inputState
} }