Remove GraphicsContext; Add rendering methods to RenderTarget (#26)

This commit is contained in:
Hajime Hoshi 2014-12-20 20:54:14 +09:00
parent d4cd858eeb
commit 01e0d42451
22 changed files with 190 additions and 275 deletions

View File

@ -24,17 +24,17 @@ import (
type debugPrintState struct { type debugPrintState struct {
textTexture *ebiten.Texture textTexture *ebiten.Texture
debugPrintRenderTarget *ebiten.RenderTarget debugPrintRenderTarget ebiten.RenderTarget
y int y int
} }
var defaultDebugPrintState = new(debugPrintState) var defaultDebugPrintState = new(debugPrintState)
func DebugPrint(gr ebiten.GraphicsContext, str string) { func DebugPrint(r ebiten.RenderTarget, str string) {
defaultDebugPrintState.DebugPrint(gr, str) defaultDebugPrintState.DebugPrint(r, str)
} }
func (d *debugPrintState) drawText(gr ebiten.GraphicsContext, str string, x, y int, clr color.Color) { func (d *debugPrintState) drawText(r ebiten.RenderTarget, str string, x, y int, clr color.Color) {
parts := []ebiten.TexturePart{} parts := []ebiten.TexturePart{}
locationX, locationY := 0, 0 locationX, locationY := 0, 0
for _, c := range str { for _, c := range str {
@ -57,10 +57,10 @@ func (d *debugPrintState) drawText(gr ebiten.GraphicsContext, str string, x, y i
geom.Concat(ebiten.TranslateGeometry(float64(x)+1, float64(y))) geom.Concat(ebiten.TranslateGeometry(float64(x)+1, float64(y)))
clrm := ebiten.ColorMatrixI() clrm := ebiten.ColorMatrixI()
clrm.Concat(ebiten.ScaleColor(clr)) clrm.Concat(ebiten.ScaleColor(clr))
gr.DrawTexture(d.textTexture, parts, geom, clrm) r.DrawTexture(d.textTexture, parts, geom, clrm)
} }
func (d *debugPrintState) DebugPrint(gr ebiten.GraphicsContext, str string) { func (d *debugPrintState) DebugPrint(r ebiten.RenderTarget, str string) {
if d.textTexture == nil { if d.textTexture == nil {
img, err := assets.TextImage() img, err := assets.TextImage()
if err != nil { if err != nil {
@ -79,6 +79,6 @@ func (d *debugPrintState) DebugPrint(gr ebiten.GraphicsContext, str string) {
panic(err) panic(err)
} }
} }
d.drawText(gr, str, 1, d.y+1, &color.RGBA{0x00, 0x00, 0x00, 0x80}) d.drawText(r, str, 1, d.y+1, &color.RGBA{0x00, 0x00, 0x00, 0x80})
d.drawText(gr, str, 0, d.y, &color.RGBA{0xff, 0xff, 0xff, 0xff}) d.drawText(r, str, 0, d.y, &color.RGBA{0xff, 0xff, 0xff, 0xff})
} }

View File

@ -31,11 +31,11 @@ const (
type Game struct { type Game struct {
count int count int
tmpRenderTarget *ebiten.RenderTarget tmpRenderTarget ebiten.RenderTarget
ebitenTexture *ebiten.Texture ebitenTexture *ebiten.Texture
} }
func (g *Game) Update(gr ebiten.GraphicsContext) error { func (g *Game) Update(r ebiten.RenderTarget) error {
g.count++ g.count++
g.count %= 600 g.count %= 600
diff := float64(g.count) * 0.2 diff := float64(g.count) * 0.2
@ -45,21 +45,26 @@ func (g *Game) Update(gr ebiten.GraphicsContext) error {
case 240 < g.count: case 240 < g.count:
diff = float64(480-g.count) * 0.2 diff = float64(480-g.count) * 0.2
} }
_ = diff
gr.PushRenderTarget(g.tmpRenderTarget) if err := g.tmpRenderTarget.Clear(); err != nil {
gr.Clear() return err
}
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
geo := ebiten.TranslateGeometry(15+float64(i)*(diff), 20) geo := ebiten.TranslateGeometry(15+float64(i)*(diff), 20)
clr := ebiten.ScaleColor(color.RGBA{0xff, 0xff, 0xff, 0x80}) clr := ebiten.ScaleColor(color.RGBA{0xff, 0xff, 0xff, 0x80})
ebiten.DrawWholeTexture(gr, g.ebitenTexture, geo, clr) if err := ebiten.DrawWholeTexture(g.tmpRenderTarget, g.ebitenTexture, geo, clr); err != nil {
return err
}
} }
gr.PopRenderTarget()
gr.Fill(color.RGBA{0x00, 0x00, 0x80, 0xff}) r.Fill(color.RGBA{0x00, 0x00, 0x80, 0xff})
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
geo := ebiten.TranslateGeometry(0, float64(i)*(diff)) geo := ebiten.TranslateGeometry(0, float64(i)*(diff))
clr := ebiten.ColorMatrixI() clr := ebiten.ColorMatrixI()
ebiten.DrawWholeTexture(gr, g.tmpRenderTarget.Texture(), geo, clr) if err := ebiten.DrawWholeTexture(r, g.tmpRenderTarget.Texture(), geo, clr); err != nil {
return err
}
} }
return nil return nil
} }

View File

@ -112,11 +112,11 @@ func (f *Field) flushLine(j int) bool {
return true return true
} }
func (f *Field) Draw(context ebiten.GraphicsContext, textures *Textures, geo ebiten.GeometryMatrix) { func (f *Field) Draw(r ebiten.RenderTarget, textures *Textures, geo ebiten.GeometryMatrix) {
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, textures, blocks, geo) drawBlocks(r, textures, blocks, geo)
} }

View File

@ -32,7 +32,7 @@ func textWidth(str string) int {
return charWidth * len(str) return charWidth * len(str)
} }
func drawText(context ebiten.GraphicsContext, textures *Textures, str string, ox, oy, scale int, clr color.Color) { func drawText(r ebiten.RenderTarget, textures *Textures, str string, ox, oy, scale int, clr color.Color) {
fontTextureId := textures.GetTexture("font") fontTextureId := textures.GetTexture("font")
parts := []ebiten.TexturePart{} parts := []ebiten.TexturePart{}
@ -56,10 +56,10 @@ func drawText(context ebiten.GraphicsContext, textures *Textures, str string, ox
geoMat := ebiten.ScaleGeometry(float64(scale), float64(scale)) geoMat := ebiten.ScaleGeometry(float64(scale), float64(scale))
geoMat.Concat(ebiten.TranslateGeometry(float64(ox), float64(oy))) geoMat.Concat(ebiten.TranslateGeometry(float64(ox), float64(oy)))
clrMat := ebiten.ScaleColor(clr) clrMat := ebiten.ScaleColor(clr)
context.DrawTexture(fontTextureId, parts, geoMat, clrMat) r.DrawTexture(fontTextureId, parts, geoMat, clrMat)
} }
func drawTextWithShadow(context ebiten.GraphicsContext, textures *Textures, str string, x, y, scale int, clr color.Color) { func drawTextWithShadow(r ebiten.RenderTarget, 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(r, textures, str, x+1, y+1, scale, color.RGBA{0, 0, 0, 0x80})
drawText(context, textures, str, x, y, scale, clr) drawText(r, textures, str, x, y, scale, clr)
} }

View File

@ -70,7 +70,7 @@ func (game *Game) isInitialized() bool {
return true return true
} }
func (game *Game) Update(g ebiten.GraphicsContext) error { func (game *Game) Update(r ebiten.RenderTarget) error {
game.once.Do(func() { game.once.Do(func() {
game.textures = NewTextures() game.textures = NewTextures()
for name, path := range texturePaths { for name, path := range texturePaths {
@ -88,6 +88,6 @@ func (game *Game) Update(g ebiten.GraphicsContext) error {
SceneManager: game.sceneManager, SceneManager: game.sceneManager,
Input: game.input, Input: game.input,
}) })
game.sceneManager.Draw(g, game.textures) game.sceneManager.Draw(r, game.textures)
return nil return nil
} }

View File

@ -109,21 +109,21 @@ func (s *GameScene) Update(state *GameState) {
} }
} }
func (s *GameScene) Draw(context ebiten.GraphicsContext, textures *Textures) { func (s *GameScene) Draw(r ebiten.RenderTarget, textures *Textures) {
context.Fill(color.White) r.Fill(color.White)
field := textures.GetTexture("empty") field := textures.GetTexture("empty")
w, h := field.Size() w, h := field.Size()
geoMat := ebiten.ScaleGeometry(float64(fieldWidth)/float64(w), float64(fieldHeight)/float64(h)) geoMat := ebiten.ScaleGeometry(float64(fieldWidth)/float64(w), float64(fieldHeight)/float64(h))
geoMat.Concat(ebiten.TranslateGeometry(20, 20)) // TODO: magic number? geoMat.Concat(ebiten.TranslateGeometry(20, 20)) // TODO: magic number?
colorMat := ebiten.ScaleColor(color.RGBA{0, 0, 0, 0x80}) colorMat := ebiten.ScaleColor(color.RGBA{0, 0, 0, 0x80})
ebiten.DrawWholeTexture(context, field, geoMat, colorMat) ebiten.DrawWholeTexture(r, field, geoMat, colorMat)
geoMat = ebiten.GeometryMatrixI() geoMat = ebiten.GeometryMatrixI()
geoMat.Concat(ebiten.TranslateGeometry(20, 20)) geoMat.Concat(ebiten.TranslateGeometry(20, 20))
s.field.Draw(context, textures, geoMat) s.field.Draw(r, textures, geoMat)
if s.currentPiece != nil { if s.currentPiece != nil {
s.currentPiece.Draw(context, textures, 20, 20, s.currentPieceX, s.currentPieceY, s.currentPieceAngle) s.currentPiece.Draw(r, textures, 20, 20, s.currentPieceX, s.currentPieceY, s.currentPieceAngle)
} }
} }

View File

@ -138,7 +138,7 @@ const blockHeight = 10
const fieldBlockNumX = 10 const fieldBlockNumX = 10
const fieldBlockNumY = 20 const fieldBlockNumY = 20
func drawBlocks(context ebiten.GraphicsContext, textures *Textures, blocks [][]BlockType, geo ebiten.GeometryMatrix) { func drawBlocks(r ebiten.RenderTarget, textures *Textures, blocks [][]BlockType, geo ebiten.GeometryMatrix) {
parts := []ebiten.TexturePart{} parts := []ebiten.TexturePart{}
for i, blockCol := range blocks { for i, blockCol := range blocks {
for j, block := range blockCol { for j, block := range blockCol {
@ -153,7 +153,7 @@ func drawBlocks(context ebiten.GraphicsContext, textures *Textures, blocks [][]B
} }
} }
blocksTexture := textures.GetTexture("blocks") blocksTexture := textures.GetTexture("blocks")
context.DrawTexture(blocksTexture, parts, geo, ebiten.ColorMatrixI()) r.DrawTexture(blocksTexture, parts, geo, ebiten.ColorMatrixI())
} }
func (p *Piece) InitialPosition() (int, int) { func (p *Piece) InitialPosition() (int, int) {
@ -213,7 +213,7 @@ func (p *Piece) AbsorbInto(field *Field, x, y int, angle Angle) {
} }
} }
func (p *Piece) Draw(context ebiten.GraphicsContext, textures *Textures, fieldX, fieldY int, pieceX, pieceY int, angle Angle) { func (p *Piece) Draw(r ebiten.RenderTarget, 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 {
@ -230,5 +230,5 @@ func (p *Piece) Draw(context ebiten.GraphicsContext, textures *Textures, fieldX,
y := fieldY + pieceY*blockHeight y := fieldY + pieceY*blockHeight
geoMat.Concat(ebiten.TranslateGeometry(float64(x), float64(y))) geoMat.Concat(ebiten.TranslateGeometry(float64(x), float64(y)))
drawBlocks(context, textures, blocks, geoMat) drawBlocks(r, textures, blocks, geoMat)
} }

View File

@ -29,7 +29,7 @@ func init() {
type Scene interface { type Scene interface {
Update(state *GameState) Update(state *GameState)
Draw(context ebiten.GraphicsContext, textures *Textures) Draw(r ebiten.RenderTarget, textures *Textures)
} }
const transitionMaxCount = 20 const transitionMaxCount = 20
@ -60,29 +60,25 @@ func (s *SceneManager) Update(state *GameState) {
} }
} }
func (s *SceneManager) Draw(context ebiten.GraphicsContext, textures *Textures) { func (s *SceneManager) Draw(r ebiten.RenderTarget, textures *Textures) {
if s.transitionCount == -1 { if s.transitionCount == -1 {
s.current.Draw(context, textures) s.current.Draw(r, textures)
return return
} }
from := textures.GetRenderTarget("scene_manager_transition_from") from := textures.GetRenderTarget("scene_manager_transition_from")
context.PushRenderTarget(from) from.Clear()
context.Clear() s.current.Draw(from, textures)
s.current.Draw(context, textures)
context.PopRenderTarget()
to := textures.GetRenderTarget("scene_manager_transition_to") to := textures.GetRenderTarget("scene_manager_transition_to")
context.PushRenderTarget(to) to.Clear()
context.Clear() s.next.Draw(to, textures)
s.next.Draw(context, textures)
context.PopRenderTarget()
color := ebiten.ColorMatrixI() color := ebiten.ColorMatrixI()
ebiten.DrawWholeTexture(context, from.Texture(), ebiten.GeometryMatrixI(), color) ebiten.DrawWholeTexture(r, from.Texture(), ebiten.GeometryMatrixI(), color)
alpha := float64(s.transitionCount) / float64(transitionMaxCount) alpha := float64(s.transitionCount) / float64(transitionMaxCount)
color.Elements[3][3] = alpha color.Elements[3][3] = alpha
ebiten.DrawWholeTexture(context, to.Texture(), ebiten.GeometryMatrixI(), color) ebiten.DrawWholeTexture(r, to.Texture(), ebiten.GeometryMatrixI(), color)
} }
func (s *SceneManager) GoTo(scene Scene) { func (s *SceneManager) GoTo(scene Scene) {

View File

@ -38,7 +38,7 @@ type Textures struct {
texturePaths chan namePath texturePaths chan namePath
renderTargetSizes chan nameSize renderTargetSizes chan nameSize
textures map[string]*ebiten.Texture textures map[string]*ebiten.Texture
renderTargets map[string]*ebiten.RenderTarget renderTargets map[string]ebiten.RenderTarget
sync.RWMutex sync.RWMutex
} }
@ -47,7 +47,7 @@ func NewTextures() *Textures {
texturePaths: make(chan namePath), texturePaths: make(chan namePath),
renderTargetSizes: make(chan nameSize), renderTargetSizes: make(chan nameSize),
textures: map[string]*ebiten.Texture{}, textures: map[string]*ebiten.Texture{},
renderTargets: map[string]*ebiten.RenderTarget{}, renderTargets: map[string]ebiten.RenderTarget{},
} }
go func() { go func() {
for { for {
@ -129,7 +129,7 @@ func (t *Textures) GetTexture(name string) *ebiten.Texture {
return t.textures[name] return t.textures[name]
} }
func (t *Textures) GetRenderTarget(name string) *ebiten.RenderTarget { func (t *Textures) GetRenderTarget(name string) ebiten.RenderTarget {
t.RLock() t.RLock()
defer t.RUnlock() defer t.RUnlock()
return t.renderTargets[name] return t.renderTargets[name]

View File

@ -40,17 +40,17 @@ func (s *TitleScene) Update(state *GameState) {
} }
} }
func (s *TitleScene) Draw(context ebiten.GraphicsContext, textures *Textures) { func (s *TitleScene) Draw(r ebiten.RenderTarget, textures *Textures) {
drawTitleBackground(context, textures, s.count) drawTitleBackground(r, textures, s.count)
drawLogo(context, textures, "BLOCKS") drawLogo(r, 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, textures, message, x, y, 1, color.RGBA{0x80, 0, 0, 0xff}) drawTextWithShadow(r, textures, message, x, y, 1, color.RGBA{0x80, 0, 0, 0xff})
} }
func drawTitleBackground(context ebiten.GraphicsContext, textures *Textures, c int) { func drawTitleBackground(r ebiten.RenderTarget, textures *Textures, c int) {
const textureWidth = 32 const textureWidth = 32
const textureHeight = 32 const textureHeight = 32
@ -70,13 +70,13 @@ func drawTitleBackground(context ebiten.GraphicsContext, textures *Textures, c i
geo := ebiten.GeometryMatrixI() geo := ebiten.GeometryMatrixI()
geo.Concat(ebiten.TranslateGeometry(float64(dx), float64(dy))) geo.Concat(ebiten.TranslateGeometry(float64(dx), float64(dy)))
clr := ebiten.ColorMatrixI() clr := ebiten.ColorMatrixI()
context.DrawTexture(backgroundTexture, parts, geo, clr) r.DrawTexture(backgroundTexture, parts, geo, clr)
} }
func drawLogo(context ebiten.GraphicsContext, textures *Textures, str string) { func drawLogo(r ebiten.RenderTarget, 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, textures, str, x, y, scale, color.RGBA{0x00, 0x00, 0x80, 0xff}) drawTextWithShadow(r, textures, str, x, y, scale, color.RGBA{0x00, 0x00, 0x80, 0xff})
} }

View File

@ -36,7 +36,7 @@ type Game struct {
gophersTexture *ebiten.Texture gophersTexture *ebiten.Texture
} }
func (g *Game) Update(gr ebiten.GraphicsContext) error { func (g *Game) Update(r ebiten.RenderTarget) error {
g.count++ g.count++
if ebiten.IsKeyPressed(ebiten.KeyLeft) { if ebiten.IsKeyPressed(ebiten.KeyLeft) {
g.horizontalCount-- g.horizontalCount--
@ -60,7 +60,7 @@ func (g *Game) Update(gr ebiten.GraphicsContext) error {
geo.Concat(ebiten.TranslateGeometry(screenWidth/2, screenHeight/2)) geo.Concat(ebiten.TranslateGeometry(screenWidth/2, screenHeight/2))
//clr := ebiten.RotateHue(float64(g.count%180) * 2 * math.Pi / 180) //clr := ebiten.RotateHue(float64(g.count%180) * 2 * math.Pi / 180)
clr := ebiten.ColorMatrixI() clr := ebiten.ColorMatrixI()
ebiten.DrawWholeTexture(gr, g.gophersTexture, geo, clr) ebiten.DrawWholeTexture(r, g.gophersTexture, geo, clr)
return nil return nil
} }

View File

@ -32,17 +32,15 @@ const mosaicRatio = 16
type Game struct { type Game struct {
gophersTexture *ebiten.Texture gophersTexture *ebiten.Texture
gophersRenderTarget *ebiten.RenderTarget gophersRenderTarget ebiten.RenderTarget
} }
func (g *Game) Update(gr ebiten.GraphicsContext) error { func (g *Game) Update(r ebiten.RenderTarget) error {
gr.PushRenderTarget(g.gophersRenderTarget)
geo := ebiten.ScaleGeometry(1.0/mosaicRatio, 1.0/mosaicRatio) geo := ebiten.ScaleGeometry(1.0/mosaicRatio, 1.0/mosaicRatio)
ebiten.DrawWholeTexture(gr, g.gophersTexture, geo, ebiten.ColorMatrixI()) ebiten.DrawWholeTexture(g.gophersRenderTarget, g.gophersTexture, geo, ebiten.ColorMatrixI())
gr.PopRenderTarget()
geo = ebiten.ScaleGeometry(mosaicRatio/2.0, mosaicRatio/2.0) geo = ebiten.ScaleGeometry(mosaicRatio/2.0, mosaicRatio/2.0)
ebiten.DrawWholeTexture(gr, g.gophersRenderTarget.Texture(), geo, ebiten.ColorMatrixI()) ebiten.DrawWholeTexture(r, g.gophersRenderTarget.Texture(), geo, ebiten.ColorMatrixI())
return nil return nil
} }

View File

@ -33,39 +33,33 @@ const (
type Game struct { type Game struct {
inited bool inited bool
count int count int
brushRenderTarget *ebiten.RenderTarget brushRenderTarget ebiten.RenderTarget
canvasRenderTarget *ebiten.RenderTarget canvasRenderTarget ebiten.RenderTarget
} }
func (g *Game) Update(gr ebiten.GraphicsContext) error { func (g *Game) Update(r ebiten.RenderTarget) error {
if ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft) { if ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft) {
g.count++ g.count++
} }
if !g.inited { if !g.inited {
gr.PushRenderTarget(g.brushRenderTarget) g.brushRenderTarget.Fill(color.White)
gr.Fill(color.White) g.canvasRenderTarget.Fill(color.White)
gr.PopRenderTarget()
gr.PushRenderTarget(g.canvasRenderTarget)
gr.Fill(color.White)
gr.PopRenderTarget()
g.inited = true g.inited = true
} }
mx, my := ebiten.CursorPosition() mx, my := ebiten.CursorPosition()
if ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft) { if ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft) {
gr.PushRenderTarget(g.canvasRenderTarget)
geo := ebiten.TranslateGeometry(float64(mx), float64(my)) geo := ebiten.TranslateGeometry(float64(mx), float64(my))
clr := ebiten.ScaleColor(color.RGBA{0xff, 0x40, 0x40, 0xff}) clr := ebiten.ScaleColor(color.RGBA{0xff, 0x40, 0x40, 0xff})
theta := 2.0 * math.Pi * float64(g.count%60) / 60.0 theta := 2.0 * math.Pi * float64(g.count%60) / 60.0
clr.Concat(ebiten.RotateHue(theta)) clr.Concat(ebiten.RotateHue(theta))
ebiten.DrawWholeTexture(gr, g.brushRenderTarget.Texture(), geo, clr) ebiten.DrawWholeTexture(g.canvasRenderTarget, g.brushRenderTarget.Texture(), geo, clr)
gr.PopRenderTarget()
} }
ebiten.DrawWholeTexture(gr, g.canvasRenderTarget.Texture(), ebiten.GeometryMatrixI(), ebiten.ColorMatrixI()) ebiten.DrawWholeTexture(r, g.canvasRenderTarget.Texture(), ebiten.GeometryMatrixI(), ebiten.ColorMatrixI())
ebitenutil.DebugPrint(gr, fmt.Sprintf("(%d, %d)", mx, my)) ebitenutil.DebugPrint(r, fmt.Sprintf("(%d, %d)", mx, my))
return nil return nil
} }

View File

@ -32,7 +32,7 @@ type Game struct {
gophersTexture *ebiten.Texture gophersTexture *ebiten.Texture
} }
func (g *Game) Update(gr ebiten.GraphicsContext) error { func (g *Game) Update(r ebiten.RenderTarget) error {
parts := []ebiten.TexturePart{} parts := []ebiten.TexturePart{}
w, h := g.gophersTexture.Size() w, h := g.gophersTexture.Size()
for i := 0; i < h; i++ { for i := 0; i < h; i++ {
@ -48,7 +48,7 @@ func (g *Game) Update(gr ebiten.GraphicsContext) error {
geo := ebiten.TranslateGeometry(-maxWidth/2, -float64(h)/2) geo := ebiten.TranslateGeometry(-maxWidth/2, -float64(h)/2)
geo.Concat(ebiten.ScaleGeometry(0.4, 0.4)) geo.Concat(ebiten.ScaleGeometry(0.4, 0.4))
geo.Concat(ebiten.TranslateGeometry(screenWidth/2, screenHeight/2)) geo.Concat(ebiten.TranslateGeometry(screenWidth/2, screenHeight/2))
gr.DrawTexture(g.gophersTexture, parts, geo, ebiten.ColorMatrixI()) r.DrawTexture(g.gophersTexture, parts, geo, ebiten.ColorMatrixI())
return nil return nil
} }

View File

@ -48,7 +48,7 @@ func glFilter(f Filter) int {
} }
// NewRenderTarget returns a new RenderTarget. // NewRenderTarget returns a new RenderTarget.
func NewRenderTarget(width, height int, filter Filter) (*RenderTarget, error) { func NewRenderTarget(width, height int, filter Filter) (RenderTarget, error) {
return currentUI.newRenderTarget(width, height, glFilter(filter)) return currentUI.newRenderTarget(width, height, glFilter(filter))
} }

View File

@ -18,7 +18,6 @@ package ebiten
import ( import (
"github.com/hajimehoshi/ebiten/internal/opengl" "github.com/hajimehoshi/ebiten/internal/opengl"
"image/color"
) )
// A Rect represents a rectangle. // A Rect represents a rectangle.
@ -36,22 +35,12 @@ type TexturePart struct {
} }
// DrawWholeTexture draws the whole texture. // DrawWholeTexture draws the whole texture.
func DrawWholeTexture(g GraphicsContext, texture *Texture, geo GeometryMatrix, color ColorMatrix) error { func DrawWholeTexture(r RenderTarget, texture *Texture, geo GeometryMatrix, color ColorMatrix) error {
w, h := texture.Size() w, h := texture.Size()
parts := []TexturePart{ parts := []TexturePart{
{Rect{0, 0, float64(w), float64(h)}, Rect{0, 0, float64(w), float64(h)}}, {Rect{0, 0, float64(w), float64(h)}, Rect{0, 0, float64(w), float64(h)}},
} }
return g.DrawTexture(texture, parts, geo, color) return r.DrawTexture(texture, parts, geo, color)
}
// A GraphicsContext is the interface that means a context of rendering.
type GraphicsContext interface {
Clear() error
Fill(clr color.Color) error
DrawTexture(texture *Texture, parts []TexturePart, geo GeometryMatrix, color ColorMatrix) error
// TODO: ScreenRenderTarget() Drawer
PushRenderTarget(id *RenderTarget)
PopRenderTarget()
} }
// Filter represents the type of filter to be used when a texture is maginified or minified. // Filter represents the type of filter to be used when a texture is maginified or minified.
@ -72,19 +61,3 @@ type Texture struct {
func (t *Texture) Size() (width int, height int) { func (t *Texture) Size() (width int, height int) {
return t.glTexture.Width(), t.glTexture.Height() return t.glTexture.Width(), t.glTexture.Height()
} }
// RenderTarget represents a render target.
type RenderTarget struct {
glRenderTarget *opengl.RenderTarget
texture *Texture
}
// Texture returns the texture of the render target.
func (r *RenderTarget) Texture() *Texture {
return r.texture
}
// Size returns the size of the render target.
func (r *RenderTarget) Size() (width int, height int) {
return r.glRenderTarget.Width(), r.glRenderTarget.Height()
}

View File

@ -19,7 +19,6 @@ package ebiten
import ( import (
"github.com/go-gl/gl" "github.com/go-gl/gl"
"github.com/hajimehoshi/ebiten/internal/opengl" "github.com/hajimehoshi/ebiten/internal/opengl"
"image/color"
) )
func newGraphicsContext(screenWidth, screenHeight, screenScale int) (*graphicsContext, error) { func newGraphicsContext(screenWidth, screenHeight, screenScale int) (*graphicsContext, error) {
@ -28,32 +27,24 @@ func newGraphicsContext(screenWidth, screenHeight, screenScale int) (*graphicsCo
return nil, err return nil, err
} }
screen, err := idsInstance.createRenderTarget(screenWidth, screenHeight, gl.NEAREST) screen, err := newRenderTarget(screenWidth, screenHeight, gl.NEAREST)
if err != nil { if err != nil {
return nil, err return nil, err
} }
c := &graphicsContext{ c := &graphicsContext{
currents: make([]*RenderTarget, 1), defaultR: &renderTarget{r, nil},
defaultR: &RenderTarget{r, nil}, screen: screen,
screen: screen, screenScale: screenScale,
screenWidth: screenWidth,
screenHeight: screenHeight,
screenScale: screenScale,
} }
return c, nil return c, nil
} }
type graphicsContext struct { type graphicsContext struct {
screen *RenderTarget screen *renderTarget
defaultR *RenderTarget defaultR *renderTarget
currents []*RenderTarget screenScale int
screenWidth int
screenHeight int
screenScale int
} }
var _ GraphicsContext = new(graphicsContext)
func (c *graphicsContext) dispose() { func (c *graphicsContext) dispose() {
// NOTE: Now this method is not used anywhere. // NOTE: Now this method is not used anywhere.
glRenderTarget := c.screen.glRenderTarget glRenderTarget := c.screen.glRenderTarget
@ -64,44 +55,20 @@ func (c *graphicsContext) dispose() {
glTexture.Dispose() glTexture.Dispose()
} }
func (c *graphicsContext) Clear() error { func (c *graphicsContext) preUpdate() error {
return c.Fill(color.RGBA{0, 0, 0, 0}) return c.screen.Clear()
} }
func (c *graphicsContext) Fill(clr color.Color) error { func (c *graphicsContext) postUpdate() error {
return idsInstance.fillRenderTarget(c.currents[len(c.currents)-1], clr)
}
func (c *graphicsContext) DrawTexture(texture *Texture, parts []TexturePart, geo GeometryMatrix, color ColorMatrix) error {
current := c.currents[len(c.currents)-1]
return idsInstance.drawTexture(current, texture, parts, geo, color)
}
func (c *graphicsContext) PushRenderTarget(renderTarget *RenderTarget) {
c.currents = append(c.currents, renderTarget)
}
func (c *graphicsContext) PopRenderTarget() {
c.currents = c.currents[:len(c.currents)-1]
}
func (c *graphicsContext) preUpdate() {
c.currents = c.currents[0:1]
c.currents[0] = c.defaultR
c.PushRenderTarget(c.screen)
c.Clear()
}
func (c *graphicsContext) postUpdate() {
c.PopRenderTarget()
// We don't need to clear the default render target (framebuffer). // We don't need to clear the default render target (framebuffer).
// For the default framebuffer, a special shader is used. // For the default framebuffer, a special shader is used.
scale := float64(c.screenScale) scale := float64(c.screenScale)
geo := ScaleGeometry(scale, scale) geo := ScaleGeometry(scale, scale)
clr := ColorMatrixI() clr := ColorMatrixI()
DrawWholeTexture(c, c.screen.texture, geo, clr) if err := DrawWholeTexture(c.defaultR, c.screen.texture, geo, clr); err != nil {
return err
}
gl.Flush() gl.Flush()
return nil
} }

View File

@ -73,16 +73,11 @@ func (r *RenderTarget) Height() int {
return r.height return r.height
} }
func (r *RenderTarget) FlipY() bool {
return r.flipY
}
func (r *RenderTarget) Dispose() { func (r *RenderTarget) Dispose() {
r.framebuffer.Delete() r.framebuffer.Delete()
} }
func createFramebuffer(nativeTexture gl.Texture) (gl.Framebuffer, error) { func createFramebuffer(nativeTexture gl.Texture) (gl.Framebuffer, error) {
// TODO: Does this affect the current rendering target?
framebuffer := gl.GenFramebuffer() framebuffer := gl.GenFramebuffer()
framebuffer.Bind() framebuffer.Bind()
@ -102,7 +97,10 @@ func (r *RenderTarget) SetAsViewport() error {
r.framebuffer.Bind() r.framebuffer.Bind()
err := gl.CheckFramebufferStatus(gl.FRAMEBUFFER) err := gl.CheckFramebufferStatus(gl.FRAMEBUFFER)
if err != gl.FRAMEBUFFER_COMPLETE { if err != gl.FRAMEBUFFER_COMPLETE {
return errors.New(fmt.Sprintf("glBindFramebuffer failed: %d", err)) if gl.GetError() != 0 {
return errors.New(fmt.Sprintf("glBindFramebuffer failed: %d", gl.GetError()))
}
return errors.New("glBindFramebuffer failed: the context is different?")
} }
width := internal.AdjustSizeForTexture(r.width) width := internal.AdjustSizeForTexture(r.width)

View File

@ -25,72 +25,79 @@ import (
"math" "math"
) )
// ids manages the current render target to be used. // TODO: Rename
// TODO: Change this name. `ids` is not appropriate for now. type RenderTarget interface {
type ids struct { Texture() *Texture
currentRenderTarget *RenderTarget Size() (width, height int)
Clear() error
Fill(clr color.Color) error
DrawTexture(texture *Texture, parts []TexturePart, geo GeometryMatrix, color ColorMatrix) error
} }
var idsInstance = &ids{} type renderTarget struct {
glRenderTarget *opengl.RenderTarget
texture *Texture
}
func (i *ids) createRenderTarget(width, height int, filter int) (*RenderTarget, error) { func newRenderTarget(width, height int, filter int) (*renderTarget, error) {
glTexture, err := opengl.NewTexture(width, height, filter) glTexture, err := opengl.NewTexture(width, height, filter)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// The current binded framebuffer can be changed.
i.currentRenderTarget = nil
glRenderTarget, err := opengl.NewRenderTargetFromTexture(glTexture) glRenderTarget, err := opengl.NewRenderTargetFromTexture(glTexture)
if err != nil { if err != nil {
return nil, err return nil, err
} }
texture := &Texture{glTexture} texture := &Texture{glTexture}
// TODO: Is |texture| necessary? renderTarget := &renderTarget{glRenderTarget, texture}
renderTarget := &RenderTarget{glRenderTarget, texture}
i.fillRenderTarget(renderTarget, color.RGBA{0, 0, 0, 0})
return renderTarget, nil return renderTarget, nil
} }
func (i *ids) fillRenderTarget(renderTarget *RenderTarget, clr color.Color) error { func (r *renderTarget) Texture() *Texture {
if err := i.setViewportIfNeeded(renderTarget); err != nil { return r.texture
}
func (r *renderTarget) Size() (width, height int) {
return r.glRenderTarget.Width(), r.glRenderTarget.Height()
}
func (r *renderTarget) Clear() error {
return r.Fill(color.RGBA{0, 0, 0, 0})
}
func (r *renderTarget) Fill(clr color.Color) error {
if err := r.glRenderTarget.SetAsViewport(); err != nil {
return err return err
} }
const max = math.MaxUint16 const max = math.MaxUint16
r, g, b, a := clr.RGBA() cr, cg, cb, ca := clr.RGBA()
gl.ClearColor(gl.GLclampf(float64(r)/max), gl.GLclampf(float64(g)/max), gl.GLclampf(float64(b)/max), gl.GLclampf(float64(a)/max)) rf := gl.GLclampf(float64(cr) / max)
gf := gl.GLclampf(float64(cg) / max)
bf := gl.GLclampf(float64(cb) / max)
af := gl.GLclampf(float64(ca) / max)
gl.ClearColor(rf, gf, bf, af)
gl.Clear(gl.COLOR_BUFFER_BIT) gl.Clear(gl.COLOR_BUFFER_BIT)
return nil return nil
} }
func (i *ids) drawTexture(target *RenderTarget, texture *Texture, parts []TexturePart, geo GeometryMatrix, color ColorMatrix) error { func (r *renderTarget) DrawTexture(texture *Texture, parts []TexturePart, geo GeometryMatrix, color ColorMatrix) error {
glTexture := texture.glTexture if err := r.glRenderTarget.SetAsViewport(); err != nil {
if err := i.setViewportIfNeeded(target); err != nil {
return err return err
} }
projectionMatrix := target.glRenderTarget.ProjectionMatrix() glTexture := texture.glTexture
quads := textureQuads(parts, glTexture.Width(), glTexture.Height()) quads := textureQuads(parts, glTexture.Width(), glTexture.Height())
w, h := target.Size()
targetNativeTexture := gl.Texture(0) targetNativeTexture := gl.Texture(0)
if target.texture != nil { if r.texture != nil {
targetNativeTexture = target.texture.glTexture.Native() targetNativeTexture = r.texture.glTexture.Native()
} }
w, h := r.Size()
projectionMatrix := r.glRenderTarget.ProjectionMatrix()
shader.DrawTexture(glTexture.Native(), targetNativeTexture, w, h, projectionMatrix, quads, &geo, &color) shader.DrawTexture(glTexture.Native(), targetNativeTexture, w, h, projectionMatrix, quads, &geo, &color)
return nil return nil
} }
func (i *ids) setViewportIfNeeded(renderTarget *RenderTarget) error {
if i.currentRenderTarget != renderTarget {
if err := renderTarget.glRenderTarget.SetAsViewport(); err != nil {
return err
}
i.currentRenderTarget = renderTarget
}
return nil
}
func u(x float64, width int) float32 { func u(x float64, width int) float32 {
return float32(x) / float32(internal.AdjustSizeForTexture(width)) return float32(x) / float32(internal.AdjustSizeForTexture(width))
} }
@ -115,3 +122,41 @@ func textureQuads(parts []TexturePart, width, height int) []shader.TextureQuad {
} }
return quads return quads
} }
type syncer interface {
Sync(func())
}
type syncRenderTarget struct {
syncer syncer
inner RenderTarget
}
func (c *syncRenderTarget) Texture() *Texture {
return c.inner.Texture()
}
func (c *syncRenderTarget) Size() (width, height int) {
return c.inner.Size()
}
func (c *syncRenderTarget) Clear() (err error) {
c.syncer.Sync(func() {
err = c.inner.Clear()
})
return
}
func (c *syncRenderTarget) Fill(clr color.Color) (err error) {
c.syncer.Sync(func() {
err = c.inner.Fill(clr)
})
return
}
func (c *syncRenderTarget) DrawTexture(texture *Texture, parts []TexturePart, geo GeometryMatrix, color ColorMatrix) (err error) {
c.syncer.Sync(func() {
err = c.inner.DrawTexture(texture, parts, geo, color)
})
return
}

2
run.go
View File

@ -18,7 +18,7 @@ package ebiten
// Run runs the game. // Run runs the game.
// This function must be called from the main thread. // This function must be called from the main thread.
func Run(f func(GraphicsContext) error, width, height, scale int, title string) error { func Run(f func(RenderTarget) error, width, height, scale int, title string) error {
err := startUI(width, height, scale, title) err := startUI(width, height, scale, title)
if err != nil { if err != nil {
return err return err

View File

@ -1,65 +0,0 @@
/*
Copyright 2014 Hajime Hoshi
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ebiten
import (
"image/color"
)
type syncer interface {
Sync(f func())
}
type syncGraphicsContext struct {
syncer syncer
innerGraphicsContext GraphicsContext
}
var _ GraphicsContext = new(syncGraphicsContext)
func (c *syncGraphicsContext) Clear() (err error) {
c.syncer.Sync(func() {
err = c.innerGraphicsContext.Clear()
})
return
}
func (c *syncGraphicsContext) Fill(clr color.Color) (err error) {
c.syncer.Sync(func() {
err = c.innerGraphicsContext.Fill(clr)
})
return
}
func (c *syncGraphicsContext) DrawTexture(texture *Texture, parts []TexturePart, geo GeometryMatrix, color ColorMatrix) (err error) {
c.syncer.Sync(func() {
err = c.innerGraphicsContext.DrawTexture(texture, parts, geo, color)
})
return
}
func (c *syncGraphicsContext) PopRenderTarget() {
c.syncer.Sync(func() {
c.innerGraphicsContext.PopRenderTarget()
})
}
func (c *syncGraphicsContext) PushRenderTarget(renderTarget *RenderTarget) {
c.syncer.Sync(func() {
c.innerGraphicsContext.PushRenderTarget(renderTarget)
})
}

26
ui.go
View File

@ -110,18 +110,22 @@ func (u *ui) Sync(f func()) {
u.use(f) u.use(f)
} }
func (u *ui) draw(f func(GraphicsContext) error) (err error) { func (u *ui) draw(f func(RenderTarget) error) (err error) {
u.use(func() { u.use(func() {
u.graphicsContext.preUpdate() err = u.graphicsContext.preUpdate()
}) })
if err = f(&syncGraphicsContext{ if err != nil {
syncer: u, return
innerGraphicsContext: u.graphicsContext, }
}); err != nil { err = f(&syncRenderTarget{syncer: u, inner: u.graphicsContext.screen})
if err != nil {
return return
} }
u.use(func() { u.use(func() {
u.graphicsContext.postUpdate() err = u.graphicsContext.postUpdate()
if err != nil {
return
}
u.window.SwapBuffers() u.window.SwapBuffers()
}) })
return return
@ -140,13 +144,13 @@ func (u *ui) newTexture(img image.Image, filter int) (*Texture, error) {
return texture, err return texture, err
} }
func (u *ui) newRenderTarget(width, height int, filter int) (*RenderTarget, error) { func (u *ui) newRenderTarget(width, height int, filter int) (RenderTarget, error) {
var renderTarget *RenderTarget var renderTarget RenderTarget
var err error var err error
u.use(func() { u.use(func() {
renderTarget, err = idsInstance.createRenderTarget(width, height, filter) renderTarget, err = newRenderTarget(width, height, filter)
}) })
return renderTarget, err return &syncRenderTarget{u, renderTarget}, err
} }
func (u *ui) run() { func (u *ui) run() {