Remove dependencies on ebiten from shader

This commit is contained in:
Hajime Hoshi 2014-12-13 14:41:38 +09:00
parent 4addf8f9af
commit d264d7a06b
14 changed files with 137 additions and 109 deletions

View File

@ -18,7 +18,7 @@ package ebiten
type affine interface { type affine interface {
dim() int dim() int
element(i, j int) float64 Element(i, j int) float64
setElement(i, j int, element float64) setElement(i, j int, element float64)
} }
@ -26,7 +26,7 @@ func isIdentity(ebiten affine) bool {
dim := ebiten.dim() dim := ebiten.dim()
for i := 0; i < dim-1; i++ { for i := 0; i < dim-1; i++ {
for j := 0; j < dim; j++ { for j := 0; j < dim; j++ {
element := ebiten.element(i, j) element := ebiten.Element(i, j)
if i == j && element != 1 { if i == j && element != 1 {
return false return false
} else if i != j && element != 0 { } else if i != j && element != 0 {
@ -47,11 +47,11 @@ func mul(lhs, rhs, result affine) {
for j := 0; j < dim; j++ { for j := 0; j < dim; j++ {
element := float64(0) element := float64(0)
for k := 0; k < dim-1; k++ { for k := 0; k < dim-1; k++ {
element += lhs.element(i, k) * element += lhs.Element(i, k) *
rhs.element(k, j) rhs.Element(k, j)
} }
if j == dim-1 { if j == dim-1 {
element += lhs.element(i, j) element += lhs.Element(i, j)
} }
result.setElement(i, j, element) result.setElement(i, j, element)
} }

View File

@ -42,6 +42,10 @@ func (c *ColorMatrix) dim() int {
return ColorMatrixDim return ColorMatrixDim
} }
func (c *ColorMatrix) Element(i, j int) float64 {
return c.Elements[i][j]
}
func (c *ColorMatrix) Concat(other ColorMatrix) { func (c *ColorMatrix) Concat(other ColorMatrix) {
result := ColorMatrix{} result := ColorMatrix{}
mul(&other, c, &result) mul(&other, c, &result)
@ -52,10 +56,6 @@ func (c *ColorMatrix) IsIdentity() bool {
return isIdentity(c) return isIdentity(c)
} }
func (c *ColorMatrix) element(i, j int) float64 {
return c.Elements[i][j]
}
func (c *ColorMatrix) setElement(i, j int, element float64) { func (c *ColorMatrix) setElement(i, j int, element float64) {
c.Elements[i][j] = element c.Elements[i][j] = element
} }

View File

@ -39,6 +39,10 @@ func (g *GeometryMatrix) dim() int {
return GeometryMatrixDim return GeometryMatrixDim
} }
func (g *GeometryMatrix) Element(i, j int) float64 {
return g.Elements[i][j]
}
func (g *GeometryMatrix) Concat(other GeometryMatrix) { func (g *GeometryMatrix) Concat(other GeometryMatrix) {
result := GeometryMatrix{} result := GeometryMatrix{}
mul(&other, g, &result) mul(&other, g, &result)
@ -49,10 +53,6 @@ func (g *GeometryMatrix) IsIdentity() bool {
return isIdentity(g) return isIdentity(g)
} }
func (g *GeometryMatrix) element(i, j int) float64 {
return g.Elements[i][j]
}
func (g *GeometryMatrix) setElement(i, j int, element float64) { func (g *GeometryMatrix) setElement(i, j int, element float64) {
g.Elements[i][j] = element g.Elements[i][j] = element
} }

View File

@ -50,6 +50,7 @@ type GraphicsContext interface {
Fill(r, g, b uint8) Fill(r, g, b uint8)
Texture(id TextureID) Drawer Texture(id TextureID) Drawer
RenderTarget(id RenderTargetID) Drawer RenderTarget(id RenderTargetID) Drawer
// TODO: ScreenRenderTarget() Drawer
PushRenderTarget(id RenderTargetID) PushRenderTarget(id RenderTargetID)
PopRenderTarget() PopRenderTarget()
} }

View File

@ -17,6 +17,7 @@ limitations under the License.
package glfw package glfw
import ( import (
"github.com/go-gl/gl"
glfw "github.com/go-gl/glfw3" glfw "github.com/go-gl/glfw3"
"github.com/hajimehoshi/ebiten" "github.com/hajimehoshi/ebiten"
"github.com/hajimehoshi/ebiten/internal/opengl" "github.com/hajimehoshi/ebiten/internal/opengl"
@ -55,7 +56,16 @@ func (c *canvas) NewTextureID(img image.Image, filter ebiten.Filter) (ebiten.Tex
var id ebiten.TextureID var id ebiten.TextureID
var err error var err error
c.use(func() { c.use(func() {
id, err = opengl.NewTextureID(img, filter) glFilter := 0
switch filter {
case ebiten.FilterNearest:
glFilter = gl.NEAREST
case ebiten.FilterLinear:
glFilter = gl.LINEAR
default:
panic("not reached")
}
id, err = opengl.NewTextureID(img, glFilter)
}) })
return id, err return id, err
} }
@ -64,7 +74,16 @@ func (c *canvas) NewRenderTargetID(width, height int, filter ebiten.Filter) (ebi
var id ebiten.RenderTargetID var id ebiten.RenderTargetID
var err error var err error
c.use(func() { c.use(func() {
id, err = opengl.NewRenderTargetID(width, height, filter) glFilter := 0
switch filter {
case ebiten.FilterNearest:
glFilter = gl.NEAREST
case ebiten.FilterLinear:
glFilter = gl.LINEAR
default:
panic("not reached")
}
id, err = opengl.NewRenderTargetID(width, height, glFilter)
}) })
return id, err return id, err
} }

View File

@ -40,7 +40,7 @@ func Initialize(screenWidth, screenHeight, screenScale int) (*GraphicsContext, e
}) })
var err error var err error
c.screenID, err = idsInstance.createRenderTarget(screenWidth, screenHeight, ebiten.FilterNearest) c.screenID, err = idsInstance.createRenderTarget(screenWidth, screenHeight, gl.NEAREST)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -41,11 +41,11 @@ var idsInstance = &ids{
currentRenderTargetId: -1, currentRenderTargetId: -1,
} }
func NewRenderTargetID(width, height int, filter ebiten.Filter) (ebiten.RenderTargetID, error) { func NewRenderTargetID(width, height int, filter int) (ebiten.RenderTargetID, error) {
return idsInstance.createRenderTarget(width, height, filter) return idsInstance.createRenderTarget(width, height, filter)
} }
func NewTextureID(img image.Image, filter ebiten.Filter) (ebiten.TextureID, error) { func NewTextureID(img image.Image, filter int) (ebiten.TextureID, error) {
return idsInstance.createTexture(img, filter) return idsInstance.createTexture(img, filter)
} }
@ -67,7 +67,7 @@ func (i *ids) toTexture(id ebiten.RenderTargetID) ebiten.TextureID {
return i.renderTargetToTexture[id] return i.renderTargetToTexture[id]
} }
func (i *ids) createTexture(img image.Image, filter ebiten.Filter) (ebiten.TextureID, error) { func (i *ids) createTexture(img image.Image, filter int) (ebiten.TextureID, error) {
texture, err := createTextureFromImage(img, filter) texture, err := createTextureFromImage(img, filter)
if err != nil { if err != nil {
return 0, err return 0, err
@ -81,7 +81,7 @@ func (i *ids) createTexture(img image.Image, filter ebiten.Filter) (ebiten.Textu
return textureId, nil return textureId, nil
} }
func (i *ids) createRenderTarget(width, height int, filter ebiten.Filter) (ebiten.RenderTargetID, error) { func (i *ids) createRenderTarget(width, height int, filter int) (ebiten.RenderTargetID, error) {
texture, err := createTexture(width, height, filter) texture, err := createTexture(width, height, filter)
if err != nil { if err != nil {
return 0, err return 0, err
@ -148,7 +148,8 @@ func (i *ids) drawTexture(target ebiten.RenderTargetID, id ebiten.TextureID, par
i.setViewportIfNeeded(target) i.setViewportIfNeeded(target)
r := i.renderTargetAt(target) r := i.renderTargetAt(target)
projectionMatrix := r.projectionMatrix() projectionMatrix := r.projectionMatrix()
shader.DrawTexture(texture.native, texture.width, texture.height, projectionMatrix, parts, geo, color) 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 ebiten.RenderTargetID) {

View File

@ -18,15 +18,14 @@ package shader
import ( import (
"github.com/go-gl/gl" "github.com/go-gl/gl"
"github.com/hajimehoshi/ebiten"
"sync" "sync"
) )
func glMatrix(ebiten [4][4]float64) [16]float32 { func glMatrix(m [4][4]float64) [16]float32 {
result := [16]float32{} result := [16]float32{}
for j := 0; j < 4; j++ { for j := 0; j < 4; j++ {
for i := 0; i < 4; i++ { for i := 0; i < 4; i++ {
result[i+j*4] = float32(ebiten[i][j]) result[i+j*4] = float32(m[i][j])
} }
} }
return result return result
@ -34,16 +33,19 @@ func glMatrix(ebiten [4][4]float64) [16]float32 {
var once sync.Once var once sync.Once
type Matrix interface {
Element(i, j int) float64
}
// TODO: Use VBO // TODO: Use VBO
func DrawTexture(native gl.Texture, width, height int, projectionMatrix [4][4]float64, parts []ebiten.TexturePart, geo ebiten.GeometryMatrix, color ebiten.ColorMatrix) { func DrawTexture(native gl.Texture, projectionMatrix [4][4]float64, quads []TextureQuad, geo Matrix, color Matrix) {
once.Do(func() { once.Do(func() {
initialize() initialize()
}) })
if len(parts) == 0 { if len(quads) == 0 {
return return
} }
quads := textureQuads(parts, width, height)
// TODO: Check performance // TODO: Check performance
shaderProgram := use(glMatrix(projectionMatrix), geo, color) shaderProgram := use(glMatrix(projectionMatrix), geo, color)
@ -67,7 +69,7 @@ func DrawTexture(native gl.Texture, width, height int, projectionMatrix [4][4]fl
vertices := []float32{} vertices := []float32{}
texCoords := []float32{} texCoords := []float32{}
indicies := []uint32{} indicies := []uint32{}
// TODO: Check len(parts) and gl.MAX_ELEMENTS_INDICES? // TODO: Check len(quads) and gl.MAX_ELEMENTS_INDICES?
for i, quad := range quads { for i, quad := range quads {
x1 := quad.VertexX1 x1 := quad.VertexX1
x2 := quad.VertexX2 x2 := quad.VertexX2

View File

@ -18,7 +18,6 @@ package shader
import ( import (
"github.com/go-gl/gl" "github.com/go-gl/gl"
"github.com/hajimehoshi/ebiten"
) )
type program struct { type program struct {
@ -81,18 +80,18 @@ func getUniformLocation(program gl.Program, name string) gl.UniformLocation {
return location return location
} }
func use(projectionMatrix [16]float32, geo ebiten.GeometryMatrix, color ebiten.ColorMatrix) gl.Program { func use(projectionMatrix [16]float32, geo Matrix, color Matrix) gl.Program {
// TODO: Check the performance. // TODO: Check the performance.
program := programColorMatrix program := programColorMatrix
getUniformLocation(program.native, "projection_matrix").UniformMatrix4fv(false, projectionMatrix) getUniformLocation(program.native, "projection_matrix").UniformMatrix4fv(false, projectionMatrix)
a := float32(geo.Elements[0][0]) a := float32(geo.Element(0, 0))
b := float32(geo.Elements[0][1]) b := float32(geo.Element(0, 1))
c := float32(geo.Elements[1][0]) c := float32(geo.Element(1, 0))
d := float32(geo.Elements[1][1]) d := float32(geo.Element(1, 1))
tx := float32(geo.Elements[0][2]) tx := float32(geo.Element(0, 2))
ty := float32(geo.Elements[1][2]) ty := float32(geo.Element(1, 2))
glModelviewMatrix := [...]float32{ glModelviewMatrix := [...]float32{
a, c, 0, 0, a, c, 0, 0,
b, d, 0, 0, b, d, 0, 0,
@ -106,7 +105,7 @@ func use(projectionMatrix [16]float32, geo ebiten.GeometryMatrix, color ebiten.C
e := [4][5]float32{} e := [4][5]float32{}
for i := 0; i < 4; i++ { for i := 0; i < 4; i++ {
for j := 0; j < 5; j++ { for j := 0; j < 5; j++ {
e[i][j] = float32(color.Elements[i][j]) e[i][j] = float32(color.Element(i, j))
} }
} }

View File

@ -16,11 +16,7 @@ limitations under the License.
package shader package shader
import ( type TextureQuad struct {
"github.com/hajimehoshi/ebiten"
)
type textureQuad struct {
VertexX1 float32 VertexX1 float32
VertexX2 float32 VertexX2 float32
VertexY1 float32 VertexY1 float32
@ -30,43 +26,3 @@ type textureQuad struct {
TextureCoordV1 float32 TextureCoordV1 float32
TextureCoordV2 float32 TextureCoordV2 float32
} }
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) []textureQuad {
quads := []textureQuad{}
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 := textureQuad{x1, x2, y1, y2, u1, u2, v1, v2}
quads = append(quads, quad)
}
return quads
}

View File

@ -19,7 +19,6 @@ package opengl
import ( import (
"fmt" "fmt"
"github.com/go-gl/gl" "github.com/go-gl/gl"
"github.com/hajimehoshi/ebiten/internal/opengl/internal/shader"
) )
func orthoProjectionMatrix(left, right, bottom, top int) [4][4]float64 { func orthoProjectionMatrix(left, right, bottom, top int) [4][4]float64 {
@ -70,18 +69,18 @@ func (r *renderTarget) setAsViewport() {
gl.BlendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ZERO, gl.ONE) gl.BlendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ZERO, gl.ONE)
width := shader.AdjustSizeForTexture(r.width) width := adjustSizeForTexture(r.width)
height := shader.AdjustSizeForTexture(r.height) height := adjustSizeForTexture(r.height)
gl.Viewport(0, 0, width, height) gl.Viewport(0, 0, width, height)
} }
func (r *renderTarget) projectionMatrix() [4][4]float64 { func (r *renderTarget) projectionMatrix() [4][4]float64 {
width := shader.AdjustSizeForTexture(r.width) width := adjustSizeForTexture(r.width)
height := shader.AdjustSizeForTexture(r.height) height := adjustSizeForTexture(r.height)
ebiten := orthoProjectionMatrix(0, width, 0, height) ebiten := orthoProjectionMatrix(0, width, 0, height)
if r.flipY { if r.flipY {
ebiten[1][1] *= -1 ebiten[1][1] *= -1
ebiten[1][3] += float64(r.height) / float64(shader.AdjustSizeForTexture(r.height)) * 2 ebiten[1][3] += float64(r.height) / float64(adjustSizeForTexture(r.height)) * 2
} }
return ebiten return ebiten
} }

View File

@ -18,8 +18,6 @@ package opengl
import ( import (
"github.com/go-gl/gl" "github.com/go-gl/gl"
"github.com/hajimehoshi/ebiten"
"github.com/hajimehoshi/ebiten/internal/opengl/internal/shader"
"image" "image"
"image/draw" "image/draw"
) )
@ -29,8 +27,8 @@ func adjustImageForTexture(img image.Image) *image.NRGBA {
adjustedImageBounds := image.Rectangle{ adjustedImageBounds := image.Rectangle{
image.ZP, image.ZP,
image.Point{ image.Point{
shader.AdjustSizeForTexture(width), adjustSizeForTexture(width),
shader.AdjustSizeForTexture(height), adjustSizeForTexture(height),
}, },
} }
if nrgba, ok := img.(*image.NRGBA); ok && img.Bounds() == adjustedImageBounds { if nrgba, ok := img.(*image.NRGBA); ok && img.Bounds() == adjustedImageBounds {
@ -52,7 +50,7 @@ type texture struct {
height int height int
} }
func createNativeTexture(textureWidth, textureHeight int, pixels []uint8, filter ebiten.Filter) gl.Texture { func createNativeTexture(textureWidth, textureHeight int, pixels []uint8, filter int) gl.Texture {
nativeTexture := gl.GenTexture() nativeTexture := gl.GenTexture()
if nativeTexture < 0 { if nativeTexture < 0 {
panic("glGenTexture failed") panic("glGenTexture failed")
@ -61,31 +59,22 @@ func createNativeTexture(textureWidth, textureHeight int, pixels []uint8, filter
nativeTexture.Bind(gl.TEXTURE_2D) nativeTexture.Bind(gl.TEXTURE_2D)
defer gl.Texture(0).Bind(gl.TEXTURE_2D) defer gl.Texture(0).Bind(gl.TEXTURE_2D)
glFilter := 0 gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, filter)
switch filter { gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, filter)
case ebiten.FilterLinear:
glFilter = gl.LINEAR
case ebiten.FilterNearest:
glFilter = gl.NEAREST
default:
panic("not reached")
}
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, glFilter)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, glFilter)
gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, textureWidth, textureHeight, 0, gl.RGBA, gl.UNSIGNED_BYTE, pixels) gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, textureWidth, textureHeight, 0, gl.RGBA, gl.UNSIGNED_BYTE, pixels)
return nativeTexture return nativeTexture
} }
func createTexture(width, height int, filter ebiten.Filter) (*texture, error) { func createTexture(width, height int, filter int) (*texture, error) {
w := shader.AdjustSizeForTexture(width) w := adjustSizeForTexture(width)
h := shader.AdjustSizeForTexture(height) h := adjustSizeForTexture(height)
native := createNativeTexture(w, h, nil, filter) native := createNativeTexture(w, h, nil, filter)
return &texture{native, width, height}, nil return &texture{native, width, height}, nil
} }
func createTextureFromImage(img image.Image, filter ebiten.Filter) (*texture, error) { func createTextureFromImage(img image.Image, filter int) (*texture, error) {
adjustedImage := adjustImageForTexture(img) adjustedImage := adjustImageForTexture(img)
size := adjustedImage.Bounds().Size() size := adjustedImage.Bounds().Size()
native := createNativeTexture(size.X, size.Y, adjustedImage.Pix, filter) native := createNativeTexture(size.X, size.Y, adjustedImage.Pix, filter)

View File

@ -0,0 +1,62 @@
/*
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
}

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package shader_test package opengl_test
import ( import (
. "." . "."