diff --git a/canvas.go b/canvas.go index a893de2c4..239d20790 100644 --- a/canvas.go +++ b/canvas.go @@ -17,7 +17,6 @@ limitations under the License. package ebiten import ( - "github.com/go-gl/gl" glfw "github.com/go-gl/glfw3" "image" "runtime" @@ -32,6 +31,29 @@ type canvas struct { funcsDone chan struct{} } +func newCanvas(window *glfw.Window, width, height, scale int) (*canvas, error) { + c := &canvas{ + window: window, + scale: scale, + funcs: make(chan func()), + funcsDone: make(chan struct{}), + } + + c.run(width, height, scale) + + // For retina displays, recalculate the scale with the framebuffer size. + windowWidth, _ := window.GetFramebufferSize() + realScale := windowWidth / width + var err error + c.use(func() { + c.graphicsContext, err = newGraphicsContext(width, height, realScale) + }) + if err != nil { + return nil, err + } + return c, nil +} + func (c *canvas) draw(game Game) (err error) { c.use(func() { c.graphicsContext.PreUpdate() @@ -50,38 +72,20 @@ func (c *canvas) isClosed() bool { return c.window.ShouldClose() } -func (c *canvas) NewTextureID(img image.Image, filter Filter) (TextureID, error) { +func (c *canvas) newTextureID(img image.Image, filter int) (TextureID, error) { var id TextureID var err error c.use(func() { - glFilter := 0 - switch filter { - case FilterNearest: - glFilter = gl.NEAREST - case FilterLinear: - glFilter = gl.LINEAR - default: - panic("not reached") - } - id, err = newTextureID(img, glFilter) + id, err = newTextureID(img, filter) }) return id, err } -func (c *canvas) NewRenderTargetID(width, height int, filter Filter) (RenderTargetID, error) { +func (c *canvas) newRenderTargetID(width, height int, filter int) (RenderTargetID, error) { var id RenderTargetID var err error c.use(func() { - glFilter := 0 - switch filter { - case FilterNearest: - glFilter = gl.NEAREST - case FilterLinear: - glFilter = gl.LINEAR - default: - panic("not reached") - } - id, err = newRenderTargetID(width, height, glFilter) + id, err = newRenderTargetID(width, height, filter) }) return id, err } @@ -104,17 +108,5 @@ func (c *canvas) use(f func()) { } func (c *canvas) update() { - c.input.Update(c.window, c.scale) -} - -func (c *canvas) IsKeyPressed(key Key) bool { - return c.input.IsKeyPressed(key) -} - -func (c *canvas) IsMouseButtonPressed(button MouseButton) bool { - return c.input.IsMouseButtonPressed(button) -} - -func (c *canvas) CursorPosition() (x, y int) { - return c.input.CursorPosition() + c.input.update(c.window, c.scale) } diff --git a/gamecontext.go b/gamecontext.go index 3667bf72f..a16f0d19c 100644 --- a/gamecontext.go +++ b/gamecontext.go @@ -17,30 +17,37 @@ limitations under the License. package ebiten import ( + "github.com/go-gl/gl" "image" ) -type Game interface { - Update() error - Draw(gr GraphicsContext) error -} - func IsKeyPressed(key Key) bool { - return currentUI.canvas.input.IsKeyPressed(key) + return currentUI.canvas.input.isKeyPressed(key) } func CursorPosition() (x, y int) { - return currentUI.canvas.input.CursorPosition() + return currentUI.canvas.input.cursorPosition() } func IsMouseButtonPressed(mouseButton MouseButton) bool { - return currentUI.canvas.input.IsMouseButtonPressed(mouseButton) + return currentUI.canvas.input.isMouseButtonPressed(mouseButton) +} + +func glFilter(f Filter) int { + switch f { + case FilterNearest: + return gl.NEAREST + case FilterLinear: + return gl.LINEAR + default: + panic("not reached") + } } func NewRenderTargetID(width, height int, filter Filter) (RenderTargetID, error) { - return currentUI.canvas.NewRenderTargetID(width, height, filter) + return currentUI.canvas.newRenderTargetID(width, height, glFilter(filter)) } func NewTextureID(img image.Image, filter Filter) (TextureID, error) { - return currentUI.canvas.NewTextureID(img, filter) + return currentUI.canvas.newTextureID(img, glFilter(filter)) } diff --git a/graphicscontext.go b/graphicscontext.go index 7145a96cb..5cb77eba9 100644 --- a/graphicscontext.go +++ b/graphicscontext.go @@ -21,7 +21,7 @@ import ( "github.com/hajimehoshi/ebiten/internal/opengl" ) -func initialize(screenWidth, screenHeight, screenScale int) (*graphicsContext, error) { +func newGraphicsContext(screenWidth, screenHeight, screenScale int) (*graphicsContext, error) { gl.Init() gl.Enable(gl.TEXTURE_2D) gl.Enable(gl.BLEND) @@ -33,11 +33,8 @@ func initialize(screenWidth, screenHeight, screenScale int) (*graphicsContext, e } // The defualt framebuffer should be 0. - c.defaultID = idsInstance.addRenderTarget(&opengl.RenderTarget{ - Width: screenWidth * screenScale, - Height: screenHeight * screenScale, - FlipY: true, - }) + r := opengl.NewRenderTarget(screenWidth*screenScale, screenHeight*screenScale, true) + c.defaultID = idsInstance.addRenderTarget(r) var err error c.screenID, err = idsInstance.createRenderTarget(screenWidth, screenHeight, gl.NEAREST) diff --git a/ids.go b/ids.go index 43bc54e25..f830fcf1a 100644 --- a/ids.go +++ b/ids.go @@ -86,14 +86,10 @@ func (i *ids) createRenderTarget(width, height int, filter int) (RenderTargetID, if err != nil { return 0, err } - framebuffer := opengl.CreateFramebuffer(texture.Native()) + // The current binded framebuffer can be changed. i.currentRenderTargetId = -1 - r := &opengl.RenderTarget{ - Framebuffer: framebuffer, - Width: texture.Width(), - Height: texture.Height(), - } + r := opengl.NewRenderTargetFromTexture(texture) i.Lock() defer i.Unlock() diff --git a/input.go b/input.go index b64865489..60bd6bd59 100644 --- a/input.go +++ b/input.go @@ -28,15 +28,15 @@ type input struct { cursorY int } -func (i *input) IsKeyPressed(key Key) bool { +func (i *input) isKeyPressed(key Key) bool { return i.keyPressed[key] } -func (i *input) IsMouseButtonPressed(button MouseButton) bool { +func (i *input) isMouseButtonPressed(button MouseButton) bool { return i.mouseButtonPressed[button] } -func (i *input) CursorPosition() (x, y int) { +func (i *input) cursorPosition() (x, y int) { return i.cursorX, i.cursorY } @@ -48,7 +48,7 @@ var glfwKeyCodeToKey = map[glfw.Key]Key{ glfw.KeyDown: KeyDown, } -func (i *input) Update(window *glfw.Window, scale int) { +func (i *input) update(window *glfw.Window, scale int) { for g, u := range glfwKeyCodeToKey { i.keyPressed[u] = window.GetKey(g) == glfw.Press } diff --git a/internal/opengl/rendertarget.go b/internal/opengl/rendertarget.go index f125b2c1d..af8795e49 100644 --- a/internal/opengl/rendertarget.go +++ b/internal/opengl/rendertarget.go @@ -36,17 +36,46 @@ func orthoProjectionMatrix(left, right, bottom, top int) [4][4]float64 { } type RenderTarget struct { - Framebuffer gl.Framebuffer - Width int - Height int - FlipY bool + framebuffer gl.Framebuffer + width int + height int + flipY bool +} + +func NewRenderTarget(width, height int, flipY bool) *RenderTarget { + return &RenderTarget{ + width: width, + height: height, + flipY: flipY, + } +} + +func NewRenderTargetFromTexture(texture *Texture) *RenderTarget { + framebuffer := createFramebuffer(texture.Native()) + return &RenderTarget{ + framebuffer: framebuffer, + width: texture.Width(), + height: texture.Height(), + } +} + +func (r *RenderTarget) Width() int { + return r.width +} + +func (r *RenderTarget) Height() int { + return r.height +} + +func (r *RenderTarget) FlipY() bool { + return r.flipY } func (r *RenderTarget) Dispose() { - r.Framebuffer.Delete() + r.framebuffer.Delete() } -func CreateFramebuffer(nativeTexture gl.Texture) gl.Framebuffer { +func createFramebuffer(nativeTexture gl.Texture) gl.Framebuffer { framebuffer := gl.GenFramebuffer() framebuffer.Bind() @@ -65,7 +94,7 @@ func CreateFramebuffer(nativeTexture gl.Texture) gl.Framebuffer { func (r *RenderTarget) SetAsViewport() { gl.Flush() - r.Framebuffer.Bind() + r.framebuffer.Bind() err := gl.CheckFramebufferStatus(gl.FRAMEBUFFER) if err != gl.FRAMEBUFFER_COMPLETE { panic(fmt.Sprintf("glBindFramebuffer failed: %d", err)) @@ -73,18 +102,18 @@ func (r *RenderTarget) SetAsViewport() { gl.BlendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ZERO, gl.ONE) - width := AdjustSizeForTexture(r.Width) - height := AdjustSizeForTexture(r.Height) + width := AdjustSizeForTexture(r.width) + height := AdjustSizeForTexture(r.height) gl.Viewport(0, 0, width, height) } func (r *RenderTarget) ProjectionMatrix() [4][4]float64 { - width := AdjustSizeForTexture(r.Width) - height := AdjustSizeForTexture(r.Height) + width := AdjustSizeForTexture(r.width) + height := AdjustSizeForTexture(r.height) m := orthoProjectionMatrix(0, width, 0, height) - if r.FlipY { + if r.flipY { m[1][1] *= -1 - m[1][3] += float64(r.Height) / float64(AdjustSizeForTexture(r.Height)) * 2 + m[1][3] += float64(r.height) / float64(AdjustSizeForTexture(r.height)) * 2 } return m } diff --git a/run.go b/run.go index 6538d78b7..7be124489 100644 --- a/run.go +++ b/run.go @@ -23,6 +23,11 @@ import ( "time" ) +type Game interface { + Update() error + Draw(gr GraphicsContext) error +} + var currentUI *ui // Run runs the game. @@ -34,10 +39,10 @@ func Run(game Game, width, height, scale int, title string, fps int) error { currentUI = nil }() - if err := ui.Start(game, width, height, scale, title); err != nil { + if err := ui.start(game, width, height, scale, title); err != nil { return err } - defer ui.Terminate() + defer ui.terminate() frameTime := time.Duration(int64(time.Second) / int64(fps)) tick := time.Tick(frameTime) @@ -45,13 +50,13 @@ func Run(game Game, width, height, scale int, title string, fps int) error { signal.Notify(sigterm, os.Interrupt, syscall.SIGTERM) for { - ui.DoEvents() - if ui.IsClosed() { + ui.doEvents() + if ui.isClosed() { return nil } select { default: - if err := ui.DrawGame(game); err != nil { + if err := ui.drawGame(game); err != nil { return err } case <-tick: diff --git a/ui.go b/ui.go index 3fba538a9..a65a5ad05 100644 --- a/ui.go +++ b/ui.go @@ -32,7 +32,7 @@ type ui struct { canvas *canvas } -func (u *ui) Start(game Game, width, height, scale int, title string) error { +func (u *ui) start(game Game, width, height, scale int, title string) error { if !glfw.Init() { return errors.New("glfw.Init() fails") } @@ -42,43 +42,28 @@ func (u *ui) Start(game Game, width, height, scale int, title string) error { return err } - c := &canvas{ - window: window, - scale: scale, - funcs: make(chan func()), - funcsDone: make(chan struct{}), - } - - c.run(width, height, scale) - - // For retina displays, recalculate the scale with the framebuffer size. - windowWidth, _ := window.GetFramebufferSize() - realScale := windowWidth / width - c.use(func() { - c.graphicsContext, err = initialize(width, height, realScale) - }) + c, err := newCanvas(window, width, height, scale) if err != nil { return err } - u.canvas = c return nil } -func (u *ui) DoEvents() { +func (u *ui) doEvents() { glfw.PollEvents() u.canvas.update() } -func (u *ui) Terminate() { +func (u *ui) terminate() { glfw.Terminate() } -func (u *ui) IsClosed() bool { +func (u *ui) isClosed() bool { return u.canvas.isClosed() } -func (u *ui) DrawGame(game Game) error { +func (u *ui) drawGame(game Game) error { return u.canvas.draw(game) }