adopt github.com/faiface/glhf

This commit is contained in:
faiface 2017-02-11 14:09:47 +01:00
parent ef76333994
commit 3300f02d21
14 changed files with 50 additions and 907 deletions

View File

@ -3,8 +3,8 @@ package pixel
import ( import (
"image/color" "image/color"
"github.com/faiface/glhf"
"github.com/faiface/mainthread" "github.com/faiface/mainthread"
"github.com/faiface/pixel/pixelgl"
"github.com/go-gl/mathgl/mgl32" "github.com/go-gl/mathgl/mgl32"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@ -13,10 +13,10 @@ import (
// //
// Canvas supports TrianglesPosition, TrianglesColor and TrianglesTexture. // Canvas supports TrianglesPosition, TrianglesColor and TrianglesTexture.
type Canvas struct { type Canvas struct {
f *pixelgl.Frame f *glhf.Frame
s *pixelgl.Shader s *glhf.Shader
copyVs *pixelgl.VertexSlice copyVs *glhf.VertexSlice
smooth bool smooth bool
drawTd TrianglesDrawer drawTd TrianglesDrawer
@ -32,8 +32,8 @@ func NewCanvas(width, height float64, smooth bool) *Canvas {
c := &Canvas{smooth: smooth} c := &Canvas{smooth: smooth}
mainthread.Call(func() { mainthread.Call(func() {
var err error var err error
c.f = pixelgl.NewFrame(int(width), int(height), smooth) c.f = glhf.NewFrame(int(width), int(height), smooth)
c.s, err = pixelgl.NewShader( c.s, err = glhf.NewShader(
canvasVertexFormat, canvasVertexFormat,
canvasUniformFormat, canvasUniformFormat,
canvasVertexShader, canvasVertexShader,
@ -43,7 +43,7 @@ func NewCanvas(width, height float64, smooth bool) *Canvas {
panic(errors.Wrap(err, "failed to create canvas")) panic(errors.Wrap(err, "failed to create canvas"))
} }
c.copyVs = pixelgl.MakeVertexSlice(c.s, 6, 6) c.copyVs = glhf.MakeVertexSlice(c.s, 6, 6)
c.copyVs.Begin() c.copyVs.Begin()
c.copyVs.SetVertexData([]float32{ c.copyVs.SetVertexData([]float32{
-1, -1, 1, 1, 1, 1, 0, 0, -1, -1, 1, 1, 1, 1, 0, 0,
@ -79,7 +79,7 @@ func (c *Canvas) SetSize(width, height float64) {
} }
mainthread.Call(func() { mainthread.Call(func() {
oldF := c.f oldF := c.f
c.f = pixelgl.NewFrame(int(width), int(height), c.smooth) c.f = glhf.NewFrame(int(width), int(height), c.smooth)
c.f.Begin() c.f.Begin()
c.s.Begin() c.s.Begin()
@ -116,7 +116,7 @@ func (c *Canvas) Clear(col color.Color) {
mainthread.CallNonBlock(func() { mainthread.CallNonBlock(func() {
c.f.Begin() c.f.Begin()
col := NRGBAModel.Convert(col).(NRGBA) col := NRGBAModel.Convert(col).(NRGBA)
pixelgl.Clear(float32(col.R), float32(col.G), float32(col.B), float32(col.A)) glhf.Clear(float32(col.R), float32(col.G), float32(col.B), float32(col.A))
c.f.End() c.f.End()
}) })
} }
@ -209,10 +209,10 @@ const (
canvasTextureVec2 canvasTextureVec2
) )
var canvasVertexFormat = pixelgl.AttrFormat{ var canvasVertexFormat = glhf.AttrFormat{
canvasPositionVec2: {Name: "position", Type: pixelgl.Vec2}, canvasPositionVec2: {Name: "position", Type: glhf.Vec2},
canvasColorVec4: {Name: "color", Type: pixelgl.Vec4}, canvasColorVec4: {Name: "color", Type: glhf.Vec4},
canvasTextureVec2: {Name: "texture", Type: pixelgl.Vec2}, canvasTextureVec2: {Name: "texture", Type: glhf.Vec2},
} }
const ( const (
@ -221,10 +221,10 @@ const (
canvasBoundsVec4 canvasBoundsVec4
) )
var canvasUniformFormat = pixelgl.AttrFormat{ var canvasUniformFormat = glhf.AttrFormat{
{Name: "maskColor", Type: pixelgl.Vec4}, {Name: "maskColor", Type: glhf.Vec4},
{Name: "transform", Type: pixelgl.Mat3}, {Name: "transform", Type: glhf.Mat3},
{Name: "bounds", Type: pixelgl.Vec4}, {Name: "bounds", Type: glhf.Vec4},
} }
var canvasVertexShader = ` var canvasVertexShader = `

View File

@ -3,23 +3,23 @@ package pixel
import ( import (
"fmt" "fmt"
"github.com/faiface/glhf"
"github.com/faiface/mainthread" "github.com/faiface/mainthread"
"github.com/faiface/pixel/pixelgl"
) )
// NewGLTriangles returns OpenGL triangles implemented using pixelgl.VertexSlice. A few notes. // NewGLTriangles returns OpenGL triangles implemented using glhf.VertexSlice. A few notes.
// //
// Triangles returned from this function support TrianglesPosition, TrianglesColor and // Triangles returned from this function support TrianglesPosition, TrianglesColor and
// TrianglesTexture. If you need to support more, you can "override" SetLen and Update method. // TrianglesTexture. If you need to support more, you can "override" SetLen and Update method.
// //
// Draw method simply draws the underlying pixelgl.VertexSlice. It needs to be called in the main // Draw method simply draws the underlying glhf.VertexSlice. It needs to be called in the main
// thread manually. Also, you need to take care of additional Target initialization or setting of // thread manually. Also, you need to take care of additional Target initialization or setting of
// uniform attributes. // uniform attributes.
func NewGLTriangles(shader *pixelgl.Shader, t Triangles) TargetTriangles { func NewGLTriangles(shader *glhf.Shader, t Triangles) TargetTriangles {
var gt *glTriangles var gt *glTriangles
mainthread.Call(func() { mainthread.Call(func() {
gt = &glTriangles{ gt = &glTriangles{
vs: pixelgl.MakeVertexSlice(shader, 0, t.Len()), vs: glhf.MakeVertexSlice(shader, 0, t.Len()),
shader: shader, shader: shader,
} }
}) })
@ -29,9 +29,9 @@ func NewGLTriangles(shader *pixelgl.Shader, t Triangles) TargetTriangles {
} }
type glTriangles struct { type glTriangles struct {
vs *pixelgl.VertexSlice vs *glhf.VertexSlice
data []float32 data []float32
shader *pixelgl.Shader shader *glhf.Shader
} }
func (gt *glTriangles) Len() int { func (gt *glTriangles) Len() int {

View File

@ -254,9 +254,13 @@ func (p *Polygon) Color() NRGBA {
// //
// However, it is less expensive than using a transform on a Target. // However, it is less expensive than using a transform on a Target.
func (p *Polygon) SetPoints(points ...Vec) { func (p *Polygon) SetPoints(points ...Vec) {
p.data.SetLen(len(points)) p.data.SetLen(3 * (len(points) - 2))
for i, pt := range points { for i := 2; i < len(points); i++ {
p.data[i].Position = pt p.data[(i-2)*3+0].Position = points[0]
p.data[(i-2)*3+1].Position = points[i-1]
p.data[(i-2)*3+2].Position = points[i]
}
for i := range p.data {
p.data[i].Color = p.col p.data[i].Color = p.col
} }
p.td.Dirty() p.td.Dirty()

View File

@ -4,8 +4,8 @@ import (
"image" "image"
"image/draw" "image/draw"
"github.com/faiface/glhf"
"github.com/faiface/mainthread" "github.com/faiface/mainthread"
"github.com/faiface/pixel/pixelgl"
) )
// Picture is a raster picture. It is usually used with sprites. // Picture is a raster picture. It is usually used with sprites.
@ -14,7 +14,7 @@ import (
// generated. After the creation, Pictures can be sliced (slicing creates a "sub-Picture" // generated. After the creation, Pictures can be sliced (slicing creates a "sub-Picture"
// from a Picture) into smaller Pictures. // from a Picture) into smaller Pictures.
type Picture struct { type Picture struct {
tex *pixelgl.Texture tex *glhf.Texture
bounds Rect bounds Rect
} }
@ -35,9 +35,9 @@ func NewPicture(img image.Image, smooth bool) *Picture {
copy(jSlice, tmp) copy(jSlice, tmp)
} }
var tex *pixelgl.Texture var tex *glhf.Texture
mainthread.Call(func() { mainthread.Call(func() {
tex = pixelgl.NewTexture( tex = glhf.NewTexture(
img.Bounds().Dx(), img.Bounds().Dx(),
img.Bounds().Dy(), img.Bounds().Dy(),
smooth, smooth,
@ -49,7 +49,7 @@ func NewPicture(img image.Image, smooth bool) *Picture {
} }
// PictureFromTexture returns a new Picture that spans the whole supplied Texture. // PictureFromTexture returns a new Picture that spans the whole supplied Texture.
func PictureFromTexture(tex *pixelgl.Texture) *Picture { func PictureFromTexture(tex *glhf.Texture) *Picture {
return &Picture{ return &Picture{
tex: tex, tex: tex,
bounds: R(0, 0, float64(tex.Width()), float64(tex.Height())), bounds: R(0, 0, float64(tex.Width()), float64(tex.Height())),
@ -88,7 +88,7 @@ func (p *Picture) Image() *image.NRGBA {
} }
// Texture returns a pointer to the underlying OpenGL texture of the Picture. // Texture returns a pointer to the underlying OpenGL texture of the Picture.
func (p *Picture) Texture() *pixelgl.Texture { func (p *Picture) Texture() *glhf.Texture {
return p.tex return p.tex
} }

View File

@ -1,80 +0,0 @@
package pixelgl
// AttrFormat defines names and types of OpenGL attributes (vertex format, uniform format, etc.).
//
// Example:
// AttrFormat{{"position", Vec2}, {"color", Vec4}, {"texCoord": Vec2}}
type AttrFormat []Attr
// Size returns the total size of all attributes of the AttrFormat.
func (af AttrFormat) Size() int {
total := 0
for _, attr := range af {
total += attr.Type.Size()
}
return total
}
// Attr represents an arbitrary OpenGL attribute, such as a vertex attribute or a shader
// uniform attribute.
type Attr struct {
Name string
Type AttrType
}
// AttrType represents the type of an OpenGL attribute.
type AttrType int
// List of all possible attribute types.
const (
Int AttrType = iota
Float
Vec2
Vec3
Vec4
Mat2
Mat23
Mat24
Mat3
Mat32
Mat34
Mat4
Mat42
Mat43
)
// Size returns the size of a type in bytes.
func (at AttrType) Size() int {
switch at {
case Int:
return 4
case Float:
return 4
case Vec2:
return 2 * 4
case Vec3:
return 3 * 4
case Vec4:
return 4 * 4
case Mat2:
return 2 * 2 * 4
case Mat23:
return 2 * 3 * 4
case Mat24:
return 2 * 4 * 4
case Mat3:
return 3 * 3 * 4
case Mat32:
return 3 * 2 * 4
case Mat34:
return 3 * 4 * 4
case Mat4:
return 4 * 4 * 4
case Mat42:
return 4 * 2 * 4
case Mat43:
return 4 * 3 * 4
default:
panic("size of vertex attribute type: invalid type")
}
}

View File

@ -1,8 +0,0 @@
// Package pixelgl provides some abstractions around the basic OpenGL primitives and
// operations.
//
// All calls should be done from the main thread using "github.com/faiface/mainthread" package.
//
// This package deliberately does not handle nor report OpenGL errors, it's up to you to
// cause none.
package pixelgl

View File

@ -1,75 +0,0 @@
package pixelgl
import (
"runtime"
"github.com/faiface/mainthread"
"github.com/go-gl/gl/v3.3-core/gl"
)
// Frame is a fixed resolution texture that you can draw on.
type Frame struct {
fb binder
tex *Texture
width, height int
}
// NewFrame creates a new fully transparent Frame with given dimensions in pixels.
func NewFrame(width, height int, smooth bool) *Frame {
f := &Frame{
fb: binder{
restoreLoc: gl.FRAMEBUFFER_BINDING,
bindFunc: func(obj uint32) {
gl.BindFramebuffer(gl.FRAMEBUFFER, obj)
},
},
width: width,
height: height,
}
gl.GenFramebuffers(1, &f.fb.obj)
f.tex = NewTexture(width, height, smooth, make([]uint8, width*height*4))
f.fb.bind()
gl.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, f.tex.tex.obj, 0)
f.fb.restore()
runtime.SetFinalizer(f, (*Frame).delete)
return f
}
func (f *Frame) delete() {
mainthread.CallNonBlock(func() {
gl.DeleteFramebuffers(1, &f.fb.obj)
})
}
// Width returns the width of the Frame in pixels.
func (f *Frame) Width() int {
return f.width
}
// Height returns the height of the Frame in pixels.
func (f *Frame) Height() int {
return f.height
}
// Begin binds the Frame. All draw operations will target this Frame until End is called.
func (f *Frame) Begin() {
f.fb.bind()
gl.Viewport(0, 0, int32(f.width), int32(f.height))
}
// End unbinds the Frame. All draw operations will go to whatever was bound before this Frame.
func (f *Frame) End() {
f.fb.restore()
}
// Texture returns the Texture that this Frame draws to. The Texture changes as you use the Frame.
//
// The Texture pointer returned from this method is always the same.
func (f *Frame) Texture() *Texture {
return f.tex
}

View File

@ -1,11 +0,0 @@
package pixelgl
// BeginEnder is an interface for manipulating OpenGL state.
//
// OpenGL is a state machine. Every object can 'enter' it's state and 'leave' it's state. For
// example, you can bind a buffer and unbind a buffer, bind a texture and unbind it, use shader
// and unuse it, and so on.
type BeginEnder interface {
Begin()
End()
}

View File

@ -1,24 +0,0 @@
package pixelgl
import "github.com/go-gl/gl/v3.3-core/gl"
// Init initializes OpenGL by loading function pointers from the active OpenGL context.
// This function must be manually run inside the main thread (using "github.com/faiface/mainthread"
// package).
//
// It must be called under the presence of an active OpenGL context, e.g., always after calling
// window.MakeContextCurrent(). Also, always call this function when switching contexts.
func Init() {
err := gl.Init()
if err != nil {
panic(err)
}
gl.Enable(gl.BLEND)
gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
}
// Clear clears the current framebuffer or window with the given color.
func Clear(r, g, b, a float32) {
gl.ClearColor(r, g, b, a)
gl.Clear(gl.COLOR_BUFFER_BIT)
}

View File

@ -1,219 +0,0 @@
package pixelgl
import (
"fmt"
"runtime"
"github.com/faiface/mainthread"
"github.com/go-gl/gl/v3.3-core/gl"
"github.com/go-gl/mathgl/mgl32"
)
// Shader is an OpenGL shader program.
type Shader struct {
program binder
vertexFmt AttrFormat
uniformFmt AttrFormat
uniformLoc []int32
}
// NewShader creates a new shader program from the specified vertex shader and fragment shader
// sources.
//
// Note that vertexShader and fragmentShader parameters must contain the source code, they're
// not filenames.
func NewShader(vertexFmt, uniformFmt AttrFormat, vertexShader, fragmentShader string) (*Shader, error) {
shader := &Shader{
program: binder{
restoreLoc: gl.CURRENT_PROGRAM,
bindFunc: func(obj uint32) {
gl.UseProgram(obj)
},
},
vertexFmt: vertexFmt,
uniformFmt: uniformFmt,
uniformLoc: make([]int32, len(uniformFmt)),
}
var vshader, fshader uint32
// vertex shader
{
vshader = gl.CreateShader(gl.VERTEX_SHADER)
src, free := gl.Strs(vertexShader)
defer free()
length := int32(len(vertexShader))
gl.ShaderSource(vshader, 1, src, &length)
gl.CompileShader(vshader)
var success int32
gl.GetShaderiv(vshader, gl.COMPILE_STATUS, &success)
if success == gl.FALSE {
var logLen int32
gl.GetShaderiv(vshader, gl.INFO_LOG_LENGTH, &logLen)
infoLog := make([]byte, logLen)
gl.GetShaderInfoLog(vshader, logLen, nil, &infoLog[0])
return nil, fmt.Errorf("error compiling vertex shader: %s", string(infoLog))
}
defer gl.DeleteShader(vshader)
}
// fragment shader
{
fshader = gl.CreateShader(gl.FRAGMENT_SHADER)
src, free := gl.Strs(fragmentShader)
defer free()
length := int32(len(fragmentShader))
gl.ShaderSource(fshader, 1, src, &length)
gl.CompileShader(fshader)
var success int32
gl.GetShaderiv(fshader, gl.COMPILE_STATUS, &success)
if success == gl.FALSE {
var logLen int32
gl.GetShaderiv(fshader, gl.INFO_LOG_LENGTH, &logLen)
infoLog := make([]byte, logLen)
gl.GetShaderInfoLog(fshader, logLen, nil, &infoLog[0])
return nil, fmt.Errorf("error compiling fragment shader: %s", string(infoLog))
}
defer gl.DeleteShader(fshader)
}
// shader program
{
shader.program.obj = gl.CreateProgram()
gl.AttachShader(shader.program.obj, vshader)
gl.AttachShader(shader.program.obj, fshader)
gl.LinkProgram(shader.program.obj)
var success int32
gl.GetProgramiv(shader.program.obj, gl.LINK_STATUS, &success)
if success == gl.FALSE {
var logLen int32
gl.GetProgramiv(shader.program.obj, gl.INFO_LOG_LENGTH, &logLen)
infoLog := make([]byte, logLen)
gl.GetProgramInfoLog(shader.program.obj, logLen, nil, &infoLog[0])
return nil, fmt.Errorf("error linking shader program: %s", string(infoLog))
}
}
// uniforms
for i, uniform := range uniformFmt {
loc := gl.GetUniformLocation(shader.program.obj, gl.Str(uniform.Name+"\x00"))
shader.uniformLoc[i] = loc
}
runtime.SetFinalizer(shader, (*Shader).delete)
return shader, nil
}
func (s *Shader) delete() {
mainthread.CallNonBlock(func() {
gl.DeleteProgram(s.program.obj)
})
}
// VertexFormat returns the vertex attribute format of this Shader. Do not change it.
func (s *Shader) VertexFormat() AttrFormat {
return s.vertexFmt
}
// UniformFormat returns the uniform attribute format of this Shader. Do not change it.
func (s *Shader) UniformFormat() AttrFormat {
return s.uniformFmt
}
// SetUniformAttr sets the value of a uniform attribute of this Shader. The attribute is
// specified by the index in the Shader's uniform format.
//
// If the uniform attribute does not exist in the Shader, this method returns false.
//
// Supplied value must correspond to the type of the attribute. Correct types are these
// (right-hand is the type of the value):
// Attr{Type: Int}: int32
// Attr{Type: Float}: float32
// Attr{Type: Vec2}: mgl32.Vec2
// Attr{Type: Vec3}: mgl32.Vec3
// Attr{Type: Vec4}: mgl32.Vec4
// Attr{Type: Mat2}: mgl32.Mat2
// Attr{Type: Mat23}: mgl32.Mat2x3
// Attr{Type: Mat24}: mgl32.Mat2x4
// Attr{Type: Mat3}: mgl32.Mat3
// Attr{Type: Mat32}: mgl32.Mat3x2
// Attr{Type: Mat34}: mgl32.Mat3x4
// Attr{Type: Mat4}: mgl32.Mat4
// Attr{Type: Mat42}: mgl32.Mat4x2
// Attr{Type: Mat43}: mgl32.Mat4x3
// No other types are supported.
//
// The Shader must be bound before calling this method.
func (s *Shader) SetUniformAttr(uniform int, value interface{}) (ok bool) {
if s.uniformLoc[uniform] < 0 {
return false
}
switch s.uniformFmt[uniform].Type {
case Int:
value := value.(int32)
gl.Uniform1iv(s.uniformLoc[uniform], 1, &value)
case Float:
value := value.(float32)
gl.Uniform1fv(s.uniformLoc[uniform], 1, &value)
case Vec2:
value := value.(mgl32.Vec2)
gl.Uniform2fv(s.uniformLoc[uniform], 1, &value[0])
case Vec3:
value := value.(mgl32.Vec3)
gl.Uniform3fv(s.uniformLoc[uniform], 1, &value[0])
case Vec4:
value := value.(mgl32.Vec4)
gl.Uniform4fv(s.uniformLoc[uniform], 1, &value[0])
case Mat2:
value := value.(mgl32.Mat2)
gl.UniformMatrix2fv(s.uniformLoc[uniform], 1, false, &value[0])
case Mat23:
value := value.(mgl32.Mat2x3)
gl.UniformMatrix2x3fv(s.uniformLoc[uniform], 1, false, &value[0])
case Mat24:
value := value.(mgl32.Mat2x4)
gl.UniformMatrix2x4fv(s.uniformLoc[uniform], 1, false, &value[0])
case Mat3:
value := value.(mgl32.Mat3)
gl.UniformMatrix3fv(s.uniformLoc[uniform], 1, false, &value[0])
case Mat32:
value := value.(mgl32.Mat3x2)
gl.UniformMatrix3x2fv(s.uniformLoc[uniform], 1, false, &value[0])
case Mat34:
value := value.(mgl32.Mat3x4)
gl.UniformMatrix3x4fv(s.uniformLoc[uniform], 1, false, &value[0])
case Mat4:
value := value.(mgl32.Mat4)
gl.UniformMatrix4fv(s.uniformLoc[uniform], 1, false, &value[0])
case Mat42:
value := value.(mgl32.Mat4x2)
gl.UniformMatrix4x2fv(s.uniformLoc[uniform], 1, false, &value[0])
case Mat43:
value := value.(mgl32.Mat4x3)
gl.UniformMatrix4x3fv(s.uniformLoc[uniform], 1, false, &value[0])
default:
panic("set uniform attr: invalid attribute type")
}
return true
}
// Begin binds the Shader program. This is necessary before using the Shader.
func (s *Shader) Begin() {
s.program.bind()
}
// End unbinds the Shader program and restores the previous one.
func (s *Shader) End() {
s.program.restore()
}

View File

@ -1,128 +0,0 @@
package pixelgl
import (
"runtime"
"github.com/faiface/mainthread"
"github.com/go-gl/gl/v3.3-core/gl"
"github.com/go-gl/mathgl/mgl32"
)
// Texture is an OpenGL texture.
type Texture struct {
tex binder
width, height int
}
// NewTexture creates a new texture with the specified width and height with some initial
// pixel values. The pixels must be a sequence of RGBA values (one byte per component).
func NewTexture(width, height int, smooth bool, pixels []uint8) *Texture {
tex := &Texture{
tex: binder{
restoreLoc: gl.TEXTURE_BINDING_2D,
bindFunc: func(obj uint32) {
gl.BindTexture(gl.TEXTURE_2D, obj)
},
},
width: width,
height: height,
}
gl.GenTextures(1, &tex.tex.obj)
tex.Begin()
defer tex.End()
// initial data
gl.TexImage2D(
gl.TEXTURE_2D,
0,
gl.RGBA,
int32(width),
int32(height),
0,
gl.RGBA,
gl.UNSIGNED_BYTE,
gl.Ptr(pixels),
)
borderColor := mgl32.Vec4{0, 0, 0, 0}
gl.TexParameterfv(gl.TEXTURE_2D, gl.TEXTURE_BORDER_COLOR, &borderColor[0])
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_BORDER)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_BORDER)
if smooth {
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
} else {
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
}
runtime.SetFinalizer(tex, (*Texture).delete)
return tex
}
func (t *Texture) delete() {
mainthread.CallNonBlock(func() {
gl.DeleteTextures(1, &t.tex.obj)
})
}
// Width returns the width of the Texture in pixels.
func (t *Texture) Width() int {
return t.width
}
// Height returns the height of the Texture in pixels.
func (t *Texture) Height() int {
return t.height
}
// SetPixels sets the content of a sub-region of the Texture. Pixels must be an RGBA byte sequence.
func (t *Texture) SetPixels(x, y, w, h int, pixels []uint8) {
if len(pixels) != w*h*4 {
panic("set pixels: wrong number of pixels")
}
gl.TexSubImage2D(
gl.TEXTURE_2D,
0,
int32(x),
int32(y),
int32(w),
int32(h),
gl.RGBA,
gl.UNSIGNED_BYTE,
gl.Ptr(pixels),
)
}
// Pixels returns the content of a sub-region of the Texture as an RGBA byte sequence.
func (t *Texture) Pixels(x, y, w, h int) []uint8 {
pixels := make([]uint8, t.width*t.height*4)
gl.GetTexImage(
gl.TEXTURE_2D,
0,
gl.RGBA,
gl.UNSIGNED_BYTE,
gl.Ptr(pixels),
)
subPixels := make([]uint8, w*h*4)
for i := 0; i < h; i++ {
row := pixels[(i+y)*t.width*4+x*4 : (i+y)*t.width*4+(x+w)*4]
subRow := subPixels[i*w*4 : (i+1)*w*4]
copy(subRow, row)
}
return subPixels
}
// Begin binds the Texture. This is necessary before using the Texture.
func (t *Texture) Begin() {
t.tex.bind()
}
// End unbinds the Texture and restores the previous one.
func (t *Texture) End() {
t.tex.restore()
}

View File

@ -1,31 +0,0 @@
package pixelgl
import "github.com/go-gl/gl/v3.3-core/gl"
type binder struct {
restoreLoc uint32
bindFunc func(uint32)
obj uint32
prev []uint32
}
func (b *binder) bind() *binder {
var prev int32
gl.GetIntegerv(b.restoreLoc, &prev)
b.prev = append(b.prev, uint32(prev))
if b.prev[len(b.prev)-1] != b.obj {
b.bindFunc(b.obj)
}
return b
}
func (b *binder) restore() *binder {
if b.prev[len(b.prev)-1] != b.obj {
b.bindFunc(b.prev[len(b.prev)-1])
}
b.prev = b.prev[:len(b.prev)-1]
return b
}

View File

@ -1,285 +0,0 @@
package pixelgl
import (
"fmt"
"runtime"
"github.com/faiface/mainthread"
"github.com/go-gl/gl/v3.3-core/gl"
"github.com/pkg/errors"
)
// VertexSlice points to a portion of (or possibly whole) vertex array. It is used as a pointer,
// contrary to Go's builtin slices. This is, so that append can be 'in-place'. That's for the good,
// because Begin/End-ing a VertexSlice would become super confusing, if append returned a new
// VertexSlice.
//
// It also implements all basic slice-like operations: appending, sub-slicing, etc.
//
// Note that you need to Begin a VertexSlice before getting or updating it's elements or drawing it.
// After you're done with it, you need to End it.
type VertexSlice struct {
va *vertexArray
i, j int
}
// MakeVertexSlice allocates a new vertex array with specified capacity and returns a VertexSlice
// that points to it's first len elements.
//
// Note, that a vertex array is specialized for a specific shader and can't be used with another
// shader.
func MakeVertexSlice(shader *Shader, len, cap int) *VertexSlice {
if len > cap {
panic("failed to make vertex slice: len > cap")
}
return &VertexSlice{
va: newVertexArray(shader, cap),
i: 0,
j: len,
}
}
// VertexFormat returns the format of vertex attributes inside the underlying vertex array of this
// VertexSlice.
func (vs *VertexSlice) VertexFormat() AttrFormat {
return vs.va.format
}
// Stride returns the number of float32 elements occupied by one vertex.
func (vs *VertexSlice) Stride() int {
return vs.va.stride / 4
}
// Len returns the length of the VertexSlice (number of vertices).
func (vs *VertexSlice) Len() int {
return vs.j - vs.i
}
// Cap returns the capacity of an underlying vertex array.
func (vs *VertexSlice) Cap() int {
return vs.va.cap - vs.i
}
// SetLen resizes the VertexSlice to length len.
func (vs *VertexSlice) SetLen(len int) {
vs.End() // vs must have been Begin-ed before calling this method
*vs = vs.grow(len)
vs.Begin()
}
// grow returns supplied vs with length changed to len. Allocates new underlying vertex array if
// necessary. The original content is preserved.
func (vs VertexSlice) grow(len int) VertexSlice {
if len <= vs.Cap() {
// capacity sufficient
return VertexSlice{
va: vs.va,
i: vs.i,
j: vs.i + len,
}
}
// grow the capacity
newCap := vs.Cap()
if newCap < 1024 {
newCap += newCap
} else {
newCap += newCap / 4
}
if newCap < len {
newCap = len
}
newVs := VertexSlice{
va: newVertexArray(vs.va.shader, newCap),
i: 0,
j: len,
}
// preserve the original content
newVs.Begin()
newVs.Slice(0, vs.Len()).SetVertexData(vs.VertexData())
newVs.End()
return newVs
}
// Slice returns a sub-slice of this VertexSlice covering the range [i, j) (relative to this
// VertexSlice).
//
// Note, that the returned VertexSlice shares an underlying vertex array with the original
// VertexSlice. Modifying the contents of one modifies corresponding contents of the other.
func (vs *VertexSlice) Slice(i, j int) *VertexSlice {
if i < 0 || j < i || j > vs.va.cap {
panic("failed to slice vertex slice: index out of range")
}
return &VertexSlice{
va: vs.va,
i: vs.i + i,
j: vs.i + j,
}
}
// SetVertexData sets the contents of the VertexSlice.
//
// The data is a slice of float32's, where each vertex attribute occupies a certain number of
// elements. Namely, Float occupies 1, Vec2 occupies 2, Vec3 occupies 3 and Vec4 occupies 4. The
// attribues in the data slice must be in the same order as in the vertex format of this Vertex
// Slice.
//
// If the length of vertices does not match the length of the VertexSlice, this methdo panics.
func (vs *VertexSlice) SetVertexData(data []float32) {
if len(data)/vs.Stride() != vs.Len() {
fmt.Println(len(data)/vs.Stride(), vs.Len())
panic("set vertex data: wrong length of vertices")
}
vs.va.setVertexData(vs.i, vs.j, data)
}
// VertexData returns the contents of the VertexSlice.
//
// The data is in the same format as with SetVertexData.
func (vs *VertexSlice) VertexData() []float32 {
return vs.va.vertexData(vs.i, vs.j)
}
// Draw draws the content of the VertexSlice.
func (vs *VertexSlice) Draw() {
vs.va.draw(vs.i, vs.j)
}
// Begin binds the underlying vertex array. Calling this method is necessary before using the VertexSlice.
func (vs *VertexSlice) Begin() {
vs.va.begin()
}
// End unbinds the underlying vertex array. Call this method when you're done with VertexSlice.
func (vs *VertexSlice) End() {
vs.va.end()
}
type vertexArray struct {
vao, vbo binder
cap int
format AttrFormat
stride int
offset []int
shader *Shader
}
const vertexArrayMinCap = 4
func newVertexArray(shader *Shader, cap int) *vertexArray {
if cap < vertexArrayMinCap {
cap = vertexArrayMinCap
}
va := &vertexArray{
vao: binder{
restoreLoc: gl.VERTEX_ARRAY_BINDING,
bindFunc: func(obj uint32) {
gl.BindVertexArray(obj)
},
},
vbo: binder{
restoreLoc: gl.ARRAY_BUFFER_BINDING,
bindFunc: func(obj uint32) {
gl.BindBuffer(gl.ARRAY_BUFFER, obj)
},
},
cap: cap,
format: shader.VertexFormat(),
stride: shader.VertexFormat().Size(),
offset: make([]int, len(shader.VertexFormat())),
shader: shader,
}
offset := 0
for i, attr := range va.format {
switch attr.Type {
case Float, Vec2, Vec3, Vec4:
default:
panic(errors.New("failed to create vertex array: invalid attribute type"))
}
va.offset[i] = offset
offset += attr.Type.Size()
}
gl.GenVertexArrays(1, &va.vao.obj)
va.vao.bind()
gl.GenBuffers(1, &va.vbo.obj)
defer va.vbo.bind().restore()
emptyData := make([]byte, cap*va.stride)
gl.BufferData(gl.ARRAY_BUFFER, len(emptyData), gl.Ptr(emptyData), gl.DYNAMIC_DRAW)
for i, attr := range va.format {
loc := gl.GetAttribLocation(shader.program.obj, gl.Str(attr.Name+"\x00"))
var size int32
switch attr.Type {
case Float:
size = 1
case Vec2:
size = 2
case Vec3:
size = 3
case Vec4:
size = 4
}
gl.VertexAttribPointer(
uint32(loc),
size,
gl.FLOAT,
false,
int32(va.stride),
gl.PtrOffset(va.offset[i]),
)
gl.EnableVertexAttribArray(uint32(loc))
}
va.vao.restore()
runtime.SetFinalizer(va, (*vertexArray).delete)
return va
}
func (va *vertexArray) delete() {
mainthread.CallNonBlock(func() {
gl.DeleteVertexArrays(1, &va.vao.obj)
gl.DeleteBuffers(1, &va.vbo.obj)
})
}
func (va *vertexArray) begin() {
va.vao.bind()
va.vbo.bind()
}
func (va *vertexArray) end() {
va.vbo.restore()
va.vao.restore()
}
func (va *vertexArray) draw(i, j int) {
gl.DrawArrays(gl.TRIANGLES, int32(i), int32(i+j))
}
func (va *vertexArray) setVertexData(i, j int, data []float32) {
if j-i == 0 {
// avoid setting 0 bytes of buffer data
return
}
gl.BufferSubData(gl.ARRAY_BUFFER, i*va.stride, len(data)*4, gl.Ptr(data))
}
func (va *vertexArray) vertexData(i, j int) []float32 {
if j-i == 0 {
// avoid getting 0 bytes of buffer data
return nil
}
data := make([]float32, (j-i)*va.stride/4)
gl.GetBufferSubData(gl.ARRAY_BUFFER, i*va.stride, len(data)*4, gl.Ptr(data))
return data
}

View File

@ -5,8 +5,8 @@ import (
"runtime" "runtime"
"github.com/faiface/glhf"
"github.com/faiface/mainthread" "github.com/faiface/mainthread"
"github.com/faiface/pixel/pixelgl"
"github.com/go-gl/glfw/v3.2/glfw" "github.com/go-gl/glfw/v3.2/glfw"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@ -56,12 +56,12 @@ type WindowConfig struct {
// Window is a window handler. Use this type to manipulate a window (input, drawing, ...). // Window is a window handler. Use this type to manipulate a window (input, drawing, ...).
type Window struct { type Window struct {
window *glfw.Window window *glfw.Window
config WindowConfig config WindowConfig
canvas *Canvas canvas *Canvas
canvasVs *pixelgl.VertexSlice canvasVs *glhf.VertexSlice
shader *pixelgl.Shader shader *glhf.Shader
// need to save these to correctly restore a fullscreen window // need to save these to correctly restore a fullscreen window
restore struct { restore struct {
@ -121,7 +121,7 @@ func NewWindow(config WindowConfig) (*Window, error) {
w.begin() w.begin()
w.end() w.end()
w.shader, err = pixelgl.NewShader( w.shader, err = glhf.NewShader(
windowVertexFormat, windowVertexFormat,
windowUniformFormat, windowUniformFormat,
windowVertexShader, windowVertexShader,
@ -131,7 +131,7 @@ func NewWindow(config WindowConfig) (*Window, error) {
return err return err
} }
w.canvasVs = pixelgl.MakeVertexSlice(w.shader, 6, 6) w.canvasVs = glhf.MakeVertexSlice(w.shader, 6, 6)
w.canvasVs.Begin() w.canvasVs.Begin()
w.canvasVs.SetVertexData([]float32{ w.canvasVs.SetVertexData([]float32{
-1, -1, 0, 0, -1, -1, 0, 0,
@ -179,7 +179,7 @@ func (w *Window) Update() {
mainthread.Call(func() { mainthread.Call(func() {
w.begin() w.begin()
pixelgl.Clear(0, 0, 0, 0) glhf.Clear(0, 0, 0, 0)
w.shader.Begin() w.shader.Begin()
w.canvas.f.Texture().Begin() w.canvas.f.Texture().Begin()
w.canvasVs.Begin() w.canvasVs.Begin()
@ -354,7 +354,7 @@ func (w *Window) Restore() {
func (w *Window) begin() { func (w *Window) begin() {
if currentWindow != w { if currentWindow != w {
w.window.MakeContextCurrent() w.window.MakeContextCurrent()
pixelgl.Init() glhf.Init()
currentWindow = w currentWindow = w
} }
} }
@ -394,12 +394,12 @@ const (
windowTextureVec2 windowTextureVec2
) )
var windowVertexFormat = pixelgl.AttrFormat{ var windowVertexFormat = glhf.AttrFormat{
windowPositionVec2: {Name: "position", Type: pixelgl.Vec2}, windowPositionVec2: {Name: "position", Type: glhf.Vec2},
windowTextureVec2: {Name: "texture", Type: pixelgl.Vec2}, windowTextureVec2: {Name: "texture", Type: glhf.Vec2},
} }
var windowUniformFormat = pixelgl.AttrFormat{} var windowUniformFormat = glhf.AttrFormat{}
var windowVertexShader = ` var windowVertexShader = `
#version 330 core #version 330 core