From f72076641a43370ed189e1be67e5456a899c75e9 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 17 Nov 2013 22:42:12 +0900 Subject: [PATCH] Refactoring --- example/main.go | 6 +- graphics/opengl/context.go | 62 +++--- graphics/opengl/rendertarget/render_target.go | 10 +- ui/cocoa/cocoa.go | 114 +++++++---- ui/glut/glut.go | 177 ------------------ ui/ui.go | 55 +----- 6 files changed, 117 insertions(+), 307 deletions(-) delete mode 100644 ui/glut/glut.go diff --git a/example/main.go b/example/main.go index 4e9d7fb59..0d85a49ec 100644 --- a/example/main.go +++ b/example/main.go @@ -11,7 +11,7 @@ import ( "github.com/hajimehoshi/go-ebiten/example/game/testpattern" "github.com/hajimehoshi/go-ebiten/ui" "github.com/hajimehoshi/go-ebiten/ui/cocoa" - "github.com/hajimehoshi/go-ebiten/ui/glut" + // "github.com/hajimehoshi/go-ebiten/ui/glut" "os" "runtime" ) @@ -58,8 +58,6 @@ func main() { fallthrough case "cocoa": u = cocoa.New(screenWidth, screenHeight, screenScale, title) - case "glut": - u = glut.New(screenWidth, screenHeight, screenScale, title) } - ui.Run(u, game) + u.Run(game) } diff --git a/graphics/opengl/context.go b/graphics/opengl/context.go index ed69d7f58..7f3c1c513 100644 --- a/graphics/opengl/context.go +++ b/graphics/opengl/context.go @@ -111,35 +111,45 @@ func (context *Context) SetOffscreen(renderTargetId graphics.RenderTargetId) { context.setOffscreen(renderTarget) } +func (context *Context) doSetOffscreen( + usingMainFramebuffer bool, + framebuffer interface{}, x, y, width, height int) { + f := framebuffer.(rendertarget.Framebuffer) + C.glBindFramebuffer(C.GL_FRAMEBUFFER, C.GLuint(f)) + err := C.glCheckFramebufferStatus(C.GL_FRAMEBUFFER) + if err != C.GL_FRAMEBUFFER_COMPLETE { + panic(fmt.Sprintf("glBindFramebuffer failed: %d", err)) + } + + C.glBlendFuncSeparate(C.GL_SRC_ALPHA, C.GL_ONE_MINUS_SRC_ALPHA, + C.GL_ZERO, C.GL_ONE) + + C.glViewport(C.GLint(x), C.GLint(y), + C.GLsizei(width), C.GLsizei(height)) + + matrix := graphics.OrthoProjectionMatrix(x, width, y, height) + if usingMainFramebuffer { + actualScreenHeight := context.screenHeight * context.screenScale + // Flip Y and move to fit with the top of the window. + matrix[1][1] *= -1 + matrix[1][3] += float64(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]) + } + } +} + func (context *Context) setOffscreen(rt *grendertarget.RenderTarget) { C.glFlush() - rt.SetAsOffscreen(func(framebuffer interface{}, x, y, width, height int) { - f := framebuffer.(rendertarget.Framebuffer) - C.glBindFramebuffer(C.GL_FRAMEBUFFER, C.GLuint(f)) - err := C.glCheckFramebufferStatus(C.GL_FRAMEBUFFER) - if err != C.GL_FRAMEBUFFER_COMPLETE { - panic(fmt.Sprintf("glBindFramebuffer failed: %d", err)) - } - - C.glBlendFuncSeparate(C.GL_SRC_ALPHA, C.GL_ONE_MINUS_SRC_ALPHA, - C.GL_ZERO, C.GL_ONE) - - C.glViewport(C.GLint(x), C.GLint(y), C.GLsizei(width), C.GLsizei(height)) - - matrix := graphics.OrthoProjectionMatrix(x, width, y, height) - if rt == context.mainFramebufferTexture { - actualScreenHeight := context.screenHeight * context.screenScale - // Flip Y and move to fit with the top of the window. - matrix[1][1] *= -1 - matrix[1][3] += float64(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]) - } - } + usingMainFramebuffer := rt == context.mainFramebufferTexture + rt.SetAsOffscreen(func(framebuffer interface{}, + x, y, width, height int) { + context.doSetOffscreen(usingMainFramebuffer, framebuffer, + x, y, width, height) }) } diff --git a/graphics/opengl/rendertarget/render_target.go b/graphics/opengl/rendertarget/render_target.go index fc5869930..0d6fea027 100644 --- a/graphics/opengl/rendertarget/render_target.go +++ b/graphics/opengl/rendertarget/render_target.go @@ -6,7 +6,7 @@ package rendertarget import "C" import ( "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" ) @@ -38,7 +38,7 @@ func createFramebuffer(nativeTexture C.GLuint) C.GLuint { } 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) if err != nil { return nil, nil, err @@ -47,15 +47,15 @@ func New(width, height int, filter texture.Filter) ( return createFramebuffer(C.GLuint(native.(texture.Native))) } framebuffer := tex.CreateFramebuffer(f) - return rendertarget.NewWithFramebuffer(tex, + return grendertarget.NewWithFramebuffer(tex, Framebuffer(framebuffer.(C.GLuint))), tex, nil } func NewWithFramebuffer(width, height int, framebuffer Framebuffer) ( - *rendertarget.RenderTarget, error) { + *grendertarget.RenderTarget, error) { tex, err := texture.NewEmpty(width, height) if err != nil { return nil, err } - return rendertarget.NewWithFramebuffer(tex, framebuffer), nil + return grendertarget.NewWithFramebuffer(tex, framebuffer), nil } diff --git a/ui/cocoa/cocoa.go b/ui/cocoa/cocoa.go index 17351f2e1..7dc8a79f4 100644 --- a/ui/cocoa/cocoa.go +++ b/ui/cocoa/cocoa.go @@ -12,20 +12,40 @@ import "C" import ( "github.com/hajimehoshi/go-ebiten" "github.com/hajimehoshi/go-ebiten/graphics/opengl" + "sync" + "time" "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 { - 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 + screenWidth int + screenHeight int + screenScale int + title string + updating chan ebiten.Game + updated chan ebiten.Game + input chan ebiten.InputState + graphicsDevice *opengl.Device + funcsExecutedOnMainThread []func() // TODO: map? + lock sync.Mutex } var currentUI *UI @@ -39,17 +59,38 @@ func New(screenWidth, screenHeight, screenScale int, title string) *UI { 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), + funcsExecutedOnMainThread: []func(){}, } currentUI = 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) defer C.free(unsafe.Pointer(cTitle)) @@ -59,32 +100,20 @@ func (ui *UI) MainLoop() { cTitle) } -func (ui *UI) ScreenWidth() int { - return ui.screenWidth +func (ui *UI) InitializeGame(game ebiten.Game) { + ui.lock.Lock() + defer ui.lock.Unlock() + ui.funcsExecutedOnMainThread = append(ui.funcsExecutedOnMainThread, func() { + game.InitTextures(ui.graphicsDevice.TextureFactory()) + }) } -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) DrawGame(game ebiten.Game) { + ui.lock.Lock() + defer ui.lock.Unlock() + ui.funcsExecutedOnMainThread = append(ui.funcsExecutedOnMainThread, func() { + ui.graphicsDevice.Update(game.Draw) + }) } //export ebiten_EbitenOpenGLView_Initialized @@ -98,14 +127,17 @@ func ebiten_EbitenOpenGLView_Initialized() { currentUI.screenHeight, currentUI.screenScale) currentUI.graphicsDevice.Init() - - game := <-currentUI.initializing - game.InitTextures(currentUI.graphicsDevice.TextureFactory()) - currentUI.initialized <- game } //export 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 currentUI.graphicsDevice.Update(game.Draw) currentUI.updated <- game diff --git a/ui/glut/glut.go b/ui/glut/glut.go deleted file mode 100644 index 74ac5dc6d..000000000 --- a/ui/glut/glut.go +++ /dev/null @@ -1,177 +0,0 @@ -// This package is experimental. -package glut - -// #cgo CFLAGS: -Wno-deprecated-declarations -// #cgo LDFLAGS: -framework GLUT -framework OpenGL -// -// #include -// #include -// -// 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() -} diff --git a/ui/ui.go b/ui/ui.go index fecc166e6..161537aed 100644 --- a/ui/ui.go +++ b/ui/ui.go @@ -2,61 +2,8 @@ package ui import ( "github.com/hajimehoshi/go-ebiten" - "time" ) type UI interface { - MainLoop() - 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 + Run(game ebiten.Game) }