Show Ebiten

This commit is contained in:
Hajime Hoshi 2013-06-19 00:48:41 +09:00
parent ab558db683
commit d4fd3adc53
8 changed files with 158 additions and 68 deletions

37
ebiten.go Normal file
View File

@ -0,0 +1,37 @@
package ebiten
import (
"time"
"github.com/hajimehoshi/go-ebiten/graphics"
"github.com/hajimehoshi/go-ebiten/ui"
)
type Game interface {
Update()
Draw(g *graphics.GraphicsContext, offscreen *graphics.Texture)
}
func Run(game Game, u ui.UI) {
ch := make(chan bool, 1)
device := graphics.NewDevice(
u.ScreenWidth(), u.ScreenHeight(), u.ScreenScale(),
func(g *graphics.GraphicsContext, offscreen *graphics.Texture) {
ticket := <-ch
game.Draw(g, offscreen)
ch<- ticket
})
go func() {
const frameTime = time.Second / 60
tick := time.Tick(frameTime)
for {
<-tick
ticket := <-ch
game.Update()
ch<- ticket
}
}()
ch<- true
u.Run(device)
}

BIN
examples/glut/ebiten.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -15,28 +15,27 @@ package main
//
import "C"
import (
"image"
"image/color"
_ "image/png"
"os"
"time"
"unsafe"
"github.com/hajimehoshi/go-ebiten"
"github.com/hajimehoshi/go-ebiten/graphics"
)
var device *graphics.Device
type DemoGame struct {
type GlutUI struct{
screenWidth int
screenHeight int
screenScale int
device *graphics.Device
}
func (game *DemoGame) Update() {
}
func (game *DemoGame) Draw(g *graphics.GraphicsContext, offscreen *graphics.Texture) {
g.Fill(&color.RGBA{R: 128, G: 128, B: 255, A: 255})
}
var currentUI *GlutUI
//export display
func display() {
device.Update()
currentUI.device.Update()
C.glutSwapBuffers()
}
@ -45,7 +44,7 @@ func idle() {
C.glutPostRedisplay()
}
func main() {
func (ui *GlutUI) Init() {
cargs := []*C.char{}
for _, arg := range os.Args {
cargs = append(cargs, C.CString(arg))
@ -57,41 +56,76 @@ func main() {
}()
cargc := C.int(len(cargs))
screenWidth := 256
screenHeight := 240
screenScale := 2
ui.screenWidth = 256
ui.screenHeight = 240
ui.screenScale = 2
C.glutInit(&cargc, &cargs[0])
C.glutInitDisplayMode(C.GLUT_RGBA);
C.glutInitWindowSize(C.int(screenWidth * screenScale),
C.int(screenHeight * screenScale))
C.glutInitWindowSize(
C.int(ui.screenWidth * ui.screenScale),
C.int(ui.screenHeight * ui.screenScale))
title := C.CString("Ebiten Demo")
defer C.free(unsafe.Pointer(title))
C.glutCreateWindow(title)
C.setGlutFuncs()
}
ch := make(chan bool, 1)
game := &DemoGame{}
device = graphics.NewDevice(screenWidth, screenHeight, screenScale,
func(g *graphics.GraphicsContext, offscreen *graphics.Texture) {
ticket := <-ch
game.Draw(g, offscreen)
ch<- ticket
})
func (ui *GlutUI) ScreenWidth() int {
return ui.screenWidth
}
go func() {
const frameTime = time.Second / 60
tick := time.Tick(frameTime)
for {
<-tick
ticket := <-ch
game.Update()
ch<- ticket
}
}()
ch<- true
func (ui *GlutUI) ScreenHeight() int {
return ui.screenHeight
}
func (ui *GlutUI) ScreenScale() int {
return ui.screenScale
}
func (ui *GlutUI) Run(device *graphics.Device) {
ui.device = device
C.glutMainLoop()
}
type DemoGame struct {
ebitenTexture *graphics.Texture
}
func (game *DemoGame) Update() {
if game.ebitenTexture == nil {
file, err := os.Open("ebiten.png")
if err != nil {
panic(err)
}
defer file.Close()
img, _, err := image.Decode(file)
if err != nil {
panic(err)
}
game.ebitenTexture = currentUI.device.NewTextureFromImage(img)
}
}
func (game *DemoGame) Draw(g *graphics.GraphicsContext, offscreen *graphics.Texture) {
g.Fill(&color.RGBA{R: 128, G: 128, B: 255, A: 255})
if game.ebitenTexture == nil {
return
}
g.DrawTexture(game.ebitenTexture,
0, 0, game.ebitenTexture.Width, game.ebitenTexture.Height,
graphics.IdentityGeometryMatrix(),
graphics.IdentityColorMatrix())
}
func main() {
game := &DemoGame{}
currentUI = &GlutUI{}
currentUI.Init()
ebiten.Run(game, currentUI)
}

View File

@ -1,10 +0,0 @@
package game
import (
_ "../graphics"
)
type Game interface {
Update()
Draw()
}

View File

@ -5,6 +5,9 @@ package graphics
// #include <OpenGL/gl.h>
// #include <stdlib.h>
import "C"
import (
"image"
)
type Device struct {
screenWidth int
@ -13,6 +16,7 @@ type Device struct {
graphicsContext *GraphicsContext
offscreenTexture *Texture
drawFunc func(*GraphicsContext, *Texture)
funcs []func()
}
func NewDevice(screenWidth, screenHeight, screenScale int,
@ -22,13 +26,19 @@ func NewDevice(screenWidth, screenHeight, screenScale int,
screenHeight: screenHeight,
screenScale: screenScale,
graphicsContext: newGraphicsContext(screenWidth, screenHeight, screenScale),
offscreenTexture: NewTexture(screenWidth, screenHeight),
drawFunc: drawFunc,
funcs: []func(){},
}
device.offscreenTexture = device.NewTexture(screenWidth, screenHeight)
return device
}
func (device *Device) Update() {
for _, f := range device.funcs {
f()
}
device.funcs = []func(){}
g := device.graphicsContext
C.glEnable(C.GL_TEXTURE_2D)
C.glTexParameteri(C.GL_TEXTURE_2D, C.GL_TEXTURE_MIN_FILTER, C.GL_NEAREST)
@ -50,3 +60,25 @@ func (device *Device) Update() {
geometryMatrix, IdentityColorMatrix())
g.flush()
}
func (device *Device) NewTexture(width, height int) *Texture {
return createTexture(device, width, height, nil)
}
func (device *Device) NewTextureFromImage(img image.Image) *Texture {
var pix []uint8
switch img.(type) {
case *image.RGBA:
pix = img.(*image.RGBA).Pix
case *image.NRGBA:
pix = img.(*image.NRGBA).Pix
default:
panic("image should be RGBA or NRGBA")
}
size := img.Bounds().Size()
return createTexture(device, size.X, size.Y, pix)
}
func (device *Device) executeWhenDrawing(f func()) {
device.funcs = append(device.funcs, f)
}

View File

@ -6,6 +6,7 @@ package graphics
// #include <OpenGL/gl.h>
import "C"
import (
"fmt"
"image/color"
"math"
"unsafe"
@ -117,8 +118,8 @@ func (context *GraphicsContext) SetOffscreen(texture *Texture) {
framebuffer = context.mainFramebuffer
}
C.glBindFramebuffer(C.GL_FRAMEBUFFER, framebuffer)
if C.glCheckFramebufferStatus(C.GL_FRAMEBUFFER) != C.GL_FRAMEBUFFER_COMPLETE {
panic("glBindFramebuffer failed")
if err := C.glCheckFramebufferStatus(C.GL_FRAMEBUFFER); err != C.GL_FRAMEBUFFER_COMPLETE {
panic(fmt.Sprintf("glBindFramebuffer failed: %d", err))
}
C.glEnable(C.GL_BLEND)
C.glBlendFunc(C.GL_SRC_ALPHA, C.GL_ONE_MINUS_SRC_ALPHA)

View File

@ -5,9 +5,7 @@ package graphics
// #include <OpenGL/gl.h>
import "C"
import (
"image"
"unsafe"
"github.com/hajimehoshi/go-ebiten/ui"
)
func Clp2(x uint64) uint64 {
@ -29,7 +27,7 @@ type Texture struct {
TextureHeight int
}
func createTexture(width, height int, pixels []uint8) *Texture{
func createTexture(device *Device, width, height int, pixels []uint8) *Texture{
textureWidth := int(Clp2(uint64(width)))
textureHeight := int(Clp2(uint64(height)))
if pixels != nil {
@ -48,12 +46,7 @@ func createTexture(width, height int, pixels []uint8) *Texture{
TextureHeight: textureHeight,
}
ch := make(chan C.GLuint)
// TODO: should wait?
go func() {
texture.id = <-ch
}()
ui.ExecuteOnUIThread(func() {
device.executeWhenDrawing(func() {
textureID := C.GLuint(0)
C.glGenTextures(1, (*C.GLuint)(&textureID))
if textureID == 0 {
@ -74,21 +67,13 @@ func createTexture(width, height int, pixels []uint8) *Texture{
C.glTexParameteri(C.GL_TEXTURE_2D, C.GL_TEXTURE_MIN_FILTER, C.GL_LINEAR)
C.glBindTexture(C.GL_TEXTURE_2D, 0)
ch<- textureID
close(ch)
// TODO: lock?
texture.id = textureID
})
return texture
}
func NewTexture(width, height int) *Texture {
return createTexture(width, height, nil)
}
func NewTextureFromRGBA(image *image.RGBA) *Texture {
return createTexture(image.Rect.Size().X, image.Rect.Size().Y, image.Pix)
}
func (texture *Texture) IsAvailable() bool {
return texture.id != 0
}

View File

@ -1,5 +1,16 @@
package ui
import (
"github.com/hajimehoshi/go-ebiten/graphics"
)
type UI interface {
ScreenWidth() int
ScreenHeight() int
ScreenScale() int
Run(device *graphics.Device)
}
func ExecuteOnUIThread(f func()) {
// TODO: implement!
f()