introduce new Target/Triangles/Drawer abstraction (very cool!)
This commit is contained in:
parent
d52be83efe
commit
4541fe2c8b
563
graphics.go
563
graphics.go
|
@ -1,416 +1,183 @@
|
||||||
package pixel
|
package pixel
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"image/color"
|
"image/color"
|
||||||
"math"
|
|
||||||
|
|
||||||
"github.com/faiface/pixel/pixelgl"
|
|
||||||
"github.com/go-gl/mathgl/mgl32"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Shape is a general drawable shape constructed from vertices.
|
// TrianglesData specifies a list of Triangles vertices with three common properties: Position,
|
||||||
|
// Color and Texture.
|
||||||
|
type TrianglesData []struct {
|
||||||
|
Position Vec
|
||||||
|
Color color.Color
|
||||||
|
Texture Vec
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the number of vertices in TrianglesData.
|
||||||
|
func (td *TrianglesData) Len() int {
|
||||||
|
return len(*td)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw is unimplemented for TrianglesData and panics.
|
||||||
|
func (td *TrianglesData) Draw() {
|
||||||
|
panic(fmt.Errorf("%T.Draw: invalid operation", td))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update copies vertex properties from the supplied Triangles into this TrianglesData.
|
||||||
//
|
//
|
||||||
// Vertices are specified in the vertex array of a shape. A shape can have a picture, a color
|
// TrianglesPosition, TrianglesColor and TrianglesTexture are supported.
|
||||||
// (mask) and a static transform.
|
func (td *TrianglesData) Update(t Triangles) {
|
||||||
|
if t.Len() > td.Len() {
|
||||||
|
*td = append(*td, make(TrianglesData, t.Len()-td.Len())...)
|
||||||
|
}
|
||||||
|
if t.Len() < td.Len() {
|
||||||
|
*td = (*td)[:t.Len()]
|
||||||
|
}
|
||||||
|
|
||||||
|
// fast path optimization
|
||||||
|
if t, ok := t.(*TrianglesData); ok {
|
||||||
|
copy(*td, *t)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if t, ok := t.(*TrianglesColorData); ok {
|
||||||
|
copy(*td, *(*TrianglesData)(t))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if t, ok := t.(*TrianglesTextureData); ok {
|
||||||
|
copy(*td, *(*TrianglesData)(t))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// slow path manual copy
|
||||||
|
if t, ok := t.(TrianglesPosition); ok {
|
||||||
|
for i := range *td {
|
||||||
|
(*td)[i].Position = t.Position(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if t, ok := t.(TrianglesColor); ok {
|
||||||
|
for i := range *td {
|
||||||
|
(*td)[i].Color = t.Color(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if t, ok := t.(TrianglesTexture); ok {
|
||||||
|
for i := range *td {
|
||||||
|
(*td)[i].Texture = t.Texture(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Position returns the position property of i-th vertex.
|
||||||
|
func (td *TrianglesData) Position(i int) Vec {
|
||||||
|
return (*td)[i].Position
|
||||||
|
}
|
||||||
|
|
||||||
|
// Color returns the color property of i-th vertex.
|
||||||
|
func (td *TrianglesData) Color(i int) color.Color {
|
||||||
|
return (*td)[i].Color
|
||||||
|
}
|
||||||
|
|
||||||
|
// Texture returns the texture property of i-th vertex.
|
||||||
|
func (td *TrianglesData) Texture(i int) Vec {
|
||||||
|
return (*td)[i].Texture
|
||||||
|
}
|
||||||
|
|
||||||
|
// TrianglesColorData is same as TrianglesData, except is lacks Texture property.
|
||||||
|
type TrianglesColorData TrianglesData
|
||||||
|
|
||||||
|
// Len returns the number of vertices in TrianglesColorData.
|
||||||
|
func (td *TrianglesColorData) Len() int {
|
||||||
|
return (*TrianglesData)(td).Len()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw is unimplemented for TrianglesColorData and panics.
|
||||||
|
func (td *TrianglesColorData) Draw() {
|
||||||
|
(*TrianglesData)(td).Draw()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update copies vertex properties from the supplied Triangles into this TrianglesColorData.
|
||||||
|
func (td *TrianglesColorData) Update(t Triangles) {
|
||||||
|
(*TrianglesData)(td).Update(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Position returns the position property of i-th vertex.
|
||||||
|
func (td *TrianglesColorData) Position(i int) Vec {
|
||||||
|
return (*TrianglesData)(td).Position(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Color returns the color property of i-th vertex.
|
||||||
|
func (td *TrianglesColorData) Color(i int) color.Color {
|
||||||
|
return (*TrianglesData)(td).Color(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TrianglesTextureData is same as TrianglesData, except is lacks Color property.
|
||||||
|
type TrianglesTextureData TrianglesData
|
||||||
|
|
||||||
|
// Len returns the number of vertices in TrianglesTextureData.
|
||||||
|
func (td *TrianglesTextureData) Len() int {
|
||||||
|
return (*TrianglesData)(td).Len()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw is unimplemented for TrianglesTextureData and panics.
|
||||||
|
func (td *TrianglesTextureData) Draw() {
|
||||||
|
(*TrianglesData)(td).Draw()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update copies vertex properties from the supplied Triangles into this TrianglesTextureData.
|
||||||
|
func (td *TrianglesTextureData) Update(t Triangles) {
|
||||||
|
(*TrianglesData)(td).Update(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Position returns the position property of i-th vertex.
|
||||||
|
func (td *TrianglesTextureData) Position(i int) Vec {
|
||||||
|
return (*TrianglesData)(td).Position(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Texture returns the texture property of i-th vertex.
|
||||||
|
func (td *TrianglesTextureData) Texture(i int) Vec {
|
||||||
|
return (*TrianglesData)(td).Texture(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TrianglesDrawer is a helper type that wraps Triangles and turns them into a Drawer.
|
||||||
//
|
//
|
||||||
// Usually you use this type only indirectly throught other specific shapes (sprites, polygons,
|
// It does so by creating a separate Triangles instance for each Target. The instances are
|
||||||
// ...) embedding it.
|
// correctly updated alongside the wrapped Triangles.
|
||||||
type Shape struct {
|
type TrianglesDrawer struct {
|
||||||
picture *Picture
|
Triangles
|
||||||
color color.Color
|
|
||||||
transform Transform
|
tris map[Target]Triangles
|
||||||
vertices []map[pixelgl.Attr]interface{}
|
dirty bool
|
||||||
vas map[Target]*pixelgl.VertexArray
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewShape creates a new shape with specified parent, picture, color, transform and vertex array.
|
func (td *TrianglesDrawer) flush() {
|
||||||
func NewShape(picture *Picture, c color.Color, transform Transform, vertices []map[pixelgl.Attr]interface{}) *Shape {
|
if !td.dirty {
|
||||||
return &Shape{
|
return
|
||||||
picture: picture,
|
}
|
||||||
color: c,
|
td.dirty = false
|
||||||
transform: transform,
|
|
||||||
vertices: vertices,
|
for _, t := range td.tris {
|
||||||
vas: make(map[Target]*pixelgl.VertexArray),
|
t.Update(td.Triangles)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetPicture changes the picture of a shape.
|
// Draw draws the wrapped Triangles onto the provided Target.
|
||||||
func (s *Shape) SetPicture(picture *Picture) {
|
func (td *TrianglesDrawer) Draw(target Target) {
|
||||||
s.picture = picture
|
if td.tris == nil {
|
||||||
|
td.tris = make(map[Target]Triangles)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Picture returns the current picture of a shape.
|
td.flush()
|
||||||
func (s *Shape) Picture() *Picture {
|
|
||||||
return s.picture
|
tri := td.tris[target]
|
||||||
|
if tri == nil {
|
||||||
|
tri = target.MakeTriangles(td.Triangles)
|
||||||
|
td.tris[target] = tri
|
||||||
|
}
|
||||||
|
tri.Draw()
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetColor changes the color (mask) of a shape.
|
// Update updates the wrapped Triangles with the supplied Triangles. Call only this method to
|
||||||
func (s *Shape) SetColor(c color.Color) {
|
// update the wrapped Triangles, otherwise the TrianglesDrawer will not work correctly.
|
||||||
s.color = c
|
func (td *TrianglesDrawer) Update(t Triangles) {
|
||||||
}
|
td.dirty = true
|
||||||
|
td.Triangles.Update(t)
|
||||||
// Color returns the current color (mask) of a shape.
|
|
||||||
func (s *Shape) Color() color.Color {
|
|
||||||
return s.color
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetTransform changes the ("static") transform of a shape.
|
|
||||||
func (s *Shape) SetTransform(transform Transform) {
|
|
||||||
s.transform = transform
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transform returns the current ("static") transform of a shape.
|
|
||||||
func (s *Shape) Transform() Transform {
|
|
||||||
return s.transform
|
|
||||||
}
|
|
||||||
|
|
||||||
// Vertices returns the vertex attribute values of all vertices in a shape.
|
|
||||||
//
|
|
||||||
// Do not change!
|
|
||||||
func (s *Shape) Vertices() []map[pixelgl.Attr]interface{} {
|
|
||||||
return s.vertices
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw draws a sprite transformed by the supplied transforms applied in the reverse order.
|
|
||||||
func (s *Shape) Draw(target Target, t ...Transform) {
|
|
||||||
mat := mgl32.Ident3()
|
|
||||||
for i := range t {
|
|
||||||
mat = mat.Mul3(t[i].Mat())
|
|
||||||
}
|
|
||||||
mat = mat.Mul3(s.transform.Mat())
|
|
||||||
|
|
||||||
c := NRGBAModel.Convert(s.color).(NRGBA)
|
|
||||||
|
|
||||||
if s.vas[target] == nil {
|
|
||||||
s.vas[target] = target.MakeVertexArray(s.vertices)
|
|
||||||
}
|
|
||||||
va := s.vas[target]
|
|
||||||
|
|
||||||
pixelgl.Do(func() {
|
|
||||||
target.Begin()
|
|
||||||
defer target.End()
|
|
||||||
|
|
||||||
target.Shader().SetUniformAttr(maskColorVec4, mgl32.Vec4{float32(c.R), float32(c.G), float32(c.B), float32(c.A)})
|
|
||||||
target.Shader().SetUniformAttr(transformMat3, mat)
|
|
||||||
|
|
||||||
if s.picture != nil {
|
|
||||||
s.picture.Texture().Begin()
|
|
||||||
va.Begin()
|
|
||||||
va.Draw()
|
|
||||||
va.End()
|
|
||||||
s.picture.Texture().End()
|
|
||||||
} else {
|
|
||||||
va.Begin()
|
|
||||||
va.Draw()
|
|
||||||
va.End()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// MultiShape is a shape composed of several other shapes. These shapes cannot be modifies
|
|
||||||
// after combined into a multishape.
|
|
||||||
//
|
|
||||||
// Using a multishape can greatly increase drawing performance. However, it's only usable when
|
|
||||||
// the relative transformations of the shapes don't change (e.g. static blocks in a level).
|
|
||||||
//
|
|
||||||
// All shapes in a multishape must share the same texture (or use no texture).
|
|
||||||
type MultiShape struct {
|
|
||||||
*Shape
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMultiShape creates a new multishape from several other shapes.
|
|
||||||
//
|
|
||||||
// If two of the supplied shapes have different pictures, this function panics.
|
|
||||||
func NewMultiShape(shapes ...*Shape) *MultiShape {
|
|
||||||
var picture *Picture
|
|
||||||
for _, shape := range shapes {
|
|
||||||
if picture != nil && shape.Picture() != nil && shape.Picture().Texture().ID() != picture.Texture().ID() {
|
|
||||||
panic(errors.New("failed to create multishape: shapes have different pictures"))
|
|
||||||
}
|
|
||||||
if shape.Picture() != nil {
|
|
||||||
picture = shape.Picture()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var vertices []map[pixelgl.Attr]interface{}
|
|
||||||
for _, shape := range shapes {
|
|
||||||
shapeVertices := shape.Vertices()
|
|
||||||
|
|
||||||
for vertex := range shapeVertices {
|
|
||||||
if pos, ok := shapeVertices[vertex][positionVec2]; ok {
|
|
||||||
pos := pos.(mgl32.Vec2)
|
|
||||||
pos = shape.Transform().Mat().Mul3x1(mgl32.Vec3{pos.X(), pos.Y(), 1}).Vec2()
|
|
||||||
shapeVertices[vertex][positionVec2] = pos
|
|
||||||
}
|
|
||||||
if color, ok := shapeVertices[vertex][colorVec4]; ok {
|
|
||||||
color := color.(mgl32.Vec4)
|
|
||||||
c := NRGBAModel.Convert(shape.Color()).(NRGBA)
|
|
||||||
color = mgl32.Vec4{
|
|
||||||
color[0] * float32(c.R),
|
|
||||||
color[1] * float32(c.G),
|
|
||||||
color[2] * float32(c.B),
|
|
||||||
color[3] * float32(c.A),
|
|
||||||
}
|
|
||||||
shapeVertices[vertex][colorVec4] = color
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
vertices = append(vertices, shapeVertices...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &MultiShape{NewShape(picture, color.White, Position(0), vertices)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sprite is a picture that can be drawn on the screen. Optionally it can be color masked
|
|
||||||
// or tranformed.
|
|
||||||
//
|
|
||||||
// Usually, you only transform objects when you're drawing them (by passing transforms to the
|
|
||||||
// Draw method). With sprites however, it can be useful to also transform them "statically". For
|
|
||||||
// example, sprites are anchor by their bottom-left corner by default. Setting a transform can
|
|
||||||
// change this anchored to the center, or wherever you want.
|
|
||||||
type Sprite struct {
|
|
||||||
*Shape
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewSprite creates a new sprite with the supplied picture. The sprite's size is the size of
|
|
||||||
// the supplied picture. If you want to change the sprite's size, change it's transform.
|
|
||||||
func NewSprite(picture *Picture) *Sprite {
|
|
||||||
w, h := picture.Bounds().Size.XY()
|
|
||||||
|
|
||||||
vertices := make([]map[pixelgl.Attr]interface{}, 4)
|
|
||||||
for i, p := range []Vec{V(0, 0), V(w, 0), V(w, h), V(0, h)} {
|
|
||||||
texCoord := V(
|
|
||||||
(picture.Bounds().X()+p.X())/float64(picture.Texture().Width()),
|
|
||||||
(picture.Bounds().Y()+p.Y())/float64(picture.Texture().Height()),
|
|
||||||
)
|
|
||||||
|
|
||||||
vertices[i] = map[pixelgl.Attr]interface{}{
|
|
||||||
positionVec2: mgl32.Vec2{float32(p.X()), float32(p.Y())},
|
|
||||||
colorVec4: mgl32.Vec4{1, 1, 1, 1},
|
|
||||||
texCoordVec2: mgl32.Vec2{float32(texCoord.X()), float32(texCoord.Y())},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
vertices = []map[pixelgl.Attr]interface{}{
|
|
||||||
vertices[0],
|
|
||||||
vertices[1],
|
|
||||||
vertices[2],
|
|
||||||
vertices[0],
|
|
||||||
vertices[2],
|
|
||||||
vertices[3],
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Sprite{NewShape(picture, color.White, Position(0), vertices)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// LineColor a line shape (with sharp ends) filled with a single color.
|
|
||||||
type LineColor struct {
|
|
||||||
*Shape
|
|
||||||
a, b Vec
|
|
||||||
width float64
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewLineColor creates a new line shape between points A and B filled with a single color.
|
|
||||||
func NewLineColor(c color.Color, a, b Vec, width float64) *LineColor {
|
|
||||||
vertices := make([]map[pixelgl.Attr]interface{}, 4)
|
|
||||||
for i := 0; i < 4; i++ {
|
|
||||||
vertices[i] = map[pixelgl.Attr]interface{}{
|
|
||||||
colorVec4: mgl32.Vec4{1, 1, 1, 1},
|
|
||||||
texCoordVec2: mgl32.Vec2{-1, -1},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
vertices = []map[pixelgl.Attr]interface{}{
|
|
||||||
vertices[0],
|
|
||||||
vertices[1],
|
|
||||||
vertices[2],
|
|
||||||
vertices[1],
|
|
||||||
vertices[2],
|
|
||||||
vertices[3],
|
|
||||||
}
|
|
||||||
|
|
||||||
lc := &LineColor{NewShape(nil, c, Position(0), vertices), a, b, width}
|
|
||||||
lc.setPoints()
|
|
||||||
return lc
|
|
||||||
}
|
|
||||||
|
|
||||||
// setPoints updates the vertex array data according to A, B and width.
|
|
||||||
func (lc *LineColor) setPoints() {
|
|
||||||
r := (lc.b - lc.a).Unit().Scaled(lc.width / 2).Rotated(math.Pi / 2)
|
|
||||||
for i, p := range []Vec{lc.a - r, lc.a + r, lc.b - r, lc.b + r} {
|
|
||||||
lc.va.SetVertexAttr(i, positionVec2, mgl32.Vec2{float32(p.X()), float32(p.Y())})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetA changes the position of the first endpoint of a line.
|
|
||||||
func (lc *LineColor) SetA(a Vec) {
|
|
||||||
lc.a = a
|
|
||||||
lc.setPoints()
|
|
||||||
}
|
|
||||||
|
|
||||||
// A returns the current position of the first endpoint of a line.
|
|
||||||
func (lc *LineColor) A() Vec {
|
|
||||||
return lc.a
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetB changes the position of the second endpoint of a line.
|
|
||||||
func (lc *LineColor) SetB(b Vec) {
|
|
||||||
lc.b = b
|
|
||||||
lc.setPoints()
|
|
||||||
}
|
|
||||||
|
|
||||||
// B returns the current position of the second endpoint of a line.
|
|
||||||
func (lc *LineColor) B() Vec {
|
|
||||||
return lc.b
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetWidth changes the width of a line.
|
|
||||||
func (lc *LineColor) SetWidth(width float64) {
|
|
||||||
lc.width = width
|
|
||||||
lc.setPoints()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Width returns the current width of a line.
|
|
||||||
func (lc *LineColor) Width() float64 {
|
|
||||||
return lc.width
|
|
||||||
}
|
|
||||||
|
|
||||||
// PolygonColor is a convex polygon shape filled with a single color.
|
|
||||||
type PolygonColor struct {
|
|
||||||
*Shape
|
|
||||||
points []Vec
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewPolygonColor creates a new polygon shape filled with a single color. Parent is an object
|
|
||||||
// that this shape belongs to, such as a window, or a graphics effect.
|
|
||||||
func NewPolygonColor(parent pixelgl.Doer, c color.Color, points ...Vec) *PolygonColor {
|
|
||||||
var va *pixelgl.VertexArray
|
|
||||||
|
|
||||||
var indices []int
|
|
||||||
for i := 2; i < len(points); i++ {
|
|
||||||
indices = append(indices, 0, i-1, i)
|
|
||||||
}
|
|
||||||
|
|
||||||
parent.Do(func(ctx pixelgl.Context) {
|
|
||||||
var err error
|
|
||||||
va, err = pixelgl.NewVertexArray(
|
|
||||||
pixelgl.ContextHolder{Context: ctx},
|
|
||||||
ctx.Shader().VertexFormat(),
|
|
||||||
len(points),
|
|
||||||
indices,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
panic(errors.Wrap(err, "failed to create polygon"))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
vertices := make([]map[pixelgl.Attr]interface{}, len(points))
|
|
||||||
|
|
||||||
for i, p := range points {
|
|
||||||
vertices[i] = map[pixelgl.Attr]interface{}{
|
|
||||||
positionVec2: mgl32.Vec2{float32(p.X()), float32(p.Y())},
|
|
||||||
colorVec4: mgl32.Vec4{1, 1, 1, 1},
|
|
||||||
texCoordVec2: mgl32.Vec2{-1, -1},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
va.SetVertices(vertices)
|
|
||||||
|
|
||||||
return &PolygonColor{NewShape(parent, nil, c, Position(0), va), points}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NumPoints returns the number of points in a polygon.
|
|
||||||
func (pc *PolygonColor) NumPoints() int {
|
|
||||||
return len(pc.points)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetPoint changes the position of a point in a polygon.
|
|
||||||
//
|
|
||||||
// If the index is out of range, this function panics.
|
|
||||||
func (pc *PolygonColor) SetPoint(i int, point Vec) {
|
|
||||||
pc.points[i] = point
|
|
||||||
pc.va.SetVertexAttr(i, positionVec2, mgl32.Vec2{float32(point.X()), float32(point.Y())})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Point returns the position of a point in a polygon.
|
|
||||||
//
|
|
||||||
// If the index is out of range, this function panics.
|
|
||||||
func (pc *PolygonColor) Point(i int) Vec {
|
|
||||||
return pc.points[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
// EllipseColor is an ellipse shape filled with a single color.
|
|
||||||
type EllipseColor struct {
|
|
||||||
*Shape
|
|
||||||
radius Vec
|
|
||||||
fill float64
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewEllipseColor creates a new ellipse shape filled with a single color. Parent is an object
|
|
||||||
// that this shape belongs to, such as a window, or a graphics effect. Fill should be a number
|
|
||||||
// between 0 and 1 which specifies how much of the ellipse will be filled (from the outside). The
|
|
||||||
// value of 1 means that the whole ellipse is filled. The value of 0 means that none of the
|
|
||||||
// ellipse is filled (which makes the ellipse invisible).
|
|
||||||
func NewEllipseColor(parent pixelgl.Doer, c color.Color, radius Vec, fill float64) *EllipseColor {
|
|
||||||
var va *pixelgl.VertexArray
|
|
||||||
|
|
||||||
const n = 256
|
|
||||||
|
|
||||||
var indices []int
|
|
||||||
for i := 2; i < (n+1)*2; i++ {
|
|
||||||
indices = append(indices, i-2, i-1, i)
|
|
||||||
}
|
|
||||||
|
|
||||||
parent.Do(func(ctx pixelgl.Context) {
|
|
||||||
var err error
|
|
||||||
va, err = pixelgl.NewVertexArray(
|
|
||||||
pixelgl.ContextHolder{Context: ctx},
|
|
||||||
ctx.Shader().VertexFormat(),
|
|
||||||
(n+1)*2,
|
|
||||||
indices,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
panic(errors.Wrap(err, "failed to create ellipse"))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
vertices := make([]map[pixelgl.Attr]interface{}, (n+1)*2)
|
|
||||||
|
|
||||||
for k := 0; k < n+1; k++ {
|
|
||||||
i, j := k*2, k*2+1
|
|
||||||
angle := math.Pi * 2 * float64(k%n) / n
|
|
||||||
|
|
||||||
vertices[i] = map[pixelgl.Attr]interface{}{
|
|
||||||
positionVec2: mgl32.Vec2{
|
|
||||||
float32(math.Cos(angle) * radius.X()),
|
|
||||||
float32(math.Sin(angle) * radius.Y()),
|
|
||||||
},
|
|
||||||
colorVec4: mgl32.Vec4{1, 1, 1, 1},
|
|
||||||
texCoordVec2: mgl32.Vec2{-1, -1},
|
|
||||||
}
|
|
||||||
|
|
||||||
vertices[j] = map[pixelgl.Attr]interface{}{
|
|
||||||
positionVec2: mgl32.Vec2{
|
|
||||||
float32(math.Cos(angle) * radius.X() * (1 - fill)),
|
|
||||||
float32(math.Sin(angle) * radius.Y() * (1 - fill)),
|
|
||||||
},
|
|
||||||
colorVec4: mgl32.Vec4{1, 1, 1, 1},
|
|
||||||
texCoordVec2: mgl32.Vec2{-1, -1},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
va.SetVertices(vertices)
|
|
||||||
|
|
||||||
return &EllipseColor{NewShape(parent, nil, c, Position(0), va), radius, fill}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Radius returns the radius of an ellipse.
|
|
||||||
func (ec *EllipseColor) Radius() Vec {
|
|
||||||
return ec.radius
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fill returns the fill ratio of an ellipse.
|
|
||||||
func (ec *EllipseColor) Fill() float64 {
|
|
||||||
return ec.fill
|
|
||||||
}
|
}
|
||||||
|
|
83
interface.go
83
interface.go
|
@ -1,21 +1,72 @@
|
||||||
package pixel
|
package pixel
|
||||||
|
|
||||||
import "github.com/faiface/pixel/pixelgl"
|
import "image/color"
|
||||||
|
|
||||||
// Target is an OpenGL graphics destination such as a window, a canvas, and so on. Something that
|
// Target is something that can be drawn onto, such as a window, a canvas, and so on.
|
||||||
// you can draw on.
|
|
||||||
type Target interface {
|
|
||||||
pixelgl.BeginEnder
|
|
||||||
Shader() *pixelgl.Shader
|
|
||||||
|
|
||||||
// MakeVertexArray returns a new vertex array drawable on the Target.
|
|
||||||
MakeVertexArray(vertices []map[pixelgl.Attr]interface{}) *pixelgl.VertexArray
|
|
||||||
}
|
|
||||||
|
|
||||||
// Drawer is anything that can be drawn. It's by no means a drawer inside your table.
|
|
||||||
//
|
//
|
||||||
// Drawer consists of a single methods: Draw. Draw takes a target and any number of transform
|
// You can notice, that there are no "drawing" methods in a Target. That's because all drawing
|
||||||
// arguments. It's up to a Drawer to make sure that it draws correctly onto the provided target.
|
// happens indirectly through Triangles instance generated via MakeTriangles method.
|
||||||
type Drawer interface {
|
type Target interface {
|
||||||
Draw(target Target, t ...Transform)
|
// MakeTriangles generates a specialized copy of the provided Triangles.
|
||||||
|
//
|
||||||
|
// When calling Draw method on the returned Triangles, the Triangles will be drawn onto the
|
||||||
|
// target that generated them.
|
||||||
|
//
|
||||||
|
// Note, that not every Target has to recognize all possible types of triangles. Some may
|
||||||
|
// only recognize TrianglesPosition and TrianglesColor and ignore all other properties (if
|
||||||
|
// present) when making new Triangles. This varies from Target to Target.
|
||||||
|
MakeTriangles(Triangles) Triangles
|
||||||
|
|
||||||
|
// These are the most basic Target "adjustment" methods.
|
||||||
|
SetTransform(...Transform)
|
||||||
|
SetMaskColor(color.Color)
|
||||||
|
SetPicture(*Picture)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Triangles represents a list of vertices, where each three vertices form a triangle. (First,
|
||||||
|
// second and third is the first triangle, fourth, fifth and sixth is the second triangle, etc.)
|
||||||
|
type Triangles interface {
|
||||||
|
// Len returns the number of vertices. The number of triangles is the number of vertices
|
||||||
|
// divided by 3.
|
||||||
|
Len() int
|
||||||
|
|
||||||
|
// Draw draws Triangles onto an associated Target (if any).
|
||||||
|
//
|
||||||
|
// Note, that this method does not have to be implemented, however it is always implemented
|
||||||
|
// for Triangles generated by a Target.
|
||||||
|
Draw()
|
||||||
|
|
||||||
|
// Update copies vertex properties from the supplied Triangles into this Triangles.
|
||||||
|
//
|
||||||
|
// Properies not supported by these Triangles should be ignored. Properties not supported by
|
||||||
|
// the supplied Triangles should be left untouched.
|
||||||
|
//
|
||||||
|
// If these Triangles and supplied Triangles have different lengths, these Triangles should
|
||||||
|
// be resized.
|
||||||
|
Update(Triangles)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drawer is something that can be drawn onto any Target.
|
||||||
|
type Drawer interface {
|
||||||
|
Draw(Target)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TrianglesPosition specifies Triangles with Position property.
|
||||||
|
type TrianglesPosition interface {
|
||||||
|
Triangles
|
||||||
|
Position(i int) Vec
|
||||||
|
}
|
||||||
|
|
||||||
|
// TrianglesColor specifies Triangles with Color property.
|
||||||
|
type TrianglesColor interface {
|
||||||
|
Triangles
|
||||||
|
Color(i int) color.Color
|
||||||
|
}
|
||||||
|
|
||||||
|
// TrianglesTexture specifies Triangles with Texture propery.
|
||||||
|
//
|
||||||
|
// Note that this represents texture coordinates, not an actual texture.
|
||||||
|
type TrianglesTexture interface {
|
||||||
|
Triangles
|
||||||
|
Texture(i int) Vec
|
||||||
}
|
}
|
||||||
|
|
231
window.go
231
window.go
|
@ -61,6 +61,7 @@ type Window struct {
|
||||||
window *glfw.Window
|
window *glfw.Window
|
||||||
config WindowConfig
|
config WindowConfig
|
||||||
shader *pixelgl.Shader
|
shader *pixelgl.Shader
|
||||||
|
pic *Picture
|
||||||
|
|
||||||
// need to save these to correctly restore a fullscreen window
|
// need to save these to correctly restore a fullscreen window
|
||||||
restore struct {
|
restore struct {
|
||||||
|
@ -120,8 +121,8 @@ func NewWindow(config WindowConfig) (*Window, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
pixelgl.Do(func() {
|
pixelgl.Do(func() {
|
||||||
w.Begin()
|
w.begin()
|
||||||
defer w.End()
|
defer w.end()
|
||||||
|
|
||||||
w.shader, err = pixelgl.NewShader(
|
w.shader, err = pixelgl.NewShader(
|
||||||
defaultVertexFormat,
|
defaultVertexFormat,
|
||||||
|
@ -129,6 +130,9 @@ func NewWindow(config WindowConfig) (*Window, error) {
|
||||||
defaultVertexShader,
|
defaultVertexShader,
|
||||||
defaultFragmentShader,
|
defaultFragmentShader,
|
||||||
)
|
)
|
||||||
|
if err != nil {
|
||||||
|
panic(errors.Wrap(err, "NewWindow: failed to create shader"))
|
||||||
|
}
|
||||||
|
|
||||||
// default uniforms
|
// default uniforms
|
||||||
w.shader.Begin()
|
w.shader.Begin()
|
||||||
|
@ -161,8 +165,8 @@ func (w *Window) Destroy() {
|
||||||
// Clear clears the window with a color.
|
// Clear clears the window with a color.
|
||||||
func (w *Window) Clear(c color.Color) {
|
func (w *Window) Clear(c color.Color) {
|
||||||
pixelgl.DoNoBlock(func() {
|
pixelgl.DoNoBlock(func() {
|
||||||
w.Begin()
|
w.begin()
|
||||||
defer w.End()
|
defer w.end()
|
||||||
|
|
||||||
c := NRGBAModel.Convert(c).(NRGBA)
|
c := NRGBAModel.Convert(c).(NRGBA)
|
||||||
gl.ClearColor(float32(c.R), float32(c.G), float32(c.B), float32(c.A))
|
gl.ClearColor(float32(c.R), float32(c.G), float32(c.B), float32(c.A))
|
||||||
|
@ -173,8 +177,8 @@ func (w *Window) Clear(c color.Color) {
|
||||||
// Update swaps buffers and polls events.
|
// Update swaps buffers and polls events.
|
||||||
func (w *Window) Update() {
|
func (w *Window) Update() {
|
||||||
pixelgl.Do(func() {
|
pixelgl.Do(func() {
|
||||||
w.Begin()
|
w.begin()
|
||||||
defer w.End()
|
defer w.end()
|
||||||
|
|
||||||
if w.config.VSync {
|
if w.config.VSync {
|
||||||
glfw.SwapInterval(1)
|
glfw.SwapInterval(1)
|
||||||
|
@ -185,8 +189,8 @@ func (w *Window) Update() {
|
||||||
w.updateInput()
|
w.updateInput()
|
||||||
|
|
||||||
pixelgl.Do(func() {
|
pixelgl.Do(func() {
|
||||||
w.Begin()
|
w.begin()
|
||||||
defer w.End()
|
defer w.end()
|
||||||
|
|
||||||
w, h := w.window.GetSize()
|
w, h := w.window.GetSize()
|
||||||
gl.Viewport(0, 0, int32(w), int32(h))
|
gl.Viewport(0, 0, int32(w), int32(h))
|
||||||
|
@ -342,13 +346,10 @@ func (w *Window) Restore() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Begin makes the OpenGL context of a window current and binds it's shader.
|
// begin makes the OpenGL context of a window current and binds it's shader.
|
||||||
//
|
|
||||||
// You usually do not need to use this method, however, you have to use it when you're
|
|
||||||
// directly using this window's context (such as drawing on it using OpenGL).
|
|
||||||
//
|
//
|
||||||
// Note, that this method must be called inside the main OpenGL thread (pixelgl.Do/DoNoBlock/DoErr/DoVal).
|
// Note, that this method must be called inside the main OpenGL thread (pixelgl.Do/DoNoBlock/DoErr/DoVal).
|
||||||
func (w *Window) Begin() {
|
func (w *Window) begin() {
|
||||||
if currentWindow != w {
|
if currentWindow != w {
|
||||||
w.window.MakeContextCurrent()
|
w.window.MakeContextCurrent()
|
||||||
pixelgl.Init()
|
pixelgl.Init()
|
||||||
|
@ -359,39 +360,187 @@ func (w *Window) Begin() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// End unbinds the shader of a window.
|
// end unbinds the shader of a window.
|
||||||
func (w *Window) End() {
|
//
|
||||||
|
// Note, that this method must be called inside the main OpenGL thread (pixelgl.Do/DoNoBlock/DoErr/DoVal).
|
||||||
|
func (w *Window) end() {
|
||||||
if w.shader != nil {
|
if w.shader != nil {
|
||||||
w.shader.End()
|
w.shader.End()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeVertexArray implements Target.
|
type windowTrianglesData []struct {
|
||||||
func (w *Window) MakeVertexArray(vertices []map[pixelgl.Attr]interface{}) *pixelgl.VertexArray {
|
position Vec
|
||||||
var va *pixelgl.VertexArray
|
color NRGBA
|
||||||
|
texture Vec
|
||||||
pixelgl.Do(func() {
|
|
||||||
w.Begin()
|
|
||||||
defer w.End()
|
|
||||||
|
|
||||||
var err error
|
|
||||||
va, err = pixelgl.NewVertexArray(w.Shader(), len(vertices))
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
va.Begin()
|
type windowTriangles struct {
|
||||||
va.SetVertices(vertices)
|
w *Window
|
||||||
va.End()
|
va *pixelgl.VertexArray
|
||||||
})
|
data windowTrianglesData
|
||||||
|
attrData []map[pixelgl.Attr]interface{}
|
||||||
|
dirty bool
|
||||||
|
}
|
||||||
|
|
||||||
return va
|
func (wt *windowTriangles) flush() {
|
||||||
|
if !wt.dirty {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
wt.dirty = false
|
||||||
|
|
||||||
|
if wt.va == nil || wt.va.NumVertices() != wt.Len() {
|
||||||
|
// reallocate vertex array
|
||||||
|
pixelgl.Do(func() {
|
||||||
|
var err error
|
||||||
|
wt.va, err = pixelgl.NewVertexArray(wt.w.shader, wt.Len())
|
||||||
|
if err != nil {
|
||||||
|
panic(errors.Wrap(err, "windowTriangles: failed to create vertex array"))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if wt.Len() > len(wt.attrData) {
|
||||||
|
wt.attrData = append(wt.attrData, make([]map[pixelgl.Attr]interface{}, wt.Len()-len(wt.attrData))...)
|
||||||
|
}
|
||||||
|
if wt.Len() < len(wt.attrData) {
|
||||||
|
wt.attrData = wt.attrData[:wt.Len()]
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, v := range wt.data {
|
||||||
|
if wt.attrData[i] == nil {
|
||||||
|
wt.attrData[i] = make(map[pixelgl.Attr]interface{})
|
||||||
|
}
|
||||||
|
wt.attrData[i][positionVec2] = mgl32.Vec2{float32(v.position.X()), float32(v.position.Y())}
|
||||||
|
wt.attrData[i][colorVec4] = mgl32.Vec4{float32(v.color.R), float32(v.color.G), float32(v.color.B), float32(v.color.A)}
|
||||||
|
wt.attrData[i][textureVec2] = mgl32.Vec2{float32(v.texture.X()), float32(v.texture.Y())}
|
||||||
|
}
|
||||||
|
|
||||||
|
pixelgl.Do(func() {
|
||||||
|
wt.va.Begin()
|
||||||
|
wt.va.SetVertices(wt.attrData)
|
||||||
|
wt.va.End()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wt *windowTriangles) Len() int {
|
||||||
|
return len(wt.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wt *windowTriangles) Draw() {
|
||||||
|
wt.flush()
|
||||||
|
pixelgl.DoNoBlock(func() {
|
||||||
|
wt.w.begin()
|
||||||
|
if wt.w.pic != nil {
|
||||||
|
wt.w.pic.Texture().Begin()
|
||||||
|
}
|
||||||
|
wt.va.Begin()
|
||||||
|
wt.va.Draw()
|
||||||
|
wt.va.End()
|
||||||
|
if wt.w.pic != nil {
|
||||||
|
wt.w.pic.Texture().End()
|
||||||
|
}
|
||||||
|
wt.w.end()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wt *windowTriangles) Update(t Triangles) {
|
||||||
|
wt.dirty = true
|
||||||
|
|
||||||
|
if t.Len() > wt.Len() {
|
||||||
|
newData := make(windowTrianglesData, t.Len())
|
||||||
|
// default attribute values
|
||||||
|
for i := range newData {
|
||||||
|
newData[i].color = NRGBA{R: 1, G: 1, B: 1, A: 1}
|
||||||
|
newData[i].texture = V(-1, -1)
|
||||||
|
}
|
||||||
|
wt.data = append(wt.data, newData...)
|
||||||
|
}
|
||||||
|
if t.Len() < wt.Len() {
|
||||||
|
wt.data = wt.data[:t.Len()]
|
||||||
|
}
|
||||||
|
|
||||||
|
if t, ok := t.(TrianglesPosition); ok {
|
||||||
|
for i := range wt.data {
|
||||||
|
wt.data[i].position = t.Position(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if t, ok := t.(TrianglesColor); ok {
|
||||||
|
for i := range wt.data {
|
||||||
|
wt.data[i].color = NRGBAModel.Convert(t.Color(i)).(NRGBA)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if t, ok := t.(TrianglesTexture); ok {
|
||||||
|
for i := range wt.data {
|
||||||
|
wt.data[i].texture = t.Texture(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wt *windowTriangles) Position(i int) Vec {
|
||||||
|
return wt.data[i].position
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wt *windowTriangles) Color(i int) color.Color {
|
||||||
|
return wt.data[i].color
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wt *windowTriangles) Texture(i int) Vec {
|
||||||
|
return wt.data[i].texture
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeTriangles generates a specialized copy of the supplied triangles that will draw onto this
|
||||||
|
// Window.
|
||||||
|
//
|
||||||
|
// Window supports TrianglesPosition, TrianglesColor and TrianglesTexture.
|
||||||
|
func (w *Window) MakeTriangles(t Triangles) Triangles {
|
||||||
|
wt := &windowTriangles{
|
||||||
|
w: w,
|
||||||
|
}
|
||||||
|
wt.Update(t)
|
||||||
|
return wt
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTransform sets a global transformation matrix for the Window.
|
||||||
|
//
|
||||||
|
// Transforms are applied right-to-left.
|
||||||
|
func (w *Window) SetTransform(t ...Transform) {
|
||||||
|
mat := mgl32.Ident3()
|
||||||
|
for i := range t {
|
||||||
|
mat = mat.Mul3(t[i].Mat())
|
||||||
|
}
|
||||||
|
|
||||||
|
pixelgl.DoNoBlock(func() {
|
||||||
|
w.begin()
|
||||||
|
w.shader.SetUniformAttr(transformMat3, mat)
|
||||||
|
w.end()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMaskColor sets a global mask color for the Window.
|
||||||
|
func (w *Window) SetMaskColor(c color.Color) {
|
||||||
|
nrgba := NRGBAModel.Convert(c).(NRGBA)
|
||||||
|
r := float32(nrgba.R)
|
||||||
|
g := float32(nrgba.G)
|
||||||
|
b := float32(nrgba.B)
|
||||||
|
a := float32(nrgba.A)
|
||||||
|
|
||||||
|
pixelgl.DoNoBlock(func() {
|
||||||
|
w.begin()
|
||||||
|
w.shader.SetUniformAttr(maskColorVec4, mgl32.Vec4{r, g, b, a})
|
||||||
|
w.end()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPicture sets a Picture that will be used in subsequent drawings onto the window.
|
||||||
|
func (w *Window) SetPicture(p *Picture) {
|
||||||
|
w.pic = p
|
||||||
}
|
}
|
||||||
|
|
||||||
var defaultVertexFormat = pixelgl.AttrFormat{
|
var defaultVertexFormat = pixelgl.AttrFormat{
|
||||||
"position": pixelgl.Vec2,
|
"position": pixelgl.Vec2,
|
||||||
"color": pixelgl.Vec4,
|
"color": pixelgl.Vec4,
|
||||||
"texCoord": pixelgl.Vec2,
|
"texture": pixelgl.Vec2,
|
||||||
}
|
}
|
||||||
|
|
||||||
var defaultUniformFormat = pixelgl.AttrFormat{
|
var defaultUniformFormat = pixelgl.AttrFormat{
|
||||||
|
@ -404,17 +553,17 @@ var defaultVertexShader = `
|
||||||
|
|
||||||
in vec2 position;
|
in vec2 position;
|
||||||
in vec4 color;
|
in vec4 color;
|
||||||
in vec2 texCoord;
|
in vec2 texture;
|
||||||
|
|
||||||
out vec4 Color;
|
out vec4 Color;
|
||||||
out vec2 TexCoord;
|
out vec2 Texture;
|
||||||
|
|
||||||
uniform mat3 transform;
|
uniform mat3 transform;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
gl_Position = vec4((transform * vec3(position.x, position.y, 1.0)).xy, 0.0, 1.0);
|
gl_Position = vec4((transform * vec3(position.x, position.y, 1.0)).xy, 0.0, 1.0);
|
||||||
Color = color;
|
Color = color;
|
||||||
TexCoord = texCoord;
|
Texture = texture;
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
@ -422,7 +571,7 @@ var defaultFragmentShader = `
|
||||||
#version 330 core
|
#version 330 core
|
||||||
|
|
||||||
in vec4 Color;
|
in vec4 Color;
|
||||||
in vec2 TexCoord;
|
in vec2 Texture;
|
||||||
|
|
||||||
out vec4 color;
|
out vec4 color;
|
||||||
|
|
||||||
|
@ -430,10 +579,10 @@ uniform vec4 maskColor;
|
||||||
uniform sampler2D tex;
|
uniform sampler2D tex;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
if (TexCoord == vec2(-1, -1)) {
|
if (Texture == vec2(-1, -1)) {
|
||||||
color = maskColor * Color;
|
color = maskColor * Color;
|
||||||
} else {
|
} else {
|
||||||
color = maskColor * Color * texture(tex, vec2(TexCoord.x, 1 - TexCoord.y));
|
color = maskColor * Color * texture(tex, vec2(Texture.x, 1 - Texture.y));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
@ -447,8 +596,8 @@ var (
|
||||||
Name: "color",
|
Name: "color",
|
||||||
Type: pixelgl.Vec4,
|
Type: pixelgl.Vec4,
|
||||||
}
|
}
|
||||||
texCoordVec2 = pixelgl.Attr{
|
textureVec2 = pixelgl.Attr{
|
||||||
Name: "texCoord",
|
Name: "texture",
|
||||||
Type: pixelgl.Vec2,
|
Type: pixelgl.Vec2,
|
||||||
}
|
}
|
||||||
maskColorVec4 = pixelgl.Attr{
|
maskColorVec4 = pixelgl.Attr{
|
||||||
|
|
Loading…
Reference in New Issue