Delete pixelgl directory
This commit is contained in:
parent
cd33322ce0
commit
6f0463cfda
|
@ -1,361 +0,0 @@
|
||||||
package pixelgl
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"image/color"
|
|
||||||
|
|
||||||
"github.com/faiface/glhf"
|
|
||||||
"github.com/faiface/mainthread"
|
|
||||||
"github.com/faiface/pixel"
|
|
||||||
"github.com/go-gl/mathgl/mgl32"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Canvas is an off-screen rectangular BasicTarget and Picture at the same time, that you can draw
|
|
||||||
// onto.
|
|
||||||
//
|
|
||||||
// It supports TrianglesPosition, TrianglesColor, TrianglesPicture and PictureColor.
|
|
||||||
type Canvas struct {
|
|
||||||
gf *GLFrame
|
|
||||||
shader *GLShader
|
|
||||||
|
|
||||||
cmp pixel.ComposeMethod
|
|
||||||
mat mgl32.Mat3
|
|
||||||
col mgl32.Vec4
|
|
||||||
smooth bool
|
|
||||||
|
|
||||||
sprite *pixel.Sprite
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ pixel.ComposeTarget = (*Canvas)(nil)
|
|
||||||
|
|
||||||
// NewCanvas creates a new empty, fully transparent Canvas with given bounds.
|
|
||||||
func NewCanvas(bounds pixel.Rect) *Canvas {
|
|
||||||
c := &Canvas{
|
|
||||||
gf: NewGLFrame(bounds),
|
|
||||||
mat: mgl32.Ident3(),
|
|
||||||
col: mgl32.Vec4{1, 1, 1, 1},
|
|
||||||
}
|
|
||||||
|
|
||||||
c.shader = NewGLShader(baseCanvasFragmentShader)
|
|
||||||
c.SetBounds(bounds)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetUniform will update the named uniform with the value of any supported underlying
|
|
||||||
// attribute variable. If the uniform already exists, including defaults, they will be reassigned
|
|
||||||
// to the new value. The value can be a pointer.
|
|
||||||
func (c *Canvas) SetUniform(name string, value interface{}) {
|
|
||||||
c.shader.SetUniform(name, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetFragmentShader allows you to set a new fragment shader on the underlying
|
|
||||||
// framebuffer. Argument "src" is the GLSL source, not a filename.
|
|
||||||
func (c *Canvas) SetFragmentShader(src string) {
|
|
||||||
c.shader.fs = src
|
|
||||||
c.shader.Update()
|
|
||||||
}
|
|
||||||
|
|
||||||
// MakeTriangles creates a specialized copy of the supplied Triangles that draws onto this Canvas.
|
|
||||||
//
|
|
||||||
// TrianglesPosition, TrianglesColor and TrianglesPicture are supported.
|
|
||||||
func (c *Canvas) MakeTriangles(t pixel.Triangles) pixel.TargetTriangles {
|
|
||||||
if gt, ok := t.(*GLTriangles); ok {
|
|
||||||
return &canvasTriangles{
|
|
||||||
GLTriangles: gt,
|
|
||||||
dst: c,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &canvasTriangles{
|
|
||||||
GLTriangles: NewGLTriangles(c.shader, t),
|
|
||||||
dst: c,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MakePicture create a specialized copy of the supplied Picture that draws onto this Canvas.
|
|
||||||
//
|
|
||||||
// PictureColor is supported.
|
|
||||||
func (c *Canvas) MakePicture(p pixel.Picture) pixel.TargetPicture {
|
|
||||||
if cp, ok := p.(*canvasPicture); ok {
|
|
||||||
return &canvasPicture{
|
|
||||||
GLPicture: cp.GLPicture,
|
|
||||||
dst: c,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if gp, ok := p.(GLPicture); ok {
|
|
||||||
return &canvasPicture{
|
|
||||||
GLPicture: gp,
|
|
||||||
dst: c,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &canvasPicture{
|
|
||||||
GLPicture: NewGLPicture(p),
|
|
||||||
dst: c,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetMatrix sets a Matrix that every point will be projected by.
|
|
||||||
func (c *Canvas) SetMatrix(m pixel.Matrix) {
|
|
||||||
// pixel.Matrix is 3x2 with an implicit 0, 0, 1 row after it. So
|
|
||||||
// [0] [2] [4] [0] [3] [6]
|
|
||||||
// [1] [3] [5] => [1] [4] [7]
|
|
||||||
// 0 0 1 0 0 1
|
|
||||||
// since all matrix ops are affine, the last row never changes, and we don't need to copy it
|
|
||||||
for i, j := range [...]int{0, 1, 3, 4, 6, 7} {
|
|
||||||
c.mat[j] = float32(m[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetColorMask sets a color that every color in triangles or a picture will be multiplied by.
|
|
||||||
func (c *Canvas) SetColorMask(col color.Color) {
|
|
||||||
rgba := pixel.Alpha(1)
|
|
||||||
if col != nil {
|
|
||||||
rgba = pixel.ToRGBA(col)
|
|
||||||
}
|
|
||||||
c.col = mgl32.Vec4{
|
|
||||||
float32(rgba.R),
|
|
||||||
float32(rgba.G),
|
|
||||||
float32(rgba.B),
|
|
||||||
float32(rgba.A),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetComposeMethod sets a Porter-Duff composition method to be used in the following draws onto
|
|
||||||
// this Canvas.
|
|
||||||
func (c *Canvas) SetComposeMethod(cmp pixel.ComposeMethod) {
|
|
||||||
c.cmp = cmp
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetBounds resizes the Canvas to the new bounds. Old content will be preserved.
|
|
||||||
func (c *Canvas) SetBounds(bounds pixel.Rect) {
|
|
||||||
c.gf.SetBounds(bounds)
|
|
||||||
if c.sprite == nil {
|
|
||||||
c.sprite = pixel.NewSprite(nil, pixel.Rect{})
|
|
||||||
}
|
|
||||||
c.sprite.Set(c, c.Bounds())
|
|
||||||
// c.sprite.SetMatrix(pixel.IM.Moved(c.Bounds().Center()))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bounds returns the rectangular bounds of the Canvas.
|
|
||||||
func (c *Canvas) Bounds() pixel.Rect {
|
|
||||||
return c.gf.Bounds()
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetSmooth sets whether stretched Pictures drawn onto this Canvas should be drawn smooth or
|
|
||||||
// pixely.
|
|
||||||
func (c *Canvas) SetSmooth(smooth bool) {
|
|
||||||
c.smooth = smooth
|
|
||||||
}
|
|
||||||
|
|
||||||
// Smooth returns whether stretched Pictures drawn onto this Canvas are set to be drawn smooth or
|
|
||||||
// pixely.
|
|
||||||
func (c *Canvas) Smooth() bool {
|
|
||||||
return c.smooth
|
|
||||||
}
|
|
||||||
|
|
||||||
// must be manually called inside mainthread
|
|
||||||
func (c *Canvas) setGlhfBounds() {
|
|
||||||
_, _, bw, bh := intBounds(c.gf.Bounds())
|
|
||||||
glhf.Bounds(0, 0, bw, bh)
|
|
||||||
}
|
|
||||||
|
|
||||||
// must be manually called inside mainthread
|
|
||||||
func setBlendFunc(cmp pixel.ComposeMethod) {
|
|
||||||
switch cmp {
|
|
||||||
case pixel.ComposeOver:
|
|
||||||
glhf.BlendFunc(glhf.One, glhf.OneMinusSrcAlpha)
|
|
||||||
case pixel.ComposeIn:
|
|
||||||
glhf.BlendFunc(glhf.DstAlpha, glhf.Zero)
|
|
||||||
case pixel.ComposeOut:
|
|
||||||
glhf.BlendFunc(glhf.OneMinusDstAlpha, glhf.Zero)
|
|
||||||
case pixel.ComposeAtop:
|
|
||||||
glhf.BlendFunc(glhf.DstAlpha, glhf.OneMinusSrcAlpha)
|
|
||||||
case pixel.ComposeRover:
|
|
||||||
glhf.BlendFunc(glhf.OneMinusDstAlpha, glhf.One)
|
|
||||||
case pixel.ComposeRin:
|
|
||||||
glhf.BlendFunc(glhf.Zero, glhf.SrcAlpha)
|
|
||||||
case pixel.ComposeRout:
|
|
||||||
glhf.BlendFunc(glhf.Zero, glhf.OneMinusSrcAlpha)
|
|
||||||
case pixel.ComposeRatop:
|
|
||||||
glhf.BlendFunc(glhf.OneMinusDstAlpha, glhf.SrcAlpha)
|
|
||||||
case pixel.ComposeXor:
|
|
||||||
glhf.BlendFunc(glhf.OneMinusDstAlpha, glhf.OneMinusSrcAlpha)
|
|
||||||
case pixel.ComposePlus:
|
|
||||||
glhf.BlendFunc(glhf.One, glhf.One)
|
|
||||||
case pixel.ComposeCopy:
|
|
||||||
glhf.BlendFunc(glhf.One, glhf.Zero)
|
|
||||||
default:
|
|
||||||
panic(errors.New("Canvas: invalid compose method"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear fills the whole Canvas with a single color.
|
|
||||||
func (c *Canvas) Clear(color color.Color) {
|
|
||||||
c.gf.Dirty()
|
|
||||||
|
|
||||||
rgba := pixel.ToRGBA(color)
|
|
||||||
|
|
||||||
// color masking
|
|
||||||
rgba = rgba.Mul(pixel.RGBA{
|
|
||||||
R: float64(c.col[0]),
|
|
||||||
G: float64(c.col[1]),
|
|
||||||
B: float64(c.col[2]),
|
|
||||||
A: float64(c.col[3]),
|
|
||||||
})
|
|
||||||
|
|
||||||
mainthread.CallNonBlock(func() {
|
|
||||||
c.setGlhfBounds()
|
|
||||||
c.gf.Frame().Begin()
|
|
||||||
glhf.Clear(
|
|
||||||
float32(rgba.R),
|
|
||||||
float32(rgba.G),
|
|
||||||
float32(rgba.B),
|
|
||||||
float32(rgba.A),
|
|
||||||
)
|
|
||||||
c.gf.Frame().End()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Color returns the color of the pixel over the given position inside the Canvas.
|
|
||||||
func (c *Canvas) Color(at pixel.Vec) pixel.RGBA {
|
|
||||||
return c.gf.Color(at)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Texture returns the underlying OpenGL Texture of this Canvas.
|
|
||||||
//
|
|
||||||
// Implements GLPicture interface.
|
|
||||||
func (c *Canvas) Texture() *glhf.Texture {
|
|
||||||
return c.gf.Texture()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Frame returns the underlying OpenGL Frame of this Canvas.
|
|
||||||
func (c *Canvas) Frame() *glhf.Frame {
|
|
||||||
return c.gf.frame
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetPixels replaces the content of the Canvas with the provided pixels. The provided slice must be
|
|
||||||
// an alpha-premultiplied RGBA sequence of correct length (4 * width * height).
|
|
||||||
func (c *Canvas) SetPixels(pixels []uint8) {
|
|
||||||
c.gf.Dirty()
|
|
||||||
|
|
||||||
mainthread.Call(func() {
|
|
||||||
tex := c.Texture()
|
|
||||||
tex.Begin()
|
|
||||||
tex.SetPixels(0, 0, tex.Width(), tex.Height(), pixels)
|
|
||||||
tex.End()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pixels returns an alpha-premultiplied RGBA sequence of the content of the Canvas.
|
|
||||||
func (c *Canvas) Pixels() []uint8 {
|
|
||||||
var pixels []uint8
|
|
||||||
|
|
||||||
mainthread.Call(func() {
|
|
||||||
tex := c.Texture()
|
|
||||||
tex.Begin()
|
|
||||||
pixels = tex.Pixels(0, 0, tex.Width(), tex.Height())
|
|
||||||
tex.End()
|
|
||||||
})
|
|
||||||
|
|
||||||
return pixels
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw draws the content of the Canvas onto another Target, transformed by the given Matrix, just
|
|
||||||
// like if it was a Sprite containing the whole Canvas.
|
|
||||||
func (c *Canvas) Draw(t pixel.Target, matrix pixel.Matrix) {
|
|
||||||
c.sprite.Draw(t, matrix)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DrawColorMask draws the content of the Canvas onto another Target, transformed by the given
|
|
||||||
// Matrix and multiplied by the given mask, just like if it was a Sprite containing the whole Canvas.
|
|
||||||
//
|
|
||||||
// If the color mask is nil, a fully opaque white mask will be used causing no effect.
|
|
||||||
func (c *Canvas) DrawColorMask(t pixel.Target, matrix pixel.Matrix, mask color.Color) {
|
|
||||||
c.sprite.DrawColorMask(t, matrix, mask)
|
|
||||||
}
|
|
||||||
|
|
||||||
type canvasTriangles struct {
|
|
||||||
*GLTriangles
|
|
||||||
dst *Canvas
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ct *canvasTriangles) draw(tex *glhf.Texture, bounds pixel.Rect) {
|
|
||||||
ct.dst.gf.Dirty()
|
|
||||||
|
|
||||||
// save the current state vars to avoid race condition
|
|
||||||
cmp := ct.dst.cmp
|
|
||||||
smt := ct.dst.smooth
|
|
||||||
mat := ct.dst.mat
|
|
||||||
col := ct.dst.col
|
|
||||||
|
|
||||||
mainthread.CallNonBlock(func() {
|
|
||||||
ct.dst.setGlhfBounds()
|
|
||||||
setBlendFunc(cmp)
|
|
||||||
|
|
||||||
frame := ct.dst.gf.Frame()
|
|
||||||
shader := ct.shader.s
|
|
||||||
|
|
||||||
frame.Begin()
|
|
||||||
shader.Begin()
|
|
||||||
|
|
||||||
ct.shader.uniformDefaults.transform = mat
|
|
||||||
ct.shader.uniformDefaults.colormask = col
|
|
||||||
dstBounds := ct.dst.Bounds()
|
|
||||||
ct.shader.uniformDefaults.bounds = mgl32.Vec4{
|
|
||||||
float32(dstBounds.Min.X),
|
|
||||||
float32(dstBounds.Min.Y),
|
|
||||||
float32(dstBounds.W()),
|
|
||||||
float32(dstBounds.H()),
|
|
||||||
}
|
|
||||||
|
|
||||||
bx, by, bw, bh := intBounds(bounds)
|
|
||||||
ct.shader.uniformDefaults.texbounds = mgl32.Vec4{
|
|
||||||
float32(bx),
|
|
||||||
float32(by),
|
|
||||||
float32(bw),
|
|
||||||
float32(bh),
|
|
||||||
}
|
|
||||||
|
|
||||||
for loc, u := range ct.shader.uniforms {
|
|
||||||
ct.shader.s.SetUniformAttr(loc, u.Value())
|
|
||||||
}
|
|
||||||
|
|
||||||
if tex == nil {
|
|
||||||
ct.vs.Begin()
|
|
||||||
ct.vs.Draw()
|
|
||||||
ct.vs.End()
|
|
||||||
} else {
|
|
||||||
tex.Begin()
|
|
||||||
|
|
||||||
if tex.Smooth() != smt {
|
|
||||||
tex.SetSmooth(smt)
|
|
||||||
}
|
|
||||||
|
|
||||||
ct.vs.Begin()
|
|
||||||
ct.vs.Draw()
|
|
||||||
ct.vs.End()
|
|
||||||
|
|
||||||
tex.End()
|
|
||||||
}
|
|
||||||
|
|
||||||
shader.End()
|
|
||||||
frame.End()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ct *canvasTriangles) Draw() {
|
|
||||||
ct.draw(nil, pixel.Rect{})
|
|
||||||
}
|
|
||||||
|
|
||||||
type canvasPicture struct {
|
|
||||||
GLPicture
|
|
||||||
dst *Canvas
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cp *canvasPicture) Draw(t pixel.TargetTriangles) {
|
|
||||||
ct := t.(*canvasTriangles)
|
|
||||||
if cp.dst != ct.dst {
|
|
||||||
panic(fmt.Errorf("(%T).Draw: TargetTriangles generated by different Canvas", cp))
|
|
||||||
}
|
|
||||||
ct.draw(cp.GLPicture.Texture(), cp.GLPicture.Bounds())
|
|
||||||
}
|
|
|
@ -1,5 +0,0 @@
|
||||||
// Package pixelgl implements efficient OpenGL targets and utilities for the Pixel game development
|
|
||||||
// library, specifically Window and Canvas.
|
|
||||||
//
|
|
||||||
// It also contains a few additional utilities to help extend Pixel with OpenGL graphical effects.
|
|
||||||
package pixelgl
|
|
|
@ -1,105 +0,0 @@
|
||||||
package pixelgl
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/faiface/glhf"
|
|
||||||
"github.com/faiface/mainthread"
|
|
||||||
"github.com/faiface/pixel"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GLFrame is a type that helps implementing OpenGL Targets. It implements most common methods to
|
|
||||||
// avoid code redundancy. It contains an glhf.Frame that you can draw on.
|
|
||||||
type GLFrame struct {
|
|
||||||
frame *glhf.Frame
|
|
||||||
bounds pixel.Rect
|
|
||||||
pixels []uint8
|
|
||||||
dirty bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewGLFrame creates a new GLFrame with the given bounds.
|
|
||||||
func NewGLFrame(bounds pixel.Rect) *GLFrame {
|
|
||||||
gf := new(GLFrame)
|
|
||||||
gf.SetBounds(bounds)
|
|
||||||
return gf
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetBounds resizes the GLFrame to the new bounds.
|
|
||||||
func (gf *GLFrame) SetBounds(bounds pixel.Rect) {
|
|
||||||
if bounds == gf.Bounds() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
mainthread.Call(func() {
|
|
||||||
oldF := gf.frame
|
|
||||||
|
|
||||||
_, _, w, h := intBounds(bounds)
|
|
||||||
if w <= 0 {
|
|
||||||
w = 1
|
|
||||||
}
|
|
||||||
if h <= 0 {
|
|
||||||
h = 1
|
|
||||||
}
|
|
||||||
gf.frame = glhf.NewFrame(w, h, false)
|
|
||||||
|
|
||||||
// preserve old content
|
|
||||||
if oldF != nil {
|
|
||||||
ox, oy, ow, oh := intBounds(bounds)
|
|
||||||
oldF.Blit(
|
|
||||||
gf.frame,
|
|
||||||
ox, oy, ox+ow, oy+oh,
|
|
||||||
ox, oy, ox+ow, oy+oh,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
gf.bounds = bounds
|
|
||||||
gf.pixels = nil
|
|
||||||
gf.dirty = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bounds returns the current GLFrame's bounds.
|
|
||||||
func (gf *GLFrame) Bounds() pixel.Rect {
|
|
||||||
return gf.bounds
|
|
||||||
}
|
|
||||||
|
|
||||||
// Color returns the color of the pixel under the specified position.
|
|
||||||
func (gf *GLFrame) Color(at pixel.Vec) pixel.RGBA {
|
|
||||||
if gf.dirty {
|
|
||||||
mainthread.Call(func() {
|
|
||||||
tex := gf.frame.Texture()
|
|
||||||
tex.Begin()
|
|
||||||
gf.pixels = tex.Pixels(0, 0, tex.Width(), tex.Height())
|
|
||||||
tex.End()
|
|
||||||
})
|
|
||||||
gf.dirty = false
|
|
||||||
}
|
|
||||||
if !gf.bounds.Contains(at) {
|
|
||||||
return pixel.Alpha(0)
|
|
||||||
}
|
|
||||||
bx, by, bw, _ := intBounds(gf.bounds)
|
|
||||||
x, y := int(at.X)-bx, int(at.Y)-by
|
|
||||||
off := y*bw + x
|
|
||||||
return pixel.RGBA{
|
|
||||||
R: float64(gf.pixels[off*4+0]) / 255,
|
|
||||||
G: float64(gf.pixels[off*4+1]) / 255,
|
|
||||||
B: float64(gf.pixels[off*4+2]) / 255,
|
|
||||||
A: float64(gf.pixels[off*4+3]) / 255,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Frame returns the GLFrame's Frame that you can draw on.
|
|
||||||
func (gf *GLFrame) Frame() *glhf.Frame {
|
|
||||||
return gf.frame
|
|
||||||
}
|
|
||||||
|
|
||||||
// Texture returns the underlying Texture of the GLFrame's Frame.
|
|
||||||
//
|
|
||||||
// Implements GLPicture interface.
|
|
||||||
func (gf *GLFrame) Texture() *glhf.Texture {
|
|
||||||
return gf.frame.Texture()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dirty marks the GLFrame as changed. Always call this method when you draw onto the GLFrame's
|
|
||||||
// Frame.
|
|
||||||
func (gf *GLFrame) Dirty() {
|
|
||||||
gf.dirty = true
|
|
||||||
}
|
|
|
@ -1,98 +0,0 @@
|
||||||
package pixelgl
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math"
|
|
||||||
|
|
||||||
"github.com/faiface/glhf"
|
|
||||||
"github.com/faiface/mainthread"
|
|
||||||
"github.com/faiface/pixel"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GLPicture is a pixel.PictureColor with a Texture. All OpenGL Targets should implement and accept
|
|
||||||
// this interface, because it enables seamless drawing of one to another.
|
|
||||||
//
|
|
||||||
// Implementing this interface on an OpenGL Target enables other OpenGL Targets to efficiently draw
|
|
||||||
// that Target onto them.
|
|
||||||
type GLPicture interface {
|
|
||||||
pixel.PictureColor
|
|
||||||
Texture() *glhf.Texture
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewGLPicture creates a new GLPicture with it's own static OpenGL texture. This function always
|
|
||||||
// allocates a new texture that cannot (shouldn't) be further modified.
|
|
||||||
func NewGLPicture(p pixel.Picture) GLPicture {
|
|
||||||
bounds := p.Bounds()
|
|
||||||
bx, by, bw, bh := intBounds(bounds)
|
|
||||||
|
|
||||||
pixels := make([]uint8, 4*bw*bh)
|
|
||||||
|
|
||||||
if pd, ok := p.(*pixel.PictureData); ok {
|
|
||||||
// PictureData short path
|
|
||||||
for y := 0; y < bh; y++ {
|
|
||||||
for x := 0; x < bw; x++ {
|
|
||||||
rgba := pd.Pix[y*pd.Stride+x]
|
|
||||||
off := (y*bw + x) * 4
|
|
||||||
pixels[off+0] = rgba.R
|
|
||||||
pixels[off+1] = rgba.G
|
|
||||||
pixels[off+2] = rgba.B
|
|
||||||
pixels[off+3] = rgba.A
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if p, ok := p.(pixel.PictureColor); ok {
|
|
||||||
for y := 0; y < bh; y++ {
|
|
||||||
for x := 0; x < bw; x++ {
|
|
||||||
at := pixel.V(
|
|
||||||
math.Max(float64(bx+x), bounds.Min.X),
|
|
||||||
math.Max(float64(by+y), bounds.Min.Y),
|
|
||||||
)
|
|
||||||
color := p.Color(at)
|
|
||||||
off := (y*bw + x) * 4
|
|
||||||
pixels[off+0] = uint8(color.R * 255)
|
|
||||||
pixels[off+1] = uint8(color.G * 255)
|
|
||||||
pixels[off+2] = uint8(color.B * 255)
|
|
||||||
pixels[off+3] = uint8(color.A * 255)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var tex *glhf.Texture
|
|
||||||
mainthread.Call(func() {
|
|
||||||
tex = glhf.NewTexture(bw, bh, false, pixels)
|
|
||||||
})
|
|
||||||
|
|
||||||
gp := &glPicture{
|
|
||||||
bounds: bounds,
|
|
||||||
tex: tex,
|
|
||||||
pixels: pixels,
|
|
||||||
}
|
|
||||||
return gp
|
|
||||||
}
|
|
||||||
|
|
||||||
type glPicture struct {
|
|
||||||
bounds pixel.Rect
|
|
||||||
tex *glhf.Texture
|
|
||||||
pixels []uint8
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gp *glPicture) Bounds() pixel.Rect {
|
|
||||||
return gp.bounds
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gp *glPicture) Texture() *glhf.Texture {
|
|
||||||
return gp.tex
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gp *glPicture) Color(at pixel.Vec) pixel.RGBA {
|
|
||||||
if !gp.bounds.Contains(at) {
|
|
||||||
return pixel.Alpha(0)
|
|
||||||
}
|
|
||||||
bx, by, bw, _ := intBounds(gp.bounds)
|
|
||||||
x, y := int(at.X)-bx, int(at.Y)-by
|
|
||||||
off := y*bw + x
|
|
||||||
return pixel.RGBA{
|
|
||||||
R: float64(gp.pixels[off*4+0]) / 255,
|
|
||||||
G: float64(gp.pixels[off*4+1]) / 255,
|
|
||||||
B: float64(gp.pixels[off*4+2]) / 255,
|
|
||||||
A: float64(gp.pixels[off*4+3]) / 255,
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,298 +0,0 @@
|
||||||
package pixelgl
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/faiface/glhf"
|
|
||||||
"github.com/faiface/mainthread"
|
|
||||||
"github.com/go-gl/mathgl/mgl32"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GLShader is a type to assist with managing a canvas's underlying
|
|
||||||
// shader configuration. This allows for customization of shaders on
|
|
||||||
// a per canvas basis.
|
|
||||||
type GLShader struct {
|
|
||||||
s *glhf.Shader
|
|
||||||
vf, uf glhf.AttrFormat
|
|
||||||
vs, fs string
|
|
||||||
|
|
||||||
uniforms []gsUniformAttr
|
|
||||||
|
|
||||||
uniformDefaults struct {
|
|
||||||
transform mgl32.Mat3
|
|
||||||
colormask mgl32.Vec4
|
|
||||||
bounds mgl32.Vec4
|
|
||||||
texbounds mgl32.Vec4
|
|
||||||
cliprect mgl32.Vec4
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type gsUniformAttr struct {
|
|
||||||
Name string
|
|
||||||
Type glhf.AttrType
|
|
||||||
value interface{}
|
|
||||||
ispointer bool
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
canvasPosition int = iota
|
|
||||||
canvasColor
|
|
||||||
canvasTexCoords
|
|
||||||
canvasIntensity
|
|
||||||
canvasClip
|
|
||||||
)
|
|
||||||
|
|
||||||
var defaultCanvasVertexFormat = glhf.AttrFormat{
|
|
||||||
canvasPosition: glhf.Attr{Name: "aPosition", Type: glhf.Vec2},
|
|
||||||
canvasColor: glhf.Attr{Name: "aColor", Type: glhf.Vec4},
|
|
||||||
canvasTexCoords: glhf.Attr{Name: "aTexCoords", Type: glhf.Vec2},
|
|
||||||
canvasIntensity: glhf.Attr{Name: "aIntensity", Type: glhf.Float},
|
|
||||||
canvasClip: glhf.Attr{Name: "aClipRect", Type: glhf.Vec4},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sets up a base shader with everything needed for a Pixel
|
|
||||||
// canvas to render correctly. The defaults can be overridden
|
|
||||||
// by simply using the SetUniform function.
|
|
||||||
func NewGLShader(fragmentShader string) *GLShader {
|
|
||||||
gs := &GLShader{
|
|
||||||
vf: defaultCanvasVertexFormat,
|
|
||||||
vs: baseCanvasVertexShader,
|
|
||||||
fs: fragmentShader,
|
|
||||||
}
|
|
||||||
|
|
||||||
gs.SetUniform("uTransform", &gs.uniformDefaults.transform)
|
|
||||||
gs.SetUniform("uColorMask", &gs.uniformDefaults.colormask)
|
|
||||||
gs.SetUniform("uBounds", &gs.uniformDefaults.bounds)
|
|
||||||
gs.SetUniform("uTexBounds", &gs.uniformDefaults.texbounds)
|
|
||||||
|
|
||||||
gs.Update()
|
|
||||||
|
|
||||||
return gs
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update reinitialize GLShader data and recompile the underlying gl shader object
|
|
||||||
func (gs *GLShader) Update() {
|
|
||||||
gs.uf = make([]glhf.Attr, len(gs.uniforms))
|
|
||||||
for idx := range gs.uniforms {
|
|
||||||
gs.uf[idx] = glhf.Attr{
|
|
||||||
Name: gs.uniforms[idx].Name,
|
|
||||||
Type: gs.uniforms[idx].Type,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var shader *glhf.Shader
|
|
||||||
mainthread.Call(func() {
|
|
||||||
var err error
|
|
||||||
shader, err = glhf.NewShader(
|
|
||||||
gs.vf,
|
|
||||||
gs.uf,
|
|
||||||
gs.vs,
|
|
||||||
gs.fs,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
panic(errors.Wrap(err, "failed to create Canvas, there's a bug in the shader"))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
gs.s = shader
|
|
||||||
}
|
|
||||||
|
|
||||||
// gets the uniform index from GLShader
|
|
||||||
func (gs *GLShader) getUniform(Name string) int {
|
|
||||||
for i, u := range gs.uniforms {
|
|
||||||
if u.Name == Name {
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetUniform appends a custom uniform name and value to the shader.
|
|
||||||
// if the uniform already exists, it will simply be overwritten.
|
|
||||||
//
|
|
||||||
// example:
|
|
||||||
//
|
|
||||||
// utime := float32(time.Since(starttime)).Seconds())
|
|
||||||
// mycanvas.shader.AddUniform("u_time", &utime)
|
|
||||||
func (gs *GLShader) SetUniform(name string, value interface{}) {
|
|
||||||
t, p := getAttrType(value)
|
|
||||||
if loc := gs.getUniform(name); loc > -1 {
|
|
||||||
gs.uniforms[loc].Name = name
|
|
||||||
gs.uniforms[loc].Type = t
|
|
||||||
gs.uniforms[loc].ispointer = p
|
|
||||||
gs.uniforms[loc].value = value
|
|
||||||
return
|
|
||||||
}
|
|
||||||
gs.uniforms = append(gs.uniforms, gsUniformAttr{
|
|
||||||
Name: name,
|
|
||||||
Type: t,
|
|
||||||
ispointer: p,
|
|
||||||
value: value,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Value returns the attribute's concrete value. If the stored value
|
|
||||||
// is a pointer, we return the dereferenced value.
|
|
||||||
func (gu *gsUniformAttr) Value() interface{} {
|
|
||||||
if !gu.ispointer {
|
|
||||||
return gu.value
|
|
||||||
}
|
|
||||||
switch gu.Type {
|
|
||||||
case glhf.Vec2:
|
|
||||||
return *gu.value.(*mgl32.Vec2)
|
|
||||||
case glhf.Vec3:
|
|
||||||
return *gu.value.(*mgl32.Vec3)
|
|
||||||
case glhf.Vec4:
|
|
||||||
return *gu.value.(*mgl32.Vec4)
|
|
||||||
case glhf.Mat2:
|
|
||||||
return *gu.value.(*mgl32.Mat2)
|
|
||||||
case glhf.Mat23:
|
|
||||||
return *gu.value.(*mgl32.Mat2x3)
|
|
||||||
case glhf.Mat24:
|
|
||||||
return *gu.value.(*mgl32.Mat2x4)
|
|
||||||
case glhf.Mat3:
|
|
||||||
return *gu.value.(*mgl32.Mat3)
|
|
||||||
case glhf.Mat32:
|
|
||||||
return *gu.value.(*mgl32.Mat3x2)
|
|
||||||
case glhf.Mat34:
|
|
||||||
return *gu.value.(*mgl32.Mat3x4)
|
|
||||||
case glhf.Mat4:
|
|
||||||
return *gu.value.(*mgl32.Mat4)
|
|
||||||
case glhf.Mat42:
|
|
||||||
return *gu.value.(*mgl32.Mat4x2)
|
|
||||||
case glhf.Mat43:
|
|
||||||
return *gu.value.(*mgl32.Mat4x3)
|
|
||||||
case glhf.Int:
|
|
||||||
return *gu.value.(*int32)
|
|
||||||
case glhf.Float:
|
|
||||||
return *gu.value.(*float32)
|
|
||||||
default:
|
|
||||||
panic("invalid attrtype")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the type identifier for any (supported) attribute variable type
|
|
||||||
// and whether or not it is a pointer of that type.
|
|
||||||
func getAttrType(v interface{}) (glhf.AttrType, bool) {
|
|
||||||
switch v.(type) {
|
|
||||||
case int32:
|
|
||||||
return glhf.Int, false
|
|
||||||
case float32:
|
|
||||||
return glhf.Float, false
|
|
||||||
case mgl32.Vec2:
|
|
||||||
return glhf.Vec2, false
|
|
||||||
case mgl32.Vec3:
|
|
||||||
return glhf.Vec3, false
|
|
||||||
case mgl32.Vec4:
|
|
||||||
return glhf.Vec4, false
|
|
||||||
case mgl32.Mat2:
|
|
||||||
return glhf.Mat2, false
|
|
||||||
case mgl32.Mat2x3:
|
|
||||||
return glhf.Mat23, false
|
|
||||||
case mgl32.Mat2x4:
|
|
||||||
return glhf.Mat24, false
|
|
||||||
case mgl32.Mat3:
|
|
||||||
return glhf.Mat3, false
|
|
||||||
case mgl32.Mat3x2:
|
|
||||||
return glhf.Mat32, false
|
|
||||||
case mgl32.Mat3x4:
|
|
||||||
return glhf.Mat34, false
|
|
||||||
case mgl32.Mat4:
|
|
||||||
return glhf.Mat4, false
|
|
||||||
case mgl32.Mat4x2:
|
|
||||||
return glhf.Mat42, false
|
|
||||||
case mgl32.Mat4x3:
|
|
||||||
return glhf.Mat43, false
|
|
||||||
case *mgl32.Vec2:
|
|
||||||
return glhf.Vec2, true
|
|
||||||
case *mgl32.Vec3:
|
|
||||||
return glhf.Vec3, true
|
|
||||||
case *mgl32.Vec4:
|
|
||||||
return glhf.Vec4, true
|
|
||||||
case *mgl32.Mat2:
|
|
||||||
return glhf.Mat2, true
|
|
||||||
case *mgl32.Mat2x3:
|
|
||||||
return glhf.Mat23, true
|
|
||||||
case *mgl32.Mat2x4:
|
|
||||||
return glhf.Mat24, true
|
|
||||||
case *mgl32.Mat3:
|
|
||||||
return glhf.Mat3, true
|
|
||||||
case *mgl32.Mat3x2:
|
|
||||||
return glhf.Mat32, true
|
|
||||||
case *mgl32.Mat3x4:
|
|
||||||
return glhf.Mat34, true
|
|
||||||
case *mgl32.Mat4:
|
|
||||||
return glhf.Mat4, true
|
|
||||||
case *mgl32.Mat4x2:
|
|
||||||
return glhf.Mat42, true
|
|
||||||
case *mgl32.Mat4x3:
|
|
||||||
return glhf.Mat43, true
|
|
||||||
case *int32:
|
|
||||||
return glhf.Int, true
|
|
||||||
case *float32:
|
|
||||||
return glhf.Float, true
|
|
||||||
default:
|
|
||||||
panic("invalid AttrType")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var baseCanvasVertexShader = `
|
|
||||||
#version 330 core
|
|
||||||
|
|
||||||
in vec2 aPosition;
|
|
||||||
in vec4 aColor;
|
|
||||||
in vec2 aTexCoords;
|
|
||||||
in float aIntensity;
|
|
||||||
in vec4 aClipRect;
|
|
||||||
in float aIsClipped;
|
|
||||||
|
|
||||||
out vec4 vColor;
|
|
||||||
out vec2 vTexCoords;
|
|
||||||
out float vIntensity;
|
|
||||||
out vec2 vPosition;
|
|
||||||
out vec4 vClipRect;
|
|
||||||
|
|
||||||
uniform mat3 uTransform;
|
|
||||||
uniform vec4 uBounds;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
vec2 transPos = (uTransform * vec3(aPosition, 1.0)).xy;
|
|
||||||
vec2 normPos = (transPos - uBounds.xy) / uBounds.zw * 2 - vec2(1, 1);
|
|
||||||
gl_Position = vec4(normPos, 0.0, 1.0);
|
|
||||||
|
|
||||||
vColor = aColor;
|
|
||||||
vPosition = aPosition;
|
|
||||||
vTexCoords = aTexCoords;
|
|
||||||
vIntensity = aIntensity;
|
|
||||||
vClipRect = aClipRect;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
var baseCanvasFragmentShader = `
|
|
||||||
#version 330 core
|
|
||||||
|
|
||||||
in vec4 vColor;
|
|
||||||
in vec2 vTexCoords;
|
|
||||||
in float vIntensity;
|
|
||||||
in vec4 vClipRect;
|
|
||||||
|
|
||||||
out vec4 fragColor;
|
|
||||||
|
|
||||||
uniform vec4 uColorMask;
|
|
||||||
uniform vec4 uTexBounds;
|
|
||||||
uniform sampler2D uTexture;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
if ((vClipRect != vec4(0,0,0,0)) && (gl_FragCoord.x < vClipRect.x || gl_FragCoord.y < vClipRect.y || gl_FragCoord.x > vClipRect.z || gl_FragCoord.y > vClipRect.w))
|
|
||||||
discard;
|
|
||||||
|
|
||||||
if (vIntensity == 0) {
|
|
||||||
fragColor = uColorMask * vColor;
|
|
||||||
} else {
|
|
||||||
fragColor = vec4(0, 0, 0, 0);
|
|
||||||
fragColor += (1 - vIntensity) * vColor;
|
|
||||||
vec2 t = (vTexCoords - uTexBounds.xy) / uTexBounds.zw;
|
|
||||||
fragColor += vIntensity * vColor * texture(uTexture, t);
|
|
||||||
fragColor *= uColorMask;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
|
@ -1,302 +0,0 @@
|
||||||
package pixelgl
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/faiface/glhf"
|
|
||||||
"github.com/faiface/mainthread"
|
|
||||||
"github.com/faiface/pixel"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GLTriangles are OpenGL triangles implemented using glhf.VertexSlice.
|
|
||||||
//
|
|
||||||
// Triangles returned from this function support TrianglesPosition, TrianglesColor and
|
|
||||||
// TrianglesPicture. If you need to support more, you can "override" SetLen and Update methods.
|
|
||||||
type GLTriangles struct {
|
|
||||||
vs *glhf.VertexSlice
|
|
||||||
data []float32
|
|
||||||
shader *GLShader
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
_ pixel.TrianglesPosition = (*GLTriangles)(nil)
|
|
||||||
_ pixel.TrianglesColor = (*GLTriangles)(nil)
|
|
||||||
_ pixel.TrianglesPicture = (*GLTriangles)(nil)
|
|
||||||
_ pixel.TrianglesClipped = (*GLTriangles)(nil)
|
|
||||||
)
|
|
||||||
|
|
||||||
// The following is a helper so that the indices of
|
|
||||||
// each of these items is easier to see/debug.
|
|
||||||
const (
|
|
||||||
triPosX = iota
|
|
||||||
triPosY
|
|
||||||
triColorR
|
|
||||||
triColorG
|
|
||||||
triColorB
|
|
||||||
triColorA
|
|
||||||
triPicX
|
|
||||||
triPicY
|
|
||||||
triIntensity
|
|
||||||
triClipMinX
|
|
||||||
triClipMinY
|
|
||||||
triClipMaxX
|
|
||||||
triClipMaxY
|
|
||||||
trisAttrLen
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewGLTriangles returns GLTriangles initialized with the data from the supplied Triangles.
|
|
||||||
//
|
|
||||||
// Only draw the Triangles using the provided Shader.
|
|
||||||
func NewGLTriangles(shader *GLShader, t pixel.Triangles) *GLTriangles {
|
|
||||||
var gt *GLTriangles
|
|
||||||
mainthread.Call(func() {
|
|
||||||
gt = &GLTriangles{
|
|
||||||
vs: glhf.MakeVertexSlice(shader.s, 0, t.Len()),
|
|
||||||
shader: shader,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
gt.SetLen(t.Len())
|
|
||||||
gt.Update(t)
|
|
||||||
return gt
|
|
||||||
}
|
|
||||||
|
|
||||||
// VertexSlice returns the VertexSlice of this GLTriangles.
|
|
||||||
//
|
|
||||||
// You can use it to draw them.
|
|
||||||
func (gt *GLTriangles) VertexSlice() *glhf.VertexSlice {
|
|
||||||
return gt.vs
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shader returns the GLTriangles's associated shader.
|
|
||||||
func (gt *GLTriangles) Shader() *GLShader {
|
|
||||||
return gt.shader
|
|
||||||
}
|
|
||||||
|
|
||||||
// Len returns the number of vertices.
|
|
||||||
func (gt *GLTriangles) Len() int {
|
|
||||||
return len(gt.data) / gt.vs.Stride()
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetLen efficiently resizes GLTriangles to len.
|
|
||||||
//
|
|
||||||
// Time complexity is amortized O(1).
|
|
||||||
func (gt *GLTriangles) SetLen(length int) {
|
|
||||||
switch {
|
|
||||||
case length > gt.Len():
|
|
||||||
needAppend := length - gt.Len()
|
|
||||||
for i := 0; i < needAppend; i++ {
|
|
||||||
gt.data = append(gt.data,
|
|
||||||
0, 0,
|
|
||||||
1, 1, 1, 1,
|
|
||||||
0, 0,
|
|
||||||
0,
|
|
||||||
0, 0, 0, 0,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
case length < gt.Len():
|
|
||||||
gt.data = gt.data[:length*gt.vs.Stride()]
|
|
||||||
default:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
mainthread.Call(func() {
|
|
||||||
gt.vs.Begin()
|
|
||||||
gt.vs.SetLen(length)
|
|
||||||
gt.vs.End()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Slice returns a sub-Triangles of this GLTriangles in range [i, j).
|
|
||||||
func (gt *GLTriangles) Slice(i, j int) pixel.Triangles {
|
|
||||||
return &GLTriangles{
|
|
||||||
vs: gt.vs.Slice(i, j),
|
|
||||||
data: gt.data[i*gt.vs.Stride() : j*gt.vs.Stride()],
|
|
||||||
shader: gt.shader,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gt *GLTriangles) updateData(t pixel.Triangles) {
|
|
||||||
// glTriangles short path
|
|
||||||
if t, ok := t.(*GLTriangles); ok {
|
|
||||||
copy(gt.data, t.data)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// TrianglesData short path
|
|
||||||
stride := gt.vs.Stride()
|
|
||||||
length := gt.Len()
|
|
||||||
if t, ok := t.(*pixel.TrianglesData); ok {
|
|
||||||
for i := 0; i < length; i++ {
|
|
||||||
var (
|
|
||||||
px, py = (*t)[i].Position.XY()
|
|
||||||
col = (*t)[i].Color
|
|
||||||
tx, ty = (*t)[i].Picture.XY()
|
|
||||||
in = (*t)[i].Intensity
|
|
||||||
rec = (*t)[i].ClipRect
|
|
||||||
)
|
|
||||||
d := gt.data[i*stride : i*stride+trisAttrLen]
|
|
||||||
d[triPosX] = float32(px)
|
|
||||||
d[triPosY] = float32(py)
|
|
||||||
d[triColorR] = float32(col.R)
|
|
||||||
d[triColorG] = float32(col.G)
|
|
||||||
d[triColorB] = float32(col.B)
|
|
||||||
d[triColorA] = float32(col.A)
|
|
||||||
d[triPicX] = float32(tx)
|
|
||||||
d[triPicY] = float32(ty)
|
|
||||||
d[triIntensity] = float32(in)
|
|
||||||
d[triClipMinX] = float32(rec.Min.X)
|
|
||||||
d[triClipMinY] = float32(rec.Min.Y)
|
|
||||||
d[triClipMaxX] = float32(rec.Max.X)
|
|
||||||
d[triClipMaxY] = float32(rec.Max.Y)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if t, ok := t.(pixel.TrianglesPosition); ok {
|
|
||||||
for i := 0; i < length; i++ {
|
|
||||||
px, py := t.Position(i).XY()
|
|
||||||
gt.data[i*stride+triPosX] = float32(px)
|
|
||||||
gt.data[i*stride+triPosY] = float32(py)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if t, ok := t.(pixel.TrianglesColor); ok {
|
|
||||||
for i := 0; i < length; i++ {
|
|
||||||
col := t.Color(i)
|
|
||||||
gt.data[i*stride+triColorR] = float32(col.R)
|
|
||||||
gt.data[i*stride+triColorG] = float32(col.G)
|
|
||||||
gt.data[i*stride+triColorB] = float32(col.B)
|
|
||||||
gt.data[i*stride+triColorA] = float32(col.A)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if t, ok := t.(pixel.TrianglesPicture); ok {
|
|
||||||
for i := 0; i < length; i++ {
|
|
||||||
pic, intensity := t.Picture(i)
|
|
||||||
gt.data[i*stride+triPicX] = float32(pic.X)
|
|
||||||
gt.data[i*stride+triPicY] = float32(pic.Y)
|
|
||||||
gt.data[i*stride+triIntensity] = float32(intensity)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if t, ok := t.(pixel.TrianglesClipped); ok {
|
|
||||||
for i := 0; i < length; i++ {
|
|
||||||
rect, _ := t.ClipRect(i)
|
|
||||||
gt.data[i*stride+triClipMinX] = float32(rect.Min.X)
|
|
||||||
gt.data[i*stride+triClipMinY] = float32(rect.Min.Y)
|
|
||||||
gt.data[i*stride+triClipMaxX] = float32(rect.Max.X)
|
|
||||||
gt.data[i*stride+triClipMaxY] = float32(rect.Max.Y)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update copies vertex properties from the supplied Triangles into this GLTriangles.
|
|
||||||
//
|
|
||||||
// The two Triangles (gt and t) must be of the same len.
|
|
||||||
func (gt *GLTriangles) Update(t pixel.Triangles) {
|
|
||||||
if gt.Len() != t.Len() {
|
|
||||||
panic(fmt.Errorf("(%T).Update: invalid triangles len", gt))
|
|
||||||
}
|
|
||||||
gt.updateData(t)
|
|
||||||
|
|
||||||
// Copy the verteces down to the glhf.VertexData
|
|
||||||
gt.CopyVertices()
|
|
||||||
}
|
|
||||||
|
|
||||||
// CopyVertices copies the GLTriangle data down to the vertex data.
|
|
||||||
func (gt *GLTriangles) CopyVertices() {
|
|
||||||
// this code is supposed to copy the vertex data and CallNonBlock the update if
|
|
||||||
// the data is small enough, otherwise it'll block and not copy the data
|
|
||||||
if len(gt.data) < 256 { // arbitrary heurestic constant
|
|
||||||
data := append([]float32{}, gt.data...)
|
|
||||||
mainthread.CallNonBlock(func() {
|
|
||||||
gt.vs.Begin()
|
|
||||||
gt.vs.SetVertexData(data)
|
|
||||||
gt.vs.End()
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
mainthread.Call(func() {
|
|
||||||
gt.vs.Begin()
|
|
||||||
gt.vs.SetVertexData(gt.data)
|
|
||||||
gt.vs.End()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy returns an independent copy of this GLTriangles.
|
|
||||||
//
|
|
||||||
// The returned Triangles are *GLTriangles as the underlying type.
|
|
||||||
func (gt *GLTriangles) Copy() pixel.Triangles {
|
|
||||||
return NewGLTriangles(gt.shader, gt)
|
|
||||||
}
|
|
||||||
|
|
||||||
// index is a helper function that returns the index in the data
|
|
||||||
// slice given the i-th vertex and the item index.
|
|
||||||
func (gt *GLTriangles) index(i, idx int) int {
|
|
||||||
return i*gt.vs.Stride() + idx
|
|
||||||
}
|
|
||||||
|
|
||||||
// Position returns the Position property of the i-th vertex.
|
|
||||||
func (gt *GLTriangles) Position(i int) pixel.Vec {
|
|
||||||
px := gt.data[gt.index(i, triPosX)]
|
|
||||||
py := gt.data[gt.index(i, triPosY)]
|
|
||||||
return pixel.V(float64(px), float64(py))
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetPosition sets the position property of the i-th vertex.
|
|
||||||
func (gt *GLTriangles) SetPosition(i int, p pixel.Vec) {
|
|
||||||
gt.data[gt.index(i, triPosX)] = float32(p.X)
|
|
||||||
gt.data[gt.index(i, triPosY)] = float32(p.Y)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Color returns the Color property of the i-th vertex.
|
|
||||||
func (gt *GLTriangles) Color(i int) pixel.RGBA {
|
|
||||||
r := gt.data[gt.index(i, triColorR)]
|
|
||||||
g := gt.data[gt.index(i, triColorG)]
|
|
||||||
b := gt.data[gt.index(i, triColorB)]
|
|
||||||
a := gt.data[gt.index(i, triColorA)]
|
|
||||||
return pixel.RGBA{
|
|
||||||
R: float64(r),
|
|
||||||
G: float64(g),
|
|
||||||
B: float64(b),
|
|
||||||
A: float64(a),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetColor sets the color property of the i-th vertex.
|
|
||||||
func (gt *GLTriangles) SetColor(i int, c pixel.RGBA) {
|
|
||||||
gt.data[gt.index(i, triColorR)] = float32(c.R)
|
|
||||||
gt.data[gt.index(i, triColorG)] = float32(c.G)
|
|
||||||
gt.data[gt.index(i, triColorB)] = float32(c.B)
|
|
||||||
gt.data[gt.index(i, triColorA)] = float32(c.A)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Picture returns the Picture property of the i-th vertex.
|
|
||||||
func (gt *GLTriangles) Picture(i int) (pic pixel.Vec, intensity float64) {
|
|
||||||
tx := gt.data[gt.index(i, triPicX)]
|
|
||||||
ty := gt.data[gt.index(i, triPicY)]
|
|
||||||
intensity = float64(gt.data[gt.index(i, triIntensity)])
|
|
||||||
return pixel.V(float64(tx), float64(ty)), intensity
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetPicture sets the picture property of the i-th vertex.
|
|
||||||
func (gt *GLTriangles) SetPicture(i int, pic pixel.Vec, intensity float64) {
|
|
||||||
gt.data[gt.index(i, triPicX)] = float32(pic.X)
|
|
||||||
gt.data[gt.index(i, triPicY)] = float32(pic.Y)
|
|
||||||
gt.data[gt.index(i, triIntensity)] = float32(intensity)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClipRect returns the Clipping rectangle property of the i-th vertex.
|
|
||||||
func (gt *GLTriangles) ClipRect(i int) (rect pixel.Rect, is bool) {
|
|
||||||
mx := gt.data[gt.index(i, triClipMinX)]
|
|
||||||
my := gt.data[gt.index(i, triClipMinY)]
|
|
||||||
ax := gt.data[gt.index(i, triClipMaxX)]
|
|
||||||
ay := gt.data[gt.index(i, triClipMaxY)]
|
|
||||||
rect = pixel.R(float64(mx), float64(my), float64(ax), float64(ay))
|
|
||||||
is = rect.Area() != 0.0
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetClipRect sets the Clipping rectangle property of the i-th vertex.
|
|
||||||
func (gt *GLTriangles) SetClipRect(i int, rect pixel.Rect) {
|
|
||||||
gt.data[gt.index(i, triClipMinX)] = float32(rect.Min.X)
|
|
||||||
gt.data[gt.index(i, triClipMinY)] = float32(rect.Min.Y)
|
|
||||||
gt.data[gt.index(i, triClipMaxX)] = float32(rect.Max.X)
|
|
||||||
gt.data[gt.index(i, triClipMaxY)] = float32(rect.Max.Y)
|
|
||||||
}
|
|
449
pixelgl/input.go
449
pixelgl/input.go
|
@ -1,449 +0,0 @@
|
||||||
package pixelgl
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/faiface/mainthread"
|
|
||||||
"github.com/faiface/pixel"
|
|
||||||
"github.com/go-gl/glfw/v3.3/glfw"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Pressed returns whether the Button is currently pressed down.
|
|
||||||
func (w *Window) Pressed(button Button) bool {
|
|
||||||
return w.currInp.buttons[button]
|
|
||||||
}
|
|
||||||
|
|
||||||
// JustPressed returns whether the Button has been pressed in the last frame.
|
|
||||||
func (w *Window) JustPressed(button Button) bool {
|
|
||||||
return w.pressEvents[button]
|
|
||||||
}
|
|
||||||
|
|
||||||
// JustReleased returns whether the Button has been released in the last frame.
|
|
||||||
func (w *Window) JustReleased(button Button) bool {
|
|
||||||
return w.releaseEvents[button]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Repeated returns whether a repeat event has been triggered on button.
|
|
||||||
//
|
|
||||||
// Repeat event occurs repeatedly when a button is held down for some time.
|
|
||||||
func (w *Window) Repeated(button Button) bool {
|
|
||||||
return w.currInp.repeat[button]
|
|
||||||
}
|
|
||||||
|
|
||||||
// MousePosition returns the current mouse position in the Window's Bounds.
|
|
||||||
func (w *Window) MousePosition() pixel.Vec {
|
|
||||||
return w.currInp.mouse
|
|
||||||
}
|
|
||||||
|
|
||||||
// MousePreviousPosition returns the previous mouse position in the Window's Bounds.
|
|
||||||
func (w *Window) MousePreviousPosition() pixel.Vec {
|
|
||||||
return w.prevInp.mouse
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetMousePosition positions the mouse cursor anywhere within the Window's Bounds.
|
|
||||||
func (w *Window) SetMousePosition(v pixel.Vec) {
|
|
||||||
mainthread.Call(func() {
|
|
||||||
if (v.X >= 0 && v.X <= w.bounds.W()) &&
|
|
||||||
(v.Y >= 0 && v.Y <= w.bounds.H()) {
|
|
||||||
w.window.SetCursorPos(
|
|
||||||
v.X+w.bounds.Min.X,
|
|
||||||
(w.bounds.H()-v.Y)+w.bounds.Min.Y,
|
|
||||||
)
|
|
||||||
w.prevInp.mouse = v
|
|
||||||
w.currInp.mouse = v
|
|
||||||
w.tempInp.mouse = v
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// MouseInsideWindow returns true if the mouse position is within the Window's Bounds.
|
|
||||||
func (w *Window) MouseInsideWindow() bool {
|
|
||||||
return w.cursorInsideWindow
|
|
||||||
}
|
|
||||||
|
|
||||||
// MouseScroll returns the mouse scroll amount (in both axes) since the last call to Window.Update.
|
|
||||||
func (w *Window) MouseScroll() pixel.Vec {
|
|
||||||
return w.currInp.scroll
|
|
||||||
}
|
|
||||||
|
|
||||||
// Typed returns the text typed on the keyboard since the last call to Window.Update.
|
|
||||||
func (w *Window) Typed() string {
|
|
||||||
return w.currInp.typed
|
|
||||||
}
|
|
||||||
|
|
||||||
// Button is a keyboard or mouse button. Why distinguish?
|
|
||||||
type Button int
|
|
||||||
|
|
||||||
// List of all mouse buttons.
|
|
||||||
const (
|
|
||||||
MouseButton1 = Button(glfw.MouseButton1)
|
|
||||||
MouseButton2 = Button(glfw.MouseButton2)
|
|
||||||
MouseButton3 = Button(glfw.MouseButton3)
|
|
||||||
MouseButton4 = Button(glfw.MouseButton4)
|
|
||||||
MouseButton5 = Button(glfw.MouseButton5)
|
|
||||||
MouseButton6 = Button(glfw.MouseButton6)
|
|
||||||
MouseButton7 = Button(glfw.MouseButton7)
|
|
||||||
MouseButton8 = Button(glfw.MouseButton8)
|
|
||||||
MouseButtonLast = Button(glfw.MouseButtonLast)
|
|
||||||
MouseButtonLeft = Button(glfw.MouseButtonLeft)
|
|
||||||
MouseButtonRight = Button(glfw.MouseButtonRight)
|
|
||||||
MouseButtonMiddle = Button(glfw.MouseButtonMiddle)
|
|
||||||
)
|
|
||||||
|
|
||||||
// List of all keyboard buttons.
|
|
||||||
const (
|
|
||||||
KeyUnknown = Button(glfw.KeyUnknown)
|
|
||||||
KeySpace = Button(glfw.KeySpace)
|
|
||||||
KeyApostrophe = Button(glfw.KeyApostrophe)
|
|
||||||
KeyComma = Button(glfw.KeyComma)
|
|
||||||
KeyMinus = Button(glfw.KeyMinus)
|
|
||||||
KeyPeriod = Button(glfw.KeyPeriod)
|
|
||||||
KeySlash = Button(glfw.KeySlash)
|
|
||||||
Key0 = Button(glfw.Key0)
|
|
||||||
Key1 = Button(glfw.Key1)
|
|
||||||
Key2 = Button(glfw.Key2)
|
|
||||||
Key3 = Button(glfw.Key3)
|
|
||||||
Key4 = Button(glfw.Key4)
|
|
||||||
Key5 = Button(glfw.Key5)
|
|
||||||
Key6 = Button(glfw.Key6)
|
|
||||||
Key7 = Button(glfw.Key7)
|
|
||||||
Key8 = Button(glfw.Key8)
|
|
||||||
Key9 = Button(glfw.Key9)
|
|
||||||
KeySemicolon = Button(glfw.KeySemicolon)
|
|
||||||
KeyEqual = Button(glfw.KeyEqual)
|
|
||||||
KeyA = Button(glfw.KeyA)
|
|
||||||
KeyB = Button(glfw.KeyB)
|
|
||||||
KeyC = Button(glfw.KeyC)
|
|
||||||
KeyD = Button(glfw.KeyD)
|
|
||||||
KeyE = Button(glfw.KeyE)
|
|
||||||
KeyF = Button(glfw.KeyF)
|
|
||||||
KeyG = Button(glfw.KeyG)
|
|
||||||
KeyH = Button(glfw.KeyH)
|
|
||||||
KeyI = Button(glfw.KeyI)
|
|
||||||
KeyJ = Button(glfw.KeyJ)
|
|
||||||
KeyK = Button(glfw.KeyK)
|
|
||||||
KeyL = Button(glfw.KeyL)
|
|
||||||
KeyM = Button(glfw.KeyM)
|
|
||||||
KeyN = Button(glfw.KeyN)
|
|
||||||
KeyO = Button(glfw.KeyO)
|
|
||||||
KeyP = Button(glfw.KeyP)
|
|
||||||
KeyQ = Button(glfw.KeyQ)
|
|
||||||
KeyR = Button(glfw.KeyR)
|
|
||||||
KeyS = Button(glfw.KeyS)
|
|
||||||
KeyT = Button(glfw.KeyT)
|
|
||||||
KeyU = Button(glfw.KeyU)
|
|
||||||
KeyV = Button(glfw.KeyV)
|
|
||||||
KeyW = Button(glfw.KeyW)
|
|
||||||
KeyX = Button(glfw.KeyX)
|
|
||||||
KeyY = Button(glfw.KeyY)
|
|
||||||
KeyZ = Button(glfw.KeyZ)
|
|
||||||
KeyLeftBracket = Button(glfw.KeyLeftBracket)
|
|
||||||
KeyBackslash = Button(glfw.KeyBackslash)
|
|
||||||
KeyRightBracket = Button(glfw.KeyRightBracket)
|
|
||||||
KeyGraveAccent = Button(glfw.KeyGraveAccent)
|
|
||||||
KeyWorld1 = Button(glfw.KeyWorld1)
|
|
||||||
KeyWorld2 = Button(glfw.KeyWorld2)
|
|
||||||
KeyEscape = Button(glfw.KeyEscape)
|
|
||||||
KeyEnter = Button(glfw.KeyEnter)
|
|
||||||
KeyTab = Button(glfw.KeyTab)
|
|
||||||
KeyBackspace = Button(glfw.KeyBackspace)
|
|
||||||
KeyInsert = Button(glfw.KeyInsert)
|
|
||||||
KeyDelete = Button(glfw.KeyDelete)
|
|
||||||
KeyRight = Button(glfw.KeyRight)
|
|
||||||
KeyLeft = Button(glfw.KeyLeft)
|
|
||||||
KeyDown = Button(glfw.KeyDown)
|
|
||||||
KeyUp = Button(glfw.KeyUp)
|
|
||||||
KeyPageUp = Button(glfw.KeyPageUp)
|
|
||||||
KeyPageDown = Button(glfw.KeyPageDown)
|
|
||||||
KeyHome = Button(glfw.KeyHome)
|
|
||||||
KeyEnd = Button(glfw.KeyEnd)
|
|
||||||
KeyCapsLock = Button(glfw.KeyCapsLock)
|
|
||||||
KeyScrollLock = Button(glfw.KeyScrollLock)
|
|
||||||
KeyNumLock = Button(glfw.KeyNumLock)
|
|
||||||
KeyPrintScreen = Button(glfw.KeyPrintScreen)
|
|
||||||
KeyPause = Button(glfw.KeyPause)
|
|
||||||
KeyF1 = Button(glfw.KeyF1)
|
|
||||||
KeyF2 = Button(glfw.KeyF2)
|
|
||||||
KeyF3 = Button(glfw.KeyF3)
|
|
||||||
KeyF4 = Button(glfw.KeyF4)
|
|
||||||
KeyF5 = Button(glfw.KeyF5)
|
|
||||||
KeyF6 = Button(glfw.KeyF6)
|
|
||||||
KeyF7 = Button(glfw.KeyF7)
|
|
||||||
KeyF8 = Button(glfw.KeyF8)
|
|
||||||
KeyF9 = Button(glfw.KeyF9)
|
|
||||||
KeyF10 = Button(glfw.KeyF10)
|
|
||||||
KeyF11 = Button(glfw.KeyF11)
|
|
||||||
KeyF12 = Button(glfw.KeyF12)
|
|
||||||
KeyF13 = Button(glfw.KeyF13)
|
|
||||||
KeyF14 = Button(glfw.KeyF14)
|
|
||||||
KeyF15 = Button(glfw.KeyF15)
|
|
||||||
KeyF16 = Button(glfw.KeyF16)
|
|
||||||
KeyF17 = Button(glfw.KeyF17)
|
|
||||||
KeyF18 = Button(glfw.KeyF18)
|
|
||||||
KeyF19 = Button(glfw.KeyF19)
|
|
||||||
KeyF20 = Button(glfw.KeyF20)
|
|
||||||
KeyF21 = Button(glfw.KeyF21)
|
|
||||||
KeyF22 = Button(glfw.KeyF22)
|
|
||||||
KeyF23 = Button(glfw.KeyF23)
|
|
||||||
KeyF24 = Button(glfw.KeyF24)
|
|
||||||
KeyF25 = Button(glfw.KeyF25)
|
|
||||||
KeyKP0 = Button(glfw.KeyKP0)
|
|
||||||
KeyKP1 = Button(glfw.KeyKP1)
|
|
||||||
KeyKP2 = Button(glfw.KeyKP2)
|
|
||||||
KeyKP3 = Button(glfw.KeyKP3)
|
|
||||||
KeyKP4 = Button(glfw.KeyKP4)
|
|
||||||
KeyKP5 = Button(glfw.KeyKP5)
|
|
||||||
KeyKP6 = Button(glfw.KeyKP6)
|
|
||||||
KeyKP7 = Button(glfw.KeyKP7)
|
|
||||||
KeyKP8 = Button(glfw.KeyKP8)
|
|
||||||
KeyKP9 = Button(glfw.KeyKP9)
|
|
||||||
KeyKPDecimal = Button(glfw.KeyKPDecimal)
|
|
||||||
KeyKPDivide = Button(glfw.KeyKPDivide)
|
|
||||||
KeyKPMultiply = Button(glfw.KeyKPMultiply)
|
|
||||||
KeyKPSubtract = Button(glfw.KeyKPSubtract)
|
|
||||||
KeyKPAdd = Button(glfw.KeyKPAdd)
|
|
||||||
KeyKPEnter = Button(glfw.KeyKPEnter)
|
|
||||||
KeyKPEqual = Button(glfw.KeyKPEqual)
|
|
||||||
KeyLeftShift = Button(glfw.KeyLeftShift)
|
|
||||||
KeyLeftControl = Button(glfw.KeyLeftControl)
|
|
||||||
KeyLeftAlt = Button(glfw.KeyLeftAlt)
|
|
||||||
KeyLeftSuper = Button(glfw.KeyLeftSuper)
|
|
||||||
KeyRightShift = Button(glfw.KeyRightShift)
|
|
||||||
KeyRightControl = Button(glfw.KeyRightControl)
|
|
||||||
KeyRightAlt = Button(glfw.KeyRightAlt)
|
|
||||||
KeyRightSuper = Button(glfw.KeyRightSuper)
|
|
||||||
KeyMenu = Button(glfw.KeyMenu)
|
|
||||||
KeyLast = Button(glfw.KeyLast)
|
|
||||||
)
|
|
||||||
|
|
||||||
// String returns a human-readable string describing the Button.
|
|
||||||
func (b Button) String() string {
|
|
||||||
name, ok := buttonNames[b]
|
|
||||||
if !ok {
|
|
||||||
return "Invalid"
|
|
||||||
}
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
|
|
||||||
var buttonNames = map[Button]string{
|
|
||||||
MouseButton4: "MouseButton4",
|
|
||||||
MouseButton5: "MouseButton5",
|
|
||||||
MouseButton6: "MouseButton6",
|
|
||||||
MouseButton7: "MouseButton7",
|
|
||||||
MouseButton8: "MouseButton8",
|
|
||||||
MouseButtonLeft: "MouseButtonLeft",
|
|
||||||
MouseButtonRight: "MouseButtonRight",
|
|
||||||
MouseButtonMiddle: "MouseButtonMiddle",
|
|
||||||
KeyUnknown: "Unknown",
|
|
||||||
KeySpace: "Space",
|
|
||||||
KeyApostrophe: "Apostrophe",
|
|
||||||
KeyComma: "Comma",
|
|
||||||
KeyMinus: "Minus",
|
|
||||||
KeyPeriod: "Period",
|
|
||||||
KeySlash: "Slash",
|
|
||||||
Key0: "0",
|
|
||||||
Key1: "1",
|
|
||||||
Key2: "2",
|
|
||||||
Key3: "3",
|
|
||||||
Key4: "4",
|
|
||||||
Key5: "5",
|
|
||||||
Key6: "6",
|
|
||||||
Key7: "7",
|
|
||||||
Key8: "8",
|
|
||||||
Key9: "9",
|
|
||||||
KeySemicolon: "Semicolon",
|
|
||||||
KeyEqual: "Equal",
|
|
||||||
KeyA: "A",
|
|
||||||
KeyB: "B",
|
|
||||||
KeyC: "C",
|
|
||||||
KeyD: "D",
|
|
||||||
KeyE: "E",
|
|
||||||
KeyF: "F",
|
|
||||||
KeyG: "G",
|
|
||||||
KeyH: "H",
|
|
||||||
KeyI: "I",
|
|
||||||
KeyJ: "J",
|
|
||||||
KeyK: "K",
|
|
||||||
KeyL: "L",
|
|
||||||
KeyM: "M",
|
|
||||||
KeyN: "N",
|
|
||||||
KeyO: "O",
|
|
||||||
KeyP: "P",
|
|
||||||
KeyQ: "Q",
|
|
||||||
KeyR: "R",
|
|
||||||
KeyS: "S",
|
|
||||||
KeyT: "T",
|
|
||||||
KeyU: "U",
|
|
||||||
KeyV: "V",
|
|
||||||
KeyW: "W",
|
|
||||||
KeyX: "X",
|
|
||||||
KeyY: "Y",
|
|
||||||
KeyZ: "Z",
|
|
||||||
KeyLeftBracket: "LeftBracket",
|
|
||||||
KeyBackslash: "Backslash",
|
|
||||||
KeyRightBracket: "RightBracket",
|
|
||||||
KeyGraveAccent: "GraveAccent",
|
|
||||||
KeyWorld1: "World1",
|
|
||||||
KeyWorld2: "World2",
|
|
||||||
KeyEscape: "Escape",
|
|
||||||
KeyEnter: "Enter",
|
|
||||||
KeyTab: "Tab",
|
|
||||||
KeyBackspace: "Backspace",
|
|
||||||
KeyInsert: "Insert",
|
|
||||||
KeyDelete: "Delete",
|
|
||||||
KeyRight: "Right",
|
|
||||||
KeyLeft: "Left",
|
|
||||||
KeyDown: "Down",
|
|
||||||
KeyUp: "Up",
|
|
||||||
KeyPageUp: "PageUp",
|
|
||||||
KeyPageDown: "PageDown",
|
|
||||||
KeyHome: "Home",
|
|
||||||
KeyEnd: "End",
|
|
||||||
KeyCapsLock: "CapsLock",
|
|
||||||
KeyScrollLock: "ScrollLock",
|
|
||||||
KeyNumLock: "NumLock",
|
|
||||||
KeyPrintScreen: "PrintScreen",
|
|
||||||
KeyPause: "Pause",
|
|
||||||
KeyF1: "F1",
|
|
||||||
KeyF2: "F2",
|
|
||||||
KeyF3: "F3",
|
|
||||||
KeyF4: "F4",
|
|
||||||
KeyF5: "F5",
|
|
||||||
KeyF6: "F6",
|
|
||||||
KeyF7: "F7",
|
|
||||||
KeyF8: "F8",
|
|
||||||
KeyF9: "F9",
|
|
||||||
KeyF10: "F10",
|
|
||||||
KeyF11: "F11",
|
|
||||||
KeyF12: "F12",
|
|
||||||
KeyF13: "F13",
|
|
||||||
KeyF14: "F14",
|
|
||||||
KeyF15: "F15",
|
|
||||||
KeyF16: "F16",
|
|
||||||
KeyF17: "F17",
|
|
||||||
KeyF18: "F18",
|
|
||||||
KeyF19: "F19",
|
|
||||||
KeyF20: "F20",
|
|
||||||
KeyF21: "F21",
|
|
||||||
KeyF22: "F22",
|
|
||||||
KeyF23: "F23",
|
|
||||||
KeyF24: "F24",
|
|
||||||
KeyF25: "F25",
|
|
||||||
KeyKP0: "KP0",
|
|
||||||
KeyKP1: "KP1",
|
|
||||||
KeyKP2: "KP2",
|
|
||||||
KeyKP3: "KP3",
|
|
||||||
KeyKP4: "KP4",
|
|
||||||
KeyKP5: "KP5",
|
|
||||||
KeyKP6: "KP6",
|
|
||||||
KeyKP7: "KP7",
|
|
||||||
KeyKP8: "KP8",
|
|
||||||
KeyKP9: "KP9",
|
|
||||||
KeyKPDecimal: "KPDecimal",
|
|
||||||
KeyKPDivide: "KPDivide",
|
|
||||||
KeyKPMultiply: "KPMultiply",
|
|
||||||
KeyKPSubtract: "KPSubtract",
|
|
||||||
KeyKPAdd: "KPAdd",
|
|
||||||
KeyKPEnter: "KPEnter",
|
|
||||||
KeyKPEqual: "KPEqual",
|
|
||||||
KeyLeftShift: "LeftShift",
|
|
||||||
KeyLeftControl: "LeftControl",
|
|
||||||
KeyLeftAlt: "LeftAlt",
|
|
||||||
KeyLeftSuper: "LeftSuper",
|
|
||||||
KeyRightShift: "RightShift",
|
|
||||||
KeyRightControl: "RightControl",
|
|
||||||
KeyRightAlt: "RightAlt",
|
|
||||||
KeyRightSuper: "RightSuper",
|
|
||||||
KeyMenu: "Menu",
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Window) initInput() {
|
|
||||||
mainthread.Call(func() {
|
|
||||||
w.window.SetMouseButtonCallback(func(_ *glfw.Window, button glfw.MouseButton, action glfw.Action, mod glfw.ModifierKey) {
|
|
||||||
switch action {
|
|
||||||
case glfw.Press:
|
|
||||||
w.tempPressEvents[Button(button)] = true
|
|
||||||
w.tempInp.buttons[Button(button)] = true
|
|
||||||
case glfw.Release:
|
|
||||||
w.tempReleaseEvents[Button(button)] = true
|
|
||||||
w.tempInp.buttons[Button(button)] = false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
w.window.SetKeyCallback(func(_ *glfw.Window, key glfw.Key, scancode int, action glfw.Action, mods glfw.ModifierKey) {
|
|
||||||
if key == glfw.KeyUnknown {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
switch action {
|
|
||||||
case glfw.Press:
|
|
||||||
w.tempPressEvents[Button(key)] = true
|
|
||||||
w.tempInp.buttons[Button(key)] = true
|
|
||||||
case glfw.Release:
|
|
||||||
w.tempReleaseEvents[Button(key)] = true
|
|
||||||
w.tempInp.buttons[Button(key)] = false
|
|
||||||
case glfw.Repeat:
|
|
||||||
w.tempInp.repeat[Button(key)] = true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
w.window.SetCursorEnterCallback(func(_ *glfw.Window, entered bool) {
|
|
||||||
w.cursorInsideWindow = entered
|
|
||||||
})
|
|
||||||
|
|
||||||
w.window.SetCursorPosCallback(func(_ *glfw.Window, x, y float64) {
|
|
||||||
w.tempInp.mouse = pixel.V(
|
|
||||||
x+w.bounds.Min.X,
|
|
||||||
(w.bounds.H()-y)+w.bounds.Min.Y,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
w.window.SetScrollCallback(func(_ *glfw.Window, xoff, yoff float64) {
|
|
||||||
w.tempInp.scroll.X += xoff
|
|
||||||
w.tempInp.scroll.Y += yoff
|
|
||||||
})
|
|
||||||
|
|
||||||
w.window.SetCharCallback(func(_ *glfw.Window, r rune) {
|
|
||||||
w.tempInp.typed += string(r)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateInput polls window events. Call this function to poll window events
|
|
||||||
// without swapping buffers. Note that the Update method invokes UpdateInput.
|
|
||||||
func (w *Window) UpdateInput() {
|
|
||||||
mainthread.Call(func() {
|
|
||||||
glfw.PollEvents()
|
|
||||||
})
|
|
||||||
w.doUpdateInput()
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateInputWait blocks until an event is received or a timeout. If timeout is 0
|
|
||||||
// then it will wait indefinitely
|
|
||||||
func (w *Window) UpdateInputWait(timeout time.Duration) {
|
|
||||||
mainthread.Call(func() {
|
|
||||||
if timeout <= 0 {
|
|
||||||
glfw.WaitEvents()
|
|
||||||
} else {
|
|
||||||
glfw.WaitEventsTimeout(timeout.Seconds())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
w.doUpdateInput()
|
|
||||||
}
|
|
||||||
|
|
||||||
// internal input bookkeeping
|
|
||||||
func (w *Window) doUpdateInput() {
|
|
||||||
w.prevInp = w.currInp
|
|
||||||
w.currInp = w.tempInp
|
|
||||||
|
|
||||||
w.pressEvents = w.tempPressEvents
|
|
||||||
w.releaseEvents = w.tempReleaseEvents
|
|
||||||
|
|
||||||
// Clear last frame's temporary status
|
|
||||||
w.tempPressEvents = [KeyLast + 1]bool{}
|
|
||||||
w.tempReleaseEvents = [KeyLast + 1]bool{}
|
|
||||||
w.tempInp.repeat = [KeyLast + 1]bool{}
|
|
||||||
w.tempInp.scroll = pixel.ZV
|
|
||||||
w.tempInp.typed = ""
|
|
||||||
|
|
||||||
w.updateJoystickInput()
|
|
||||||
}
|
|
|
@ -1,193 +0,0 @@
|
||||||
package pixelgl
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/go-gl/glfw/v3.3/glfw"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Joystick is a joystick or controller (gamepad).
|
|
||||||
type Joystick int
|
|
||||||
|
|
||||||
// List all of the joysticks.
|
|
||||||
const (
|
|
||||||
Joystick1 = Joystick(glfw.Joystick1)
|
|
||||||
Joystick2 = Joystick(glfw.Joystick2)
|
|
||||||
Joystick3 = Joystick(glfw.Joystick3)
|
|
||||||
Joystick4 = Joystick(glfw.Joystick4)
|
|
||||||
Joystick5 = Joystick(glfw.Joystick5)
|
|
||||||
Joystick6 = Joystick(glfw.Joystick6)
|
|
||||||
Joystick7 = Joystick(glfw.Joystick7)
|
|
||||||
Joystick8 = Joystick(glfw.Joystick8)
|
|
||||||
Joystick9 = Joystick(glfw.Joystick9)
|
|
||||||
Joystick10 = Joystick(glfw.Joystick10)
|
|
||||||
Joystick11 = Joystick(glfw.Joystick11)
|
|
||||||
Joystick12 = Joystick(glfw.Joystick12)
|
|
||||||
Joystick13 = Joystick(glfw.Joystick13)
|
|
||||||
Joystick14 = Joystick(glfw.Joystick14)
|
|
||||||
Joystick15 = Joystick(glfw.Joystick15)
|
|
||||||
Joystick16 = Joystick(glfw.Joystick16)
|
|
||||||
|
|
||||||
JoystickLast = Joystick(glfw.JoystickLast)
|
|
||||||
)
|
|
||||||
|
|
||||||
// GamepadAxis corresponds to a gamepad axis.
|
|
||||||
type GamepadAxis int
|
|
||||||
|
|
||||||
// Gamepad axis IDs.
|
|
||||||
const (
|
|
||||||
AxisLeftX = GamepadAxis(glfw.AxisLeftX)
|
|
||||||
AxisLeftY = GamepadAxis(glfw.AxisLeftY)
|
|
||||||
AxisRightX = GamepadAxis(glfw.AxisRightX)
|
|
||||||
AxisRightY = GamepadAxis(glfw.AxisRightY)
|
|
||||||
AxisLeftTrigger = GamepadAxis(glfw.AxisLeftTrigger)
|
|
||||||
AxisRightTrigger = GamepadAxis(glfw.AxisRightTrigger)
|
|
||||||
AxisLast = GamepadAxis(glfw.AxisLast)
|
|
||||||
)
|
|
||||||
|
|
||||||
// GamepadButton corresponds to a gamepad button.
|
|
||||||
type GamepadButton int
|
|
||||||
|
|
||||||
// Gamepad button IDs.
|
|
||||||
const (
|
|
||||||
ButtonA = GamepadButton(glfw.ButtonA)
|
|
||||||
ButtonB = GamepadButton(glfw.ButtonB)
|
|
||||||
ButtonX = GamepadButton(glfw.ButtonX)
|
|
||||||
ButtonY = GamepadButton(glfw.ButtonY)
|
|
||||||
ButtonLeftBumper = GamepadButton(glfw.ButtonLeftBumper)
|
|
||||||
ButtonRightBumper = GamepadButton(glfw.ButtonRightBumper)
|
|
||||||
ButtonBack = GamepadButton(glfw.ButtonBack)
|
|
||||||
ButtonStart = GamepadButton(glfw.ButtonStart)
|
|
||||||
ButtonGuide = GamepadButton(glfw.ButtonGuide)
|
|
||||||
ButtonLeftThumb = GamepadButton(glfw.ButtonLeftThumb)
|
|
||||||
ButtonRightThumb = GamepadButton(glfw.ButtonRightThumb)
|
|
||||||
ButtonDpadUp = GamepadButton(glfw.ButtonDpadUp)
|
|
||||||
ButtonDpadRight = GamepadButton(glfw.ButtonDpadRight)
|
|
||||||
ButtonDpadDown = GamepadButton(glfw.ButtonDpadDown)
|
|
||||||
ButtonDpadLeft = GamepadButton(glfw.ButtonDpadLeft)
|
|
||||||
ButtonLast = GamepadButton(glfw.ButtonLast)
|
|
||||||
ButtonCross = GamepadButton(glfw.ButtonCross)
|
|
||||||
ButtonCircle = GamepadButton(glfw.ButtonCircle)
|
|
||||||
ButtonSquare = GamepadButton(glfw.ButtonSquare)
|
|
||||||
ButtonTriangle = GamepadButton(glfw.ButtonTriangle)
|
|
||||||
)
|
|
||||||
|
|
||||||
// JoystickPresent returns if the joystick is currently connected.
|
|
||||||
//
|
|
||||||
// This API is experimental.
|
|
||||||
func (w *Window) JoystickPresent(js Joystick) bool {
|
|
||||||
return w.currJoy.connected[js]
|
|
||||||
}
|
|
||||||
|
|
||||||
// JoystickName returns the name of the joystick. A disconnected joystick will return an
|
|
||||||
// empty string.
|
|
||||||
//
|
|
||||||
// This API is experimental.
|
|
||||||
func (w *Window) JoystickName(js Joystick) string {
|
|
||||||
return w.currJoy.name[js]
|
|
||||||
}
|
|
||||||
|
|
||||||
// JoystickButtonCount returns the number of buttons a connected joystick has.
|
|
||||||
//
|
|
||||||
// This API is experimental.
|
|
||||||
func (w *Window) JoystickButtonCount(js Joystick) int {
|
|
||||||
return len(w.currJoy.buttons[js])
|
|
||||||
}
|
|
||||||
|
|
||||||
// JoystickAxisCount returns the number of axes a connected joystick has.
|
|
||||||
//
|
|
||||||
// This API is experimental.
|
|
||||||
func (w *Window) JoystickAxisCount(js Joystick) int {
|
|
||||||
return len(w.currJoy.axis[js])
|
|
||||||
}
|
|
||||||
|
|
||||||
// JoystickPressed returns whether the joystick Button is currently pressed down.
|
|
||||||
// If the button index is out of range, this will return false.
|
|
||||||
//
|
|
||||||
// This API is experimental.
|
|
||||||
func (w *Window) JoystickPressed(js Joystick, button GamepadButton) bool {
|
|
||||||
return w.currJoy.getButton(js, int(button))
|
|
||||||
}
|
|
||||||
|
|
||||||
// JoystickJustPressed returns whether the joystick Button has just been pressed down.
|
|
||||||
// If the button index is out of range, this will return false.
|
|
||||||
//
|
|
||||||
// This API is experimental.
|
|
||||||
func (w *Window) JoystickJustPressed(js Joystick, button GamepadButton) bool {
|
|
||||||
return w.currJoy.getButton(js, int(button)) && !w.prevJoy.getButton(js, int(button))
|
|
||||||
}
|
|
||||||
|
|
||||||
// JoystickJustReleased returns whether the joystick Button has just been released up.
|
|
||||||
// If the button index is out of range, this will return false.
|
|
||||||
//
|
|
||||||
// This API is experimental.
|
|
||||||
func (w *Window) JoystickJustReleased(js Joystick, button GamepadButton) bool {
|
|
||||||
return !w.currJoy.getButton(js, int(button)) && w.prevJoy.getButton(js, int(button))
|
|
||||||
}
|
|
||||||
|
|
||||||
// JoystickAxis returns the value of a joystick axis at the last call to Window.Update.
|
|
||||||
// If the axis index is out of range, this will return 0.
|
|
||||||
//
|
|
||||||
// This API is experimental.
|
|
||||||
func (w *Window) JoystickAxis(js Joystick, axis GamepadAxis) float64 {
|
|
||||||
return w.currJoy.getAxis(js, int(axis))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Used internally during Window.UpdateInput to update the state of the joysticks.
|
|
||||||
func (w *Window) updateJoystickInput() {
|
|
||||||
for js := Joystick1; js <= JoystickLast; js++ {
|
|
||||||
// Determine and store if the joystick was connected
|
|
||||||
joystickPresent := glfw.Joystick(js).Present()
|
|
||||||
w.tempJoy.connected[js] = joystickPresent
|
|
||||||
|
|
||||||
if joystickPresent {
|
|
||||||
if glfw.Joystick(js).IsGamepad() {
|
|
||||||
gamepadInputs := glfw.Joystick(js).GetGamepadState()
|
|
||||||
|
|
||||||
w.tempJoy.buttons[js] = gamepadInputs.Buttons[:]
|
|
||||||
w.tempJoy.axis[js] = gamepadInputs.Axes[:]
|
|
||||||
} else {
|
|
||||||
w.tempJoy.buttons[js] = glfw.Joystick(js).GetButtons()
|
|
||||||
w.tempJoy.axis[js] = glfw.Joystick(js).GetAxes()
|
|
||||||
}
|
|
||||||
|
|
||||||
if !w.currJoy.connected[js] {
|
|
||||||
// The joystick was recently connected, we get the name
|
|
||||||
w.tempJoy.name[js] = glfw.Joystick(js).GetName()
|
|
||||||
} else {
|
|
||||||
// Use the name from the previous one
|
|
||||||
w.tempJoy.name[js] = w.currJoy.name[js]
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
w.tempJoy.buttons[js] = []glfw.Action{}
|
|
||||||
w.tempJoy.axis[js] = []float32{}
|
|
||||||
w.tempJoy.name[js] = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
w.prevJoy = w.currJoy
|
|
||||||
w.currJoy = w.tempJoy
|
|
||||||
}
|
|
||||||
|
|
||||||
type joystickState struct {
|
|
||||||
connected [JoystickLast + 1]bool
|
|
||||||
name [JoystickLast + 1]string
|
|
||||||
buttons [JoystickLast + 1][]glfw.Action
|
|
||||||
axis [JoystickLast + 1][]float32
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns if a button on a joystick is down, returning false if the button or joystick is invalid.
|
|
||||||
func (js *joystickState) getButton(joystick Joystick, button int) bool {
|
|
||||||
// Check that the joystick and button is valid, return false by default
|
|
||||||
if js.buttons[joystick] == nil || button >= len(js.buttons[joystick]) || button < 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return js.buttons[joystick][byte(button)] == glfw.Press
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the value of a joystick axis, returning 0 if the button or joystick is invalid.
|
|
||||||
func (js *joystickState) getAxis(joystick Joystick, axis int) float64 {
|
|
||||||
// Check that the joystick and axis is valid, return 0 by default.
|
|
||||||
if js.axis[joystick] == nil || axis >= len(js.axis[joystick]) || axis < 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return float64(js.axis[joystick][axis])
|
|
||||||
}
|
|
|
@ -1,124 +0,0 @@
|
||||||
package pixelgl
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/faiface/mainthread"
|
|
||||||
"github.com/go-gl/glfw/v3.3/glfw"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Monitor represents a physical display attached to your computer.
|
|
||||||
type Monitor struct {
|
|
||||||
monitor *glfw.Monitor
|
|
||||||
}
|
|
||||||
|
|
||||||
// VideoMode represents all properties of a video mode and is
|
|
||||||
// associated with a monitor if it is used in fullscreen mode.
|
|
||||||
type VideoMode struct {
|
|
||||||
// Width is the width of the vide mode in pixels.
|
|
||||||
Width int
|
|
||||||
// Height is the height of the video mode in pixels.
|
|
||||||
Height int
|
|
||||||
// RefreshRate holds the refresh rate of the associated monitor in Hz.
|
|
||||||
RefreshRate int
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrimaryMonitor returns the main monitor (usually the one with the taskbar and stuff).
|
|
||||||
func PrimaryMonitor() *Monitor {
|
|
||||||
var monitor *glfw.Monitor
|
|
||||||
mainthread.Call(func() {
|
|
||||||
monitor = glfw.GetPrimaryMonitor()
|
|
||||||
})
|
|
||||||
return &Monitor{
|
|
||||||
monitor: monitor,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Monitors returns a slice of all currently available monitors.
|
|
||||||
func Monitors() []*Monitor {
|
|
||||||
var monitors []*Monitor
|
|
||||||
mainthread.Call(func() {
|
|
||||||
for _, monitor := range glfw.GetMonitors() {
|
|
||||||
monitors = append(monitors, &Monitor{monitor: monitor})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return monitors
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name returns a human-readable name of the Monitor.
|
|
||||||
func (m *Monitor) Name() string {
|
|
||||||
var name string
|
|
||||||
mainthread.Call(func() {
|
|
||||||
name = m.monitor.GetName()
|
|
||||||
})
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
|
|
||||||
// PhysicalSize returns the size of the display area of the Monitor in millimeters.
|
|
||||||
func (m *Monitor) PhysicalSize() (width, height float64) {
|
|
||||||
var wi, hi int
|
|
||||||
mainthread.Call(func() {
|
|
||||||
wi, hi = m.monitor.GetPhysicalSize()
|
|
||||||
})
|
|
||||||
width = float64(wi)
|
|
||||||
height = float64(hi)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Position returns the position of the upper-left corner of the Monitor in screen coordinates.
|
|
||||||
func (m *Monitor) Position() (x, y float64) {
|
|
||||||
var xi, yi int
|
|
||||||
mainthread.Call(func() {
|
|
||||||
xi, yi = m.monitor.GetPos()
|
|
||||||
})
|
|
||||||
x = float64(xi)
|
|
||||||
y = float64(yi)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Size returns the resolution of the Monitor in pixels.
|
|
||||||
func (m *Monitor) Size() (width, height float64) {
|
|
||||||
var mode *glfw.VidMode
|
|
||||||
mainthread.Call(func() {
|
|
||||||
mode = m.monitor.GetVideoMode()
|
|
||||||
})
|
|
||||||
width = float64(mode.Width)
|
|
||||||
height = float64(mode.Height)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// BitDepth returns the number of bits per color of the Monitor.
|
|
||||||
func (m *Monitor) BitDepth() (red, green, blue int) {
|
|
||||||
var mode *glfw.VidMode
|
|
||||||
mainthread.Call(func() {
|
|
||||||
mode = m.monitor.GetVideoMode()
|
|
||||||
})
|
|
||||||
red = mode.RedBits
|
|
||||||
green = mode.GreenBits
|
|
||||||
blue = mode.BlueBits
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// RefreshRate returns the refresh frequency of the Monitor in Hz (refreshes/second).
|
|
||||||
func (m *Monitor) RefreshRate() (rate float64) {
|
|
||||||
var mode *glfw.VidMode
|
|
||||||
mainthread.Call(func() {
|
|
||||||
mode = m.monitor.GetVideoMode()
|
|
||||||
})
|
|
||||||
rate = float64(mode.RefreshRate)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// VideoModes returns all available video modes for the monitor.
|
|
||||||
func (m *Monitor) VideoModes() (vmodes []VideoMode) {
|
|
||||||
var modes []*glfw.VidMode
|
|
||||||
mainthread.Call(func() {
|
|
||||||
modes = m.monitor.GetVideoModes()
|
|
||||||
})
|
|
||||||
for _, mode := range modes {
|
|
||||||
vmodes = append(vmodes, VideoMode{
|
|
||||||
Width: mode.Width,
|
|
||||||
Height: mode.Height,
|
|
||||||
RefreshRate: mode.RefreshRate,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
package pixelgl
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/faiface/mainthread"
|
|
||||||
"github.com/go-gl/glfw/v3.3/glfw"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Run is essentially the main function of PixelGL. It exists mainly due to the technical
|
|
||||||
// limitations of OpenGL and operating systems. In short, all graphics and window manipulating calls
|
|
||||||
// must be done from the main thread. Run makes this possible.
|
|
||||||
//
|
|
||||||
// Call this function from the main function of your application. This is necessary, so that Run
|
|
||||||
// runs on the main thread.
|
|
||||||
//
|
|
||||||
// func run() {
|
|
||||||
// // interact with Pixel and PixelGL from here (even concurrently)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func main() {
|
|
||||||
// pixel.Run(run)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// You can spawn any number of goroutines from your run function and interact with PixelGL
|
|
||||||
// concurrently. The only condition is that the Run function is called from your main function.
|
|
||||||
func Run(run func()) {
|
|
||||||
err := glfw.Init()
|
|
||||||
if err != nil {
|
|
||||||
panic(errors.Wrap(err, "failed to initialize GLFW"))
|
|
||||||
}
|
|
||||||
defer glfw.Terminate()
|
|
||||||
mainthread.Run(run)
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
package pixelgl
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math"
|
|
||||||
|
|
||||||
"github.com/faiface/pixel"
|
|
||||||
)
|
|
||||||
|
|
||||||
func intBounds(bounds pixel.Rect) (x, y, w, h int) {
|
|
||||||
x0 := int(math.Floor(bounds.Min.X))
|
|
||||||
y0 := int(math.Floor(bounds.Min.Y))
|
|
||||||
x1 := int(math.Ceil(bounds.Max.X))
|
|
||||||
y1 := int(math.Ceil(bounds.Max.Y))
|
|
||||||
return x0, y0, x1 - x0, y1 - y0
|
|
||||||
}
|
|
|
@ -1,544 +0,0 @@
|
||||||
package pixelgl
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"image"
|
|
||||||
"image/color"
|
|
||||||
"runtime"
|
|
||||||
|
|
||||||
"github.com/faiface/glhf"
|
|
||||||
"github.com/faiface/mainthread"
|
|
||||||
"github.com/faiface/pixel"
|
|
||||||
"github.com/go-gl/gl/v3.3-core/gl"
|
|
||||||
"github.com/go-gl/glfw/v3.3/glfw"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
// WindowConfig is a structure for specifying all possible properties of a Window. Properties are
|
|
||||||
// chosen in such a way, that you usually only need to set a few of them - defaults (zeros) should
|
|
||||||
// usually be sensible.
|
|
||||||
//
|
|
||||||
// Note that you always need to set the Bounds of a Window.
|
|
||||||
type WindowConfig struct {
|
|
||||||
// Title at the top of the Window.
|
|
||||||
Title string
|
|
||||||
|
|
||||||
// Icon specifies the icon images available to be used by the window. This is usually
|
|
||||||
// displayed in the top bar of the window or in the task bar of the desktop environment.
|
|
||||||
//
|
|
||||||
// If passed one image, it will use that image, if passed an array of images those of or
|
|
||||||
// closest to the sizes desired by the system are selected. The desired image sizes varies
|
|
||||||
// depending on platform and system settings. The selected images will be rescaled as
|
|
||||||
// needed. Good sizes include 16x16, 32x32 and 48x48.
|
|
||||||
//
|
|
||||||
// Note: Setting this value doesn't have an effect on OSX. You'll need to set the icon when
|
|
||||||
// bundling your application for release.
|
|
||||||
Icon []pixel.Picture
|
|
||||||
|
|
||||||
// Bounds specify the bounds of the Window in pixels.
|
|
||||||
Bounds pixel.Rect
|
|
||||||
|
|
||||||
// Initial window position
|
|
||||||
Position pixel.Vec
|
|
||||||
|
|
||||||
// If set to nil, the Window will be windowed. Otherwise it will be fullscreen on the
|
|
||||||
// specified Monitor.
|
|
||||||
Monitor *Monitor
|
|
||||||
|
|
||||||
// Resizable specifies whether the window will be resizable by the user.
|
|
||||||
Resizable bool
|
|
||||||
|
|
||||||
// Undecorated Window omits the borders and decorations (close button, etc.).
|
|
||||||
Undecorated bool
|
|
||||||
|
|
||||||
// NoIconify specifies whether fullscreen windows should not automatically
|
|
||||||
// iconify (and restore the previous video mode) on focus loss.
|
|
||||||
NoIconify bool
|
|
||||||
|
|
||||||
// AlwaysOnTop specifies whether the windowed mode window will be floating
|
|
||||||
// above other regular windows, also called topmost or always-on-top.
|
|
||||||
// This is intended primarily for debugging purposes and cannot be used to
|
|
||||||
// implement proper full screen windows.
|
|
||||||
AlwaysOnTop bool
|
|
||||||
|
|
||||||
// TransparentFramebuffer specifies whether the window framebuffer will be
|
|
||||||
// transparent. If enabled and supported by the system, the window
|
|
||||||
// framebuffer alpha channel will be used to combine the framebuffer with
|
|
||||||
// the background. This does not affect window decorations.
|
|
||||||
TransparentFramebuffer bool
|
|
||||||
|
|
||||||
// VSync (vertical synchronization) synchronizes Window's framerate with the framerate of
|
|
||||||
// the monitor.
|
|
||||||
VSync bool
|
|
||||||
|
|
||||||
// Maximized specifies whether the window is maximized.
|
|
||||||
Maximized bool
|
|
||||||
|
|
||||||
// Invisible specifies whether the window will be initially hidden.
|
|
||||||
// You can make the window visible later using Window.Show().
|
|
||||||
Invisible bool
|
|
||||||
|
|
||||||
//SamplesMSAA specifies the level of MSAA to be used. Must be one of 0, 2, 4, 8, 16. 0 to disable.
|
|
||||||
SamplesMSAA int
|
|
||||||
}
|
|
||||||
|
|
||||||
// Window is a window handler. Use this type to manipulate a window (input, drawing, etc.).
|
|
||||||
type Window struct {
|
|
||||||
window *glfw.Window
|
|
||||||
|
|
||||||
bounds pixel.Rect
|
|
||||||
canvas *Canvas
|
|
||||||
vsync bool
|
|
||||||
cursorVisible bool
|
|
||||||
cursorInsideWindow bool
|
|
||||||
|
|
||||||
// need to save these to correctly restore a fullscreen window
|
|
||||||
restore struct {
|
|
||||||
xpos, ypos, width, height int
|
|
||||||
}
|
|
||||||
|
|
||||||
prevInp, currInp, tempInp struct {
|
|
||||||
mouse pixel.Vec
|
|
||||||
buttons [KeyLast + 1]bool
|
|
||||||
repeat [KeyLast + 1]bool
|
|
||||||
scroll pixel.Vec
|
|
||||||
typed string
|
|
||||||
}
|
|
||||||
|
|
||||||
pressEvents, tempPressEvents [KeyLast + 1]bool
|
|
||||||
releaseEvents, tempReleaseEvents [KeyLast + 1]bool
|
|
||||||
|
|
||||||
prevJoy, currJoy, tempJoy joystickState
|
|
||||||
}
|
|
||||||
|
|
||||||
var currWin *Window
|
|
||||||
|
|
||||||
// NewWindow creates a new Window with it's properties specified in the provided config.
|
|
||||||
//
|
|
||||||
// If Window creation fails, an error is returned (e.g. due to unavailable graphics device).
|
|
||||||
func NewWindow(cfg WindowConfig) (*Window, error) {
|
|
||||||
bool2int := map[bool]int{
|
|
||||||
true: glfw.True,
|
|
||||||
false: glfw.False,
|
|
||||||
}
|
|
||||||
|
|
||||||
w := &Window{bounds: cfg.Bounds, cursorVisible: true}
|
|
||||||
|
|
||||||
flag := false
|
|
||||||
for _, v := range []int{0, 2, 4, 8, 16} {
|
|
||||||
if cfg.SamplesMSAA == v {
|
|
||||||
flag = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !flag {
|
|
||||||
return nil, fmt.Errorf("invalid value '%v' for msaaSamples", cfg.SamplesMSAA)
|
|
||||||
}
|
|
||||||
|
|
||||||
err := mainthread.CallErr(func() error {
|
|
||||||
var err error
|
|
||||||
|
|
||||||
glfw.WindowHint(glfw.ContextVersionMajor, 3)
|
|
||||||
glfw.WindowHint(glfw.ContextVersionMinor, 3)
|
|
||||||
glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile)
|
|
||||||
glfw.WindowHint(glfw.OpenGLForwardCompatible, glfw.True)
|
|
||||||
|
|
||||||
glfw.WindowHint(glfw.Resizable, bool2int[cfg.Resizable])
|
|
||||||
glfw.WindowHint(glfw.Decorated, bool2int[!cfg.Undecorated])
|
|
||||||
glfw.WindowHint(glfw.Floating, bool2int[cfg.AlwaysOnTop])
|
|
||||||
glfw.WindowHint(glfw.AutoIconify, bool2int[!cfg.NoIconify])
|
|
||||||
glfw.WindowHint(glfw.TransparentFramebuffer, bool2int[cfg.TransparentFramebuffer])
|
|
||||||
glfw.WindowHint(glfw.Maximized, bool2int[cfg.Maximized])
|
|
||||||
glfw.WindowHint(glfw.Visible, bool2int[!cfg.Invisible])
|
|
||||||
glfw.WindowHint(glfw.Samples, cfg.SamplesMSAA)
|
|
||||||
|
|
||||||
if cfg.Position.X != 0 || cfg.Position.Y != 0 {
|
|
||||||
glfw.WindowHint(glfw.Visible, glfw.False)
|
|
||||||
}
|
|
||||||
|
|
||||||
var share *glfw.Window
|
|
||||||
if currWin != nil {
|
|
||||||
share = currWin.window
|
|
||||||
}
|
|
||||||
_, _, width, height := intBounds(cfg.Bounds)
|
|
||||||
w.window, err = glfw.CreateWindow(
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
cfg.Title,
|
|
||||||
nil,
|
|
||||||
share,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.Position.X != 0 || cfg.Position.Y != 0 {
|
|
||||||
w.window.SetPos(int(cfg.Position.X), int(cfg.Position.Y))
|
|
||||||
w.window.Show()
|
|
||||||
}
|
|
||||||
|
|
||||||
// enter the OpenGL context
|
|
||||||
w.begin()
|
|
||||||
glhf.Init()
|
|
||||||
gl.Enable(gl.MULTISAMPLE)
|
|
||||||
w.end()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "creating window failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(cfg.Icon) > 0 {
|
|
||||||
imgs := make([]image.Image, len(cfg.Icon))
|
|
||||||
for i, icon := range cfg.Icon {
|
|
||||||
pic := pixel.PictureDataFromPicture(icon)
|
|
||||||
imgs[i] = pic.Image()
|
|
||||||
}
|
|
||||||
mainthread.Call(func() {
|
|
||||||
w.window.SetIcon(imgs)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
w.SetVSync(cfg.VSync)
|
|
||||||
|
|
||||||
w.initInput()
|
|
||||||
w.SetMonitor(cfg.Monitor)
|
|
||||||
|
|
||||||
w.canvas = NewCanvas(cfg.Bounds)
|
|
||||||
w.Update()
|
|
||||||
|
|
||||||
runtime.SetFinalizer(w, (*Window).Destroy)
|
|
||||||
|
|
||||||
return w, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Destroy destroys the Window. The Window can't be used any further.
|
|
||||||
func (w *Window) Destroy() {
|
|
||||||
mainthread.Call(func() {
|
|
||||||
w.window.Destroy()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update swaps buffers and polls events. Call this method at the end of each frame.
|
|
||||||
func (w *Window) Update() {
|
|
||||||
w.SwapBuffers()
|
|
||||||
w.UpdateInput()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClipboardText returns the current value of the systems clipboard.
|
|
||||||
func (w *Window) ClipboardText() string {
|
|
||||||
return w.window.GetClipboardString()
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetClipboardText passes the given string to the underlying glfw window to set the
|
|
||||||
// systems clipboard.
|
|
||||||
func (w *Window) SetClipboardText(text string) {
|
|
||||||
w.window.SetClipboardString(text)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SwapBuffers swaps buffers. Call this to swap buffers without polling window events.
|
|
||||||
// Note that Update invokes SwapBuffers.
|
|
||||||
func (w *Window) SwapBuffers() {
|
|
||||||
mainthread.Call(func() {
|
|
||||||
_, _, oldW, oldH := intBounds(w.bounds)
|
|
||||||
newW, newH := w.window.GetSize()
|
|
||||||
w.bounds = w.bounds.ResizedMin(w.bounds.Size().Add(pixel.V(
|
|
||||||
float64(newW-oldW),
|
|
||||||
float64(newH-oldH),
|
|
||||||
)))
|
|
||||||
})
|
|
||||||
|
|
||||||
w.canvas.SetBounds(w.bounds)
|
|
||||||
|
|
||||||
mainthread.Call(func() {
|
|
||||||
w.begin()
|
|
||||||
|
|
||||||
framebufferWidth, framebufferHeight := w.window.GetFramebufferSize()
|
|
||||||
glhf.Bounds(0, 0, framebufferWidth, framebufferHeight)
|
|
||||||
|
|
||||||
glhf.Clear(0, 0, 0, 0)
|
|
||||||
w.canvas.gf.Frame().Begin()
|
|
||||||
w.canvas.gf.Frame().Blit(
|
|
||||||
nil,
|
|
||||||
0, 0, w.canvas.Texture().Width(), w.canvas.Texture().Height(),
|
|
||||||
0, 0, framebufferWidth, framebufferHeight,
|
|
||||||
)
|
|
||||||
w.canvas.gf.Frame().End()
|
|
||||||
|
|
||||||
if w.vsync {
|
|
||||||
glfw.SwapInterval(1)
|
|
||||||
} else {
|
|
||||||
glfw.SwapInterval(0)
|
|
||||||
}
|
|
||||||
w.window.SwapBuffers()
|
|
||||||
w.end()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetClosed sets the closed flag of the Window.
|
|
||||||
//
|
|
||||||
// This is useful when overriding the user's attempt to close the Window, or just to close the
|
|
||||||
// Window from within the program.
|
|
||||||
func (w *Window) SetClosed(closed bool) {
|
|
||||||
mainthread.Call(func() {
|
|
||||||
w.window.SetShouldClose(closed)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Closed returns the closed flag of the Window, which reports whether the Window should be closed.
|
|
||||||
//
|
|
||||||
// The closed flag is automatically set when a user attempts to close the Window.
|
|
||||||
func (w *Window) Closed() bool {
|
|
||||||
var closed bool
|
|
||||||
mainthread.Call(func() {
|
|
||||||
closed = w.window.ShouldClose()
|
|
||||||
})
|
|
||||||
return closed
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetTitle changes the title of the Window.
|
|
||||||
func (w *Window) SetTitle(title string) {
|
|
||||||
mainthread.Call(func() {
|
|
||||||
w.window.SetTitle(title)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetBounds sets the bounds of the Window in pixels. Bounds can be fractional, but the actual size
|
|
||||||
// of the window will be rounded to integers.
|
|
||||||
func (w *Window) SetBounds(bounds pixel.Rect) {
|
|
||||||
w.bounds = bounds
|
|
||||||
mainthread.Call(func() {
|
|
||||||
_, _, width, height := intBounds(bounds)
|
|
||||||
w.window.SetSize(width, height)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetPos sets the position, in screen coordinates, of the upper-left corner
|
|
||||||
// of the client area of the window. Position can be fractional, but the actual position
|
|
||||||
// of the window will be rounded to integers.
|
|
||||||
//
|
|
||||||
// If it is a full screen window, this function does nothing.
|
|
||||||
func (w *Window) SetPos(pos pixel.Vec) {
|
|
||||||
mainthread.Call(func() {
|
|
||||||
left, top := int(pos.X), int(pos.Y)
|
|
||||||
w.window.SetPos(left, top)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPos gets the position, in screen coordinates, of the upper-left corner
|
|
||||||
// of the client area of the window. The position is rounded to integers.
|
|
||||||
func (w *Window) GetPos() pixel.Vec {
|
|
||||||
var v pixel.Vec
|
|
||||||
mainthread.Call(func() {
|
|
||||||
x, y := w.window.GetPos()
|
|
||||||
v = pixel.V(float64(x), float64(y))
|
|
||||||
})
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bounds returns the current bounds of the Window.
|
|
||||||
func (w *Window) Bounds() pixel.Rect {
|
|
||||||
return w.bounds
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Window) setFullscreen(monitor *Monitor) {
|
|
||||||
mainthread.Call(func() {
|
|
||||||
w.restore.xpos, w.restore.ypos = w.window.GetPos()
|
|
||||||
w.restore.width, w.restore.height = w.window.GetSize()
|
|
||||||
|
|
||||||
mode := monitor.monitor.GetVideoMode()
|
|
||||||
|
|
||||||
w.window.SetMonitor(
|
|
||||||
monitor.monitor,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
mode.Width,
|
|
||||||
mode.Height,
|
|
||||||
mode.RefreshRate,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Window) setWindowed() {
|
|
||||||
mainthread.Call(func() {
|
|
||||||
w.window.SetMonitor(
|
|
||||||
nil,
|
|
||||||
w.restore.xpos,
|
|
||||||
w.restore.ypos,
|
|
||||||
w.restore.width,
|
|
||||||
w.restore.height,
|
|
||||||
0,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetMonitor sets the Window fullscreen on the given Monitor. If the Monitor is nil, the Window
|
|
||||||
// will be restored to windowed state instead.
|
|
||||||
//
|
|
||||||
// The Window will be automatically set to the Monitor's resolution. If you want a different
|
|
||||||
// resolution, you will need to set it manually with SetBounds method.
|
|
||||||
func (w *Window) SetMonitor(monitor *Monitor) {
|
|
||||||
if w.Monitor() != monitor {
|
|
||||||
if monitor != nil {
|
|
||||||
w.setFullscreen(monitor)
|
|
||||||
} else {
|
|
||||||
w.setWindowed()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Monitor returns a monitor the Window is fullscreen on. If the Window is not fullscreen, this
|
|
||||||
// function returns nil.
|
|
||||||
func (w *Window) Monitor() *Monitor {
|
|
||||||
var monitor *glfw.Monitor
|
|
||||||
mainthread.Call(func() {
|
|
||||||
monitor = w.window.GetMonitor()
|
|
||||||
})
|
|
||||||
if monitor == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return &Monitor{
|
|
||||||
monitor: monitor,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Focused returns true if the Window has input focus.
|
|
||||||
func (w *Window) Focused() bool {
|
|
||||||
var focused bool
|
|
||||||
mainthread.Call(func() {
|
|
||||||
focused = w.window.GetAttrib(glfw.Focused) == glfw.True
|
|
||||||
})
|
|
||||||
return focused
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetVSync sets whether the Window's Update should synchronize with the monitor refresh rate.
|
|
||||||
func (w *Window) SetVSync(vsync bool) {
|
|
||||||
w.vsync = vsync
|
|
||||||
}
|
|
||||||
|
|
||||||
// VSync returns whether the Window is set to synchronize with the monitor refresh rate.
|
|
||||||
func (w *Window) VSync() bool {
|
|
||||||
return w.vsync
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetCursorVisible sets the visibility of the mouse cursor inside the Window client area.
|
|
||||||
func (w *Window) SetCursorVisible(visible bool) {
|
|
||||||
w.cursorVisible = visible
|
|
||||||
mainthread.Call(func() {
|
|
||||||
if visible {
|
|
||||||
w.window.SetInputMode(glfw.CursorMode, glfw.CursorNormal)
|
|
||||||
} else {
|
|
||||||
w.window.SetInputMode(glfw.CursorMode, glfw.CursorHidden)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetCursorDisabled hides the cursor and provides unlimited virtual cursor movement
|
|
||||||
// make cursor visible using SetCursorVisible
|
|
||||||
func (w *Window) SetCursorDisabled() {
|
|
||||||
w.cursorVisible = false
|
|
||||||
mainthread.Call(func() {
|
|
||||||
w.window.SetInputMode(glfw.CursorMode, glfw.CursorDisabled)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// CursorVisible returns the visibility status of the mouse cursor.
|
|
||||||
func (w *Window) CursorVisible() bool {
|
|
||||||
return w.cursorVisible
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: must be called inside the main thread.
|
|
||||||
func (w *Window) begin() {
|
|
||||||
if currWin != w {
|
|
||||||
w.window.MakeContextCurrent()
|
|
||||||
currWin = w
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: must be called inside the main thread.
|
|
||||||
func (w *Window) end() {
|
|
||||||
// nothing, really
|
|
||||||
}
|
|
||||||
|
|
||||||
// MakeTriangles generates a specialized copy of the supplied Triangles that will draw onto this
|
|
||||||
// Window.
|
|
||||||
//
|
|
||||||
// Window supports TrianglesPosition, TrianglesColor and TrianglesPicture.
|
|
||||||
func (w *Window) MakeTriangles(t pixel.Triangles) pixel.TargetTriangles {
|
|
||||||
return w.canvas.MakeTriangles(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MakePicture generates a specialized copy of the supplied Picture that will draw onto this Window.
|
|
||||||
//
|
|
||||||
// Window supports PictureColor.
|
|
||||||
func (w *Window) MakePicture(p pixel.Picture) pixel.TargetPicture {
|
|
||||||
return w.canvas.MakePicture(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetMatrix sets a Matrix that every point will be projected by.
|
|
||||||
func (w *Window) SetMatrix(m pixel.Matrix) {
|
|
||||||
w.canvas.SetMatrix(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetColorMask sets a global color mask for the Window.
|
|
||||||
func (w *Window) SetColorMask(c color.Color) {
|
|
||||||
w.canvas.SetColorMask(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetComposeMethod sets a Porter-Duff composition method to be used in the following draws onto
|
|
||||||
// this Window.
|
|
||||||
func (w *Window) SetComposeMethod(cmp pixel.ComposeMethod) {
|
|
||||||
w.canvas.SetComposeMethod(cmp)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetSmooth sets whether the stretched Pictures drawn onto this Window should be drawn smooth or
|
|
||||||
// pixely.
|
|
||||||
func (w *Window) SetSmooth(smooth bool) {
|
|
||||||
w.canvas.SetSmooth(smooth)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Smooth returns whether the stretched Pictures drawn onto this Window are set to be drawn smooth
|
|
||||||
// or pixely.
|
|
||||||
func (w *Window) Smooth() bool {
|
|
||||||
return w.canvas.Smooth()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear clears the Window with a single color.
|
|
||||||
func (w *Window) Clear(c color.Color) {
|
|
||||||
w.canvas.Clear(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Color returns the color of the pixel over the given position inside the Window.
|
|
||||||
func (w *Window) Color(at pixel.Vec) pixel.RGBA {
|
|
||||||
return w.canvas.Color(at)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Canvas returns the window's underlying Canvas
|
|
||||||
func (w *Window) Canvas() *Canvas {
|
|
||||||
return w.canvas
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show makes the window visible, if it was previously hidden. If the window is
|
|
||||||
// already visible or is in full screen mode, this function does nothing.
|
|
||||||
func (w *Window) Show() {
|
|
||||||
mainthread.Call(func() {
|
|
||||||
w.window.Show()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clipboard returns the contents of the system clipboard.
|
|
||||||
func (w *Window) Clipboard() string {
|
|
||||||
var clipboard string
|
|
||||||
mainthread.Call(func() {
|
|
||||||
clipboard = w.window.GetClipboardString()
|
|
||||||
})
|
|
||||||
return clipboard
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetClipboardString sets the system clipboard to the specified UTF-8 encoded string.
|
|
||||||
func (w *Window) SetClipboard(str string) {
|
|
||||||
mainthread.Call(func() {
|
|
||||||
w.window.SetClipboardString(str)
|
|
||||||
})
|
|
||||||
}
|
|
Loading…
Reference in New Issue