diff --git a/graphics/opengl/canvas.go b/graphics/opengl/canvas.go new file mode 100644 index 000000000..86817477e --- /dev/null +++ b/graphics/opengl/canvas.go @@ -0,0 +1,121 @@ +package opengl + +// #cgo LDFLAGS: -framework OpenGL +// +// #include +// #include +import "C" +import ( + "github.com/hajimehoshi/go-ebiten/graphics" + "github.com/hajimehoshi/go-ebiten/graphics/matrix" + "github.com/hajimehoshi/go-ebiten/graphics/opengl/offscreen" + "github.com/hajimehoshi/go-ebiten/graphics/opengl/texture" + "image" + "math" +) + +type Canvas struct { + screenId graphics.RenderTargetId + ids *ids + offscreen *offscreen.Offscreen +} + +func newCanvas(screenWidth, screenHeight, screenScale int) *Canvas { + canvas := &Canvas{ + ids: newIds(), + offscreen: offscreen.New(screenWidth, screenHeight, screenScale), + } + + var err error + canvas.screenId, err = canvas.createRenderTarget( + screenWidth, screenHeight, texture.FilterNearest) + if err != nil { + panic("initializing the offscreen failed: " + err.Error()) + } + + canvas.Init() + + return canvas +} + +func (canvas *Canvas) Clear() { + canvas.Fill(0, 0, 0) +} + +func (canvas *Canvas) Fill(r, g, b uint8) { + const max = float64(math.MaxUint8) + C.glClearColor( + C.GLclampf(float64(r)/max), + C.GLclampf(float64(g)/max), + C.GLclampf(float64(b)/max), + 1) + C.glClear(C.GL_COLOR_BUFFER_BIT) +} + +func (canvas *Canvas) DrawTexture( + id graphics.TextureId, + geometryMatrix matrix.Geometry, colorMatrix matrix.Color) { + tex := canvas.ids.TextureAt(id) + canvas.offscreen.DrawTexture(tex, geometryMatrix, colorMatrix) +} + +func (canvas *Canvas) DrawRenderTarget( + id graphics.RenderTargetId, + geometryMatrix matrix.Geometry, colorMatrix matrix.Color) { + canvas.DrawTexture(canvas.ids.ToTexture(id), geometryMatrix, colorMatrix) +} + +func (canvas *Canvas) DrawTextureParts( + id graphics.TextureId, parts []graphics.TexturePart, + geometryMatrix matrix.Geometry, colorMatrix matrix.Color) { + tex := canvas.ids.TextureAt(id) + canvas.offscreen.DrawTextureParts(tex, parts, geometryMatrix, colorMatrix) +} + +func (canvas *Canvas) DrawRenderTargetParts( + id graphics.RenderTargetId, parts []graphics.TexturePart, + geometryMatrix matrix.Geometry, colorMatrix matrix.Color) { + canvas.DrawTextureParts(canvas.ids.ToTexture(id), parts, geometryMatrix, colorMatrix) +} + +// Init initializes the canvas. The initial state is saved for each GL canvas. +func (canvas *Canvas) Init() { + C.glEnable(C.GL_TEXTURE_2D) + C.glEnable(C.GL_BLEND) +} + +func (canvas *Canvas) ResetOffscreen() { + canvas.SetOffscreen(canvas.screenId) +} + +func (canvas *Canvas) SetOffscreen(renderTargetId graphics.RenderTargetId) { + renderTarget := canvas.ids.RenderTargetAt(renderTargetId) + canvas.offscreen.Set(renderTarget) +} + +func (canvas *Canvas) setMainFramebufferOffscreen() { + canvas.offscreen.SetMainFramebuffer() +} + +func (canvas *Canvas) flush() { + C.glFlush() +} + +func (canvas *Canvas) createRenderTarget(width, height int, filter texture.Filter) ( + graphics.RenderTargetId, error) { + renderTargetId, err := canvas.ids.CreateRenderTarget(width, height, filter) + if err != nil { + return 0, err + } + return renderTargetId, nil +} + +func (canvas *Canvas) CreateRenderTarget(width, height int) ( + graphics.RenderTargetId, error) { + return canvas.createRenderTarget(width, height, texture.FilterLinear) +} + +func (canvas *Canvas) CreateTextureFromImage(img image.Image) ( + graphics.TextureId, error) { + return canvas.ids.CreateTextureFromImage(img) +} diff --git a/graphics/opengl/context.go b/graphics/opengl/context.go deleted file mode 100644 index 6b7e40972..000000000 --- a/graphics/opengl/context.go +++ /dev/null @@ -1,121 +0,0 @@ -package opengl - -// #cgo LDFLAGS: -framework OpenGL -// -// #include -// #include -import "C" -import ( - "github.com/hajimehoshi/go-ebiten/graphics" - "github.com/hajimehoshi/go-ebiten/graphics/matrix" - "github.com/hajimehoshi/go-ebiten/graphics/opengl/offscreen" - "github.com/hajimehoshi/go-ebiten/graphics/opengl/texture" - "image" - "math" -) - -type Context struct { - screenId graphics.RenderTargetId - ids *ids - offscreen *offscreen.Offscreen -} - -func newContext(screenWidth, screenHeight, screenScale int) *Context { - context := &Context{ - ids: newIds(), - offscreen: offscreen.New(screenWidth, screenHeight, screenScale), - } - - var err error - context.screenId, err = context.createRenderTarget( - screenWidth, screenHeight, texture.FilterNearest) - if err != nil { - panic("initializing the offscreen failed: " + err.Error()) - } - - context.Init() - - return context -} - -func (context *Context) Clear() { - context.Fill(0, 0, 0) -} - -func (context *Context) Fill(r, g, b uint8) { - const max = float64(math.MaxUint8) - C.glClearColor( - C.GLclampf(float64(r)/max), - C.GLclampf(float64(g)/max), - C.GLclampf(float64(b)/max), - 1) - C.glClear(C.GL_COLOR_BUFFER_BIT) -} - -func (context *Context) DrawTexture( - id graphics.TextureId, - geometryMatrix matrix.Geometry, colorMatrix matrix.Color) { - tex := context.ids.TextureAt(id) - context.offscreen.DrawTexture(tex, geometryMatrix, colorMatrix) -} - -func (context *Context) DrawRenderTarget( - id graphics.RenderTargetId, - geometryMatrix matrix.Geometry, colorMatrix matrix.Color) { - context.DrawTexture(context.ids.ToTexture(id), geometryMatrix, colorMatrix) -} - -func (context *Context) DrawTextureParts( - id graphics.TextureId, parts []graphics.TexturePart, - geometryMatrix matrix.Geometry, colorMatrix matrix.Color) { - tex := context.ids.TextureAt(id) - context.offscreen.DrawTextureParts(tex, parts, geometryMatrix, colorMatrix) -} - -func (context *Context) DrawRenderTargetParts( - id graphics.RenderTargetId, parts []graphics.TexturePart, - geometryMatrix matrix.Geometry, colorMatrix matrix.Color) { - context.DrawTextureParts(context.ids.ToTexture(id), parts, geometryMatrix, colorMatrix) -} - -// Init initializes the context. The initial state is saved for each GL context. -func (context *Context) Init() { - C.glEnable(C.GL_TEXTURE_2D) - C.glEnable(C.GL_BLEND) -} - -func (context *Context) ResetOffscreen() { - context.SetOffscreen(context.screenId) -} - -func (context *Context) SetOffscreen(renderTargetId graphics.RenderTargetId) { - renderTarget := context.ids.RenderTargetAt(renderTargetId) - context.offscreen.Set(renderTarget) -} - -func (context *Context) setMainFramebufferOffscreen() { - context.offscreen.SetMainFramebuffer() -} - -func (context *Context) flush() { - C.glFlush() -} - -func (context *Context) createRenderTarget(width, height int, filter texture.Filter) ( - graphics.RenderTargetId, error) { - renderTargetId, err := context.ids.CreateRenderTarget(width, height, filter) - if err != nil { - return 0, err - } - return renderTargetId, nil -} - -func (context *Context) CreateRenderTarget(width, height int) ( - graphics.RenderTargetId, error) { - return context.createRenderTarget(width, height, texture.FilterLinear) -} - -func (context *Context) CreateTextureFromImage(img image.Image) ( - graphics.TextureId, error) { - return context.ids.CreateTextureFromImage(img) -} diff --git a/graphics/opengl/device.go b/graphics/opengl/device.go index 561756d15..672521857 100644 --- a/graphics/opengl/device.go +++ b/graphics/opengl/device.go @@ -7,42 +7,42 @@ import ( ) type Device struct { - context *Context + canvas *Canvas screenScale int } func NewDevice(screenWidth, screenHeight, screenScale int) *Device { - context := newContext(screenWidth, screenHeight, screenScale) + canvas := newCanvas(screenWidth, screenHeight, screenScale) return &Device{ - context: context, + canvas: canvas, screenScale: screenScale, } } func (d *Device) Update(draw func(graphics.Canvas)) { - context := d.context - context.Init() - context.ResetOffscreen() - context.Clear() + canvas := d.canvas + canvas.Init() + canvas.ResetOffscreen() + canvas.Clear() - draw(context) + draw(canvas) - context.flush() - context.setMainFramebufferOffscreen() - context.Clear() + canvas.flush() + canvas.setMainFramebufferOffscreen() + canvas.Clear() scale := float64(d.screenScale) geometryMatrix := matrix.IdentityGeometry() geometryMatrix.Scale(scale, scale) - context.DrawRenderTarget(context.screenId, + canvas.DrawRenderTarget(canvas.screenId, geometryMatrix, matrix.IdentityColor()) - context.flush() + canvas.flush() } func (d *Device) CreateRenderTarget(width, height int) (graphics.RenderTargetId, error) { - return d.context.CreateRenderTarget(width, height) + return d.canvas.CreateRenderTarget(width, height) } func (d *Device) CreateTexture(img image.Image) (graphics.TextureId, error) { - return d.context.CreateTextureFromImage(img) + return d.canvas.CreateTextureFromImage(img) } diff --git a/ui/cocoa/cocoa.go b/ui/cocoa/cocoa.go index ed1d225fd..3a42ccca7 100644 --- a/ui/cocoa/cocoa.go +++ b/ui/cocoa/cocoa.go @@ -44,14 +44,15 @@ func New(screenWidth, screenHeight, screenScale int, title string) *UI { u.textureFactory = runTextureFactory() - u.textureFactory.UseContext(func() { + u.textureFactory.useContext(func() { u.graphicsDevice = opengl.NewDevice( u.screenWidth, u.screenHeight, u.screenScale) }) - u.window = u.textureFactory.CreateWindow( + u.window = u.textureFactory.createWindow( + u, u.screenWidth*u.screenScale, u.screenHeight*u.screenScale, title) @@ -74,7 +75,7 @@ func (u *UI) CreateTexture(tag interface{}, img image.Image) { go func() { var id graphics.TextureId var err error - u.textureFactory.UseContext(func() { + u.textureFactory.useContext(func() { id, err = u.graphicsDevice.CreateTexture(img) }) e := graphics.TextureCreatedEvent{ @@ -90,7 +91,7 @@ func (u *UI) CreateRenderTarget(tag interface{}, width, height int) { go func() { var id graphics.RenderTargetId var err error - u.textureFactory.UseContext(func() { + u.textureFactory.useContext(func() { id, err = u.graphicsDevice.CreateRenderTarget(width, height) }) e := graphics.RenderTargetCreatedEvent{ @@ -111,9 +112,7 @@ func (u *UI) RenderTargetCreated() <-chan graphics.RenderTargetCreatedEvent { } func (u *UI) Draw(f func(graphics.Canvas)) { - u.window.UseContext(func() { - u.graphicsDevice.Update(f) - }) + u.window.Draw(f) } //export ebiten_ScreenSizeUpdated diff --git a/ui/cocoa/texture_factory.go b/ui/cocoa/texture_factory.go index c70a5bc27..ad083df7b 100644 --- a/ui/cocoa/texture_factory.go +++ b/ui/cocoa/texture_factory.go @@ -45,11 +45,11 @@ func (t *textureFactory) loop() { } } -func (t *textureFactory) UseContext(f func()) { +func (t *textureFactory) useContext(f func()) { t.funcs <- f <-t.funcsDone } -func (t *textureFactory) CreateWindow(width, height int, title string) *window { - return runWindow(width, height, title, t.sharedContext) +func (t *textureFactory) createWindow(ui *UI, width, height int, title string) *window { + return runWindow(ui, width, height, title, t.sharedContext) } diff --git a/ui/cocoa/window.go b/ui/cocoa/window.go index 3de324b35..8154e6ed4 100644 --- a/ui/cocoa/window.go +++ b/ui/cocoa/window.go @@ -11,18 +11,21 @@ package cocoa // import "C" import ( + "github.com/hajimehoshi/go-ebiten/graphics" "runtime" "unsafe" ) type window struct { + ui *UI native unsafe.Pointer funcs chan func() funcsDone chan struct{} } -func runWindow(width, height int, title string, sharedContext unsafe.Pointer) *window { +func runWindow(ui *UI, width, height int, title string, sharedContext unsafe.Pointer) *window { w := &window{ + ui: ui, funcs: make(chan func()), funcsDone: make(chan struct{}), } @@ -58,7 +61,13 @@ func (w *window) loop() { } } -func (w *window) UseContext(f func()) { +func (w *window) Draw(f func(graphics.Canvas)) { + w.useContext(func() { + w.ui.graphicsDevice.Update(f) + }) +} + +func (w *window) useContext(f func()) { w.funcs <- f <-w.funcsDone } diff --git a/ui/ui.go b/ui/ui.go index 6dd4fe7b9..2c0653ebe 100644 --- a/ui/ui.go +++ b/ui/ui.go @@ -24,3 +24,13 @@ type UI interface { Draw(func(graphics.Canvas)) UIEvents } + +type WindowEvents interface { + ScreenSizeUpdated() <-chan ScreenSizeUpdatedEvent + InputStateUpdated() <-chan InputStateUpdatedEvent +} + +type Window interface { + Draw(func(graphics.Canvas)) + WindowEvents +}