diff --git a/pixelgl/canvas.go b/pixelgl/canvas.go deleted file mode 100644 index c0e8b3d..0000000 --- a/pixelgl/canvas.go +++ /dev/null @@ -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()) -} diff --git a/pixelgl/doc.go b/pixelgl/doc.go deleted file mode 100644 index c8acbbc..0000000 --- a/pixelgl/doc.go +++ /dev/null @@ -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 diff --git a/pixelgl/glframe.go b/pixelgl/glframe.go deleted file mode 100644 index a6ef881..0000000 --- a/pixelgl/glframe.go +++ /dev/null @@ -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 -} diff --git a/pixelgl/glpicture.go b/pixelgl/glpicture.go deleted file mode 100644 index 659f014..0000000 --- a/pixelgl/glpicture.go +++ /dev/null @@ -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, - } -} diff --git a/pixelgl/glshader.go b/pixelgl/glshader.go deleted file mode 100644 index 6352519..0000000 --- a/pixelgl/glshader.go +++ /dev/null @@ -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; - } -} -` diff --git a/pixelgl/gltriangles.go b/pixelgl/gltriangles.go deleted file mode 100644 index de6fe80..0000000 --- a/pixelgl/gltriangles.go +++ /dev/null @@ -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) -} diff --git a/pixelgl/input.go b/pixelgl/input.go deleted file mode 100644 index 75060ae..0000000 --- a/pixelgl/input.go +++ /dev/null @@ -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() -} diff --git a/pixelgl/joystick.go b/pixelgl/joystick.go deleted file mode 100644 index 25fd374..0000000 --- a/pixelgl/joystick.go +++ /dev/null @@ -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]) -} diff --git a/pixelgl/monitor.go b/pixelgl/monitor.go deleted file mode 100644 index 1d6077a..0000000 --- a/pixelgl/monitor.go +++ /dev/null @@ -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 -} diff --git a/pixelgl/run.go b/pixelgl/run.go deleted file mode 100644 index c2822fb..0000000 --- a/pixelgl/run.go +++ /dev/null @@ -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) -} diff --git a/pixelgl/util.go b/pixelgl/util.go deleted file mode 100644 index c11ea3c..0000000 --- a/pixelgl/util.go +++ /dev/null @@ -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 -} diff --git a/pixelgl/window.go b/pixelgl/window.go deleted file mode 100644 index 8ceb77f..0000000 --- a/pixelgl/window.go +++ /dev/null @@ -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) - }) -}