From d264d7a06bbc22df934aeadca5b0032dd64bb45c Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 13 Dec 2014 14:41:38 +0900 Subject: [PATCH] Remove dependencies on ebiten from shader --- affine.go | 10 +-- colormatrix.go | 8 +-- geometrymatrix.go | 8 +-- graphics.go | 1 + internal/glfw/canvas.go | 23 ++++++- internal/opengl/context.go | 2 +- internal/opengl/ids.go | 11 ++-- .../opengl/internal/shader/drawtexture.go | 16 ++--- internal/opengl/internal/shader/program.go | 17 +++-- .../opengl/internal/shader/texturequad.go | 46 +------------- internal/opengl/rendertarget.go | 11 ++-- internal/opengl/texture.go | 29 +++------ internal/opengl/texturequad.go | 62 +++++++++++++++++++ .../{internal/shader => }/texturequad_test.go | 2 +- 14 files changed, 137 insertions(+), 109 deletions(-) create mode 100644 internal/opengl/texturequad.go rename internal/opengl/{internal/shader => }/texturequad_test.go (97%) diff --git a/affine.go b/affine.go index 295132082..6ec14472f 100644 --- a/affine.go +++ b/affine.go @@ -18,7 +18,7 @@ package ebiten type affine interface { dim() int - element(i, j int) float64 + Element(i, j int) float64 setElement(i, j int, element float64) } @@ -26,7 +26,7 @@ func isIdentity(ebiten affine) bool { dim := ebiten.dim() for i := 0; i < dim-1; i++ { for j := 0; j < dim; j++ { - element := ebiten.element(i, j) + element := ebiten.Element(i, j) if i == j && element != 1 { return false } else if i != j && element != 0 { @@ -47,11 +47,11 @@ func mul(lhs, rhs, result affine) { for j := 0; j < dim; j++ { element := float64(0) for k := 0; k < dim-1; k++ { - element += lhs.element(i, k) * - rhs.element(k, j) + element += lhs.Element(i, k) * + rhs.Element(k, j) } if j == dim-1 { - element += lhs.element(i, j) + element += lhs.Element(i, j) } result.setElement(i, j, element) } diff --git a/colormatrix.go b/colormatrix.go index 213ac95f3..d2aec220e 100644 --- a/colormatrix.go +++ b/colormatrix.go @@ -42,6 +42,10 @@ func (c *ColorMatrix) dim() int { return ColorMatrixDim } +func (c *ColorMatrix) Element(i, j int) float64 { + return c.Elements[i][j] +} + func (c *ColorMatrix) Concat(other ColorMatrix) { result := ColorMatrix{} mul(&other, c, &result) @@ -52,10 +56,6 @@ func (c *ColorMatrix) IsIdentity() bool { 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) { c.Elements[i][j] = element } diff --git a/geometrymatrix.go b/geometrymatrix.go index 1316d6070..970a62a28 100644 --- a/geometrymatrix.go +++ b/geometrymatrix.go @@ -39,6 +39,10 @@ func (g *GeometryMatrix) dim() int { return GeometryMatrixDim } +func (g *GeometryMatrix) Element(i, j int) float64 { + return g.Elements[i][j] +} + func (g *GeometryMatrix) Concat(other GeometryMatrix) { result := GeometryMatrix{} mul(&other, g, &result) @@ -49,10 +53,6 @@ func (g *GeometryMatrix) IsIdentity() bool { 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) { g.Elements[i][j] = element } diff --git a/graphics.go b/graphics.go index 11f3016cc..cb0bece64 100644 --- a/graphics.go +++ b/graphics.go @@ -50,6 +50,7 @@ type GraphicsContext interface { Fill(r, g, b uint8) Texture(id TextureID) Drawer RenderTarget(id RenderTargetID) Drawer + // TODO: ScreenRenderTarget() Drawer PushRenderTarget(id RenderTargetID) PopRenderTarget() } diff --git a/internal/glfw/canvas.go b/internal/glfw/canvas.go index 19450ca77..2523c3ad7 100644 --- a/internal/glfw/canvas.go +++ b/internal/glfw/canvas.go @@ -17,6 +17,7 @@ limitations under the License. package glfw import ( + "github.com/go-gl/gl" glfw "github.com/go-gl/glfw3" "github.com/hajimehoshi/ebiten" "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 err error 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 } @@ -64,7 +74,16 @@ func (c *canvas) NewRenderTargetID(width, height int, filter ebiten.Filter) (ebi var id ebiten.RenderTargetID var err error 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 } diff --git a/internal/opengl/context.go b/internal/opengl/context.go index 5c79dd8c5..0f8455055 100644 --- a/internal/opengl/context.go +++ b/internal/opengl/context.go @@ -40,7 +40,7 @@ func Initialize(screenWidth, screenHeight, screenScale int) (*GraphicsContext, e }) var err error - c.screenID, err = idsInstance.createRenderTarget(screenWidth, screenHeight, ebiten.FilterNearest) + c.screenID, err = idsInstance.createRenderTarget(screenWidth, screenHeight, gl.NEAREST) if err != nil { return nil, err } diff --git a/internal/opengl/ids.go b/internal/opengl/ids.go index 6c5189cd6..2cbe728ac 100644 --- a/internal/opengl/ids.go +++ b/internal/opengl/ids.go @@ -41,11 +41,11 @@ var idsInstance = &ids{ 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) } -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) } @@ -67,7 +67,7 @@ func (i *ids) toTexture(id ebiten.RenderTargetID) ebiten.TextureID { 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) if err != nil { return 0, err @@ -81,7 +81,7 @@ func (i *ids) createTexture(img image.Image, filter ebiten.Filter) (ebiten.Textu 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) if err != nil { return 0, err @@ -148,7 +148,8 @@ func (i *ids) drawTexture(target ebiten.RenderTargetID, id ebiten.TextureID, par i.setViewportIfNeeded(target) r := i.renderTargetAt(target) 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) { diff --git a/internal/opengl/internal/shader/drawtexture.go b/internal/opengl/internal/shader/drawtexture.go index 5392a11ad..280f6d04a 100644 --- a/internal/opengl/internal/shader/drawtexture.go +++ b/internal/opengl/internal/shader/drawtexture.go @@ -18,15 +18,14 @@ package shader import ( "github.com/go-gl/gl" - "github.com/hajimehoshi/ebiten" "sync" ) -func glMatrix(ebiten [4][4]float64) [16]float32 { +func glMatrix(m [4][4]float64) [16]float32 { result := [16]float32{} for j := 0; j < 4; j++ { for i := 0; i < 4; i++ { - result[i+j*4] = float32(ebiten[i][j]) + result[i+j*4] = float32(m[i][j]) } } return result @@ -34,16 +33,19 @@ func glMatrix(ebiten [4][4]float64) [16]float32 { var once sync.Once +type Matrix interface { + Element(i, j int) float64 +} + // 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() { initialize() }) - if len(parts) == 0 { + if len(quads) == 0 { return } - quads := textureQuads(parts, width, height) // TODO: Check performance shaderProgram := use(glMatrix(projectionMatrix), geo, color) @@ -67,7 +69,7 @@ func DrawTexture(native gl.Texture, width, height int, projectionMatrix [4][4]fl vertices := []float32{} texCoords := []float32{} 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 { x1 := quad.VertexX1 x2 := quad.VertexX2 diff --git a/internal/opengl/internal/shader/program.go b/internal/opengl/internal/shader/program.go index a0d3b785c..7385b5844 100644 --- a/internal/opengl/internal/shader/program.go +++ b/internal/opengl/internal/shader/program.go @@ -18,7 +18,6 @@ package shader import ( "github.com/go-gl/gl" - "github.com/hajimehoshi/ebiten" ) type program struct { @@ -81,18 +80,18 @@ func getUniformLocation(program gl.Program, name string) gl.UniformLocation { 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. program := programColorMatrix getUniformLocation(program.native, "projection_matrix").UniformMatrix4fv(false, projectionMatrix) - a := float32(geo.Elements[0][0]) - b := float32(geo.Elements[0][1]) - c := float32(geo.Elements[1][0]) - d := float32(geo.Elements[1][1]) - tx := float32(geo.Elements[0][2]) - ty := float32(geo.Elements[1][2]) + a := float32(geo.Element(0, 0)) + b := float32(geo.Element(0, 1)) + c := float32(geo.Element(1, 0)) + d := float32(geo.Element(1, 1)) + tx := float32(geo.Element(0, 2)) + ty := float32(geo.Element(1, 2)) glModelviewMatrix := [...]float32{ a, c, 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{} for i := 0; i < 4; i++ { for j := 0; j < 5; j++ { - e[i][j] = float32(color.Elements[i][j]) + e[i][j] = float32(color.Element(i, j)) } } diff --git a/internal/opengl/internal/shader/texturequad.go b/internal/opengl/internal/shader/texturequad.go index 3f9245d7d..4a3758da9 100644 --- a/internal/opengl/internal/shader/texturequad.go +++ b/internal/opengl/internal/shader/texturequad.go @@ -16,11 +16,7 @@ limitations under the License. package shader -import ( - "github.com/hajimehoshi/ebiten" -) - -type textureQuad struct { +type TextureQuad struct { VertexX1 float32 VertexX2 float32 VertexY1 float32 @@ -30,43 +26,3 @@ type textureQuad struct { TextureCoordV1 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 -} diff --git a/internal/opengl/rendertarget.go b/internal/opengl/rendertarget.go index 4ffd7e633..510866af2 100644 --- a/internal/opengl/rendertarget.go +++ b/internal/opengl/rendertarget.go @@ -19,7 +19,6 @@ package opengl import ( "fmt" "github.com/go-gl/gl" - "github.com/hajimehoshi/ebiten/internal/opengl/internal/shader" ) 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) - width := shader.AdjustSizeForTexture(r.width) - height := shader.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 := shader.AdjustSizeForTexture(r.width) - height := shader.AdjustSizeForTexture(r.height) + 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(shader.AdjustSizeForTexture(r.height)) * 2 + ebiten[1][3] += float64(r.height) / float64(adjustSizeForTexture(r.height)) * 2 } return ebiten } diff --git a/internal/opengl/texture.go b/internal/opengl/texture.go index 05d6cc20a..ae0af2c1b 100644 --- a/internal/opengl/texture.go +++ b/internal/opengl/texture.go @@ -18,8 +18,6 @@ package opengl import ( "github.com/go-gl/gl" - "github.com/hajimehoshi/ebiten" - "github.com/hajimehoshi/ebiten/internal/opengl/internal/shader" "image" "image/draw" ) @@ -29,8 +27,8 @@ func adjustImageForTexture(img image.Image) *image.NRGBA { adjustedImageBounds := image.Rectangle{ image.ZP, image.Point{ - shader.AdjustSizeForTexture(width), - shader.AdjustSizeForTexture(height), + adjustSizeForTexture(width), + adjustSizeForTexture(height), }, } if nrgba, ok := img.(*image.NRGBA); ok && img.Bounds() == adjustedImageBounds { @@ -52,7 +50,7 @@ type texture struct { 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() if nativeTexture < 0 { panic("glGenTexture failed") @@ -61,31 +59,22 @@ func createNativeTexture(textureWidth, textureHeight int, pixels []uint8, filter nativeTexture.Bind(gl.TEXTURE_2D) defer gl.Texture(0).Bind(gl.TEXTURE_2D) - glFilter := 0 - switch 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.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, filter) + gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, filter) gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, textureWidth, textureHeight, 0, gl.RGBA, gl.UNSIGNED_BYTE, pixels) return nativeTexture } -func createTexture(width, height int, filter ebiten.Filter) (*texture, error) { - w := shader.AdjustSizeForTexture(width) - h := shader.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 } -func createTextureFromImage(img image.Image, filter ebiten.Filter) (*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) diff --git a/internal/opengl/texturequad.go b/internal/opengl/texturequad.go new file mode 100644 index 000000000..a2a66ed68 --- /dev/null +++ b/internal/opengl/texturequad.go @@ -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 +} diff --git a/internal/opengl/internal/shader/texturequad_test.go b/internal/opengl/texturequad_test.go similarity index 97% rename from internal/opengl/internal/shader/texturequad_test.go rename to internal/opengl/texturequad_test.go index 2d465c690..f25a73a2d 100644 --- a/internal/opengl/internal/shader/texturequad_test.go +++ b/internal/opengl/texturequad_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package shader_test +package opengl_test import ( . "."