Add Textures

This commit is contained in:
Hajime Hoshi 2014-05-03 15:25:41 +09:00
parent 322d0f1924
commit 4e6aeaa291
12 changed files with 220 additions and 128 deletions

View File

@ -93,11 +93,11 @@ func (f *Field) Flush() {
} }
} }
func (f *Field) Draw(context graphics.Context, geo matrix.Geometry) { func (f *Field) Draw(context graphics.Context, textures *Textures, geo matrix.Geometry) {
blocks := make([][]BlockType, len(f.blocks)) blocks := make([][]BlockType, len(f.blocks))
for i, blockCol := range f.blocks { for i, blockCol := range f.blocks {
blocks[i] = make([]BlockType, len(blockCol)) blocks[i] = make([]BlockType, len(blockCol))
copy(blocks[i], blockCol[:]) copy(blocks[i], blockCol[:])
} }
drawBlocks(context, blocks, geo) drawBlocks(context, textures, blocks, geo)
} }

View File

@ -17,8 +17,13 @@ func textWidth(str string) int {
return charWidth * len(str) return charWidth * len(str)
} }
func drawText(context graphics.Context, str string, x, y, scale int, clr color.Color) { func drawText(
fontTextureId := drawInfo.textures["font"] context graphics.Context,
textures *Textures,
str string,
x, y, scale int,
clr color.Color) {
fontTextureId := textures.GetTexture("font")
parts := []graphics.TexturePart{} parts := []graphics.TexturePart{}
locationX := 0 locationX := 0
@ -48,7 +53,12 @@ func drawText(context graphics.Context, str string, x, y, scale int, clr color.C
context.Texture(fontTextureId).Draw(parts, geoMat, clrMat) context.Texture(fontTextureId).Draw(parts, geoMat, clrMat)
} }
func drawTextWithShadow(context graphics.Context, str string, x, y, scale int, clr color.Color) { func drawTextWithShadow(
drawText(context, str, x+1, y+1, scale, color.RGBA{0, 0, 0, 0x80}) context graphics.Context,
drawText(context, str, x, y, scale, clr) textures *Textures,
str string,
x, y, scale int,
clr color.Color) {
drawText(context, textures, str, x+1, y+1, scale, color.RGBA{0, 0, 0, 0x80})
drawText(context, textures, str, x, y, scale, clr)
} }

View File

@ -3,9 +3,6 @@ package blocks
import ( import (
"github.com/hajimehoshi/go-ebiten/graphics" "github.com/hajimehoshi/go-ebiten/graphics"
"github.com/hajimehoshi/go-ebiten/ui" "github.com/hajimehoshi/go-ebiten/ui"
"image"
_ "image/png"
"os"
) )
type Size struct { type Size struct {
@ -13,20 +10,12 @@ type Size struct {
Height int Height int
} }
const ScreenWidth = 256 // TODO: Should they be global??
const ScreenHeight = 240
var texturePaths = map[string]string{} var texturePaths = map[string]string{}
var renderTargetSizes = map[string]Size{} var renderTargetSizes = map[string]Size{}
// TODO: Make this not a global variable. const ScreenWidth = 256
var drawInfo = struct { const ScreenHeight = 240
textures map[string]graphics.TextureId
renderTargets map[string]graphics.RenderTargetId
}{
textures: map[string]graphics.TextureId{},
renderTargets: map[string]graphics.RenderTargetId{},
}
type GameState struct { type GameState struct {
SceneManager *SceneManager SceneManager *SceneManager
@ -36,65 +25,26 @@ type GameState struct {
type Game struct { type Game struct {
sceneManager *SceneManager sceneManager *SceneManager
input *Input input *Input
} textures *Textures
func loadImage(path string) (image.Image, error) {
file, err := os.Open(path)
if err != nil {
return nil, err
}
defer file.Close()
img, _, err := image.Decode(file)
if err != nil {
return nil, err
}
return img, nil
}
func (game *Game) startLoadingTextures(textureFactory graphics.TextureFactory) {
for tag, path := range texturePaths {
tag := tag
path := path
go func() {
img, err := loadImage(path)
if err != nil {
panic(err)
}
textureFactory.CreateTexture(tag, img, graphics.FilterNearest)
}()
}
for tag, size := range renderTargetSizes {
tag := tag
size := size
go func() {
textureFactory.CreateRenderTarget(tag, size.Width, size.Height, graphics.FilterNearest)
}()
}
} }
func NewGame(textureFactory graphics.TextureFactory) *Game { func NewGame(textureFactory graphics.TextureFactory) *Game {
game := &Game{ game := &Game{
sceneManager: NewSceneManager(NewTitleScene()), sceneManager: NewSceneManager(NewTitleScene()),
input: NewInput(), input: NewInput(),
textures: NewTextures(textureFactory),
}
for name, path := range texturePaths {
game.textures.RequestTexture(name, path)
}
for name, size := range renderTargetSizes {
game.textures.RequestRenderTarget(name, size)
} }
game.startLoadingTextures(textureFactory)
return game return game
} }
func (game *Game) HandleEvent(e interface{}) { func (game *Game) HandleEvent(e interface{}) {
switch e := e.(type) { switch e := e.(type) {
case graphics.TextureCreatedEvent:
if e.Error != nil {
panic(e.Error)
}
drawInfo.textures[e.Tag.(string)] = e.Id
case graphics.RenderTargetCreatedEvent:
if e.Error != nil {
panic(e.Error)
}
drawInfo.renderTargets[e.Tag.(string)] = e.Id
case ui.KeyStateUpdatedEvent: case ui.KeyStateUpdatedEvent:
game.input.UpdateKeys(e.Keys) game.input.UpdateKeys(e.Keys)
case ui.MouseStateUpdatedEvent: case ui.MouseStateUpdatedEvent:
@ -102,11 +52,15 @@ func (game *Game) HandleEvent(e interface{}) {
} }
func (game *Game) isInitialized() bool { func (game *Game) isInitialized() bool {
if len(drawInfo.textures) < len(texturePaths) { for name, _ := range texturePaths {
return false if !game.textures.Has(name) {
return false
}
} }
if len(drawInfo.renderTargets) < len(renderTargetSizes) { for name, _ := range renderTargetSizes {
return false if !game.textures.Has(name) {
return false
}
} }
return true return true
} }
@ -126,5 +80,5 @@ func (game *Game) Draw(context graphics.Context) {
if !game.isInitialized() { if !game.isInitialized() {
return return
} }
game.sceneManager.Draw(context) game.sceneManager.Draw(context, game.textures)
} }

View File

@ -97,10 +97,10 @@ func (s *GameScene) Update(state *GameState) {
} }
} }
func (s *GameScene) Draw(context graphics.Context) { func (s *GameScene) Draw(context graphics.Context, textures *Textures) {
context.Fill(0xff, 0xff, 0xff) context.Fill(0xff, 0xff, 0xff)
field := drawInfo.textures["empty"] field := textures.GetTexture("empty")
geoMat := matrix.IdentityGeometry() geoMat := matrix.IdentityGeometry()
geoMat.Scale( geoMat.Scale(
float64(fieldWidth)/float64(emptyWidth), float64(fieldWidth)/float64(emptyWidth),
@ -117,11 +117,12 @@ func (s *GameScene) Draw(context graphics.Context) {
geoMat = matrix.IdentityGeometry() geoMat = matrix.IdentityGeometry()
geoMat.Translate(20, 20) geoMat.Translate(20, 20)
s.field.Draw(context, geoMat) s.field.Draw(context, textures, geoMat)
if s.currentPiece != nil { if s.currentPiece != nil {
s.currentPiece.Draw( s.currentPiece.Draw(
context, context,
textures,
20, 20, 20, 20,
s.currentPieceX, s.currentPieceX,
s.currentPieceY, s.currentPieceY,

View File

@ -123,7 +123,11 @@ const blockHeight = 10
const fieldBlockNumX = 10 const fieldBlockNumX = 10
const fieldBlockNumY = 20 const fieldBlockNumY = 20
func drawBlocks(context graphics.Context, blocks [][]BlockType, geo matrix.Geometry) { func drawBlocks(
context graphics.Context,
textures *Textures,
blocks [][]BlockType,
geo matrix.Geometry) {
parts := []graphics.TexturePart{} parts := []graphics.TexturePart{}
for i, blockCol := range blocks { for i, blockCol := range blocks {
for j, block := range blockCol { for j, block := range blockCol {
@ -143,7 +147,7 @@ func drawBlocks(context graphics.Context, blocks [][]BlockType, geo matrix.Geome
}) })
} }
} }
blocksTexture := drawInfo.textures["blocks"] blocksTexture := textures.GetTexture("blocks")
context.Texture(blocksTexture).Draw(parts, geo, matrix.IdentityColor()) context.Texture(blocksTexture).Draw(parts, geo, matrix.IdentityColor())
} }
@ -205,7 +209,12 @@ func (p *Piece) AbsorbInto(field *Field, x, y int, angle Angle) {
} }
} }
func (p *Piece) Draw(context graphics.Context, fieldX, fieldY int, pieceX, pieceY int, angle Angle) { func (p *Piece) Draw(
context graphics.Context,
textures *Textures,
fieldX, fieldY int,
pieceX, pieceY int,
angle Angle) {
size := len(p.blocks) size := len(p.blocks)
blocks := make([][]BlockType, size) blocks := make([][]BlockType, size)
for i, _ := range p.blocks { for i, _ := range p.blocks {
@ -222,5 +231,5 @@ func (p *Piece) Draw(context graphics.Context, fieldX, fieldY int, pieceX, piece
y := fieldY + pieceY*blockHeight y := fieldY + pieceY*blockHeight
geoMat.Translate(float64(x), float64(y)) geoMat.Translate(float64(x), float64(y))
drawBlocks(context, blocks, geoMat) drawBlocks(context, textures, blocks, geoMat)
} }

View File

@ -14,7 +14,7 @@ func init() {
type Scene interface { type Scene interface {
Update(state *GameState) Update(state *GameState)
Draw(context graphics.Context) Draw(context graphics.Context, textures *Textures)
} }
const transitionMaxCount = 20 const transitionMaxCount = 20
@ -45,20 +45,20 @@ func (s *SceneManager) Update(state *GameState) {
} }
} }
func (s *SceneManager) Draw(context graphics.Context) { func (s *SceneManager) Draw(context graphics.Context, textures *Textures) {
if s.transitionCount == -1 { if s.transitionCount == -1 {
s.current.Draw(context) s.current.Draw(context, textures)
return return
} }
from := drawInfo.renderTargets["scene_manager_transition_from"] from := textures.GetRenderTarget("scene_manager_transition_from")
context.SetOffscreen(from) context.SetOffscreen(from)
context.Clear() context.Clear()
s.current.Draw(context) s.current.Draw(context, textures)
to := drawInfo.renderTargets["scene_manager_transition_to"] to := textures.GetRenderTarget("scene_manager_transition_to")
context.SetOffscreen(to) context.SetOffscreen(to)
context.Clear() context.Clear()
s.next.Draw(context) s.next.Draw(context, textures)
context.ResetOffscreen() context.ResetOffscreen()
color := matrix.IdentityColor() color := matrix.IdentityColor()

131
example/blocks/textures.go Normal file
View File

@ -0,0 +1,131 @@
package blocks
import (
"github.com/hajimehoshi/go-ebiten/graphics"
"image"
_ "image/png"
"os"
"sync"
)
type namePath struct {
name string
path string
}
type nameSize struct {
name string
size Size
}
type Textures struct {
textureFactory graphics.TextureFactory
texturePaths chan namePath
renderTargetSizes chan nameSize
textures map[string]graphics.TextureId
renderTargets map[string]graphics.RenderTargetId
sync.RWMutex
}
func NewTextures(textureFactory graphics.TextureFactory) *Textures {
textures := &Textures{
textureFactory: textureFactory,
texturePaths: make(chan namePath),
renderTargetSizes: make(chan nameSize),
textures: map[string]graphics.TextureId{},
renderTargets: map[string]graphics.RenderTargetId{},
}
go textures.loop()
return textures
}
func loadImage(path string) (image.Image, error) {
file, err := os.Open(path)
if err != nil {
return nil, err
}
defer file.Close()
img, _, err := image.Decode(file)
if err != nil {
return nil, err
}
return img, nil
}
func (t *Textures) loop() {
for {
select {
case p := <-t.texturePaths:
name := p.name
path := p.path
go func() {
img, err := loadImage(path)
if err != nil {
panic(err)
}
ch := t.textureFactory.CreateTexture(
img,
graphics.FilterNearest)
go func() {
e := <-ch
if e.Error != nil {
panic(e.Error)
}
t.Lock()
defer t.Unlock()
t.textures[name] = e.Id
}()
}()
case s := <-t.renderTargetSizes:
name := s.name
size := s.size
go func() {
ch := t.textureFactory.CreateRenderTarget(
size.Width,
size.Height,
graphics.FilterNearest)
go func() {
e := <-ch
if e.Error != nil {
panic(e.Error)
}
t.Lock()
defer t.Unlock()
t.renderTargets[name] = e.Id
}()
}()
}
}
}
func (t *Textures) RequestTexture(name string, path string) {
t.texturePaths <- namePath{name, path}
}
func (t *Textures) RequestRenderTarget(name string, size Size) {
t.renderTargetSizes <- nameSize{name, size}
}
func (t *Textures) Has(name string) bool {
t.RLock()
defer t.RUnlock()
_, ok := t.textures[name]
if ok {
return true
}
_, ok = t.renderTargets[name]
return ok
}
func (t *Textures) GetTexture(name string) graphics.TextureId {
t.RLock()
defer t.RUnlock()
return t.textures[name]
}
func (t *Textures) GetRenderTarget(name string) graphics.RenderTargetId {
t.RLock()
defer t.RUnlock()
return t.renderTargets[name]
}

View File

@ -26,21 +26,21 @@ func (s *TitleScene) Update(state *GameState) {
} }
} }
func (s *TitleScene) Draw(context graphics.Context) { func (s *TitleScene) Draw(context graphics.Context, textures *Textures) {
drawTitleBackground(context, s.count) drawTitleBackground(context, textures, s.count)
drawLogo(context, "BLOCKS") drawLogo(context, textures, "BLOCKS")
message := "PRESS SPACE TO START" message := "PRESS SPACE TO START"
x := (ScreenWidth - textWidth(message)) / 2 x := (ScreenWidth - textWidth(message)) / 2
y := ScreenHeight - 48 y := ScreenHeight - 48
drawTextWithShadow(context, message, x, y, 1, color.RGBA{0x80, 0, 0, 0xff}) drawTextWithShadow(context, textures, message, x, y, 1, color.RGBA{0x80, 0, 0, 0xff})
} }
func drawTitleBackground(context graphics.Context, c int) { func drawTitleBackground(context graphics.Context, textures *Textures, c int) {
const textureWidth = 32 const textureWidth = 32
const textureHeight = 32 const textureHeight = 32
backgroundTextureId := drawInfo.textures["background"] backgroundTextureId := textures.GetTexture("background")
parts := []graphics.TexturePart{} parts := []graphics.TexturePart{}
for j := -1; j < ScreenHeight/textureHeight+1; j++ { for j := -1; j < ScreenHeight/textureHeight+1; j++ {
for i := 0; i < ScreenWidth/textureWidth+1; i++ { for i := 0; i < ScreenWidth/textureWidth+1; i++ {
@ -60,10 +60,10 @@ func drawTitleBackground(context graphics.Context, c int) {
context.Texture(backgroundTextureId).Draw(parts, geo, clr) context.Texture(backgroundTextureId).Draw(parts, geo, clr)
} }
func drawLogo(context graphics.Context, str string) { func drawLogo(context graphics.Context, textures *Textures, str string) {
scale := 4 scale := 4
textWidth := textWidth(str) * scale textWidth := textWidth(str) * scale
x := (ScreenWidth - textWidth) / 2 x := (ScreenWidth - textWidth) / 2
y := 32 y := 32
drawTextWithShadow(context, str, x, y, scale, color.RGBA{0x00, 0x00, 0x80, 0xff}) drawTextWithShadow(context, textures, str, x, y, scale, color.RGBA{0x00, 0x00, 0x80, 0xff})
} }

View File

@ -38,15 +38,12 @@ func main() {
go func() { go func() {
defer close(quit) defer close(quit)
textureFactoryEvents := textureFactory.Events()
windowEvents := window.Events() windowEvents := window.Events()
var game Game = blocks.NewGame(textureFactory) var game Game = blocks.NewGame(textureFactory)
frameTime := time.Duration(int64(time.Second) / int64(fps)) frameTime := time.Duration(int64(time.Second) / int64(fps))
tick := time.Tick(frameTime) tick := time.Tick(frameTime)
for { for {
select { select {
case e := <-textureFactoryEvents:
game.HandleEvent(e)
case e := <-windowEvents: case e := <-windowEvents:
game.HandleEvent(e) game.HandleEvent(e)
if _, ok := e.(ui.WindowClosedEvent); ok { if _, ok := e.(ui.WindowClosedEvent); ok {

View File

@ -77,11 +77,11 @@ func (c *Context) Fill(r, g, b uint8) {
} }
func (c *Context) Texture(id graphics.TextureId) graphics.Drawer { func (c *Context) Texture(id graphics.TextureId) graphics.Drawer {
return &TextureWithContext{id, c} return &textureWithContext{id, c}
} }
func (c *Context) RenderTarget(id graphics.RenderTargetId) graphics.Drawer { func (c *Context) RenderTarget(id graphics.RenderTargetId) graphics.Drawer {
return &TextureWithContext{c.ids.toTexture(id), c} return &textureWithContext{c.ids.toTexture(id), c}
} }
func (c *Context) ResetOffscreen() { func (c *Context) ResetOffscreen() {
@ -92,12 +92,12 @@ func (c *Context) SetOffscreen(renderTargetId graphics.RenderTargetId) {
c.currentId = renderTargetId c.currentId = renderTargetId
} }
type TextureWithContext struct { type textureWithContext struct {
id graphics.TextureId id graphics.TextureId
context *Context context *Context
} }
func (t *TextureWithContext) Draw( func (t *textureWithContext) Draw(
parts []graphics.TexturePart, parts []graphics.TexturePart,
geo matrix.Geometry, geo matrix.Geometry,
color matrix.Color) { color matrix.Color) {

View File

@ -18,19 +18,16 @@ type TextureId int
type RenderTargetId int type RenderTargetId int
type TextureCreatedEvent struct { type TextureCreatedEvent struct {
Tag interface{}
Id TextureId Id TextureId
Error error Error error
} }
type RenderTargetCreatedEvent struct { type RenderTargetCreatedEvent struct {
Tag interface{}
Id RenderTargetId Id RenderTargetId
Error error Error error
} }
type TextureFactory interface { type TextureFactory interface {
CreateRenderTarget(tag interface{}, width, height int, filter Filter) CreateRenderTarget(width, height int, filter Filter) <-chan RenderTargetCreatedEvent
CreateTexture(tag interface{}, img image.Image, filter Filter) CreateTexture(img image.Image, filter Filter) <-chan TextureCreatedEvent
Events() <-chan interface{}
} }

View File

@ -16,7 +16,6 @@ import (
type sharedContext struct { type sharedContext struct {
inited chan struct{} inited chan struct{}
events chan interface{}
funcs chan func() funcs chan func()
funcsDone chan struct{} funcsDone chan struct{}
gameWindows chan *GameWindow gameWindows chan *GameWindow
@ -72,15 +71,10 @@ func (t *sharedContext) createGameWindow(width, height, scale int, title string)
return w return w
} }
func (t *sharedContext) Events() <-chan interface{} { func (t *sharedContext) CreateTexture(
if t.events != nil { img image.Image,
return t.events filter graphics.Filter) <-chan graphics.TextureCreatedEvent {
} ch := make(chan graphics.TextureCreatedEvent)
t.events = make(chan interface{})
return t.events
}
func (t *sharedContext) CreateTexture(tag interface{}, img image.Image, filter graphics.Filter) {
go func() { go func() {
<-t.inited <-t.inited
var id graphics.TextureId var id graphics.TextureId
@ -88,18 +82,19 @@ func (t *sharedContext) CreateTexture(tag interface{}, img image.Image, filter g
t.useGLContext(func() { t.useGLContext(func() {
id, err = opengl.CreateTexture(img, filter) id, err = opengl.CreateTexture(img, filter)
}) })
if t.events == nil { ch <- graphics.TextureCreatedEvent{
return
}
t.events <- graphics.TextureCreatedEvent{
Tag: tag,
Id: id, Id: id,
Error: err, Error: err,
} }
close(ch)
}() }()
return ch
} }
func (t *sharedContext) CreateRenderTarget(tag interface{}, width, height int, filter graphics.Filter) { func (t *sharedContext) CreateRenderTarget(
width, height int,
filter graphics.Filter) <-chan graphics.RenderTargetCreatedEvent {
ch := make(chan graphics.RenderTargetCreatedEvent)
go func() { go func() {
<-t.inited <-t.inited
var id graphics.RenderTargetId var id graphics.RenderTargetId
@ -107,13 +102,11 @@ func (t *sharedContext) CreateRenderTarget(tag interface{}, width, height int, f
t.useGLContext(func() { t.useGLContext(func() {
id, err = opengl.CreateRenderTarget(width, height, filter) id, err = opengl.CreateRenderTarget(width, height, filter)
}) })
if t.events == nil { ch <- graphics.RenderTargetCreatedEvent{
return
}
t.events <- graphics.RenderTargetCreatedEvent{
Tag: tag,
Id: id, Id: id,
Error: err, Error: err,
} }
close(ch)
}() }()
return ch
} }