Remove dependencies on ebiten from opengl

This commit is contained in:
Hajime Hoshi 2014-12-14 15:26:10 +09:00
parent d264d7a06b
commit 76b7da090c
9 changed files with 166 additions and 167 deletions

View File

@ -16,6 +16,11 @@ limitations under the License.
package ebiten
import (
"github.com/hajimehoshi/ebiten/internal/opengl"
"github.com/hajimehoshi/ebiten/internal/opengl/internal/shader"
)
// A Rect represents a rectangle.
type Rect struct {
X int
@ -81,3 +86,28 @@ type RenderTargetID int
func (i RenderTargetID) IsNil() bool {
return i == 0
}
func u(x int, width int) float32 {
return float32(x) / float32(opengl.AdjustSizeForTexture(width))
}
func v(y int, height int) float32 {
return float32(y) / float32(opengl.AdjustSizeForTexture(height))
}
func textureQuads(parts []TexturePart, width, height int) []shader.TextureQuad {
quads := make([]shader.TextureQuad, 0, len(parts))
for _, part := range parts {
x1 := float32(part.LocationX)
x2 := float32(part.LocationX + part.Source.Width)
y1 := float32(part.LocationY)
y2 := float32(part.LocationY + part.Source.Height)
u1 := u(part.Source.X, width)
u2 := u(part.Source.X+part.Source.Width, width)
v1 := v(part.Source.Y, height)
v2 := v(part.Source.Y+part.Source.Height, height)
quad := shader.TextureQuad{x1, x2, y1, y2, u1, u2, v1, v2}
quads = append(quads, quad)
}
return quads
}

View File

@ -14,29 +14,29 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package opengl
package ebiten
import (
"github.com/go-gl/gl"
"github.com/hajimehoshi/ebiten"
"github.com/hajimehoshi/ebiten/internal/opengl"
)
func Initialize(screenWidth, screenHeight, screenScale int) (*GraphicsContext, error) {
func Initialize(screenWidth, screenHeight, screenScale int) (*graphicsContext, error) {
gl.Init()
gl.Enable(gl.TEXTURE_2D)
gl.Enable(gl.BLEND)
c := &GraphicsContext{
c := &graphicsContext{
screenWidth: screenWidth,
screenHeight: screenHeight,
screenScale: screenScale,
}
// The defualt framebuffer should be 0.
c.defaultID = idsInstance.addRenderTarget(&renderTarget{
width: screenWidth * screenScale,
height: screenHeight * screenScale,
flipY: true,
c.defaultID = idsInstance.addRenderTarget(&opengl.RenderTarget{
Width: screenWidth * screenScale,
Height: screenHeight * screenScale,
FlipY: true,
})
var err error
@ -46,76 +46,76 @@ func Initialize(screenWidth, screenHeight, screenScale int) (*GraphicsContext, e
}
// TODO: This is a special stack only for clearing. Can we change this?
c.currentIDs = []ebiten.RenderTargetID{c.screenID}
c.currentIDs = []RenderTargetID{c.screenID}
c.Clear()
return c, nil
}
type GraphicsContext struct {
screenID ebiten.RenderTargetID
defaultID ebiten.RenderTargetID
currentIDs []ebiten.RenderTargetID
type graphicsContext struct {
screenID RenderTargetID
defaultID RenderTargetID
currentIDs []RenderTargetID
screenWidth int
screenHeight int
screenScale int
}
var _ ebiten.GraphicsContext = new(GraphicsContext)
var _ GraphicsContext = new(graphicsContext)
func (c *GraphicsContext) dispose() {
func (c *graphicsContext) dispose() {
// NOTE: Now this method is not used anywhere.
idsInstance.deleteRenderTarget(c.screenID)
}
func (c *GraphicsContext) Clear() {
func (c *graphicsContext) Clear() {
c.Fill(0, 0, 0)
}
func (c *GraphicsContext) Fill(r, g, b uint8) {
func (c *graphicsContext) Fill(r, g, b uint8) {
idsInstance.fillRenderTarget(c.currentIDs[len(c.currentIDs)-1], r, g, b)
}
func (c *GraphicsContext) Texture(id ebiten.TextureID) ebiten.Drawer {
func (c *graphicsContext) Texture(id TextureID) Drawer {
return &textureWithContext{id, c}
}
func (c *GraphicsContext) RenderTarget(id ebiten.RenderTargetID) ebiten.Drawer {
func (c *graphicsContext) RenderTarget(id RenderTargetID) Drawer {
return &textureWithContext{idsInstance.toTexture(id), c}
}
func (c *GraphicsContext) PushRenderTarget(renderTargetID ebiten.RenderTargetID) {
func (c *graphicsContext) PushRenderTarget(renderTargetID RenderTargetID) {
c.currentIDs = append(c.currentIDs, renderTargetID)
}
func (c *GraphicsContext) PopRenderTarget() {
func (c *graphicsContext) PopRenderTarget() {
c.currentIDs = c.currentIDs[:len(c.currentIDs)-1]
}
func (c *GraphicsContext) PreUpdate() {
c.currentIDs = []ebiten.RenderTargetID{c.defaultID}
func (c *graphicsContext) PreUpdate() {
c.currentIDs = []RenderTargetID{c.defaultID}
c.PushRenderTarget(c.screenID)
c.Clear()
}
func (c *GraphicsContext) PostUpdate() {
func (c *graphicsContext) PostUpdate() {
c.PopRenderTarget()
c.Clear()
scale := float64(c.screenScale)
geo := ebiten.GeometryMatrixI()
geo := GeometryMatrixI()
geo.Scale(scale, scale)
ebiten.DrawWhole(c.RenderTarget(c.screenID), c.screenWidth, c.screenHeight, geo, ebiten.ColorMatrixI())
DrawWhole(c.RenderTarget(c.screenID), c.screenWidth, c.screenHeight, geo, ColorMatrixI())
gl.Flush()
}
type textureWithContext struct {
id ebiten.TextureID
context *GraphicsContext
id TextureID
context *graphicsContext
}
func (t *textureWithContext) Draw(parts []ebiten.TexturePart, geo ebiten.GeometryMatrix, color ebiten.ColorMatrix) {
func (t *textureWithContext) Draw(parts []TexturePart, geo GeometryMatrix, color ColorMatrix) {
currentID := t.context.currentIDs[len(t.context.currentIDs)-1]
idsInstance.drawTexture(currentID, t.id, parts, geo, color)
}

View File

@ -14,11 +14,11 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package opengl
package ebiten
import (
"github.com/go-gl/gl"
"github.com/hajimehoshi/ebiten"
"github.com/hajimehoshi/ebiten/internal/opengl"
"github.com/hajimehoshi/ebiten/internal/opengl/internal/shader"
"image"
"math"
@ -26,49 +26,49 @@ import (
)
type ids struct {
textures map[ebiten.TextureID]*texture
renderTargets map[ebiten.RenderTargetID]*renderTarget
renderTargetToTexture map[ebiten.RenderTargetID]ebiten.TextureID
textures map[TextureID]*opengl.Texture
renderTargets map[RenderTargetID]*opengl.RenderTarget
renderTargetToTexture map[RenderTargetID]TextureID
lastId int
currentRenderTargetId ebiten.RenderTargetID
currentRenderTargetId RenderTargetID
sync.RWMutex
}
var idsInstance = &ids{
textures: map[ebiten.TextureID]*texture{},
renderTargets: map[ebiten.RenderTargetID]*renderTarget{},
renderTargetToTexture: map[ebiten.RenderTargetID]ebiten.TextureID{},
textures: map[TextureID]*opengl.Texture{},
renderTargets: map[RenderTargetID]*opengl.RenderTarget{},
renderTargetToTexture: map[RenderTargetID]TextureID{},
currentRenderTargetId: -1,
}
func NewRenderTargetID(width, height int, filter int) (ebiten.RenderTargetID, error) {
func NewRenderTargetID(width, height int, filter int) (RenderTargetID, error) {
return idsInstance.createRenderTarget(width, height, filter)
}
func NewTextureID(img image.Image, filter int) (ebiten.TextureID, error) {
func NewTextureID(img image.Image, filter int) (TextureID, error) {
return idsInstance.createTexture(img, filter)
}
func (i *ids) textureAt(id ebiten.TextureID) *texture {
func (i *ids) textureAt(id TextureID) *opengl.Texture {
i.RLock()
defer i.RUnlock()
return i.textures[id]
}
func (i *ids) renderTargetAt(id ebiten.RenderTargetID) *renderTarget {
func (i *ids) renderTargetAt(id RenderTargetID) *opengl.RenderTarget {
i.RLock()
defer i.RUnlock()
return i.renderTargets[id]
}
func (i *ids) toTexture(id ebiten.RenderTargetID) ebiten.TextureID {
func (i *ids) toTexture(id RenderTargetID) TextureID {
i.RLock()
defer i.RUnlock()
return i.renderTargetToTexture[id]
}
func (i *ids) createTexture(img image.Image, filter int) (ebiten.TextureID, error) {
texture, err := createTextureFromImage(img, filter)
func (i *ids) createTexture(img image.Image, filter int) (TextureID, error) {
texture, err := opengl.CreateTextureFromImage(img, filter)
if err != nil {
return 0, err
}
@ -76,31 +76,31 @@ func (i *ids) createTexture(img image.Image, filter int) (ebiten.TextureID, erro
i.Lock()
defer i.Unlock()
i.lastId++
textureId := ebiten.TextureID(i.lastId)
textureId := TextureID(i.lastId)
i.textures[textureId] = texture
return textureId, nil
}
func (i *ids) createRenderTarget(width, height int, filter int) (ebiten.RenderTargetID, error) {
texture, err := createTexture(width, height, filter)
func (i *ids) createRenderTarget(width, height int, filter int) (RenderTargetID, error) {
texture, err := opengl.CreateTexture(width, height, filter)
if err != nil {
return 0, err
}
framebuffer := createFramebuffer(gl.Texture(texture.native))
framebuffer := opengl.CreateFramebuffer(texture.Native())
// The current binded framebuffer can be changed.
i.currentRenderTargetId = -1
r := &renderTarget{
framebuffer: framebuffer,
width: texture.width,
height: texture.height,
r := &opengl.RenderTarget{
Framebuffer: framebuffer,
Width: texture.Width(),
Height: texture.Height(),
}
i.Lock()
defer i.Unlock()
i.lastId++
textureId := ebiten.TextureID(i.lastId)
textureId := TextureID(i.lastId)
i.lastId++
renderTargetId := ebiten.RenderTargetID(i.lastId)
renderTargetId := RenderTargetID(i.lastId)
i.textures[textureId] = texture
i.renderTargets[renderTargetId] = r
@ -110,17 +110,17 @@ func (i *ids) createRenderTarget(width, height int, filter int) (ebiten.RenderTa
}
// NOTE: renderTarget can't be used as a texture.
func (i *ids) addRenderTarget(renderTarget *renderTarget) ebiten.RenderTargetID {
func (i *ids) addRenderTarget(renderTarget *opengl.RenderTarget) RenderTargetID {
i.Lock()
defer i.Unlock()
i.lastId++
id := ebiten.RenderTargetID(i.lastId)
id := RenderTargetID(i.lastId)
i.renderTargets[id] = renderTarget
return id
}
func (i *ids) deleteRenderTarget(id ebiten.RenderTargetID) {
func (i *ids) deleteRenderTarget(id RenderTargetID) {
i.Lock()
defer i.Unlock()
@ -128,34 +128,34 @@ func (i *ids) deleteRenderTarget(id ebiten.RenderTargetID) {
textureId := i.renderTargetToTexture[id]
texture := i.textures[textureId]
renderTarget.dispose()
texture.dispose()
renderTarget.Dispose()
texture.Dispose()
delete(i.renderTargets, id)
delete(i.renderTargetToTexture, id)
delete(i.textures, textureId)
}
func (i *ids) fillRenderTarget(id ebiten.RenderTargetID, r, g, b uint8) {
func (i *ids) fillRenderTarget(id RenderTargetID, r, g, b uint8) {
i.setViewportIfNeeded(id)
const max = float64(math.MaxUint8)
gl.ClearColor(gl.GLclampf(float64(r)/max), gl.GLclampf(float64(g)/max), gl.GLclampf(float64(b)/max), 1)
gl.Clear(gl.COLOR_BUFFER_BIT)
}
func (i *ids) drawTexture(target ebiten.RenderTargetID, id ebiten.TextureID, parts []ebiten.TexturePart, geo ebiten.GeometryMatrix, color ebiten.ColorMatrix) {
func (i *ids) drawTexture(target RenderTargetID, id TextureID, parts []TexturePart, geo GeometryMatrix, color ColorMatrix) {
texture := i.textureAt(id)
i.setViewportIfNeeded(target)
r := i.renderTargetAt(target)
projectionMatrix := r.projectionMatrix()
quads := textureQuads(parts, texture.width, texture.height)
shader.DrawTexture(texture.native, projectionMatrix, quads, &geo, &color)
projectionMatrix := r.ProjectionMatrix()
quads := textureQuads(parts, texture.Width(), texture.Height())
shader.DrawTexture(texture.Native(), projectionMatrix, quads, &geo, &color)
}
func (i *ids) setViewportIfNeeded(id ebiten.RenderTargetID) {
func (i *ids) setViewportIfNeeded(id RenderTargetID) {
r := i.renderTargetAt(id)
if i.currentRenderTargetId != id {
r.setAsViewport()
r.SetAsViewport()
i.currentRenderTargetId = id
}
}

View File

@ -20,15 +20,20 @@ import (
"github.com/go-gl/gl"
glfw "github.com/go-gl/glfw3"
"github.com/hajimehoshi/ebiten"
"github.com/hajimehoshi/ebiten/internal/opengl"
"image"
"runtime"
)
type GraphicsContext interface {
ebiten.GraphicsContext
PreUpdate()
PostUpdate()
}
type canvas struct {
window *glfw.Window
scale int
graphicsContext *opengl.GraphicsContext
graphicsContext GraphicsContext
input input
funcs chan func()
funcsDone chan struct{}
@ -65,7 +70,7 @@ func (c *canvas) NewTextureID(img image.Image, filter ebiten.Filter) (ebiten.Tex
default:
panic("not reached")
}
id, err = opengl.NewTextureID(img, glFilter)
id, err = ebiten.NewTextureID(img, glFilter)
})
return id, err
}
@ -83,7 +88,7 @@ func (c *canvas) NewRenderTargetID(width, height int, filter ebiten.Filter) (ebi
default:
panic("not reached")
}
id, err = opengl.NewRenderTargetID(width, height, glFilter)
id, err = ebiten.NewRenderTargetID(width, height, glFilter)
})
return id, err
}

View File

@ -21,7 +21,6 @@ import (
"fmt"
glfw "github.com/go-gl/glfw3"
"github.com/hajimehoshi/ebiten"
"github.com/hajimehoshi/ebiten/internal/opengl"
)
func init() {
@ -57,7 +56,7 @@ func (u *UI) Start(game ebiten.Game, width, height, scale int, title string) err
windowWidth, _ := window.GetFramebufferSize()
realScale := windowWidth / width
c.use(func() {
c.graphicsContext, err = opengl.Initialize(width, height, realScale)
c.graphicsContext, err = ebiten.Initialize(width, height, realScale)
})
if err != nil {
return err

View File

@ -35,14 +35,18 @@ func orthoProjectionMatrix(left, right, bottom, top int) [4][4]float64 {
}
}
type renderTarget struct {
framebuffer gl.Framebuffer
width int
height int
flipY bool
type RenderTarget struct {
Framebuffer gl.Framebuffer
Width int
Height int
FlipY bool
}
func createFramebuffer(nativeTexture gl.Texture) gl.Framebuffer {
func (r *RenderTarget) Dispose() {
r.Framebuffer.Delete()
}
func CreateFramebuffer(nativeTexture gl.Texture) gl.Framebuffer {
framebuffer := gl.GenFramebuffer()
framebuffer.Bind()
@ -59,9 +63,9 @@ func createFramebuffer(nativeTexture gl.Texture) gl.Framebuffer {
return framebuffer
}
func (r *renderTarget) setAsViewport() {
func (r *RenderTarget) SetAsViewport() {
gl.Flush()
r.framebuffer.Bind()
r.Framebuffer.Bind()
err := gl.CheckFramebufferStatus(gl.FRAMEBUFFER)
if err != gl.FRAMEBUFFER_COMPLETE {
panic(fmt.Sprintf("glBindFramebuffer failed: %d", err))
@ -69,22 +73,18 @@ func (r *renderTarget) setAsViewport() {
gl.BlendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ZERO, gl.ONE)
width := adjustSizeForTexture(r.width)
height := adjustSizeForTexture(r.height)
width := AdjustSizeForTexture(r.Width)
height := AdjustSizeForTexture(r.Height)
gl.Viewport(0, 0, width, height)
}
func (r *renderTarget) projectionMatrix() [4][4]float64 {
width := adjustSizeForTexture(r.width)
height := adjustSizeForTexture(r.height)
ebiten := orthoProjectionMatrix(0, width, 0, height)
if r.flipY {
ebiten[1][1] *= -1
ebiten[1][3] += float64(r.height) / float64(adjustSizeForTexture(r.height)) * 2
func (r *RenderTarget) ProjectionMatrix() [4][4]float64 {
width := AdjustSizeForTexture(r.Width)
height := AdjustSizeForTexture(r.Height)
m := orthoProjectionMatrix(0, width, 0, height)
if r.FlipY {
m[1][1] *= -1
m[1][3] += float64(r.Height) / float64(AdjustSizeForTexture(r.Height)) * 2
}
return ebiten
}
func (r *renderTarget) dispose() {
r.framebuffer.Delete()
return m
}

View File

@ -22,13 +22,28 @@ import (
"image/draw"
)
func NextPowerOf2(x uint64) uint64 {
x -= 1
x |= (x >> 1)
x |= (x >> 2)
x |= (x >> 4)
x |= (x >> 8)
x |= (x >> 16)
x |= (x >> 32)
return x + 1
}
func AdjustSizeForTexture(size int) int {
return int(NextPowerOf2(uint64(size)))
}
func adjustImageForTexture(img image.Image) *image.NRGBA {
width, height := img.Bounds().Size().X, img.Bounds().Size().Y
adjustedImageBounds := image.Rectangle{
image.ZP,
image.Point{
adjustSizeForTexture(width),
adjustSizeForTexture(height),
AdjustSizeForTexture(width),
AdjustSizeForTexture(height),
},
}
if nrgba, ok := img.(*image.NRGBA); ok && img.Bounds() == adjustedImageBounds {
@ -44,12 +59,24 @@ func adjustImageForTexture(img image.Image) *image.NRGBA {
return adjustedImage
}
type texture struct {
type Texture struct {
native gl.Texture
width int
height int
}
func (t *Texture) Native() gl.Texture {
return t.native
}
func (t *Texture) Width() int {
return t.width
}
func (t *Texture) Height() int {
return t.height
}
func createNativeTexture(textureWidth, textureHeight int, pixels []uint8, filter int) gl.Texture {
nativeTexture := gl.GenTexture()
if nativeTexture < 0 {
@ -67,20 +94,20 @@ func createNativeTexture(textureWidth, textureHeight int, pixels []uint8, filter
return nativeTexture
}
func createTexture(width, height int, filter int) (*texture, error) {
w := adjustSizeForTexture(width)
h := adjustSizeForTexture(height)
func CreateTexture(width, height int, filter int) (*Texture, error) {
w := AdjustSizeForTexture(width)
h := AdjustSizeForTexture(height)
native := createNativeTexture(w, h, nil, filter)
return &texture{native, width, height}, nil
return &Texture{native, width, height}, nil
}
func createTextureFromImage(img image.Image, filter int) (*texture, error) {
func CreateTextureFromImage(img image.Image, filter int) (*Texture, error) {
adjustedImage := adjustImageForTexture(img)
size := adjustedImage.Bounds().Size()
native := createNativeTexture(size.X, size.Y, adjustedImage.Pix, filter)
return &texture{native, size.X, size.Y}, nil
return &Texture{native, size.X, size.Y}, nil
}
func (t *texture) dispose() {
func (t *Texture) Dispose() {
t.native.Delete()
}

View File

@ -1,62 +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 opengl
import (
"github.com/hajimehoshi/ebiten"
"github.com/hajimehoshi/ebiten/internal/opengl/internal/shader"
)
func NextPowerOf2(x uint64) uint64 {
x -= 1
x |= (x >> 1)
x |= (x >> 2)
x |= (x >> 4)
x |= (x >> 8)
x |= (x >> 16)
x |= (x >> 32)
return x + 1
}
func adjustSizeForTexture(size int) int {
return int(NextPowerOf2(uint64(size)))
}
func u(x int, width int) float32 {
return float32(x) / float32(adjustSizeForTexture(width))
}
func v(y int, height int) float32 {
return float32(y) / float32(adjustSizeForTexture(height))
}
func textureQuads(parts []ebiten.TexturePart, width, height int) []shader.TextureQuad {
quads := make([]shader.TextureQuad, 0, len(parts))
for _, part := range parts {
x1 := float32(part.LocationX)
x2 := float32(part.LocationX + part.Source.Width)
y1 := float32(part.LocationY)
y2 := float32(part.LocationY + part.Source.Height)
u1 := u(part.Source.X, width)
u2 := u(part.Source.X+part.Source.Width, width)
v1 := v(part.Source.Y, height)
v2 := v(part.Source.Y+part.Source.Height, height)
quad := shader.TextureQuad{x1, x2, y1, y2, u1, u2, v1, v2}
quads = append(quads, quad)
}
return quads
}