graphics: Add DrawTriangles

Fixes #624
This commit is contained in:
Hajime Hoshi 2018-06-12 10:33:09 +09:00
parent 08e931782a
commit 2360b2930f
5 changed files with 252 additions and 2 deletions

120
examples/triangle/main.go Normal file
View File

@ -0,0 +1,120 @@
// Copyright 2018 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 example jsgo
package main
import (
"fmt"
"image/color"
"log"
"math"
"github.com/hajimehoshi/ebiten"
"github.com/hajimehoshi/ebiten/ebitenutil"
)
const (
screenWidth = 640
screenHeight = 480
)
var (
emptyImage, _ = ebiten.NewImage(16, 16, ebiten.FilterDefault)
)
func init() {
emptyImage.Fill(color.White)
}
var (
vertices []ebiten.Vertex
indices []uint16
)
func init() {
const (
num = 120
centerX = screenWidth / 2
centerY = screenHeight / 2
r = 160
)
for i := 0; i < num; i++ {
theta := float64(i) / num * 2 * math.Pi
cr := float32(0)
cg := float32(0)
cb := float32(0)
if 0 <= i && i < 2*num/3 {
cr = 2 * float32(i) / float32(num/3)
}
if num/3 <= i && i < 2*num/3 {
cr = 2 - 2*float32(i-num/3)/float32(num/3)
}
if num/3 <= i && i < 2*num/3 {
cg = 2 * float32(i-num/3) / float32(num/3)
}
if 2*num/3 <= i && i < num {
cg = 2 - 2*float32(i-2*num/3)/float32(num/3)
}
if 2*num/3 <= i && i < num {
cb = 2 * float32(i-2*num/3) / float32(num/3)
}
if 0 <= i && i < num/3 {
cb = 2 - 2*float32(i)/float32(num/3)
}
vertices = append(vertices, ebiten.Vertex{
DstX: float32(r*math.Cos(theta)) + centerX,
DstY: float32(r*math.Sin(theta)) + centerY,
SrcX: 0,
SrcY: 0,
ColorR: cr,
ColorG: cg,
ColorB: cb,
ColorA: 1,
})
}
vertices = append(vertices, ebiten.Vertex{
DstX: centerX,
DstY: centerY,
SrcX: 0,
SrcY: 0,
ColorR: 1,
ColorG: 1,
ColorB: 1,
ColorA: 1,
})
for i := 0; i < num; i++ {
indices = append(indices, uint16(i), uint16(i+1)%num, num)
}
}
func update(screen *ebiten.Image) error {
if ebiten.IsDrawingSkipped() {
return nil
}
op := &ebiten.DrawTrianglesOptions{}
screen.DrawTriangles(vertices, indices, emptyImage, op)
ebitenutil.DebugPrint(screen, fmt.Sprintf("TPS: %0.2f", ebiten.CurrentTPS()))
return nil
}
func main() {
if err := ebiten.Run(update, screenWidth, screenHeight, 1, "Triangle (Ebiten Demo)"); err != nil {
log.Fatal(err)
}
}

View File

@ -338,6 +338,84 @@ func (i *Image) drawImage(img *Image, options *DrawImageOptions) {
i.disposeMipmaps()
}
// Vertex represents a vertex passed to DrawTriangles.
//
// Note that this API is experimental.
type Vertex struct {
// DstX and DstY represents a point on a destination image.
DstX float32
DstY float32
// SrcX and SrcY represents a point on a source image.
SrcX float32
SrcY float32
// ColorR/ColorG/ColorB/ColorA represents color scaling values.
// 1 means the original source image color is used.
// 0 means a transparent color is used.
ColorR float32
ColorG float32
ColorB float32
ColorA float32
}
// DrawTrianglesOptions represents options to render triangles on an image.
//
// Note that this API is experimental.
type DrawTrianglesOptions struct {
// ColorM is a color matrix to draw.
// The default (zero) value is identity, which doesn't change any color.
// ColorM is applied before vertex color scale is applied.
ColorM ColorM
// CompositeMode is a composite mode to draw.
// The default (zero) value is regular alpha blending.
CompositeMode CompositeMode
// Filter is a type of texture filter.
// The default (zero) value is FilterDefault.
Filter Filter
}
// DrawTriangles draws a triangle with the specified vertices and their indices.
//
// If len(indices) is not multiple of 3, DrawTriangles panics.
//
// The rule in which DrawTriangles works effectively is same as DrawImage's.
//
// In contrast to DrawImage, DrawTriangles doesn't care source image edges.
// This means that you might need to add 1px gap on a source region when you render an image by DrawTriangles.
// Note that Ebiten creates texture atlases internally, so you still have to care this even when
// you render a single image.
//
// Note that this API is experimental.
func (i *Image) DrawTriangles(vertices []Vertex, indices []uint16, img *Image, options *DrawTrianglesOptions) {
if len(indices)%3 != 0 {
panic("ebiten: len(indices) % 3 must be 0")
}
// TODO: Check the maximum value of indices and len(vertices)?
if options == nil {
options = &DrawTrianglesOptions{}
}
mode := opengl.CompositeMode(options.CompositeMode)
filter := graphics.FilterNearest
if options.Filter != FilterDefault {
filter = graphics.Filter(options.Filter)
} else if img.filter != FilterDefault {
filter = graphics.Filter(img.filter)
}
vs := []float32{}
src := img.shareableImages[0]
for _, v := range vertices {
vs = append(vs, src.Vertex(float32(v.DstX), float32(v.DstY), v.SrcX, v.SrcY, v.ColorR, v.ColorG, v.ColorB, v.ColorA)...)
}
i.shareableImages[0].DrawImage(img.shareableImages[0], vs, indices, options.ColorM.impl, mode, filter)
}
// Bounds returns the bounds of the image.
func (i *Image) Bounds() image.Rectangle {
w, h := i.Size()

View File

@ -56,10 +56,19 @@ varying vec2 varying_tex_coord_min;
varying vec2 varying_tex_coord_max;
varying vec4 varying_color_scale;
bool isNaN(float x) {
return x != x;
}
void main(void) {
varying_tex_coord = vec2(tex_coord[0], tex_coord[1]);
varying_tex_coord_min = vec2(min(tex_coord[0], tex_coord[2]), min(tex_coord[1], tex_coord[3]));
varying_tex_coord_max = vec2(max(tex_coord[0], tex_coord[2]), max(tex_coord[1], tex_coord[3]));
if (!isNaN(tex_coord[2]) && !isNaN(tex_coord[3])) {
varying_tex_coord_min = vec2(min(tex_coord[0], tex_coord[2]), min(tex_coord[1], tex_coord[3]));
varying_tex_coord_max = vec2(max(tex_coord[0], tex_coord[2]), max(tex_coord[1], tex_coord[3]));
} else {
varying_tex_coord_min = vec2(0, 0);
varying_tex_coord_max = vec2(1, 1);
}
varying_color_scale = color_scale;
gl_Position = projection_matrix * vec4(vertex, 0, 1);
}

View File

@ -15,6 +15,8 @@
package graphicsutil
import (
"math"
"github.com/hajimehoshi/ebiten/internal/graphics"
"github.com/hajimehoshi/ebiten/internal/opengl"
)
@ -135,3 +137,35 @@ var (
func QuadIndices() []uint16 {
return quadIndices
}
var (
nan32 = float32(math.NaN())
)
func Vertex(width, height int, dx, dy, sx, sy float32, cr, cg, cb, ca float32) []float32 {
if !isPowerOf2(width) {
panic("not reached")
}
if !isPowerOf2(height) {
panic("not reached")
}
wf := float32(width)
hf := float32(height)
// Specifying a range explicitly here is redundant but this helps optimization
// to eliminate boundry checks.
vs := theVerticesBackend.sliceForOneQuad()[0:10]
vs[0] = dx
vs[1] = dy
vs[2] = sx / wf
vs[3] = sy / hf
vs[4] = nan32
vs[5] = nan32
vs[6] = cr
vs[7] = cg
vs[8] = cb
vs[9] = ca
return vs
}

View File

@ -193,6 +193,15 @@ func (i *Image) QuadVertices(sx0, sy0, sx1, sy1 int, a, b, c, d, tx, ty float32,
return graphicsutil.QuadVertices(w, h, sx0+ox, sy0+oy, sx1+ox, sy1+oy, a, b, c, d, tx, ty, cr, cg, cb, ca)
}
func (i *Image) Vertex(dx, dy, sx, sy float32, cr, cg, cb, ca float32) []float32 {
if i.backend == nil {
i.allocate(true)
}
ox, oy, _, _ := i.region()
w, h := i.backend.restorable.SizePowerOf2()
return graphicsutil.Vertex(w, h, dx, dy, sx+float32(ox), sy+float32(oy), cr, cg, cb, ca)
}
const MaxCountForShare = 10
func (i *Image) DrawImage(img *Image, vertices []float32, indices []uint16, colorm *affine.ColorM, mode opengl.CompositeMode, filter graphics.Filter) {