diff --git a/example/main.go b/example/main.go index ae0542e68..18e99c02c 100644 --- a/example/main.go +++ b/example/main.go @@ -4,7 +4,7 @@ import ( "github.com/hajimehoshi/ebiten/example/blocks" "github.com/hajimehoshi/ebiten/graphics" "github.com/hajimehoshi/ebiten/ui" - "github.com/hajimehoshi/ebiten/ui/dummy" + "github.com/hajimehoshi/ebiten/ui/glfw" "os" "os/signal" "runtime" @@ -29,11 +29,11 @@ func main() { const frameTime = time.Duration(int64(time.Second) / int64(fps)) const title = "Ebiten Demo" - u := new(dummy.UI) + u := new(glfw.UI) canvas := u.CreateCanvas(screenWidth, screenHeight, screenScale, title) - textureFactory := new(dummy.TextureFactory) - var game Game = blocks.NewGame(NewTextures(textureFactory)) + textureFactory := u.TextureFactory() + game := blocks.NewGame(NewTextures(textureFactory)) tick := time.Tick(frameTime) sigterm := make(chan os.Signal, 1) diff --git a/example/textures.go b/example/textures.go index 41ddc340f..8bdf28274 100644 --- a/example/textures.go +++ b/example/textures.go @@ -67,9 +67,7 @@ func (t *Textures) loopMain() { if err != nil { panic(err) } - id, err := t.textureFactory.CreateTexture( - img, - graphics.FilterNearest) + id, err := t.textureFactory.CreateTexture(img, graphics.FilterNearest) if err != nil { panic(err) } @@ -81,10 +79,7 @@ func (t *Textures) loopMain() { name := s.name size := s.size go func() { - id, err := t.textureFactory.CreateRenderTarget( - size.Width, - size.Height, - graphics.FilterNearest) + id, err := t.textureFactory.CreateRenderTarget(size.Width, size.Height, graphics.FilterNearest) if err != nil { panic(err) } diff --git a/graphics/opengl/context.go b/graphics/opengl/context.go index 5d156419b..d9e5c4778 100644 --- a/graphics/opengl/context.go +++ b/graphics/opengl/context.go @@ -42,8 +42,7 @@ func NewContext(screenWidth, screenHeight, screenScale int) *Context { context.defaultId = idsInstance.addRenderTarget(defaultRenderTarget) var err error - context.screenId, err = idsInstance.createRenderTarget( - screenWidth, screenHeight, graphics.FilterNearest) + context.screenId, err = idsInstance.createRenderTarget(screenWidth, screenHeight, graphics.FilterNearest) if err != nil { panic("initializing the offscreen failed: " + err.Error()) } @@ -60,6 +59,7 @@ func (c *Context) Dispose() { idsInstance.deleteRenderTarget(c.screenId) } +// TODO: This interface is confusing: Can we change this? func (c *Context) Update(draw func(graphics.Context)) { c.ResetOffscreen() c.Clear() @@ -72,12 +72,7 @@ func (c *Context) Update(draw func(graphics.Context)) { scale := float64(c.screenScale) geo := matrix.IdentityGeometry() geo.Scale(scale, scale) - graphics.DrawWhole( - c.RenderTarget(c.screenId), - c.screenWidth, - c.screenHeight, - geo, - matrix.IdentityColor()) + graphics.DrawWhole(c.RenderTarget(c.screenId), c.screenWidth, c.screenHeight, geo, matrix.IdentityColor()) flush() } @@ -111,9 +106,6 @@ type textureWithContext struct { context *Context } -func (t *textureWithContext) Draw( - parts []graphics.TexturePart, - geo matrix.Geometry, - color matrix.Color) { +func (t *textureWithContext) Draw(parts []graphics.TexturePart, geo matrix.Geometry, color matrix.Color) { idsInstance.drawTexture(t.context.currentId, t.id, parts, geo, color) } diff --git a/ui/glfw/canvas.go b/ui/glfw/canvas.go new file mode 100644 index 000000000..136e2c5ce --- /dev/null +++ b/ui/glfw/canvas.go @@ -0,0 +1,91 @@ +package glfw + +import ( + glfw "github.com/go-gl/glfw3" + "github.com/hajimehoshi/ebiten/graphics" + "github.com/hajimehoshi/ebiten/graphics/opengl" + "github.com/hajimehoshi/ebiten/ui" + "image" + "runtime" +) + +type Canvas struct { + window *glfw.Window + context *opengl.Context + funcs chan func() + funcsDone chan struct{} +} + +func NewCanvas(width, height, scale int, title string) *Canvas { + window, err := glfw.CreateWindow(width*scale, height*scale, title, nil, nil) + if err != nil { + panic(err) + } + canvas := &Canvas{ + window: window, + funcs: make(chan func()), + funcsDone: make(chan struct{}), + } + + // For retina displays, recalculate the scale with the framebuffer size. + windowWidth, windowHeight := window.GetFramebufferSize() + realScale := windowWidth / width + _ = windowHeight + + canvas.run() + canvas.use(func() { + canvas.context = opengl.NewContext(width, height, realScale) + }) + return canvas +} + +func (c *Canvas) Draw(f func(graphics.Context)) { + c.use(func() { + c.context.Update(f) + c.window.SwapBuffers() + }) +} + +func (c *Canvas) IsClosed() bool { + return c.window.ShouldClose() +} + +func (c *Canvas) InputState() ui.InputState { + return &InputState{newKeys(), -1, -1} +} + +func (c *Canvas) CreateTexture(img image.Image, filter graphics.Filter) (graphics.TextureId, error) { + var id graphics.TextureId + var err error + c.use(func() { + id, err = opengl.CreateTexture(img, filter) + }) + return id, err +} + +func (c *Canvas) CreateRenderTarget(width, height int, filter graphics.Filter) (graphics.RenderTargetId, error) { + var id graphics.RenderTargetId + var err error + c.use(func() { + id, err = opengl.CreateRenderTarget(width, height, filter) + }) + return id, err +} + +func (c *Canvas) run() { + go func() { + runtime.LockOSThread() + c.window.MakeContextCurrent() + glfw.SwapInterval(1) + for { + f := <-c.funcs + f() + c.funcsDone <- struct{}{} + } + }() +} + +func (c *Canvas) use(f func()) { + c.funcs <- f + <-c.funcsDone +} diff --git a/ui/glfw/inputstate.go b/ui/glfw/inputstate.go new file mode 100644 index 000000000..5f5dfce42 --- /dev/null +++ b/ui/glfw/inputstate.go @@ -0,0 +1,50 @@ +package glfw + +import ( + "github.com/hajimehoshi/ebiten/ui" +) + +type Keys map[ui.Key]struct{} + +func newKeys() Keys { + return Keys(map[ui.Key]struct{}{}) +} + +func (k Keys) clone() Keys { + n := newKeys() + for key, value := range k { + n[key] = value + } + return n +} + +func (k Keys) add(key ui.Key) { + k[key] = struct{}{} +} + +func (k Keys) remove(key ui.Key) { + delete(k, key) +} + +func (k Keys) Includes(key ui.Key) bool { + _, ok := k[key] + return ok +} + +type InputState struct { + pressedKeys Keys + mouseX int + mouseY int +} + +func (i *InputState) PressedKeys() ui.Keys { + return i.pressedKeys +} + +func (i *InputState) MouseX() int { + return i.mouseX +} + +func (i *InputState) MouseY() int { + return i.mouseY +} diff --git a/ui/glfw/ui.go b/ui/glfw/ui.go new file mode 100644 index 000000000..81fe67dc7 --- /dev/null +++ b/ui/glfw/ui.go @@ -0,0 +1,43 @@ +package glfw + +import ( + glfw "github.com/go-gl/glfw3" + "github.com/hajimehoshi/ebiten/graphics" + "github.com/hajimehoshi/ebiten/ui" + "log" +) + +func init() { + glfw.SetErrorCallback(func (err glfw.ErrorCode, desc string) { + log.Fatalf("%v: %v\n", err, desc) + }) +} + +type UI struct { + canvas *Canvas +} + +func (u *UI) CreateCanvas(width, height, scale int, title string) ui.Canvas { + if !glfw.Init() { + panic("glfw.Init() fails") + } + glfw.WindowHint(glfw.Resizable, glfw.False) + //glfw.WindowHint(glfw.ClientAPI, glfw.OpenGLESAPI) + u.canvas = NewCanvas(width, height, scale, title) + return u.canvas +} + +func (u *UI) Start() { +} + +func (u *UI) DoEvents() { + glfw.PollEvents() +} + +func (u *UI) Terminate() { + glfw.Terminate() +} + +func (u *UI) TextureFactory() graphics.TextureFactory { + return u.canvas +}