From ebf7f0df00e904fd5d4d09467c59b5b1a60a69b0 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Tue, 25 Oct 2016 01:56:59 +0900 Subject: [PATCH] graphics: Add geometry matrix info to vertices --- imageimpl.go | 2 +- imageparts.go | 31 +++++++++++++++++-------- internal/graphics/program.go | 13 +++++++++++ internal/graphics/shader.go | 13 +++++++++-- math.go | 45 ++++++++++++++++++++++++++++++++++++ math_js.go | 31 +++++++++++++++++++++++++ 6 files changed, 123 insertions(+), 12 deletions(-) create mode 100644 math.go create mode 100644 math_js.go diff --git a/imageimpl.go b/imageimpl.go index d3cdfe9fe..08f0a1eac 100644 --- a/imageimpl.go +++ b/imageimpl.go @@ -128,7 +128,7 @@ func (i *imageImpl) DrawImage(image *Image, options *DrawImageOptions) error { } } w, h := image.impl.restorable.Size() - vs := vertices(parts, w, h) + vs := vertices(parts, w, h, &options.GeoM) if len(vs) == 0 { return nil } diff --git a/imageparts.go b/imageparts.go index de06ef4dc..4101f65c6 100644 --- a/imageparts.go +++ b/imageparts.go @@ -77,7 +77,8 @@ func v(y, height2p int) int16 { return int16(math.MaxInt16 * y / height2p) } -func vertices(parts ImageParts, width, height int) []uint8 { +func vertices(parts ImageParts, width, height int, geo *GeoM) []uint8 { + // TODO: This function should be in graphics package? totalSize := graphics.QuadVertexSizeInBytes() oneSize := totalSize / 4 l := parts.Len() @@ -86,6 +87,12 @@ func vertices(parts ImageParts, width, height int) []uint8 { height2p := graphics.NextPowerOf2Int(height) n := 0 vs := make([]int16, 16) + geoBytes := floatBytes(geo.Element(0, 0), + geo.Element(0, 1), + geo.Element(1, 0), + geo.Element(1, 1), + geo.Element(0, 2), + geo.Element(1, 2)) for i := 0; i < l; i++ { dx0, dy0, dx1, dy1 := parts.Dst(i) if dx0 == dx1 || dy0 == dy1 { @@ -114,15 +121,21 @@ func vertices(parts ImageParts, width, height int) []uint8 { vs[14] = u1 vs[15] = v1 // Use direct assign here. `append` function might be slow on browsers. - if endian.IsLittle() { - for i, v := range vs { - vertices[totalSize*n+oneSize*(i/4)+2*(i%4)] = uint8(v) - vertices[totalSize*n+oneSize*(i/4)+2*(i%4)+1] = uint8(v >> 8) + for j := 0; j < 4; j++ { + offset := totalSize*n + oneSize*j + if endian.IsLittle() { + for k, v := range vs[4*j : 4*j+4] { + vertices[offset+2*k] = uint8(v) + vertices[offset+2*k+1] = uint8(v >> 8) + } + } else { + for k, v := range vs[4*j : 4*j+4] { + vertices[offset+2*k] = uint8(v >> 8) + vertices[offset+2*k+1] = uint8(v) + } } - } else { - for i, v := range vs { - vertices[totalSize*n+oneSize*(i/4)+2*(i%4)] = uint8(v >> 8) - vertices[totalSize*n+oneSize*(i/4)+2*(i%4)+1] = uint8(v) + for k, g := range geoBytes { + vertices[offset+8+k] = g } } n++ diff --git a/internal/graphics/program.go b/internal/graphics/program.go index e7788c994..1eed62833 100644 --- a/internal/graphics/program.go +++ b/internal/graphics/program.go @@ -65,6 +65,7 @@ func (a *arrayBufferLayout) disable(c *opengl.Context, program opengl.Program) { var ( theArrayBufferLayout = arrayBufferLayout{ + // Note that GL_MAX_VERTEX_ATTRIBS is at least 16. parts: []arrayBufferLayoutPart{ { name: "vertex", @@ -78,6 +79,18 @@ var ( num: 2, normalize: true, }, + { + name: "geo_matrix", + dataType: opengl.Float, + num: 4, + normalize: false, + }, + { + name: "geo_matrix_translation", + dataType: opengl.Float, + num: 2, + normalize: false, + }, }, } ) diff --git a/internal/graphics/shader.go b/internal/graphics/shader.go index 2ca56994e..011b176b5 100644 --- a/internal/graphics/shader.go +++ b/internal/graphics/shader.go @@ -39,14 +39,23 @@ func shader(c *opengl.Context, id shaderId) string { var shaders = map[shaderId]string{ shaderVertexModelview: ` uniform highp mat4 projection_matrix; -uniform highp mat4 modelview_matrix; +uniform highp mat4 modelview_matrix; // TODO: Remove this attribute highp vec2 vertex; attribute highp vec2 tex_coord; +attribute highp vec4 geo_matrix; +attribute highp vec2 geo_matrix_translation; varying highp vec2 vertex_out_tex_coord; void main(void) { vertex_out_tex_coord = tex_coord; - gl_Position = projection_matrix * modelview_matrix * vec4(vertex, 0, 1); + mat4 x = modelview_matrix; // temporary hack not to omit modelview_matrix + mat4 geom = mat4( + vec4(geo_matrix[0], geo_matrix[2], 0, 0), + vec4(geo_matrix[1], geo_matrix[3], 0, 0), + vec4(0, 0, 1, 0), + vec4(geo_matrix_translation, 0, 1) + ); + gl_Position = projection_matrix * geom * vec4(vertex, 0, 1); } `, shaderFragmentTexture: ` diff --git a/math.go b/math.go new file mode 100644 index 000000000..6e2f54e4f --- /dev/null +++ b/math.go @@ -0,0 +1,45 @@ +// Copyright 2016 The Ebiten Authors +// +// 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. + +// +build !js + +package ebiten + +import ( + "unsafe" + + "github.com/hajimehoshi/ebiten/internal/endian" +) + +func floatBytes(xs ...float64) []uint8 { + bits := make([]uint8, 0, len(xs)*4) + for _, x := range xs { + x32 := float32(x) + n := *(*uint32)(unsafe.Pointer(&x32)) + if endian.IsLittle() { + bits = append(bits, + uint8(n), + uint8(n>>8), + uint8(n>>16), + uint8(n>>24)) + } else { + bits = append(bits, + uint8(n>>24), + uint8(n>>16), + uint8(n>>8), + uint8(n)) + } + } + return bits +} diff --git a/math_js.go b/math_js.go new file mode 100644 index 000000000..b5ee9581f --- /dev/null +++ b/math_js.go @@ -0,0 +1,31 @@ +// Copyright 2016 The Ebiten Authors +// +// 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. + +// +build js + +package ebiten + +import ( + "github.com/gopherjs/gopherjs/js" +) + +func floatBytes(xs ...float64) []uint8 { + a := js.Global.Get("ArrayBuffer").New(4 * len(xs)) + af32 := js.Global.Get("Float32Array").New(a) + a8 := js.Global.Get("Uint8Array").New(a) + for i, x := range xs { + af32.SetIndex(i, x) + } + return a8.Interface().([]uint8) +}