Use dep to vendor things
This commit is contained in:
21
vendor/github.com/faiface/glhf/LICENSE
generated
vendored
Normal file
21
vendor/github.com/faiface/glhf/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Michal Štrba
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
191
vendor/github.com/faiface/glhf/README.md
generated
vendored
Normal file
191
vendor/github.com/faiface/glhf/README.md
generated
vendored
Normal file
@@ -0,0 +1,191 @@
|
||||
# glhf [](http://godoc.org/github.com/faiface/glhf) [](https://goreportcard.com/report/github.com/faiface/glhf)
|
||||
|
||||
open**GL** **H**ave **F**un - A Go package that makes life with OpenGL enjoyable.
|
||||
|
||||
```
|
||||
go get github.com/faiface/glhf
|
||||
```
|
||||
|
||||
## Main features
|
||||
|
||||
- Garbage collected OpenGL objects
|
||||
- Dynamically sized vertex slices (vertex arrays are boring)
|
||||
- Textures, Shaders, Frames (reasonably managed framebuffers)
|
||||
- Always possible to use standard OpenGL with `glhf`
|
||||
|
||||
## Motivation
|
||||
|
||||
OpenGL is verbose, it's usage patterns are repetitive and it's manual memory management doesn't fit
|
||||
Go's design. When making a game development library, it's usually desirable to create some
|
||||
higher-level abstractions around OpenGL. This library is a take on that.
|
||||
|
||||
## Contribute!
|
||||
|
||||
The library is young and many features are still missing. If you find a bug, have a proposal or a
|
||||
feature request, _do an issue_!. If you know how to implement something that's missing, _do a pull
|
||||
request_.
|
||||
|
||||
## Code
|
||||
|
||||
The following are parts of the demo program, which can be found in the [examples](https://github.com/faiface/glhf/tree/master/examples/demo).
|
||||
|
||||
```go
|
||||
// ... GLFW window creation and stuff ...
|
||||
|
||||
// vertex shader source
|
||||
var vertexShader = `
|
||||
#version 330 core
|
||||
|
||||
in vec2 position;
|
||||
in vec2 texture;
|
||||
|
||||
out vec2 Texture;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(position, 0.0, 1.0);
|
||||
Texture = texture;
|
||||
}
|
||||
`
|
||||
|
||||
// fragment shader source
|
||||
var fragmentShader = `
|
||||
#version 330 core
|
||||
|
||||
in vec2 Texture;
|
||||
|
||||
out vec4 color;
|
||||
|
||||
uniform sampler2D tex;
|
||||
|
||||
void main() {
|
||||
color = texture(tex, Texture);
|
||||
}
|
||||
`
|
||||
|
||||
var (
|
||||
// Here we define a vertex format of our vertex slice. It's actually a basic slice
|
||||
// literal.
|
||||
//
|
||||
// The vertex format consists of names and types of the attributes. The name is the
|
||||
// name that the attribute is referenced by inside a shader.
|
||||
vertexFormat = glhf.AttrFormat{
|
||||
{Name: "position", Type: glhf.Vec2},
|
||||
{Name: "texture", Type: glhf.Vec2},
|
||||
}
|
||||
|
||||
// Here we declare some variables for later use.
|
||||
shader *glhf.Shader
|
||||
texture *glhf.Texture
|
||||
slice *glhf.VertexSlice
|
||||
)
|
||||
|
||||
// Here we load an image from a file. The loadImage function is not within the library, it
|
||||
// just loads and returns a image.NRGBA.
|
||||
gopherImage, err := loadImage("celebrate.png")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Every OpenGL call needs to be done inside the main thread.
|
||||
mainthread.Call(func() {
|
||||
var err error
|
||||
|
||||
// Here we create a shader. The second argument is the format of the uniform
|
||||
// attributes. Since our shader has no uniform attributes, the format is empty.
|
||||
shader, err = glhf.NewShader(vertexFormat, glhf.AttrFormat{}, vertexShader, fragmentShader)
|
||||
|
||||
// If the shader compilation did not go successfully, an error with a full
|
||||
// description is returned.
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// We create a texture from the loaded image.
|
||||
texture = glhf.NewTexture(
|
||||
gopherImage.Bounds().Dx(),
|
||||
gopherImage.Bounds().Dy(),
|
||||
true,
|
||||
gopherImage.Pix,
|
||||
)
|
||||
|
||||
// And finally, we make a vertex slice, which is basically a dynamically sized
|
||||
// vertex array. The length of the slice is 6 and the capacity is the same.
|
||||
//
|
||||
// The slice inherits the vertex format of the supplied shader. Also, it should
|
||||
// only be used with that shader.
|
||||
slice = glhf.MakeVertexSlice(shader, 6, 6)
|
||||
|
||||
// Before we use a slice, we need to Begin it. The same holds for all objects in
|
||||
// GLHF.
|
||||
slice.Begin()
|
||||
|
||||
// We assign data to the vertex slice. The values are in the order as in the vertex
|
||||
// format of the slice (shader). Each two floats correspond to an attribute of type
|
||||
// glhf.Vec2.
|
||||
slice.SetVertexData([]float32{
|
||||
-1, -1, 0, 1,
|
||||
+1, -1, 1, 1,
|
||||
+1, +1, 1, 0,
|
||||
|
||||
-1, -1, 0, 1,
|
||||
+1, +1, 1, 0,
|
||||
-1, +1, 0, 0,
|
||||
})
|
||||
|
||||
// When we're done with the slice, we End it.
|
||||
slice.End()
|
||||
})
|
||||
|
||||
shouldQuit := false
|
||||
for !shouldQuit {
|
||||
mainthread.Call(func() {
|
||||
// ... GLFW stuff ...
|
||||
|
||||
// Clear the window.
|
||||
glhf.Clear(1, 1, 1, 1)
|
||||
|
||||
// Here we Begin/End all necessary objects and finally draw the vertex
|
||||
// slice.
|
||||
shader.Begin()
|
||||
texture.Begin()
|
||||
slice.Begin()
|
||||
slice.Draw()
|
||||
slice.End()
|
||||
texture.End()
|
||||
shader.End()
|
||||
|
||||
// ... GLFW stuff ...
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
## FAQ
|
||||
|
||||
### Which version of OpenGL does GLHF use?
|
||||
|
||||
It uses OpenGL 3.3 and uses
|
||||
[`github.com/go-gl/gl/v3.3-core/gl`](https://github.com/go-gl/gl/tree/master/v3.3-core/gl).
|
||||
|
||||
### Why do I have to use `github.com/faiface/mainthread` package with GLHF?
|
||||
|
||||
First of all, OpenGL has to be done from one thread and many operating systems require, that the one
|
||||
thread will be the main thread of your application.
|
||||
|
||||
But why that specific package? GLHF uses the `mainthread` package to do the garbage collection of
|
||||
OpenGL objects, which is super convenient. So in order for it to work correctly, you have to
|
||||
initialize the `mainthread` package through `mainthread.Run`. However, once you call this function
|
||||
there is no way to run functions on the main thread, except for through the `mainthread` package.
|
||||
|
||||
### Why is the important XY feature not included?
|
||||
|
||||
I probably didn't need it yet. If you want that features, create an issue or implement it and do a
|
||||
pull request.
|
||||
|
||||
### Does GLHF create windows for me?
|
||||
|
||||
No. You have to use another library for windowing, e.g.
|
||||
[github.com/go-gl/glfw/v3.2/glfw](https://github.com/go-gl/glfw/tree/master/v3.2/glfw).
|
||||
|
||||
### Why no tests?
|
||||
|
||||
If you find a way to automatically test OpenGL, I may add tests.
|
80
vendor/github.com/faiface/glhf/attr.go
generated
vendored
Normal file
80
vendor/github.com/faiface/glhf/attr.go
generated
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
package glhf
|
||||
|
||||
// AttrFormat defines names and types of OpenGL attributes (vertex format, uniform format, etc.).
|
||||
//
|
||||
// Example:
|
||||
// AttrFormat{{"position", Vec2}, {"color", Vec4}, {"texCoord": Vec2}}
|
||||
type AttrFormat []Attr
|
||||
|
||||
// Size returns the total size of all attributes of the AttrFormat.
|
||||
func (af AttrFormat) Size() int {
|
||||
total := 0
|
||||
for _, attr := range af {
|
||||
total += attr.Type.Size()
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
// Attr represents an arbitrary OpenGL attribute, such as a vertex attribute or a shader
|
||||
// uniform attribute.
|
||||
type Attr struct {
|
||||
Name string
|
||||
Type AttrType
|
||||
}
|
||||
|
||||
// AttrType represents the type of an OpenGL attribute.
|
||||
type AttrType int
|
||||
|
||||
// List of all possible attribute types.
|
||||
const (
|
||||
Int AttrType = iota
|
||||
Float
|
||||
Vec2
|
||||
Vec3
|
||||
Vec4
|
||||
Mat2
|
||||
Mat23
|
||||
Mat24
|
||||
Mat3
|
||||
Mat32
|
||||
Mat34
|
||||
Mat4
|
||||
Mat42
|
||||
Mat43
|
||||
)
|
||||
|
||||
// Size returns the size of a type in bytes.
|
||||
func (at AttrType) Size() int {
|
||||
switch at {
|
||||
case Int:
|
||||
return 4
|
||||
case Float:
|
||||
return 4
|
||||
case Vec2:
|
||||
return 2 * 4
|
||||
case Vec3:
|
||||
return 3 * 4
|
||||
case Vec4:
|
||||
return 4 * 4
|
||||
case Mat2:
|
||||
return 2 * 2 * 4
|
||||
case Mat23:
|
||||
return 2 * 3 * 4
|
||||
case Mat24:
|
||||
return 2 * 4 * 4
|
||||
case Mat3:
|
||||
return 3 * 3 * 4
|
||||
case Mat32:
|
||||
return 3 * 2 * 4
|
||||
case Mat34:
|
||||
return 3 * 4 * 4
|
||||
case Mat4:
|
||||
return 4 * 4 * 4
|
||||
case Mat42:
|
||||
return 4 * 2 * 4
|
||||
case Mat43:
|
||||
return 4 * 3 * 4
|
||||
default:
|
||||
panic("size of vertex attribute type: invalid type")
|
||||
}
|
||||
}
|
7
vendor/github.com/faiface/glhf/doc.go
generated
vendored
Normal file
7
vendor/github.com/faiface/glhf/doc.go
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
// Package glhf provides abstractions around the basic OpenGL primitives and operations.
|
||||
//
|
||||
// All calls should be done from the main thread using "github.com/faiface/mainthread" package.
|
||||
//
|
||||
// This package deliberately does not handle nor report trivial OpenGL errors, it's up to you to
|
||||
// cause none. It does of course report errors like shader compilation error and such.
|
||||
package glhf
|
108
vendor/github.com/faiface/glhf/frame.go
generated
vendored
Normal file
108
vendor/github.com/faiface/glhf/frame.go
generated
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
package glhf
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
|
||||
"github.com/faiface/mainthread"
|
||||
"github.com/go-gl/gl/v3.3-core/gl"
|
||||
)
|
||||
|
||||
// Frame is a fixed resolution texture that you can draw on.
|
||||
type Frame struct {
|
||||
fb, rf, df binder // framebuffer, read framebuffer, draw framebuffer
|
||||
tex *Texture
|
||||
}
|
||||
|
||||
// NewFrame creates a new fully transparent Frame with given dimensions in pixels.
|
||||
func NewFrame(width, height int, smooth bool) *Frame {
|
||||
f := &Frame{
|
||||
fb: binder{
|
||||
restoreLoc: gl.FRAMEBUFFER_BINDING,
|
||||
bindFunc: func(obj uint32) {
|
||||
gl.BindFramebuffer(gl.FRAMEBUFFER, obj)
|
||||
},
|
||||
},
|
||||
rf: binder{
|
||||
restoreLoc: gl.READ_FRAMEBUFFER_BINDING,
|
||||
bindFunc: func(obj uint32) {
|
||||
gl.BindFramebuffer(gl.READ_FRAMEBUFFER, obj)
|
||||
},
|
||||
},
|
||||
df: binder{
|
||||
restoreLoc: gl.DRAW_FRAMEBUFFER_BINDING,
|
||||
bindFunc: func(obj uint32) {
|
||||
gl.BindFramebuffer(gl.DRAW_FRAMEBUFFER, obj)
|
||||
},
|
||||
},
|
||||
tex: NewTexture(width, height, smooth, make([]uint8, width*height*4)),
|
||||
}
|
||||
|
||||
gl.GenFramebuffers(1, &f.fb.obj)
|
||||
|
||||
f.fb.bind()
|
||||
gl.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, f.tex.tex.obj, 0)
|
||||
f.fb.restore()
|
||||
|
||||
runtime.SetFinalizer(f, (*Frame).delete)
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *Frame) delete() {
|
||||
mainthread.CallNonBlock(func() {
|
||||
gl.DeleteFramebuffers(1, &f.fb.obj)
|
||||
})
|
||||
}
|
||||
|
||||
// ID returns the OpenGL framebuffer ID of this Frame.
|
||||
func (f *Frame) ID() uint32 {
|
||||
return f.fb.obj
|
||||
}
|
||||
|
||||
// Begin binds the Frame. All draw operations will target this Frame until End is called.
|
||||
func (f *Frame) Begin() {
|
||||
f.fb.bind()
|
||||
}
|
||||
|
||||
// End unbinds the Frame. All draw operations will go to whatever was bound before this Frame.
|
||||
func (f *Frame) End() {
|
||||
f.fb.restore()
|
||||
}
|
||||
|
||||
// Blit copies rectangle (sx0, sy0, sx1, sy1) in this Frame onto rectangle (dx0, dy0, dx1, dy1) in
|
||||
// dst Frame.
|
||||
//
|
||||
// If the dst Frame is nil, the destination will be the framebuffer 0, which is the screen.
|
||||
//
|
||||
// If the sizes of the rectangles don't match, the source will be stretched to fit the destination
|
||||
// rectangle. The stretch will be either smooth or pixely according to the source Frame's
|
||||
// smoothness.
|
||||
func (f *Frame) Blit(dst *Frame, sx0, sy0, sx1, sy1, dx0, dy0, dx1, dy1 int) {
|
||||
f.rf.obj = f.fb.obj
|
||||
if dst != nil {
|
||||
f.df.obj = dst.fb.obj
|
||||
} else {
|
||||
f.df.obj = 0
|
||||
}
|
||||
f.rf.bind()
|
||||
f.df.bind()
|
||||
|
||||
filter := gl.NEAREST
|
||||
if f.tex.smooth {
|
||||
filter = gl.LINEAR
|
||||
}
|
||||
|
||||
gl.BlitFramebuffer(
|
||||
int32(sx0), int32(sy0), int32(sx1), int32(sy1),
|
||||
int32(dx0), int32(dy0), int32(dx1), int32(dy1),
|
||||
gl.COLOR_BUFFER_BIT, uint32(filter),
|
||||
)
|
||||
|
||||
f.rf.restore()
|
||||
f.df.restore()
|
||||
}
|
||||
|
||||
// Texture returns the Frame's underlying Texture that the Frame draws on.
|
||||
func (f *Frame) Texture() *Texture {
|
||||
return f.tex
|
||||
}
|
11
vendor/github.com/faiface/glhf/interface.go
generated
vendored
Normal file
11
vendor/github.com/faiface/glhf/interface.go
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
package glhf
|
||||
|
||||
// BeginEnder is an interface for manipulating OpenGL state.
|
||||
//
|
||||
// OpenGL is a state machine. Every object can 'enter' it's state and 'leave' it's state. For
|
||||
// example, you can bind a buffer and unbind a buffer, bind a texture and unbind it, use shader
|
||||
// and unuse it, and so on.
|
||||
type BeginEnder interface {
|
||||
Begin()
|
||||
End()
|
||||
}
|
51
vendor/github.com/faiface/glhf/orphan.go
generated
vendored
Normal file
51
vendor/github.com/faiface/glhf/orphan.go
generated
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
package glhf
|
||||
|
||||
import "github.com/go-gl/gl/v3.3-core/gl"
|
||||
|
||||
// Init initializes OpenGL by loading function pointers from the active OpenGL context.
|
||||
// This function must be manually run inside the main thread (using "github.com/faiface/mainthread"
|
||||
// package).
|
||||
//
|
||||
// It must be called under the presence of an active OpenGL context, e.g., always after calling
|
||||
// window.MakeContextCurrent(). Also, always call this function when switching contexts.
|
||||
func Init() {
|
||||
err := gl.Init()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
gl.Enable(gl.BLEND)
|
||||
gl.Enable(gl.SCISSOR_TEST)
|
||||
gl.BlendEquation(gl.FUNC_ADD)
|
||||
}
|
||||
|
||||
// Clear clears the current framebuffer or window with the given color.
|
||||
func Clear(r, g, b, a float32) {
|
||||
gl.ClearColor(r, g, b, a)
|
||||
gl.Clear(gl.COLOR_BUFFER_BIT)
|
||||
}
|
||||
|
||||
// Bounds sets the drawing bounds in pixels. Drawing outside bounds is always discarted.
|
||||
//
|
||||
// Calling this function is equivalent to setting viewport and scissor in OpenGL.
|
||||
func Bounds(x, y, w, h int) {
|
||||
gl.Viewport(int32(x), int32(y), int32(w), int32(h))
|
||||
gl.Scissor(int32(x), int32(y), int32(w), int32(h))
|
||||
}
|
||||
|
||||
// BlendFactor represents a source or destination blend factor.
|
||||
type BlendFactor int
|
||||
|
||||
// Here's the list of all blend factors.
|
||||
const (
|
||||
One = BlendFactor(gl.ONE)
|
||||
Zero = BlendFactor(gl.ZERO)
|
||||
SrcAlpha = BlendFactor(gl.SRC_ALPHA)
|
||||
DstAlpha = BlendFactor(gl.DST_ALPHA)
|
||||
OneMinusSrcAlpha = BlendFactor(gl.ONE_MINUS_SRC_ALPHA)
|
||||
OneMinusDstAlpha = BlendFactor(gl.ONE_MINUS_DST_ALPHA)
|
||||
)
|
||||
|
||||
// BlendFunc sets the source and destination blend factor.
|
||||
func BlendFunc(src, dst BlendFactor) {
|
||||
gl.BlendFunc(uint32(src), uint32(dst))
|
||||
}
|
224
vendor/github.com/faiface/glhf/shader.go
generated
vendored
Normal file
224
vendor/github.com/faiface/glhf/shader.go
generated
vendored
Normal file
@@ -0,0 +1,224 @@
|
||||
package glhf
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
"github.com/faiface/mainthread"
|
||||
"github.com/go-gl/gl/v3.3-core/gl"
|
||||
"github.com/go-gl/mathgl/mgl32"
|
||||
)
|
||||
|
||||
// Shader is an OpenGL shader program.
|
||||
type Shader struct {
|
||||
program binder
|
||||
vertexFmt AttrFormat
|
||||
uniformFmt AttrFormat
|
||||
uniformLoc []int32
|
||||
}
|
||||
|
||||
// NewShader creates a new shader program from the specified vertex shader and fragment shader
|
||||
// sources.
|
||||
//
|
||||
// Note that vertexShader and fragmentShader parameters must contain the source code, they're
|
||||
// not filenames.
|
||||
func NewShader(vertexFmt, uniformFmt AttrFormat, vertexShader, fragmentShader string) (*Shader, error) {
|
||||
shader := &Shader{
|
||||
program: binder{
|
||||
restoreLoc: gl.CURRENT_PROGRAM,
|
||||
bindFunc: func(obj uint32) {
|
||||
gl.UseProgram(obj)
|
||||
},
|
||||
},
|
||||
vertexFmt: vertexFmt,
|
||||
uniformFmt: uniformFmt,
|
||||
uniformLoc: make([]int32, len(uniformFmt)),
|
||||
}
|
||||
|
||||
var vshader, fshader uint32
|
||||
|
||||
// vertex shader
|
||||
{
|
||||
vshader = gl.CreateShader(gl.VERTEX_SHADER)
|
||||
src, free := gl.Strs(vertexShader)
|
||||
defer free()
|
||||
length := int32(len(vertexShader))
|
||||
gl.ShaderSource(vshader, 1, src, &length)
|
||||
gl.CompileShader(vshader)
|
||||
|
||||
var success int32
|
||||
gl.GetShaderiv(vshader, gl.COMPILE_STATUS, &success)
|
||||
if success == gl.FALSE {
|
||||
var logLen int32
|
||||
gl.GetShaderiv(vshader, gl.INFO_LOG_LENGTH, &logLen)
|
||||
|
||||
infoLog := make([]byte, logLen)
|
||||
gl.GetShaderInfoLog(vshader, logLen, nil, &infoLog[0])
|
||||
return nil, fmt.Errorf("error compiling vertex shader: %s", string(infoLog))
|
||||
}
|
||||
|
||||
defer gl.DeleteShader(vshader)
|
||||
}
|
||||
|
||||
// fragment shader
|
||||
{
|
||||
fshader = gl.CreateShader(gl.FRAGMENT_SHADER)
|
||||
src, free := gl.Strs(fragmentShader)
|
||||
defer free()
|
||||
length := int32(len(fragmentShader))
|
||||
gl.ShaderSource(fshader, 1, src, &length)
|
||||
gl.CompileShader(fshader)
|
||||
|
||||
var success int32
|
||||
gl.GetShaderiv(fshader, gl.COMPILE_STATUS, &success)
|
||||
if success == gl.FALSE {
|
||||
var logLen int32
|
||||
gl.GetShaderiv(fshader, gl.INFO_LOG_LENGTH, &logLen)
|
||||
|
||||
infoLog := make([]byte, logLen)
|
||||
gl.GetShaderInfoLog(fshader, logLen, nil, &infoLog[0])
|
||||
return nil, fmt.Errorf("error compiling fragment shader: %s", string(infoLog))
|
||||
}
|
||||
|
||||
defer gl.DeleteShader(fshader)
|
||||
}
|
||||
|
||||
// shader program
|
||||
{
|
||||
shader.program.obj = gl.CreateProgram()
|
||||
gl.AttachShader(shader.program.obj, vshader)
|
||||
gl.AttachShader(shader.program.obj, fshader)
|
||||
gl.LinkProgram(shader.program.obj)
|
||||
|
||||
var success int32
|
||||
gl.GetProgramiv(shader.program.obj, gl.LINK_STATUS, &success)
|
||||
if success == gl.FALSE {
|
||||
var logLen int32
|
||||
gl.GetProgramiv(shader.program.obj, gl.INFO_LOG_LENGTH, &logLen)
|
||||
|
||||
infoLog := make([]byte, logLen)
|
||||
gl.GetProgramInfoLog(shader.program.obj, logLen, nil, &infoLog[0])
|
||||
return nil, fmt.Errorf("error linking shader program: %s", string(infoLog))
|
||||
}
|
||||
}
|
||||
|
||||
// uniforms
|
||||
for i, uniform := range uniformFmt {
|
||||
loc := gl.GetUniformLocation(shader.program.obj, gl.Str(uniform.Name+"\x00"))
|
||||
shader.uniformLoc[i] = loc
|
||||
}
|
||||
|
||||
runtime.SetFinalizer(shader, (*Shader).delete)
|
||||
|
||||
return shader, nil
|
||||
}
|
||||
|
||||
func (s *Shader) delete() {
|
||||
mainthread.CallNonBlock(func() {
|
||||
gl.DeleteProgram(s.program.obj)
|
||||
})
|
||||
}
|
||||
|
||||
// ID returns the OpenGL ID of this Shader.
|
||||
func (s *Shader) ID() uint32 {
|
||||
return s.program.obj
|
||||
}
|
||||
|
||||
// VertexFormat returns the vertex attribute format of this Shader. Do not change it.
|
||||
func (s *Shader) VertexFormat() AttrFormat {
|
||||
return s.vertexFmt
|
||||
}
|
||||
|
||||
// UniformFormat returns the uniform attribute format of this Shader. Do not change it.
|
||||
func (s *Shader) UniformFormat() AttrFormat {
|
||||
return s.uniformFmt
|
||||
}
|
||||
|
||||
// SetUniformAttr sets the value of a uniform attribute of this Shader. The attribute is
|
||||
// specified by the index in the Shader's uniform format.
|
||||
//
|
||||
// If the uniform attribute does not exist in the Shader, this method returns false.
|
||||
//
|
||||
// Supplied value must correspond to the type of the attribute. Correct types are these
|
||||
// (right-hand is the type of the value):
|
||||
// Attr{Type: Int}: int32
|
||||
// Attr{Type: Float}: float32
|
||||
// Attr{Type: Vec2}: mgl32.Vec2
|
||||
// Attr{Type: Vec3}: mgl32.Vec3
|
||||
// Attr{Type: Vec4}: mgl32.Vec4
|
||||
// Attr{Type: Mat2}: mgl32.Mat2
|
||||
// Attr{Type: Mat23}: mgl32.Mat2x3
|
||||
// Attr{Type: Mat24}: mgl32.Mat2x4
|
||||
// Attr{Type: Mat3}: mgl32.Mat3
|
||||
// Attr{Type: Mat32}: mgl32.Mat3x2
|
||||
// Attr{Type: Mat34}: mgl32.Mat3x4
|
||||
// Attr{Type: Mat4}: mgl32.Mat4
|
||||
// Attr{Type: Mat42}: mgl32.Mat4x2
|
||||
// Attr{Type: Mat43}: mgl32.Mat4x3
|
||||
// No other types are supported.
|
||||
//
|
||||
// The Shader must be bound before calling this method.
|
||||
func (s *Shader) SetUniformAttr(uniform int, value interface{}) (ok bool) {
|
||||
if s.uniformLoc[uniform] < 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
switch s.uniformFmt[uniform].Type {
|
||||
case Int:
|
||||
value := value.(int32)
|
||||
gl.Uniform1iv(s.uniformLoc[uniform], 1, &value)
|
||||
case Float:
|
||||
value := value.(float32)
|
||||
gl.Uniform1fv(s.uniformLoc[uniform], 1, &value)
|
||||
case Vec2:
|
||||
value := value.(mgl32.Vec2)
|
||||
gl.Uniform2fv(s.uniformLoc[uniform], 1, &value[0])
|
||||
case Vec3:
|
||||
value := value.(mgl32.Vec3)
|
||||
gl.Uniform3fv(s.uniformLoc[uniform], 1, &value[0])
|
||||
case Vec4:
|
||||
value := value.(mgl32.Vec4)
|
||||
gl.Uniform4fv(s.uniformLoc[uniform], 1, &value[0])
|
||||
case Mat2:
|
||||
value := value.(mgl32.Mat2)
|
||||
gl.UniformMatrix2fv(s.uniformLoc[uniform], 1, false, &value[0])
|
||||
case Mat23:
|
||||
value := value.(mgl32.Mat2x3)
|
||||
gl.UniformMatrix2x3fv(s.uniformLoc[uniform], 1, false, &value[0])
|
||||
case Mat24:
|
||||
value := value.(mgl32.Mat2x4)
|
||||
gl.UniformMatrix2x4fv(s.uniformLoc[uniform], 1, false, &value[0])
|
||||
case Mat3:
|
||||
value := value.(mgl32.Mat3)
|
||||
gl.UniformMatrix3fv(s.uniformLoc[uniform], 1, false, &value[0])
|
||||
case Mat32:
|
||||
value := value.(mgl32.Mat3x2)
|
||||
gl.UniformMatrix3x2fv(s.uniformLoc[uniform], 1, false, &value[0])
|
||||
case Mat34:
|
||||
value := value.(mgl32.Mat3x4)
|
||||
gl.UniformMatrix3x4fv(s.uniformLoc[uniform], 1, false, &value[0])
|
||||
case Mat4:
|
||||
value := value.(mgl32.Mat4)
|
||||
gl.UniformMatrix4fv(s.uniformLoc[uniform], 1, false, &value[0])
|
||||
case Mat42:
|
||||
value := value.(mgl32.Mat4x2)
|
||||
gl.UniformMatrix4x2fv(s.uniformLoc[uniform], 1, false, &value[0])
|
||||
case Mat43:
|
||||
value := value.(mgl32.Mat4x3)
|
||||
gl.UniformMatrix4x3fv(s.uniformLoc[uniform], 1, false, &value[0])
|
||||
default:
|
||||
panic("set uniform attr: invalid attribute type")
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Begin binds the Shader program. This is necessary before using the Shader.
|
||||
func (s *Shader) Begin() {
|
||||
s.program.bind()
|
||||
}
|
||||
|
||||
// End unbinds the Shader program and restores the previous one.
|
||||
func (s *Shader) End() {
|
||||
s.program.restore()
|
||||
}
|
148
vendor/github.com/faiface/glhf/texture.go
generated
vendored
Normal file
148
vendor/github.com/faiface/glhf/texture.go
generated
vendored
Normal file
@@ -0,0 +1,148 @@
|
||||
package glhf
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
|
||||
"github.com/faiface/mainthread"
|
||||
"github.com/go-gl/gl/v3.3-core/gl"
|
||||
"github.com/go-gl/mathgl/mgl32"
|
||||
)
|
||||
|
||||
// Texture is an OpenGL texture.
|
||||
type Texture struct {
|
||||
tex binder
|
||||
width, height int
|
||||
smooth bool
|
||||
}
|
||||
|
||||
// NewTexture creates a new texture with the specified width and height with some initial
|
||||
// pixel values. The pixels must be a sequence of RGBA values (one byte per component).
|
||||
func NewTexture(width, height int, smooth bool, pixels []uint8) *Texture {
|
||||
tex := &Texture{
|
||||
tex: binder{
|
||||
restoreLoc: gl.TEXTURE_BINDING_2D,
|
||||
bindFunc: func(obj uint32) {
|
||||
gl.BindTexture(gl.TEXTURE_2D, obj)
|
||||
},
|
||||
},
|
||||
width: width,
|
||||
height: height,
|
||||
}
|
||||
|
||||
gl.GenTextures(1, &tex.tex.obj)
|
||||
|
||||
tex.Begin()
|
||||
defer tex.End()
|
||||
|
||||
// initial data
|
||||
gl.TexImage2D(
|
||||
gl.TEXTURE_2D,
|
||||
0,
|
||||
gl.RGBA,
|
||||
int32(width),
|
||||
int32(height),
|
||||
0,
|
||||
gl.RGBA,
|
||||
gl.UNSIGNED_BYTE,
|
||||
gl.Ptr(pixels),
|
||||
)
|
||||
|
||||
borderColor := mgl32.Vec4{0, 0, 0, 0}
|
||||
gl.TexParameterfv(gl.TEXTURE_2D, gl.TEXTURE_BORDER_COLOR, &borderColor[0])
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_BORDER)
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_BORDER)
|
||||
|
||||
tex.SetSmooth(smooth)
|
||||
|
||||
runtime.SetFinalizer(tex, (*Texture).delete)
|
||||
|
||||
return tex
|
||||
}
|
||||
|
||||
func (t *Texture) delete() {
|
||||
mainthread.CallNonBlock(func() {
|
||||
gl.DeleteTextures(1, &t.tex.obj)
|
||||
})
|
||||
}
|
||||
|
||||
// ID returns the OpenGL ID of this Texture.
|
||||
func (t *Texture) ID() uint32 {
|
||||
return t.tex.obj
|
||||
}
|
||||
|
||||
// Width returns the width of the Texture in pixels.
|
||||
func (t *Texture) Width() int {
|
||||
return t.width
|
||||
}
|
||||
|
||||
// Height returns the height of the Texture in pixels.
|
||||
func (t *Texture) Height() int {
|
||||
return t.height
|
||||
}
|
||||
|
||||
// SetPixels sets the content of a sub-region of the Texture. Pixels must be an RGBA byte sequence.
|
||||
func (t *Texture) SetPixels(x, y, w, h int, pixels []uint8) {
|
||||
if len(pixels) != w*h*4 {
|
||||
panic("set pixels: wrong number of pixels")
|
||||
}
|
||||
gl.TexSubImage2D(
|
||||
gl.TEXTURE_2D,
|
||||
0,
|
||||
int32(x),
|
||||
int32(y),
|
||||
int32(w),
|
||||
int32(h),
|
||||
gl.RGBA,
|
||||
gl.UNSIGNED_BYTE,
|
||||
gl.Ptr(pixels),
|
||||
)
|
||||
}
|
||||
|
||||
// Pixels returns the content of a sub-region of the Texture as an RGBA byte sequence.
|
||||
func (t *Texture) Pixels(x, y, w, h int) []uint8 {
|
||||
pixels := make([]uint8, t.width*t.height*4)
|
||||
gl.GetTexImage(
|
||||
gl.TEXTURE_2D,
|
||||
0,
|
||||
gl.RGBA,
|
||||
gl.UNSIGNED_BYTE,
|
||||
gl.Ptr(pixels),
|
||||
)
|
||||
subPixels := make([]uint8, w*h*4)
|
||||
for i := 0; i < h; i++ {
|
||||
row := pixels[(i+y)*t.width*4+x*4 : (i+y)*t.width*4+(x+w)*4]
|
||||
subRow := subPixels[i*w*4 : (i+1)*w*4]
|
||||
copy(subRow, row)
|
||||
}
|
||||
return subPixels
|
||||
}
|
||||
|
||||
// SetSmooth sets whether the Texture should be drawn "smoothly" or "pixely".
|
||||
//
|
||||
// It affects how the Texture is drawn when zoomed. Smooth interpolates between the neighbour
|
||||
// pixels, while pixely always chooses the nearest pixel.
|
||||
func (t *Texture) SetSmooth(smooth bool) {
|
||||
t.smooth = smooth
|
||||
if smooth {
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
|
||||
} else {
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
|
||||
}
|
||||
}
|
||||
|
||||
// Smooth returns whether the Texture is set to be drawn "smooth" or "pixely".
|
||||
func (t *Texture) Smooth() bool {
|
||||
return t.smooth
|
||||
}
|
||||
|
||||
// Begin binds the Texture. This is necessary before using the Texture.
|
||||
func (t *Texture) Begin() {
|
||||
t.tex.bind()
|
||||
}
|
||||
|
||||
// End unbinds the Texture and restores the previous one.
|
||||
func (t *Texture) End() {
|
||||
t.tex.restore()
|
||||
}
|
31
vendor/github.com/faiface/glhf/util.go
generated
vendored
Normal file
31
vendor/github.com/faiface/glhf/util.go
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
package glhf
|
||||
|
||||
import "github.com/go-gl/gl/v3.3-core/gl"
|
||||
|
||||
type binder struct {
|
||||
restoreLoc uint32
|
||||
bindFunc func(uint32)
|
||||
|
||||
obj uint32
|
||||
|
||||
prev []uint32
|
||||
}
|
||||
|
||||
func (b *binder) bind() *binder {
|
||||
var prev int32
|
||||
gl.GetIntegerv(b.restoreLoc, &prev)
|
||||
b.prev = append(b.prev, uint32(prev))
|
||||
|
||||
if b.prev[len(b.prev)-1] != b.obj {
|
||||
b.bindFunc(b.obj)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *binder) restore() *binder {
|
||||
if b.prev[len(b.prev)-1] != b.obj {
|
||||
b.bindFunc(b.prev[len(b.prev)-1])
|
||||
}
|
||||
b.prev = b.prev[:len(b.prev)-1]
|
||||
return b
|
||||
}
|
285
vendor/github.com/faiface/glhf/vertex.go
generated
vendored
Normal file
285
vendor/github.com/faiface/glhf/vertex.go
generated
vendored
Normal file
@@ -0,0 +1,285 @@
|
||||
package glhf
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
"github.com/faiface/mainthread"
|
||||
"github.com/go-gl/gl/v3.3-core/gl"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// VertexSlice points to a portion of (or possibly whole) vertex array. It is used as a pointer,
|
||||
// contrary to Go's builtin slices. This is, so that append can be 'in-place'. That's for the good,
|
||||
// because Begin/End-ing a VertexSlice would become super confusing, if append returned a new
|
||||
// VertexSlice.
|
||||
//
|
||||
// It also implements all basic slice-like operations: appending, sub-slicing, etc.
|
||||
//
|
||||
// Note that you need to Begin a VertexSlice before getting or updating it's elements or drawing it.
|
||||
// After you're done with it, you need to End it.
|
||||
type VertexSlice struct {
|
||||
va *vertexArray
|
||||
i, j int
|
||||
}
|
||||
|
||||
// MakeVertexSlice allocates a new vertex array with specified capacity and returns a VertexSlice
|
||||
// that points to it's first len elements.
|
||||
//
|
||||
// Note, that a vertex array is specialized for a specific shader and can't be used with another
|
||||
// shader.
|
||||
func MakeVertexSlice(shader *Shader, len, cap int) *VertexSlice {
|
||||
if len > cap {
|
||||
panic("failed to make vertex slice: len > cap")
|
||||
}
|
||||
return &VertexSlice{
|
||||
va: newVertexArray(shader, cap),
|
||||
i: 0,
|
||||
j: len,
|
||||
}
|
||||
}
|
||||
|
||||
// VertexFormat returns the format of vertex attributes inside the underlying vertex array of this
|
||||
// VertexSlice.
|
||||
func (vs *VertexSlice) VertexFormat() AttrFormat {
|
||||
return vs.va.format
|
||||
}
|
||||
|
||||
// Stride returns the number of float32 elements occupied by one vertex.
|
||||
func (vs *VertexSlice) Stride() int {
|
||||
return vs.va.stride / 4
|
||||
}
|
||||
|
||||
// Len returns the length of the VertexSlice (number of vertices).
|
||||
func (vs *VertexSlice) Len() int {
|
||||
return vs.j - vs.i
|
||||
}
|
||||
|
||||
// Cap returns the capacity of an underlying vertex array.
|
||||
func (vs *VertexSlice) Cap() int {
|
||||
return vs.va.cap - vs.i
|
||||
}
|
||||
|
||||
// SetLen resizes the VertexSlice to length len.
|
||||
func (vs *VertexSlice) SetLen(len int) {
|
||||
vs.End() // vs must have been Begin-ed before calling this method
|
||||
*vs = vs.grow(len)
|
||||
vs.Begin()
|
||||
}
|
||||
|
||||
// grow returns supplied vs with length changed to len. Allocates new underlying vertex array if
|
||||
// necessary. The original content is preserved.
|
||||
func (vs VertexSlice) grow(len int) VertexSlice {
|
||||
if len <= vs.Cap() {
|
||||
// capacity sufficient
|
||||
return VertexSlice{
|
||||
va: vs.va,
|
||||
i: vs.i,
|
||||
j: vs.i + len,
|
||||
}
|
||||
}
|
||||
|
||||
// grow the capacity
|
||||
newCap := vs.Cap()
|
||||
if newCap < 1024 {
|
||||
newCap += newCap
|
||||
} else {
|
||||
newCap += newCap / 4
|
||||
}
|
||||
if newCap < len {
|
||||
newCap = len
|
||||
}
|
||||
newVs := VertexSlice{
|
||||
va: newVertexArray(vs.va.shader, newCap),
|
||||
i: 0,
|
||||
j: len,
|
||||
}
|
||||
// preserve the original content
|
||||
newVs.Begin()
|
||||
newVs.Slice(0, vs.Len()).SetVertexData(vs.VertexData())
|
||||
newVs.End()
|
||||
return newVs
|
||||
}
|
||||
|
||||
// Slice returns a sub-slice of this VertexSlice covering the range [i, j) (relative to this
|
||||
// VertexSlice).
|
||||
//
|
||||
// Note, that the returned VertexSlice shares an underlying vertex array with the original
|
||||
// VertexSlice. Modifying the contents of one modifies corresponding contents of the other.
|
||||
func (vs *VertexSlice) Slice(i, j int) *VertexSlice {
|
||||
if i < 0 || j < i || j > vs.va.cap {
|
||||
panic("failed to slice vertex slice: index out of range")
|
||||
}
|
||||
return &VertexSlice{
|
||||
va: vs.va,
|
||||
i: vs.i + i,
|
||||
j: vs.i + j,
|
||||
}
|
||||
}
|
||||
|
||||
// SetVertexData sets the contents of the VertexSlice.
|
||||
//
|
||||
// The data is a slice of float32's, where each vertex attribute occupies a certain number of
|
||||
// elements. Namely, Float occupies 1, Vec2 occupies 2, Vec3 occupies 3 and Vec4 occupies 4. The
|
||||
// attribues in the data slice must be in the same order as in the vertex format of this Vertex
|
||||
// Slice.
|
||||
//
|
||||
// If the length of vertices does not match the length of the VertexSlice, this methdo panics.
|
||||
func (vs *VertexSlice) SetVertexData(data []float32) {
|
||||
if len(data)/vs.Stride() != vs.Len() {
|
||||
fmt.Println(len(data)/vs.Stride(), vs.Len())
|
||||
panic("set vertex data: wrong length of vertices")
|
||||
}
|
||||
vs.va.setVertexData(vs.i, vs.j, data)
|
||||
}
|
||||
|
||||
// VertexData returns the contents of the VertexSlice.
|
||||
//
|
||||
// The data is in the same format as with SetVertexData.
|
||||
func (vs *VertexSlice) VertexData() []float32 {
|
||||
return vs.va.vertexData(vs.i, vs.j)
|
||||
}
|
||||
|
||||
// Draw draws the content of the VertexSlice.
|
||||
func (vs *VertexSlice) Draw() {
|
||||
vs.va.draw(vs.i, vs.j)
|
||||
}
|
||||
|
||||
// Begin binds the underlying vertex array. Calling this method is necessary before using the VertexSlice.
|
||||
func (vs *VertexSlice) Begin() {
|
||||
vs.va.begin()
|
||||
}
|
||||
|
||||
// End unbinds the underlying vertex array. Call this method when you're done with VertexSlice.
|
||||
func (vs *VertexSlice) End() {
|
||||
vs.va.end()
|
||||
}
|
||||
|
||||
type vertexArray struct {
|
||||
vao, vbo binder
|
||||
cap int
|
||||
format AttrFormat
|
||||
stride int
|
||||
offset []int
|
||||
shader *Shader
|
||||
}
|
||||
|
||||
const vertexArrayMinCap = 4
|
||||
|
||||
func newVertexArray(shader *Shader, cap int) *vertexArray {
|
||||
if cap < vertexArrayMinCap {
|
||||
cap = vertexArrayMinCap
|
||||
}
|
||||
|
||||
va := &vertexArray{
|
||||
vao: binder{
|
||||
restoreLoc: gl.VERTEX_ARRAY_BINDING,
|
||||
bindFunc: func(obj uint32) {
|
||||
gl.BindVertexArray(obj)
|
||||
},
|
||||
},
|
||||
vbo: binder{
|
||||
restoreLoc: gl.ARRAY_BUFFER_BINDING,
|
||||
bindFunc: func(obj uint32) {
|
||||
gl.BindBuffer(gl.ARRAY_BUFFER, obj)
|
||||
},
|
||||
},
|
||||
cap: cap,
|
||||
format: shader.VertexFormat(),
|
||||
stride: shader.VertexFormat().Size(),
|
||||
offset: make([]int, len(shader.VertexFormat())),
|
||||
shader: shader,
|
||||
}
|
||||
|
||||
offset := 0
|
||||
for i, attr := range va.format {
|
||||
switch attr.Type {
|
||||
case Float, Vec2, Vec3, Vec4:
|
||||
default:
|
||||
panic(errors.New("failed to create vertex array: invalid attribute type"))
|
||||
}
|
||||
va.offset[i] = offset
|
||||
offset += attr.Type.Size()
|
||||
}
|
||||
|
||||
gl.GenVertexArrays(1, &va.vao.obj)
|
||||
|
||||
va.vao.bind()
|
||||
|
||||
gl.GenBuffers(1, &va.vbo.obj)
|
||||
defer va.vbo.bind().restore()
|
||||
|
||||
emptyData := make([]byte, cap*va.stride)
|
||||
gl.BufferData(gl.ARRAY_BUFFER, len(emptyData), gl.Ptr(emptyData), gl.DYNAMIC_DRAW)
|
||||
|
||||
for i, attr := range va.format {
|
||||
loc := gl.GetAttribLocation(shader.program.obj, gl.Str(attr.Name+"\x00"))
|
||||
|
||||
var size int32
|
||||
switch attr.Type {
|
||||
case Float:
|
||||
size = 1
|
||||
case Vec2:
|
||||
size = 2
|
||||
case Vec3:
|
||||
size = 3
|
||||
case Vec4:
|
||||
size = 4
|
||||
}
|
||||
|
||||
gl.VertexAttribPointer(
|
||||
uint32(loc),
|
||||
size,
|
||||
gl.FLOAT,
|
||||
false,
|
||||
int32(va.stride),
|
||||
gl.PtrOffset(va.offset[i]),
|
||||
)
|
||||
gl.EnableVertexAttribArray(uint32(loc))
|
||||
}
|
||||
|
||||
va.vao.restore()
|
||||
|
||||
runtime.SetFinalizer(va, (*vertexArray).delete)
|
||||
|
||||
return va
|
||||
}
|
||||
|
||||
func (va *vertexArray) delete() {
|
||||
mainthread.CallNonBlock(func() {
|
||||
gl.DeleteVertexArrays(1, &va.vao.obj)
|
||||
gl.DeleteBuffers(1, &va.vbo.obj)
|
||||
})
|
||||
}
|
||||
|
||||
func (va *vertexArray) begin() {
|
||||
va.vao.bind()
|
||||
va.vbo.bind()
|
||||
}
|
||||
|
||||
func (va *vertexArray) end() {
|
||||
va.vbo.restore()
|
||||
va.vao.restore()
|
||||
}
|
||||
|
||||
func (va *vertexArray) draw(i, j int) {
|
||||
gl.DrawArrays(gl.TRIANGLES, int32(i), int32(i+j))
|
||||
}
|
||||
|
||||
func (va *vertexArray) setVertexData(i, j int, data []float32) {
|
||||
if j-i == 0 {
|
||||
// avoid setting 0 bytes of buffer data
|
||||
return
|
||||
}
|
||||
gl.BufferSubData(gl.ARRAY_BUFFER, i*va.stride, len(data)*4, gl.Ptr(data))
|
||||
}
|
||||
|
||||
func (va *vertexArray) vertexData(i, j int) []float32 {
|
||||
if j-i == 0 {
|
||||
// avoid getting 0 bytes of buffer data
|
||||
return nil
|
||||
}
|
||||
data := make([]float32, (j-i)*va.stride/4)
|
||||
gl.GetBufferSubData(gl.ARRAY_BUFFER, i*va.stride, len(data)*4, gl.Ptr(data))
|
||||
return data
|
||||
}
|
21
vendor/github.com/faiface/mainthread/LICENSE
generated
vendored
Normal file
21
vendor/github.com/faiface/mainthread/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Michal Štrba
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
71
vendor/github.com/faiface/mainthread/README.md
generated
vendored
Normal file
71
vendor/github.com/faiface/mainthread/README.md
generated
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
# mainthread [](http://godoc.org/github.com/faiface/mainthread) [](https://goreportcard.com/report/github.com/faiface/mainthread)
|
||||
|
||||
Package mainthread allows you to run code on the main operating system thread.
|
||||
|
||||
`go get github.com/faiface/mainthread`
|
||||
|
||||
Operating systems often require, that code which deals with windows and graphics has to run on the
|
||||
main thread. This is however somehow challenging in Go due to Go's concurrent nature.
|
||||
|
||||
This package makes it easily possible.
|
||||
|
||||
All you need to do is put your main code into a separate function and call `mainthread.Run` from
|
||||
your real main, like this:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/faiface/mainthread"
|
||||
)
|
||||
|
||||
func run() {
|
||||
// now we can run stuff on the main thread like this
|
||||
mainthread.Call(func() {
|
||||
fmt.Println("printing from the main thread")
|
||||
})
|
||||
fmt.Println("printing from another thread")
|
||||
}
|
||||
|
||||
func main() {
|
||||
mainthread.Run(run) // enables mainthread package and runs run in a separate goroutine
|
||||
}
|
||||
```
|
||||
|
||||
## More functions
|
||||
|
||||
If you don't wish to wait until a function finishes running on the main thread, use
|
||||
`mainthread.CallNonBlock`:
|
||||
|
||||
```go
|
||||
mainthread.CallNonBlock(func() {
|
||||
fmt.Println("i'm in the main thread")
|
||||
})
|
||||
fmt.Println("but imma be likely printed first, cuz i don't wait")
|
||||
```
|
||||
|
||||
If you want to get some value returned from the main thread, you can use `mainthread.CallErr` or
|
||||
`mainthread.CallVal`:
|
||||
|
||||
```go
|
||||
err := mainthread.CallErr(func() error {
|
||||
return nil // i don't do nothing wrong
|
||||
})
|
||||
val := mainthread.CallVal(func() interface{} {
|
||||
return 42 // the meaning of life, universe and everything
|
||||
})
|
||||
```
|
||||
|
||||
If `mainthread.CallErr` or `mainthread.CallVal` aren't sufficient for you, you can just assign
|
||||
variables from within the main thread:
|
||||
|
||||
```go
|
||||
var x, y int
|
||||
mainthread.Call(func() {
|
||||
x, y = 1, 2
|
||||
})
|
||||
```
|
||||
|
||||
However, be careful with `mainthread.CallNonBlock` when dealing with local variables.
|
87
vendor/github.com/faiface/mainthread/mainthread.go
generated
vendored
Normal file
87
vendor/github.com/faiface/mainthread/mainthread.go
generated
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
package mainthread
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// CallQueueCap is the capacity of the call queue. This means how many calls to CallNonBlock will not
|
||||
// block until some call finishes.
|
||||
//
|
||||
// The default value is 16 and should be good for 99% usecases.
|
||||
var CallQueueCap = 16
|
||||
|
||||
var (
|
||||
callQueue chan func()
|
||||
)
|
||||
|
||||
func init() {
|
||||
runtime.LockOSThread()
|
||||
}
|
||||
|
||||
func checkRun() {
|
||||
if callQueue == nil {
|
||||
panic(errors.New("mainthread: did not call Run"))
|
||||
}
|
||||
}
|
||||
|
||||
// Run enables mainthread package functionality. To use mainthread package, put your main function
|
||||
// code into the run function (the argument to Run) and simply call Run from the real main function.
|
||||
//
|
||||
// Run returns when run (argument) function finishes.
|
||||
func Run(run func()) {
|
||||
callQueue = make(chan func(), CallQueueCap)
|
||||
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
run()
|
||||
done <- struct{}{}
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case f := <-callQueue:
|
||||
f()
|
||||
case <-done:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CallNonBlock queues function f on the main thread and returns immediately. Does not wait until f
|
||||
// finishes.
|
||||
func CallNonBlock(f func()) {
|
||||
checkRun()
|
||||
callQueue <- f
|
||||
}
|
||||
|
||||
// Call queues function f on the main thread and blocks until the function f finishes.
|
||||
func Call(f func()) {
|
||||
checkRun()
|
||||
done := make(chan struct{})
|
||||
callQueue <- func() {
|
||||
f()
|
||||
done <- struct{}{}
|
||||
}
|
||||
<-done
|
||||
}
|
||||
|
||||
// CallErr queues function f on the main thread and returns an error returned by f.
|
||||
func CallErr(f func() error) error {
|
||||
checkRun()
|
||||
errChan := make(chan error)
|
||||
callQueue <- func() {
|
||||
errChan <- f()
|
||||
}
|
||||
return <-errChan
|
||||
}
|
||||
|
||||
// CallVal queues function f on the main thread and returns a value returned by f.
|
||||
func CallVal(f func() interface{}) interface{} {
|
||||
checkRun()
|
||||
respChan := make(chan interface{})
|
||||
callQueue <- func() {
|
||||
respChan <- f()
|
||||
}
|
||||
return <-respChan
|
||||
}
|
2
vendor/github.com/faiface/pixel/.gitignore
generated
vendored
Normal file
2
vendor/github.com/faiface/pixel/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
test
|
||||
.vscode
|
23
vendor/github.com/faiface/pixel/.travis.yml
generated
vendored
Normal file
23
vendor/github.com/faiface/pixel/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
language: go
|
||||
sudo: false
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- xorg-dev
|
||||
- libx11-dev
|
||||
- libxrandr-dev
|
||||
- libxinerama-dev
|
||||
- libxcursor-dev
|
||||
- libxi-dev
|
||||
- libopenal-dev
|
||||
- libasound2-dev
|
||||
go:
|
||||
- 1.8
|
||||
- 1.7.4
|
||||
- tip
|
||||
install:
|
||||
- go get -t ./...
|
||||
script:
|
||||
- go test -i -race ./...
|
||||
- go test -v -race ./...
|
||||
|
14
vendor/github.com/faiface/pixel/CONTRIBUTING.md
generated
vendored
Normal file
14
vendor/github.com/faiface/pixel/CONTRIBUTING.md
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
# Contributing to Pixel
|
||||
|
||||
:tada: Hi! I'm really glad you're considering contributing to Pixel! :tada:
|
||||
|
||||
## Here are a few ways you can contribute
|
||||
|
||||
1. **Make a community example** and place it inside the [examples/community](examples/community) folder.
|
||||
2. **Add tests**. There only few tests in Pixel at the moment. Take a look at them and make some similar.
|
||||
3. **Add a small feature or an improvement**. Feel like some small feature is missing? Just make a PR. Be ready that I might reject it, though, if I don't find it particularly appealing.
|
||||
4. **Join the big development** by joining the discussion at the [Gitter](https://gitter.im/pixellib/Lobby), where we can discuss bigger changes and implement them after that.
|
||||
|
||||
## How to make a pull request
|
||||
|
||||
Go gives you a nice surprise when attempting to make a PR on Github. The thing is, that when user _xyz_ forks Pixel on Github, it ends up in _github.com/xyz/pixel_, which fucks up your import paths. Here's how you deal with that: https://www.reddit.com/r/golang/comments/2jdcw1/how_do_you_deal_with_github_forking/.
|
21
vendor/github.com/faiface/pixel/LICENSE
generated
vendored
Normal file
21
vendor/github.com/faiface/pixel/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016 Michal Štrba
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
160
vendor/github.com/faiface/pixel/README.md
generated
vendored
Normal file
160
vendor/github.com/faiface/pixel/README.md
generated
vendored
Normal file
@@ -0,0 +1,160 @@
|
||||
# Pixel [](https://travis-ci.org/faiface/pixel) [](https://godoc.org/github.com/faiface/pixel) [](https://goreportcard.com/report/github.com/faiface/pixel) [](https://gitter.im/pixellib/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||
|
||||
A hand-crafted 2D game library in Go. Take a look into the [features](#features) to see what it can
|
||||
do.
|
||||
|
||||
```
|
||||
go get github.com/faiface/pixel
|
||||
```
|
||||
|
||||
See [requirements](#requirements) for the list of libraries necessary for compilation.
|
||||
|
||||
## Tutorial
|
||||
|
||||
The [Wiki of this repo](https://github.com/faiface/pixel/wiki) contains an extensive tutorial
|
||||
covering several topics of Pixel. Here's the content of the tutorial parts so far:
|
||||
|
||||
- [Creating a Window](https://github.com/faiface/pixel/wiki/Creating-a-Window)
|
||||
- [Drawing a Sprite](https://github.com/faiface/pixel/wiki/Drawing-a-Sprite)
|
||||
- [Moving, scaling and rotating with Matrix](https://github.com/faiface/pixel/wiki/Moving,-scaling-and-rotating-with-Matrix)
|
||||
- [Pressing keys and clicking mouse](https://github.com/faiface/pixel/wiki/Pressing-keys-and-clicking-mouse)
|
||||
- [Drawing efficiently with Batch](https://github.com/faiface/pixel/wiki/Drawing-efficiently-with-Batch)
|
||||
- [Drawing shapes with IMDraw](https://github.com/faiface/pixel/wiki/Drawing-shapes-with-IMDraw)
|
||||
- [Typing text on the screen](https://github.com/faiface/pixel/wiki/Typing-text-on-the-screen)
|
||||
|
||||
## Examples
|
||||
|
||||
The [examples](https://github.com/faiface/pixel/tree/master/examples) directory contains a few
|
||||
examples demonstrating Pixel's functionality.
|
||||
|
||||
**To run an example**, navigate to it's directory, then `go run` the `main.go` file. For example:
|
||||
|
||||
```
|
||||
$ cd examples/platformer
|
||||
$ go run main.go
|
||||
```
|
||||
|
||||
Here are some screenshots from the examples!
|
||||
|
||||
| [Lights](examples/lights) | [Platformer](examples/platformer) |
|
||||
| --- | --- |
|
||||
|  |  |
|
||||
|
||||
| [Smoke](examples/smoke) | [Typewriter](examples/typewriter) |
|
||||
| --- | --- |
|
||||
|  |  |
|
||||
|
||||
| [Raycaster](examples/community/raycaster) | [Starfield](examples/community/starfield) |
|
||||
| --- | --- |
|
||||
|  |  |
|
||||
|
||||
## Features
|
||||
|
||||
Here's the list of the main features in Pixel. Although Pixel is still under heavy development,
|
||||
**there should be no major breakage in the API.** This is not a 100% guarantee, though.
|
||||
|
||||
- Fast 2D graphics
|
||||
- Sprites
|
||||
- Primitive shapes with immediate mode style
|
||||
[IMDraw](https://github.com/faiface/pixel/wiki/Drawing-shapes-with-IMDraw) (circles, rectangles,
|
||||
lines, ...)
|
||||
- Optimized drawing with [Batch](https://github.com/faiface/pixel/wiki/Drawing-efficiently-with-Batch)
|
||||
- Text drawing with [text](https://godoc.org/github.com/faiface/pixel/text) package
|
||||
- Audio through a separate [Beep](https://github.com/faiface/beep) library.
|
||||
- Simple and convenient API
|
||||
- Drawing a sprite to a window is as simple as `sprite.Draw(window, matrix)`
|
||||
- Wanna know where the center of a window is? `window.Bounds().Center()`
|
||||
- [...](https://godoc.org/github.com/faiface/pixel)
|
||||
- Full documentation and tutorial
|
||||
- Works on Linux, macOS and Windows
|
||||
- Window creation and manipulation (resizing, fullscreen, multiple windows, ...)
|
||||
- Keyboard (key presses, text input) and mouse input without events
|
||||
- Well integrated with the Go standard library
|
||||
- Use `"image"` package for loading pictures
|
||||
- Use `"time"` package for measuring delta time and FPS
|
||||
- Use `"image/color"` for colors, or use Pixel's own `color.Color` format, which supports easy
|
||||
multiplication and a few more features
|
||||
- Pixel uses `float64` throughout the library, compatible with `"math"` package
|
||||
- Geometry transformations with
|
||||
[Matrix](https://github.com/faiface/pixel/wiki/Moving,-scaling-and-rotating-with-Matrix)
|
||||
- Moving, scaling, rotating
|
||||
- Easy camera implementation
|
||||
- Off-screen drawing to Canvas or any other target (Batch, IMDraw, ...)
|
||||
- Fully garbage collected, no `Close` or `Dispose` methods
|
||||
- Full [Porter-Duff](http://ssp.impulsetrain.com/porterduff.html) composition, which enables
|
||||
- 2D lighting
|
||||
- Cutting holes into objects
|
||||
- Much more...
|
||||
- Pixel let's you draw stuff and do your job, it doesn't impose any particular style or paradigm
|
||||
- Platform and backend independent [core](https://godoc.org/github.com/faiface/pixel)
|
||||
- Core Target/Triangles/Picture pattern makes it easy to create new drawing targets that do
|
||||
arbitrarily crazy stuff (e.g. graphical effects)
|
||||
- Small codebase, ~5K lines of code, including the backend [glhf](https://github.com/faiface/glhf)
|
||||
package
|
||||
|
||||
## Missing features
|
||||
|
||||
Pixel is in development and still missing few critical features. Here're the most critical ones.
|
||||
|
||||
- ~~Audio~~
|
||||
- ~~Drawing text~~
|
||||
- Antialiasing (filtering is supported, though)
|
||||
- ~~Advanced window manipulation (cursor hiding, window icon, ...)~~
|
||||
- Better support for Hi-DPI displays
|
||||
- Mobile (and perhaps HTML5?) backend
|
||||
- More advanced graphical effects (e.g. blur)
|
||||
- Tests and benchmarks
|
||||
|
||||
**Implementing these features will get us to the 1.0 release.** Contribute, so that it's as soon as
|
||||
possible!
|
||||
|
||||
## Requirements
|
||||
|
||||
If you're using Windows and having trouble building Pixel, please check [this
|
||||
guide](https://github.com/faiface/pixel/wiki/Building-Pixel-on-Windows) on the
|
||||
[wiki](https://github.com/faiface/pixel/wiki).
|
||||
|
||||
[PixelGL](https://godoc.org/github.com/faiface/pixel/pixelgl) backend uses OpenGL to render
|
||||
graphics. Because of that, OpenGL development libraries are needed for compilation. The dependencies
|
||||
are same as for [GLFW](https://github.com/go-gl/glfw).
|
||||
|
||||
The OpenGL version used is **OpenGL 3.3**.
|
||||
|
||||
- On macOS, you need Xcode or Command Line Tools for Xcode (`xcode-select --install`) for required
|
||||
headers and libraries.
|
||||
- On Ubuntu/Debian-like Linux distributions, you need `libgl1-mesa-dev` and `xorg-dev` packages.
|
||||
- On CentOS/Fedora-like Linux distributions, you need `libX11-devel libXcursor-devel libXrandr-devel
|
||||
libXinerama-devel mesa-libGL-devel libXi-devel` packages.
|
||||
- See [here](http://www.glfw.org/docs/latest/compile.html#compile_deps) for full details.
|
||||
|
||||
**The combination of Go 1.8, macOS and latest XCode seems to be problematic** as mentioned in issue
|
||||
[#7](https://github.com/faiface/pixel/issues/7). This issue is probably not related to Pixel.
|
||||
**Upgrading to Go 1.8.1 fixes the issue.**
|
||||
|
||||
## Contributing
|
||||
|
||||
Pixel is in, let's say, mid-stage of development. Many of the important features are here, some are
|
||||
missing. That's why **contributions are very important and welcome!** All alone, I will be able to
|
||||
finish the library, but it'll take a lot of time. With your help, it'll take much less. I encourage
|
||||
everyone to contribute, even with just an idea. Especially welcome are **issues** and **pull
|
||||
requests**.
|
||||
|
||||
**However, I won't accept everything. Pixel is being developed with thought and care.** Each
|
||||
component was designed and re-designed multiple times. Code and API quality is very important here.
|
||||
API is focused on simplicity and expressiveness.
|
||||
|
||||
When contributing, keep these goals in mind. It doesn't mean that I'll only accept perfect pull
|
||||
requests. It just means that I might not like your idea. Or that your pull requests could need some
|
||||
rewriting. That's perfectly fine, don't let it put you off. In the end, we'll just end up with a
|
||||
better result.
|
||||
|
||||
Take a look at [CONTRIBUTING.md](CONTRIBUTING.md) for further information.
|
||||
|
||||
For any kind of discussion, feel free to use our
|
||||
[Gitter](https://gitter.im/pixellib/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
community.
|
||||
|
||||
## License
|
||||
|
||||
[MIT](LICENSE)
|
163
vendor/github.com/faiface/pixel/batch.go
generated
vendored
Normal file
163
vendor/github.com/faiface/pixel/batch.go
generated
vendored
Normal file
@@ -0,0 +1,163 @@
|
||||
package pixel
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image/color"
|
||||
)
|
||||
|
||||
// Batch is a Target that allows for efficient drawing of many objects with the same Picture.
|
||||
//
|
||||
// To put an object into a Batch, just draw it onto it:
|
||||
// object.Draw(batch)
|
||||
type Batch struct {
|
||||
cont Drawer
|
||||
|
||||
mat Matrix
|
||||
col RGBA
|
||||
}
|
||||
|
||||
var _ BasicTarget = (*Batch)(nil)
|
||||
|
||||
// NewBatch creates an empty Batch with the specified Picture and container.
|
||||
//
|
||||
// The container is where objects get accumulated. Batch will support precisely those Triangles
|
||||
// properties, that the supplied container supports. If you retain access to the container and
|
||||
// change it, call Dirty to notify Batch about the change.
|
||||
//
|
||||
// Note, that if the container does not support TrianglesColor, color masking will not work.
|
||||
func NewBatch(container Triangles, pic Picture) *Batch {
|
||||
b := &Batch{cont: Drawer{Triangles: container, Picture: pic}}
|
||||
b.SetMatrix(IM)
|
||||
b.SetColorMask(Alpha(1))
|
||||
return b
|
||||
}
|
||||
|
||||
// Dirty notifies Batch about an external modification of it's container. If you retain access to
|
||||
// the Batch's container and change it, call Dirty to notify Batch about the change.
|
||||
//
|
||||
// container := &pixel.TrianglesData{}
|
||||
// batch := pixel.NewBatch(container, nil)
|
||||
// container.SetLen(10) // container changed from outside of Batch
|
||||
// batch.Dirty() // notify Batch about the change
|
||||
func (b *Batch) Dirty() {
|
||||
b.cont.Dirty()
|
||||
}
|
||||
|
||||
// Clear removes all objects from the Batch.
|
||||
func (b *Batch) Clear() {
|
||||
b.cont.Triangles.SetLen(0)
|
||||
b.cont.Dirty()
|
||||
}
|
||||
|
||||
// Draw draws all objects that are currently in the Batch onto another Target.
|
||||
func (b *Batch) Draw(t Target) {
|
||||
b.cont.Draw(t)
|
||||
}
|
||||
|
||||
// SetMatrix sets a Matrix that every point will be projected by.
|
||||
func (b *Batch) SetMatrix(m Matrix) {
|
||||
b.mat = m
|
||||
}
|
||||
|
||||
// SetColorMask sets a mask color used in the following draws onto the Batch.
|
||||
func (b *Batch) SetColorMask(c color.Color) {
|
||||
if c == nil {
|
||||
b.col = Alpha(1)
|
||||
return
|
||||
}
|
||||
b.col = ToRGBA(c)
|
||||
}
|
||||
|
||||
// MakeTriangles returns a specialized copy of the provided Triangles that draws onto this Batch.
|
||||
func (b *Batch) MakeTriangles(t Triangles) TargetTriangles {
|
||||
bt := &batchTriangles{
|
||||
tri: t.Copy(),
|
||||
tmp: MakeTrianglesData(t.Len()),
|
||||
dst: b,
|
||||
}
|
||||
return bt
|
||||
}
|
||||
|
||||
// MakePicture returns a specialized copy of the provided Picture that draws onto this Batch.
|
||||
func (b *Batch) MakePicture(p Picture) TargetPicture {
|
||||
if p != b.cont.Picture {
|
||||
panic(fmt.Errorf("(%T).MakePicture: Picture is not the Batch's Picture", b))
|
||||
}
|
||||
bp := &batchPicture{
|
||||
pic: p,
|
||||
dst: b,
|
||||
}
|
||||
return bp
|
||||
}
|
||||
|
||||
type batchTriangles struct {
|
||||
tri Triangles
|
||||
tmp *TrianglesData
|
||||
dst *Batch
|
||||
}
|
||||
|
||||
func (bt *batchTriangles) Len() int {
|
||||
return bt.tri.Len()
|
||||
}
|
||||
|
||||
func (bt *batchTriangles) SetLen(len int) {
|
||||
bt.tri.SetLen(len)
|
||||
bt.tmp.SetLen(len)
|
||||
}
|
||||
|
||||
func (bt *batchTriangles) Slice(i, j int) Triangles {
|
||||
return &batchTriangles{
|
||||
tri: bt.tri.Slice(i, j),
|
||||
tmp: bt.tmp.Slice(i, j).(*TrianglesData),
|
||||
dst: bt.dst,
|
||||
}
|
||||
}
|
||||
|
||||
func (bt *batchTriangles) Update(t Triangles) {
|
||||
bt.tri.Update(t)
|
||||
}
|
||||
|
||||
func (bt *batchTriangles) Copy() Triangles {
|
||||
return &batchTriangles{
|
||||
tri: bt.tri.Copy(),
|
||||
tmp: bt.tmp.Copy().(*TrianglesData),
|
||||
dst: bt.dst,
|
||||
}
|
||||
}
|
||||
|
||||
func (bt *batchTriangles) draw(bp *batchPicture) {
|
||||
bt.tmp.Update(bt.tri)
|
||||
|
||||
for i := range *bt.tmp {
|
||||
(*bt.tmp)[i].Position = bt.dst.mat.Project((*bt.tmp)[i].Position)
|
||||
(*bt.tmp)[i].Color = bt.dst.col.Mul((*bt.tmp)[i].Color)
|
||||
}
|
||||
|
||||
cont := bt.dst.cont.Triangles
|
||||
cont.SetLen(cont.Len() + bt.tri.Len())
|
||||
added := cont.Slice(cont.Len()-bt.tri.Len(), cont.Len())
|
||||
added.Update(bt.tri)
|
||||
added.Update(bt.tmp)
|
||||
bt.dst.cont.Dirty()
|
||||
}
|
||||
|
||||
func (bt *batchTriangles) Draw() {
|
||||
bt.draw(nil)
|
||||
}
|
||||
|
||||
type batchPicture struct {
|
||||
pic Picture
|
||||
dst *Batch
|
||||
}
|
||||
|
||||
func (bp *batchPicture) Bounds() Rect {
|
||||
return bp.pic.Bounds()
|
||||
}
|
||||
|
||||
func (bp *batchPicture) Draw(t TargetTriangles) {
|
||||
bt := t.(*batchTriangles)
|
||||
if bp.dst != bt.dst {
|
||||
panic(fmt.Errorf("(%T).Draw: TargetTriangles generated by different Batch", bp))
|
||||
}
|
||||
bt.draw(bp)
|
||||
}
|
97
vendor/github.com/faiface/pixel/color.go
generated
vendored
Normal file
97
vendor/github.com/faiface/pixel/color.go
generated
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
package pixel
|
||||
|
||||
import "image/color"
|
||||
|
||||
// RGBA represents an alpha-premultiplied RGBA color with components within range [0, 1].
|
||||
//
|
||||
// The difference between color.RGBA is that the value range is [0, 1] and the values are floats.
|
||||
type RGBA struct {
|
||||
R, G, B, A float64
|
||||
}
|
||||
|
||||
// RGB returns a fully opaque RGBA color with the given RGB values.
|
||||
//
|
||||
// A common way to construct a transparent color is to create one with RGB constructor, then
|
||||
// multiply it by a color obtained from the Alpha constructor.
|
||||
func RGB(r, g, b float64) RGBA {
|
||||
return RGBA{r, g, b, 1}
|
||||
}
|
||||
|
||||
// Alpha returns a white RGBA color with the given alpha component.
|
||||
func Alpha(a float64) RGBA {
|
||||
return RGBA{a, a, a, a}
|
||||
}
|
||||
|
||||
// Add adds color d to color c component-wise and returns the result (the components are not
|
||||
// clamped).
|
||||
func (c RGBA) Add(d RGBA) RGBA {
|
||||
return RGBA{
|
||||
R: c.R + d.R,
|
||||
G: c.G + d.G,
|
||||
B: c.B + d.B,
|
||||
A: c.A + d.A,
|
||||
}
|
||||
}
|
||||
|
||||
// Sub subtracts color d from color c component-wise and returns the result (the components
|
||||
// are not clamped).
|
||||
func (c RGBA) Sub(d RGBA) RGBA {
|
||||
return RGBA{
|
||||
R: c.R - d.R,
|
||||
G: c.G - d.G,
|
||||
B: c.B - d.B,
|
||||
A: c.A - d.A,
|
||||
}
|
||||
}
|
||||
|
||||
// Mul multiplies color c by color d component-wise (the components are not clamped).
|
||||
func (c RGBA) Mul(d RGBA) RGBA {
|
||||
return RGBA{
|
||||
R: c.R * d.R,
|
||||
G: c.G * d.G,
|
||||
B: c.B * d.B,
|
||||
A: c.A * d.A,
|
||||
}
|
||||
}
|
||||
|
||||
// Scaled multiplies each component of color c by scale and returns the result (the components
|
||||
// are not clamped).
|
||||
func (c RGBA) Scaled(scale float64) RGBA {
|
||||
return RGBA{
|
||||
R: c.R * scale,
|
||||
G: c.G * scale,
|
||||
B: c.B * scale,
|
||||
A: c.A * scale,
|
||||
}
|
||||
}
|
||||
|
||||
// RGBA returns alpha-premultiplied red, green, blue and alpha components of the RGBA color.
|
||||
func (c RGBA) RGBA() (r, g, b, a uint32) {
|
||||
r = uint32(0xffff * c.R)
|
||||
g = uint32(0xffff * c.G)
|
||||
b = uint32(0xffff * c.B)
|
||||
a = uint32(0xffff * c.A)
|
||||
return
|
||||
}
|
||||
|
||||
// ToRGBA converts a color to RGBA format. Using this function is preferred to using RGBAModel, for
|
||||
// performance (using RGBAModel introduces additional unnecessary allocations).
|
||||
func ToRGBA(c color.Color) RGBA {
|
||||
if c, ok := c.(RGBA); ok {
|
||||
return c
|
||||
}
|
||||
r, g, b, a := c.RGBA()
|
||||
return RGBA{
|
||||
float64(r) / 0xffff,
|
||||
float64(g) / 0xffff,
|
||||
float64(b) / 0xffff,
|
||||
float64(a) / 0xffff,
|
||||
}
|
||||
}
|
||||
|
||||
// RGBAModel converts colors to RGBA format.
|
||||
var RGBAModel = color.ModelFunc(rgbaModel)
|
||||
|
||||
func rgbaModel(c color.Color) color.Color {
|
||||
return ToRGBA(c)
|
||||
}
|
65
vendor/github.com/faiface/pixel/compose.go
generated
vendored
Normal file
65
vendor/github.com/faiface/pixel/compose.go
generated
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
package pixel
|
||||
|
||||
import "errors"
|
||||
|
||||
// ComposeTarget is a BasicTarget capable of Porter-Duff composition.
|
||||
type ComposeTarget interface {
|
||||
BasicTarget
|
||||
|
||||
// SetComposeMethod sets a Porter-Duff composition method to be used.
|
||||
SetComposeMethod(ComposeMethod)
|
||||
}
|
||||
|
||||
// ComposeMethod is a Porter-Duff composition method.
|
||||
type ComposeMethod int
|
||||
|
||||
// Here's the list of all available Porter-Duff composition methods. Use ComposeOver for the basic
|
||||
// alpha blending.
|
||||
const (
|
||||
ComposeOver ComposeMethod = iota
|
||||
ComposeIn
|
||||
ComposeOut
|
||||
ComposeAtop
|
||||
ComposeRover
|
||||
ComposeRin
|
||||
ComposeRout
|
||||
ComposeRatop
|
||||
ComposeXor
|
||||
ComposePlus
|
||||
ComposeCopy
|
||||
)
|
||||
|
||||
// Compose composes two colors together according to the ComposeMethod. A is the foreground, B is
|
||||
// the background.
|
||||
func (cm ComposeMethod) Compose(a, b RGBA) RGBA {
|
||||
var fa, fb float64
|
||||
|
||||
switch cm {
|
||||
case ComposeOver:
|
||||
fa, fb = 1, 1-a.A
|
||||
case ComposeIn:
|
||||
fa, fb = b.A, 0
|
||||
case ComposeOut:
|
||||
fa, fb = 1-b.A, 0
|
||||
case ComposeAtop:
|
||||
fa, fb = b.A, 1-a.A
|
||||
case ComposeRover:
|
||||
fa, fb = 1-b.A, 1
|
||||
case ComposeRin:
|
||||
fa, fb = 0, a.A
|
||||
case ComposeRout:
|
||||
fa, fb = 0, 1-a.A
|
||||
case ComposeRatop:
|
||||
fa, fb = 1-b.A, a.A
|
||||
case ComposeXor:
|
||||
fa, fb = 1-b.A, 1-a.A
|
||||
case ComposePlus:
|
||||
fa, fb = 1, 1
|
||||
case ComposeCopy:
|
||||
fa, fb = 1, 0
|
||||
default:
|
||||
panic(errors.New("Compose: invalid ComposeMethod"))
|
||||
}
|
||||
|
||||
return a.Mul(Alpha(fa)).Add(b.Mul(Alpha(fb)))
|
||||
}
|
276
vendor/github.com/faiface/pixel/data.go
generated
vendored
Normal file
276
vendor/github.com/faiface/pixel/data.go
generated
vendored
Normal file
@@ -0,0 +1,276 @@
|
||||
package pixel
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
"image/draw"
|
||||
"math"
|
||||
)
|
||||
|
||||
// TrianglesData specifies a list of Triangles vertices with three common properties:
|
||||
// TrianglesPosition, TrianglesColor and TrianglesPicture.
|
||||
type TrianglesData []struct {
|
||||
Position Vec
|
||||
Color RGBA
|
||||
Picture Vec
|
||||
Intensity float64
|
||||
}
|
||||
|
||||
// MakeTrianglesData creates TrianglesData of length len initialized with default property values.
|
||||
//
|
||||
// Prefer this function to make(TrianglesData, len), because make zeros them, while this function
|
||||
// does the correct intialization.
|
||||
func MakeTrianglesData(len int) *TrianglesData {
|
||||
td := &TrianglesData{}
|
||||
td.SetLen(len)
|
||||
return td
|
||||
}
|
||||
|
||||
// Len returns the number of vertices in TrianglesData.
|
||||
func (td *TrianglesData) Len() int {
|
||||
return len(*td)
|
||||
}
|
||||
|
||||
// SetLen resizes TrianglesData to len, while keeping the original content.
|
||||
//
|
||||
// If len is greater than TrianglesData's current length, the new data is filled with default
|
||||
// values ((0, 0), white, (0, 0), 0).
|
||||
func (td *TrianglesData) SetLen(len int) {
|
||||
if len > td.Len() {
|
||||
needAppend := len - td.Len()
|
||||
for i := 0; i < needAppend; i++ {
|
||||
*td = append(*td, struct {
|
||||
Position Vec
|
||||
Color RGBA
|
||||
Picture Vec
|
||||
Intensity float64
|
||||
}{Color: RGBA{1, 1, 1, 1}})
|
||||
}
|
||||
}
|
||||
if len < td.Len() {
|
||||
*td = (*td)[:len]
|
||||
}
|
||||
}
|
||||
|
||||
// Slice returns a sub-Triangles of this TrianglesData.
|
||||
func (td *TrianglesData) Slice(i, j int) Triangles {
|
||||
s := TrianglesData((*td)[i:j])
|
||||
return &s
|
||||
}
|
||||
|
||||
func (td *TrianglesData) updateData(t Triangles) {
|
||||
// fast path optimization
|
||||
if t, ok := t.(*TrianglesData); ok {
|
||||
copy(*td, *t)
|
||||
return
|
||||
}
|
||||
|
||||
// slow path manual copy
|
||||
if t, ok := t.(TrianglesPosition); ok {
|
||||
for i := range *td {
|
||||
(*td)[i].Position = t.Position(i)
|
||||
}
|
||||
}
|
||||
if t, ok := t.(TrianglesColor); ok {
|
||||
for i := range *td {
|
||||
(*td)[i].Color = t.Color(i)
|
||||
}
|
||||
}
|
||||
if t, ok := t.(TrianglesPicture); ok {
|
||||
for i := range *td {
|
||||
(*td)[i].Picture, (*td)[i].Intensity = t.Picture(i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update copies vertex properties from the supplied Triangles into this TrianglesData.
|
||||
//
|
||||
// TrianglesPosition, TrianglesColor and TrianglesTexture are supported.
|
||||
func (td *TrianglesData) Update(t Triangles) {
|
||||
if td.Len() != t.Len() {
|
||||
panic(fmt.Errorf("(%T).Update: invalid triangles length", td))
|
||||
}
|
||||
td.updateData(t)
|
||||
}
|
||||
|
||||
// Copy returns an exact independent copy of this TrianglesData.
|
||||
func (td *TrianglesData) Copy() Triangles {
|
||||
copyTd := TrianglesData{}
|
||||
copyTd.SetLen(td.Len())
|
||||
copyTd.Update(td)
|
||||
return ©Td
|
||||
}
|
||||
|
||||
// Position returns the position property of i-th vertex.
|
||||
func (td *TrianglesData) Position(i int) Vec {
|
||||
return (*td)[i].Position
|
||||
}
|
||||
|
||||
// Color returns the color property of i-th vertex.
|
||||
func (td *TrianglesData) Color(i int) RGBA {
|
||||
return (*td)[i].Color
|
||||
}
|
||||
|
||||
// Picture returns the picture property of i-th vertex.
|
||||
func (td *TrianglesData) Picture(i int) (pic Vec, intensity float64) {
|
||||
return (*td)[i].Picture, (*td)[i].Intensity
|
||||
}
|
||||
|
||||
// PictureData specifies an in-memory rectangular area of pixels and implements Picture and
|
||||
// PictureColor.
|
||||
//
|
||||
// Pixels are small rectangles of unit size of form (x, y, x+1, y+1), where x and y are integers.
|
||||
// PictureData contains and assigns a color to all pixels that are at least partially contained
|
||||
// within it's Bounds (Rect).
|
||||
//
|
||||
// The struct's innards are exposed for convenience, manual modification is at your own risk.
|
||||
//
|
||||
// The format of the pixels is color.RGBA and not pixel.RGBA for a very serious reason:
|
||||
// pixel.RGBA takes up 8x more memory than color.RGBA.
|
||||
type PictureData struct {
|
||||
Pix []color.RGBA
|
||||
Stride int
|
||||
Rect Rect
|
||||
}
|
||||
|
||||
// MakePictureData creates a zero-initialized PictureData covering the given rectangle.
|
||||
func MakePictureData(rect Rect) *PictureData {
|
||||
w := int(math.Ceil(rect.Max.X)) - int(math.Floor(rect.Min.X))
|
||||
h := int(math.Ceil(rect.Max.Y)) - int(math.Floor(rect.Min.Y))
|
||||
pd := &PictureData{
|
||||
Stride: w,
|
||||
Rect: rect,
|
||||
}
|
||||
pd.Pix = make([]color.RGBA, w*h)
|
||||
return pd
|
||||
}
|
||||
|
||||
func verticalFlip(rgba *image.RGBA) {
|
||||
bounds := rgba.Bounds()
|
||||
width := bounds.Dx()
|
||||
|
||||
tmpRow := make([]uint8, width*4)
|
||||
for i, j := 0, bounds.Dy()-1; i < j; i, j = i+1, j-1 {
|
||||
iRow := rgba.Pix[i*rgba.Stride : i*rgba.Stride+width*4]
|
||||
jRow := rgba.Pix[j*rgba.Stride : j*rgba.Stride+width*4]
|
||||
|
||||
copy(tmpRow, iRow)
|
||||
copy(iRow, jRow)
|
||||
copy(jRow, tmpRow)
|
||||
}
|
||||
}
|
||||
|
||||
// PictureDataFromImage converts an image.Image into PictureData.
|
||||
//
|
||||
// The resulting PictureData's Bounds will be the equivalent of the supplied image.Image's Bounds.
|
||||
func PictureDataFromImage(img image.Image) *PictureData {
|
||||
var rgba *image.RGBA
|
||||
if rgbaImg, ok := img.(*image.RGBA); ok {
|
||||
rgba = rgbaImg
|
||||
} else {
|
||||
rgba = image.NewRGBA(img.Bounds())
|
||||
draw.Draw(rgba, rgba.Bounds(), img, img.Bounds().Min, draw.Src)
|
||||
}
|
||||
|
||||
verticalFlip(rgba)
|
||||
|
||||
pd := MakePictureData(R(
|
||||
float64(rgba.Bounds().Min.X),
|
||||
float64(rgba.Bounds().Min.Y),
|
||||
float64(rgba.Bounds().Max.X),
|
||||
float64(rgba.Bounds().Max.Y),
|
||||
))
|
||||
|
||||
for i := range pd.Pix {
|
||||
pd.Pix[i].R = rgba.Pix[i*4+0]
|
||||
pd.Pix[i].G = rgba.Pix[i*4+1]
|
||||
pd.Pix[i].B = rgba.Pix[i*4+2]
|
||||
pd.Pix[i].A = rgba.Pix[i*4+3]
|
||||
}
|
||||
|
||||
return pd
|
||||
}
|
||||
|
||||
// PictureDataFromPicture converts an arbitrary Picture into PictureData (the conversion may be
|
||||
// lossy, because PictureData works with unit-sized pixels).
|
||||
//
|
||||
// Bounds are preserved.
|
||||
func PictureDataFromPicture(pic Picture) *PictureData {
|
||||
if pd, ok := pic.(*PictureData); ok {
|
||||
return pd
|
||||
}
|
||||
|
||||
bounds := pic.Bounds()
|
||||
pd := MakePictureData(bounds)
|
||||
|
||||
if pic, ok := pic.(PictureColor); ok {
|
||||
for y := math.Floor(bounds.Min.Y); y < bounds.Max.Y; y++ {
|
||||
for x := math.Floor(bounds.Min.X); x < bounds.Max.X; x++ {
|
||||
// this together with the Floor is a trick to get all of the pixels
|
||||
at := V(
|
||||
math.Max(x, bounds.Min.X),
|
||||
math.Max(y, bounds.Min.Y),
|
||||
)
|
||||
col := pic.Color(at)
|
||||
pd.Pix[pd.Index(at)] = color.RGBA{
|
||||
R: uint8(col.R * 255),
|
||||
G: uint8(col.G * 255),
|
||||
B: uint8(col.B * 255),
|
||||
A: uint8(col.A * 255),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pd
|
||||
}
|
||||
|
||||
// Image converts PictureData into an image.RGBA.
|
||||
//
|
||||
// The resulting image.RGBA's Bounds will be equivalent of the PictureData's Bounds.
|
||||
func (pd *PictureData) Image() *image.RGBA {
|
||||
bounds := image.Rect(
|
||||
int(math.Floor(pd.Rect.Min.X)),
|
||||
int(math.Floor(pd.Rect.Min.Y)),
|
||||
int(math.Ceil(pd.Rect.Max.X)),
|
||||
int(math.Ceil(pd.Rect.Max.Y)),
|
||||
)
|
||||
rgba := image.NewRGBA(bounds)
|
||||
|
||||
i := 0
|
||||
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
|
||||
for x := bounds.Min.X; x < bounds.Max.X; x++ {
|
||||
off := pd.Index(V(float64(x), float64(y)))
|
||||
rgba.Pix[i*4+0] = pd.Pix[off].R
|
||||
rgba.Pix[i*4+1] = pd.Pix[off].G
|
||||
rgba.Pix[i*4+2] = pd.Pix[off].B
|
||||
rgba.Pix[i*4+3] = pd.Pix[off].A
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
verticalFlip(rgba)
|
||||
|
||||
return rgba
|
||||
}
|
||||
|
||||
// Index returns the index of the pixel at the specified position inside the Pix slice.
|
||||
func (pd *PictureData) Index(at Vec) int {
|
||||
at = at.Sub(pd.Rect.Min.Map(math.Floor))
|
||||
x, y := int(at.X), int(at.Y)
|
||||
return y*pd.Stride + x
|
||||
}
|
||||
|
||||
// Bounds returns the bounds of this PictureData.
|
||||
func (pd *PictureData) Bounds() Rect {
|
||||
return pd.Rect
|
||||
}
|
||||
|
||||
// Color returns the color located at the given position.
|
||||
func (pd *PictureData) Color(at Vec) RGBA {
|
||||
if !pd.Rect.Contains(at) {
|
||||
return RGBA{0, 0, 0, 0}
|
||||
}
|
||||
return ToRGBA(pd.Pix[pd.Index(at)])
|
||||
}
|
7
vendor/github.com/faiface/pixel/doc.go
generated
vendored
Normal file
7
vendor/github.com/faiface/pixel/doc.go
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
// Package pixel implements platform and backend agnostic core of the Pixel game development
|
||||
// library.
|
||||
//
|
||||
// It specifies the core Target, Triangles, Picture pattern and implements standard elements, such
|
||||
// as Sprite, Batch, Vec, Matrix and RGBA in addition to the basic Triangles and Picture
|
||||
// implementations: TrianglesData and PictureData.
|
||||
package pixel
|
96
vendor/github.com/faiface/pixel/drawer.go
generated
vendored
Normal file
96
vendor/github.com/faiface/pixel/drawer.go
generated
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
package pixel
|
||||
|
||||
// Drawer glues all the fundamental interfaces (Target, Triangles, Picture) into a coherent and the
|
||||
// only intended usage pattern.
|
||||
//
|
||||
// Drawer makes it possible to draw any combination of Triangles and Picture onto any Target
|
||||
// efficiently.
|
||||
//
|
||||
// To create a Drawer, just assign it's Triangles and Picture fields:
|
||||
//
|
||||
// d := pixel.Drawer{Triangles: t, Picture: p}
|
||||
//
|
||||
// If Triangles is nil, nothing will be drawn. If Picture is nil, Triangles will be drawn without a
|
||||
// Picture.
|
||||
//
|
||||
// Whenever you change the Triangles, call Dirty to notify Drawer that Triangles changed. You don't
|
||||
// need to notify Drawer about a change of the Picture.
|
||||
//
|
||||
// Note, that Drawer caches the results of MakePicture from Targets it's drawn to for each Picture
|
||||
// it's set to. What it means is that using a Drawer with an unbounded number of Pictures leads to a
|
||||
// memory leak, since Drawer caches them and never forgets. In such a situation, create a new Drawer
|
||||
// for each Picture.
|
||||
type Drawer struct {
|
||||
Triangles Triangles
|
||||
Picture Picture
|
||||
|
||||
targets map[Target]*drawerTarget
|
||||
inited bool
|
||||
}
|
||||
|
||||
type drawerTarget struct {
|
||||
tris TargetTriangles
|
||||
pics map[Picture]TargetPicture
|
||||
clean bool
|
||||
}
|
||||
|
||||
func (d *Drawer) lazyInit() {
|
||||
if !d.inited {
|
||||
d.targets = make(map[Target]*drawerTarget)
|
||||
d.inited = true
|
||||
}
|
||||
}
|
||||
|
||||
// Dirty marks the Triangles of this Drawer as changed. If not called, changes will not be visible
|
||||
// when drawing.
|
||||
func (d *Drawer) Dirty() {
|
||||
d.lazyInit()
|
||||
|
||||
for _, t := range d.targets {
|
||||
t.clean = false
|
||||
}
|
||||
}
|
||||
|
||||
// Draw efficiently draws Triangles with Picture onto the provided Target.
|
||||
//
|
||||
// If Triangles is nil, nothing will be drawn. If Picture is nil, Triangles will be drawn without a
|
||||
// Picture.
|
||||
func (d *Drawer) Draw(t Target) {
|
||||
d.lazyInit()
|
||||
|
||||
if d.Triangles == nil {
|
||||
return
|
||||
}
|
||||
|
||||
dt := d.targets[t]
|
||||
if dt == nil {
|
||||
dt = &drawerTarget{
|
||||
pics: make(map[Picture]TargetPicture),
|
||||
}
|
||||
d.targets[t] = dt
|
||||
}
|
||||
|
||||
if dt.tris == nil {
|
||||
dt.tris = t.MakeTriangles(d.Triangles)
|
||||
dt.clean = true
|
||||
}
|
||||
|
||||
if !dt.clean {
|
||||
dt.tris.SetLen(d.Triangles.Len())
|
||||
dt.tris.Update(d.Triangles)
|
||||
dt.clean = true
|
||||
}
|
||||
|
||||
if d.Picture == nil {
|
||||
dt.tris.Draw()
|
||||
return
|
||||
}
|
||||
|
||||
pic := dt.pics[d.Picture]
|
||||
if pic == nil {
|
||||
pic = t.MakePicture(d.Picture)
|
||||
dt.pics[d.Picture] = pic
|
||||
}
|
||||
|
||||
pic.Draw(dt.tris)
|
||||
}
|
21
vendor/github.com/faiface/pixel/examples/community/maze/LICENSE
generated
vendored
Normal file
21
vendor/github.com/faiface/pixel/examples/community/maze/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 stephen
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
405
vendor/github.com/faiface/pixel/geometry.go
generated
vendored
Normal file
405
vendor/github.com/faiface/pixel/geometry.go
generated
vendored
Normal file
@@ -0,0 +1,405 @@
|
||||
package pixel
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
)
|
||||
|
||||
// Clamp returns x clamped to the interval [min, max].
|
||||
//
|
||||
// If x is less than min, min is returned. If x is more than max, max is returned. Otherwise, x is
|
||||
// returned.
|
||||
func Clamp(x, min, max float64) float64 {
|
||||
if x < min {
|
||||
return min
|
||||
}
|
||||
if x > max {
|
||||
return max
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// Vec is a 2D vector type with X and Y coordinates.
|
||||
//
|
||||
// Create vectors with the V constructor:
|
||||
//
|
||||
// u := pixel.V(1, 2)
|
||||
// v := pixel.V(8, -3)
|
||||
//
|
||||
// Use various methods to manipulate them:
|
||||
//
|
||||
// w := u.Add(v)
|
||||
// fmt.Println(w) // Vec(9, -1)
|
||||
// fmt.Println(u.Sub(v)) // Vec(-7, 5)
|
||||
// u = pixel.V(2, 3)
|
||||
// v = pixel.V(8, 1)
|
||||
// if u.X < 0 {
|
||||
// fmt.Println("this won't happen")
|
||||
// }
|
||||
// x := u.Unit().Dot(v.Unit())
|
||||
type Vec struct {
|
||||
X, Y float64
|
||||
}
|
||||
|
||||
// ZV is a zero vector.
|
||||
var ZV = Vec{0, 0}
|
||||
|
||||
// V returns a new 2D vector with the given coordinates.
|
||||
func V(x, y float64) Vec {
|
||||
return Vec{x, y}
|
||||
}
|
||||
|
||||
// Unit returns a vector of length 1 facing the given angle.
|
||||
func Unit(angle float64) Vec {
|
||||
return Vec{1, 0}.Rotated(angle)
|
||||
}
|
||||
|
||||
// String returns the string representation of the vector u.
|
||||
//
|
||||
// u := pixel.V(4.5, -1.3)
|
||||
// u.String() // returns "Vec(4.5, -1.3)"
|
||||
// fmt.Println(u) // Vec(4.5, -1.3)
|
||||
func (u Vec) String() string {
|
||||
return fmt.Sprintf("Vec(%v, %v)", u.X, u.Y)
|
||||
}
|
||||
|
||||
// XY returns the components of the vector in two return values.
|
||||
func (u Vec) XY() (x, y float64) {
|
||||
return u.X, u.Y
|
||||
}
|
||||
|
||||
// Add returns the sum of vectors u and v.
|
||||
func (u Vec) Add(v Vec) Vec {
|
||||
return Vec{
|
||||
u.X + v.X,
|
||||
u.Y + v.Y,
|
||||
}
|
||||
}
|
||||
|
||||
// Sub returns the difference betweeen vectors u and v.
|
||||
func (u Vec) Sub(v Vec) Vec {
|
||||
return Vec{
|
||||
u.X - v.X,
|
||||
u.Y - v.Y,
|
||||
}
|
||||
}
|
||||
|
||||
// To returns the vector from u to v. Equivalent to v.Sub(u).
|
||||
func (u Vec) To(v Vec) Vec {
|
||||
return Vec{
|
||||
v.X - u.X,
|
||||
v.Y - u.Y,
|
||||
}
|
||||
}
|
||||
|
||||
// Scaled returns the vector u multiplied by c.
|
||||
func (u Vec) Scaled(c float64) Vec {
|
||||
return Vec{u.X * c, u.Y * c}
|
||||
}
|
||||
|
||||
// ScaledXY returns the vector u multiplied by the vector v component-wise.
|
||||
func (u Vec) ScaledXY(v Vec) Vec {
|
||||
return Vec{u.X * v.X, u.Y * v.Y}
|
||||
}
|
||||
|
||||
// Len returns the length of the vector u.
|
||||
func (u Vec) Len() float64 {
|
||||
return math.Hypot(u.X, u.Y)
|
||||
}
|
||||
|
||||
// Angle returns the angle between the vector u and the x-axis. The result is in range [-Pi, Pi].
|
||||
func (u Vec) Angle() float64 {
|
||||
return math.Atan2(u.Y, u.X)
|
||||
}
|
||||
|
||||
// Unit returns a vector of length 1 facing the direction of u (has the same angle).
|
||||
func (u Vec) Unit() Vec {
|
||||
if u.X == 0 && u.Y == 0 {
|
||||
return Vec{1, 0}
|
||||
}
|
||||
return u.Scaled(1 / u.Len())
|
||||
}
|
||||
|
||||
// Rotated returns the vector u rotated by the given angle in radians.
|
||||
func (u Vec) Rotated(angle float64) Vec {
|
||||
sin, cos := math.Sincos(angle)
|
||||
return Vec{
|
||||
u.X*cos - u.Y*sin,
|
||||
u.X*sin + u.Y*cos,
|
||||
}
|
||||
}
|
||||
|
||||
// Normal returns a vector normal to u. Equivalent to u.Rotated(math.Pi / 2), but faster.
|
||||
func (u Vec) Normal() Vec {
|
||||
return Vec{-u.Y, u.X}
|
||||
}
|
||||
|
||||
// Dot returns the dot product of vectors u and v.
|
||||
func (u Vec) Dot(v Vec) float64 {
|
||||
return u.X*v.X + u.Y*v.Y
|
||||
}
|
||||
|
||||
// Cross return the cross product of vectors u and v.
|
||||
func (u Vec) Cross(v Vec) float64 {
|
||||
return u.X*v.Y - v.X*u.Y
|
||||
}
|
||||
|
||||
// Project returns a projection (or component) of vector u in the direction of vector v.
|
||||
//
|
||||
// Behaviour is undefined if v is a zero vector.
|
||||
func (u Vec) Project(v Vec) Vec {
|
||||
len := u.Dot(v) / v.Len()
|
||||
return v.Unit().Scaled(len)
|
||||
}
|
||||
|
||||
// Map applies the function f to both x and y components of the vector u and returns the modified
|
||||
// vector.
|
||||
//
|
||||
// u := pixel.V(10.5, -1.5)
|
||||
// v := u.Map(math.Floor) // v is Vec(10, -2), both components of u floored
|
||||
func (u Vec) Map(f func(float64) float64) Vec {
|
||||
return Vec{
|
||||
f(u.X),
|
||||
f(u.Y),
|
||||
}
|
||||
}
|
||||
|
||||
// Lerp returns a linear interpolation between vectors a and b.
|
||||
//
|
||||
// This function basically returns a point along the line between a and b and t chooses which one.
|
||||
// If t is 0, then a will be returned, if t is 1, b will be returned. Anything between 0 and 1 will
|
||||
// return the appropriate point between a and b and so on.
|
||||
func Lerp(a, b Vec, t float64) Vec {
|
||||
return a.Scaled(1 - t).Add(b.Scaled(t))
|
||||
}
|
||||
|
||||
// Rect is a 2D rectangle aligned with the axes of the coordinate system. It is defined by two
|
||||
// points, Min and Max.
|
||||
//
|
||||
// The invariant should hold, that Max's components are greater or equal than Min's components
|
||||
// respectively.
|
||||
type Rect struct {
|
||||
Min, Max Vec
|
||||
}
|
||||
|
||||
// R returns a new Rect with given the Min and Max coordinates.
|
||||
//
|
||||
// Note that the returned rectangle is not automatically normalized.
|
||||
func R(minX, minY, maxX, maxY float64) Rect {
|
||||
return Rect{
|
||||
Min: Vec{minX, minY},
|
||||
Max: Vec{maxX, maxY},
|
||||
}
|
||||
}
|
||||
|
||||
// String returns the string representation of the Rect.
|
||||
//
|
||||
// r := pixel.R(100, 50, 200, 300)
|
||||
// r.String() // returns "Rect(100, 50, 200, 300)"
|
||||
// fmt.Println(r) // Rect(100, 50, 200, 300)
|
||||
func (r Rect) String() string {
|
||||
return fmt.Sprintf("Rect(%v, %v, %v, %v)", r.Min.X, r.Min.Y, r.Max.X, r.Max.Y)
|
||||
}
|
||||
|
||||
// Norm returns the Rect in normal form, such that Max is component-wise greater or equal than Min.
|
||||
func (r Rect) Norm() Rect {
|
||||
return Rect{
|
||||
Min: Vec{
|
||||
math.Min(r.Min.X, r.Max.X),
|
||||
math.Min(r.Min.Y, r.Max.Y),
|
||||
},
|
||||
Max: Vec{
|
||||
math.Max(r.Min.X, r.Max.X),
|
||||
math.Max(r.Min.Y, r.Max.Y),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// W returns the width of the Rect.
|
||||
func (r Rect) W() float64 {
|
||||
return r.Max.X - r.Min.X
|
||||
}
|
||||
|
||||
// H returns the height of the Rect.
|
||||
func (r Rect) H() float64 {
|
||||
return r.Max.Y - r.Min.Y
|
||||
}
|
||||
|
||||
// Size returns the vector of width and height of the Rect.
|
||||
func (r Rect) Size() Vec {
|
||||
return V(r.W(), r.H())
|
||||
}
|
||||
|
||||
// Area returns the area of r. If r is not normalized, area may be negative.
|
||||
func (r Rect) Area() float64 {
|
||||
return r.W() * r.H()
|
||||
}
|
||||
|
||||
// Center returns the position of the center of the Rect.
|
||||
func (r Rect) Center() Vec {
|
||||
return Lerp(r.Min, r.Max, 0.5)
|
||||
}
|
||||
|
||||
// Moved returns the Rect moved (both Min and Max) by the given vector delta.
|
||||
func (r Rect) Moved(delta Vec) Rect {
|
||||
return Rect{
|
||||
Min: r.Min.Add(delta),
|
||||
Max: r.Max.Add(delta),
|
||||
}
|
||||
}
|
||||
|
||||
// Resized returns the Rect resized to the given size while keeping the position of the given
|
||||
// anchor.
|
||||
//
|
||||
// r.Resized(r.Min, size) // resizes while keeping the position of the lower-left corner
|
||||
// r.Resized(r.Max, size) // same with the top-right corner
|
||||
// r.Resized(r.Center(), size) // resizes around the center
|
||||
//
|
||||
// This function does not make sense for resizing a rectangle of zero area and will panic. Use
|
||||
// ResizedMin in the case of zero area.
|
||||
func (r Rect) Resized(anchor, size Vec) Rect {
|
||||
if r.W()*r.H() == 0 {
|
||||
panic(fmt.Errorf("(%T).Resize: zero area", r))
|
||||
}
|
||||
fraction := Vec{size.X / r.W(), size.Y / r.H()}
|
||||
return Rect{
|
||||
Min: anchor.Add(r.Min.Sub(anchor).ScaledXY(fraction)),
|
||||
Max: anchor.Add(r.Max.Sub(anchor).ScaledXY(fraction)),
|
||||
}
|
||||
}
|
||||
|
||||
// ResizedMin returns the Rect resized to the given size while keeping the position of the Rect's
|
||||
// Min.
|
||||
//
|
||||
// Sizes of zero area are safe here.
|
||||
func (r Rect) ResizedMin(size Vec) Rect {
|
||||
return Rect{
|
||||
Min: r.Min,
|
||||
Max: r.Min.Add(size),
|
||||
}
|
||||
}
|
||||
|
||||
// Contains checks whether a vector u is contained within this Rect (including it's borders).
|
||||
func (r Rect) Contains(u Vec) bool {
|
||||
return r.Min.X <= u.X && u.X <= r.Max.X && r.Min.Y <= u.Y && u.Y <= r.Max.Y
|
||||
}
|
||||
|
||||
// Union returns the minimal Rect which covers both r and s. Rects r and s must be normalized.
|
||||
func (r Rect) Union(s Rect) Rect {
|
||||
return R(
|
||||
math.Min(r.Min.X, s.Min.X),
|
||||
math.Min(r.Min.Y, s.Min.Y),
|
||||
math.Max(r.Max.X, s.Max.X),
|
||||
math.Max(r.Max.Y, s.Max.Y),
|
||||
)
|
||||
}
|
||||
|
||||
// Intersect returns the maximal Rect which is covered by both r and s. Rects r and s must be normalized.
|
||||
//
|
||||
// If r and s don't overlap, this function returns R(0, 0, 0, 0).
|
||||
func (r Rect) Intersect(s Rect) Rect {
|
||||
t := R(
|
||||
math.Max(r.Min.X, s.Min.X),
|
||||
math.Max(r.Min.Y, s.Min.Y),
|
||||
math.Min(r.Max.X, s.Max.X),
|
||||
math.Min(r.Max.Y, s.Max.Y),
|
||||
)
|
||||
if t.Min.X >= t.Max.X || t.Min.Y >= t.Max.Y {
|
||||
return Rect{}
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// Matrix is a 3x2 affine matrix that can be used for all kinds of spatial transforms, such
|
||||
// as movement, scaling and rotations.
|
||||
//
|
||||
// Matrix has a handful of useful methods, each of which adds a transformation to the matrix. For
|
||||
// example:
|
||||
//
|
||||
// pixel.IM.Moved(pixel.V(100, 200)).Rotated(pixel.ZV, math.Pi/2)
|
||||
//
|
||||
// This code creates a Matrix that first moves everything by 100 units horizontally and 200 units
|
||||
// vertically and then rotates everything by 90 degrees around the origin.
|
||||
//
|
||||
// Layout is:
|
||||
// [0] [2] [4]
|
||||
// [1] [3] [5]
|
||||
// 0 0 1 (implicit row)
|
||||
type Matrix [6]float64
|
||||
|
||||
// IM stands for identity matrix. Does nothing, no transformation.
|
||||
var IM = Matrix{1, 0, 0, 1, 0, 0}
|
||||
|
||||
// String returns a string representation of the Matrix.
|
||||
//
|
||||
// m := pixel.IM
|
||||
// fmt.Println(m) // Matrix(1 0 0 | 0 1 0)
|
||||
func (m Matrix) String() string {
|
||||
return fmt.Sprintf(
|
||||
"Matrix(%v %v %v | %v %v %v)",
|
||||
m[0], m[2], m[4],
|
||||
m[1], m[3], m[5],
|
||||
)
|
||||
}
|
||||
|
||||
// Moved moves everything by the delta vector.
|
||||
func (m Matrix) Moved(delta Vec) Matrix {
|
||||
m[4], m[5] = m[4]+delta.X, m[5]+delta.Y
|
||||
return m
|
||||
}
|
||||
|
||||
// ScaledXY scales everything around a given point by the scale factor in each axis respectively.
|
||||
func (m Matrix) ScaledXY(around Vec, scale Vec) Matrix {
|
||||
m[4], m[5] = m[4]-around.X, m[5]-around.Y
|
||||
m[0], m[2], m[4] = m[0]*scale.X, m[2]*scale.X, m[4]*scale.X
|
||||
m[1], m[3], m[5] = m[1]*scale.Y, m[3]*scale.Y, m[5]*scale.Y
|
||||
m[4], m[5] = m[4]+around.X, m[5]+around.Y
|
||||
return m
|
||||
}
|
||||
|
||||
// Scaled scales everything around a given point by the scale factor.
|
||||
func (m Matrix) Scaled(around Vec, scale float64) Matrix {
|
||||
return m.ScaledXY(around, V(scale, scale))
|
||||
}
|
||||
|
||||
// Rotated rotates everything around a given point by the given angle in radians.
|
||||
func (m Matrix) Rotated(around Vec, angle float64) Matrix {
|
||||
sint, cost := math.Sincos(angle)
|
||||
m[4], m[5] = m[4]-around.X, m[5]-around.Y
|
||||
m = m.Chained(Matrix{cost, sint, -sint, cost, 0, 0})
|
||||
m[4], m[5] = m[4]+around.X, m[5]+around.Y
|
||||
return m
|
||||
}
|
||||
|
||||
// Chained adds another Matrix to this one. All tranformations by the next Matrix will be applied
|
||||
// after the transformations of this Matrix.
|
||||
func (m Matrix) Chained(next Matrix) Matrix {
|
||||
return Matrix{
|
||||
next[0]*m[0] + next[2]*m[1],
|
||||
next[1]*m[0] + next[3]*m[1],
|
||||
next[0]*m[2] + next[2]*m[3],
|
||||
next[1]*m[2] + next[3]*m[3],
|
||||
next[0]*m[4] + next[2]*m[5] + next[4],
|
||||
next[1]*m[4] + next[3]*m[5] + next[5],
|
||||
}
|
||||
}
|
||||
|
||||
// Project applies all transformations added to the Matrix to a vector u and returns the result.
|
||||
//
|
||||
// Time complexity is O(1).
|
||||
func (m Matrix) Project(u Vec) Vec {
|
||||
return Vec{m[0]*u.X + m[2]*u.Y + m[4], m[1]*u.X + m[3]*u.Y + m[5]}
|
||||
}
|
||||
|
||||
// Unproject does the inverse operation to Project.
|
||||
//
|
||||
// It turns out that multiplying a vector by the inverse matrix of m can be nearly-accomplished by
|
||||
// subtracting the translate part of the matrix and multplying by the inverse of the top-left 2x2
|
||||
// matrix, and the inverse of a 2x2 matrix is simple enough to just be inlined in the computation.
|
||||
//
|
||||
// Time complexity is O(1).
|
||||
func (m Matrix) Unproject(u Vec) Vec {
|
||||
d := (m[0] * m[3]) - (m[1] * m[2])
|
||||
u.X, u.Y = (u.X-m[4])/d, (u.Y-m[5])/d
|
||||
return Vec{u.X*m[3] - u.Y*m[1], u.Y*m[0] - u.X*m[2]}
|
||||
}
|
627
vendor/github.com/faiface/pixel/imdraw/imdraw.go
generated
vendored
Normal file
627
vendor/github.com/faiface/pixel/imdraw/imdraw.go
generated
vendored
Normal file
@@ -0,0 +1,627 @@
|
||||
// Package imdraw implements a basic primitive geometry shape and pictured polygon drawing for Pixel
|
||||
// with a nice immediate-mode-like API.
|
||||
package imdraw
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"math"
|
||||
|
||||
"github.com/faiface/pixel"
|
||||
)
|
||||
|
||||
// IMDraw is an immediate-mode-like shape drawer and BasicTarget. IMDraw supports TrianglesPosition,
|
||||
// TrianglesColor, TrianglesPicture and PictureColor.
|
||||
//
|
||||
// IMDraw, other than a regular BasicTarget, is used to draw shapes. To draw shapes, you first need
|
||||
// to Push some points to IMDraw:
|
||||
//
|
||||
// imd := pixel.NewIMDraw(pic) // use nil pic if you only want to draw primitive shapes
|
||||
// imd.Push(pixel.V(100, 100))
|
||||
// imd.Push(pixel.V(500, 100))
|
||||
//
|
||||
// Once you have Pushed some points, you can use them to draw a shape, such as a line:
|
||||
//
|
||||
// imd.Line(20) // draws a 20 units thick line
|
||||
//
|
||||
// Set exported fields to change properties of Pushed points:
|
||||
//
|
||||
// imd.Color = pixel.RGB(1, 0, 0)
|
||||
// imd.Push(pixel.V(200, 200))
|
||||
// imd.Circle(400, 0)
|
||||
//
|
||||
// Here is the list of all available point properties (need to be set before Pushing a point):
|
||||
// - Color - applies to all
|
||||
// - Picture - coordinates, only applies to filled polygons
|
||||
// - Intensity - picture intensity, only applies to filled polygons
|
||||
// - Precision - curve drawing precision, only applies to circles and ellipses
|
||||
// - EndShape - shape of the end of a line, only applies to lines and outlines
|
||||
//
|
||||
// And here's the list of all shapes that can be drawn (all, except for line, can be filled or
|
||||
// outlined):
|
||||
// - Line
|
||||
// - Polygon
|
||||
// - Circle
|
||||
// - Circle arc
|
||||
// - Ellipse
|
||||
// - Ellipse arc
|
||||
type IMDraw struct {
|
||||
Color color.Color
|
||||
Picture pixel.Vec
|
||||
Intensity float64
|
||||
Precision int
|
||||
EndShape EndShape
|
||||
|
||||
points []point
|
||||
pool [][]point
|
||||
matrix pixel.Matrix
|
||||
mask pixel.RGBA
|
||||
|
||||
tri *pixel.TrianglesData
|
||||
batch *pixel.Batch
|
||||
}
|
||||
|
||||
var _ pixel.BasicTarget = (*IMDraw)(nil)
|
||||
|
||||
type point struct {
|
||||
pos pixel.Vec
|
||||
col pixel.RGBA
|
||||
pic pixel.Vec
|
||||
in float64
|
||||
precision int
|
||||
endshape EndShape
|
||||
}
|
||||
|
||||
// EndShape specifies the shape of an end of a line or a curve.
|
||||
type EndShape int
|
||||
|
||||
const (
|
||||
// NoEndShape leaves a line point with no special end shape.
|
||||
NoEndShape EndShape = iota
|
||||
|
||||
// SharpEndShape is a sharp triangular end shape.
|
||||
SharpEndShape
|
||||
|
||||
// RoundEndShape is a circular end shape.
|
||||
RoundEndShape
|
||||
)
|
||||
|
||||
// New creates a new empty IMDraw. An optional Picture can be used to draw with a Picture.
|
||||
//
|
||||
// If you just want to draw primitive shapes, pass nil as the Picture.
|
||||
func New(pic pixel.Picture) *IMDraw {
|
||||
tri := &pixel.TrianglesData{}
|
||||
im := &IMDraw{
|
||||
tri: tri,
|
||||
batch: pixel.NewBatch(tri, pic),
|
||||
}
|
||||
im.SetMatrix(pixel.IM)
|
||||
im.SetColorMask(pixel.Alpha(1))
|
||||
im.Reset()
|
||||
return im
|
||||
}
|
||||
|
||||
// Clear removes all drawn shapes from the IM. This does not remove Pushed points.
|
||||
func (imd *IMDraw) Clear() {
|
||||
imd.tri.SetLen(0)
|
||||
imd.batch.Dirty()
|
||||
}
|
||||
|
||||
// Reset restores all point properties to defaults and removes all Pushed points.
|
||||
//
|
||||
// This does not affect matrix and color mask set by SetMatrix and SetColorMask.
|
||||
func (imd *IMDraw) Reset() {
|
||||
imd.points = imd.points[:0]
|
||||
imd.Color = pixel.Alpha(1)
|
||||
imd.Picture = pixel.ZV
|
||||
imd.Intensity = 0
|
||||
imd.Precision = 64
|
||||
imd.EndShape = NoEndShape
|
||||
}
|
||||
|
||||
// Draw draws all currently drawn shapes inside the IM onto another Target.
|
||||
//
|
||||
// Note, that IMDraw's matrix and color mask have no effect here.
|
||||
func (imd *IMDraw) Draw(t pixel.Target) {
|
||||
imd.batch.Draw(t)
|
||||
}
|
||||
|
||||
// Push adds some points to the IM queue. All Pushed points will have the same properties except for
|
||||
// the position.
|
||||
func (imd *IMDraw) Push(pts ...pixel.Vec) {
|
||||
if _, ok := imd.Color.(pixel.RGBA); !ok {
|
||||
imd.Color = pixel.ToRGBA(imd.Color)
|
||||
}
|
||||
opts := point{
|
||||
col: imd.Color.(pixel.RGBA),
|
||||
pic: imd.Picture,
|
||||
in: imd.Intensity,
|
||||
precision: imd.Precision,
|
||||
endshape: imd.EndShape,
|
||||
}
|
||||
for _, pt := range pts {
|
||||
imd.pushPt(pt, opts)
|
||||
}
|
||||
}
|
||||
|
||||
func (imd *IMDraw) pushPt(pos pixel.Vec, pt point) {
|
||||
pt.pos = pos
|
||||
imd.points = append(imd.points, pt)
|
||||
}
|
||||
|
||||
// SetMatrix sets a Matrix that all further points will be transformed by.
|
||||
func (imd *IMDraw) SetMatrix(m pixel.Matrix) {
|
||||
imd.matrix = m
|
||||
imd.batch.SetMatrix(imd.matrix)
|
||||
}
|
||||
|
||||
// SetColorMask sets a color that all further point's color will be multiplied by.
|
||||
func (imd *IMDraw) SetColorMask(color color.Color) {
|
||||
imd.mask = pixel.ToRGBA(color)
|
||||
imd.batch.SetColorMask(imd.mask)
|
||||
}
|
||||
|
||||
// MakeTriangles returns a specialized copy of the provided Triangles that draws onto this IMDraw.
|
||||
func (imd *IMDraw) MakeTriangles(t pixel.Triangles) pixel.TargetTriangles {
|
||||
return imd.batch.MakeTriangles(t)
|
||||
}
|
||||
|
||||
// MakePicture returns a specialized copy of the provided Picture that draws onto this IMDraw.
|
||||
func (imd *IMDraw) MakePicture(p pixel.Picture) pixel.TargetPicture {
|
||||
return imd.batch.MakePicture(p)
|
||||
}
|
||||
|
||||
// Line draws a polyline of the specified thickness between the Pushed points.
|
||||
func (imd *IMDraw) Line(thickness float64) {
|
||||
imd.polyline(thickness, false)
|
||||
}
|
||||
|
||||
// Rectangle draws a rectangle between each two subsequent Pushed points. Drawing a rectangle
|
||||
// between two points means drawing a rectangle with sides parallel to the axes of the coordinate
|
||||
// system, where the two points specify it's two opposite corners.
|
||||
//
|
||||
// If the thickness is 0, rectangles will be filled, otherwise will be outlined with the given
|
||||
// thickness.
|
||||
func (imd *IMDraw) Rectangle(thickness float64) {
|
||||
if thickness == 0 {
|
||||
imd.fillRectangle()
|
||||
} else {
|
||||
imd.outlineRectangle(thickness)
|
||||
}
|
||||
}
|
||||
|
||||
// Polygon draws a polygon from the Pushed points. If the thickness is 0, the convex polygon will be
|
||||
// filled. Otherwise, an outline of the specified thickness will be drawn. The outline does not have
|
||||
// to be convex.
|
||||
//
|
||||
// Note, that the filled polygon does not have to be strictly convex. The way it's drawn is that a
|
||||
// triangle is drawn between each two adjacent points and the first Pushed point. You can use this
|
||||
// property to draw certain kinds of concave polygons.
|
||||
func (imd *IMDraw) Polygon(thickness float64) {
|
||||
if thickness == 0 {
|
||||
imd.fillPolygon()
|
||||
} else {
|
||||
imd.polyline(thickness, true)
|
||||
}
|
||||
}
|
||||
|
||||
// Circle draws a circle of the specified radius around each Pushed point. If the thickness is 0,
|
||||
// the circle will be filled, otherwise a circle outline of the specified thickness will be drawn.
|
||||
func (imd *IMDraw) Circle(radius, thickness float64) {
|
||||
if thickness == 0 {
|
||||
imd.fillEllipseArc(pixel.V(radius, radius), 0, 2*math.Pi)
|
||||
} else {
|
||||
imd.outlineEllipseArc(pixel.V(radius, radius), 0, 2*math.Pi, thickness, false)
|
||||
}
|
||||
}
|
||||
|
||||
// CircleArc draws a circle arc of the specified radius around each Pushed point. If the thickness
|
||||
// is 0, the arc will be filled, otherwise will be outlined. The arc starts at the low angle and
|
||||
// continues to the high angle. If low<high, the arc will be drawn counterclockwise. Otherwise it
|
||||
// will be clockwise. The angles are not normalized by any means.
|
||||
//
|
||||
// imd.CircleArc(40, 0, 8*math.Pi, 0)
|
||||
//
|
||||
// This line will fill the whole circle 4 times.
|
||||
func (imd *IMDraw) CircleArc(radius, low, high, thickness float64) {
|
||||
if thickness == 0 {
|
||||
imd.fillEllipseArc(pixel.V(radius, radius), low, high)
|
||||
} else {
|
||||
imd.outlineEllipseArc(pixel.V(radius, radius), low, high, thickness, true)
|
||||
}
|
||||
}
|
||||
|
||||
// Ellipse draws an ellipse of the specified radius in each axis around each Pushed points. If the
|
||||
// thickness is 0, the ellipse will be filled, otherwise an ellipse outline of the specified
|
||||
// thickness will be drawn.
|
||||
func (imd *IMDraw) Ellipse(radius pixel.Vec, thickness float64) {
|
||||
if thickness == 0 {
|
||||
imd.fillEllipseArc(radius, 0, 2*math.Pi)
|
||||
} else {
|
||||
imd.outlineEllipseArc(radius, 0, 2*math.Pi, thickness, false)
|
||||
}
|
||||
}
|
||||
|
||||
// EllipseArc draws an ellipse arc of the specified radius in each axis around each Pushed point. If
|
||||
// the thickness is 0, the arc will be filled, otherwise will be outlined. The arc starts at the low
|
||||
// angle and continues to the high angle. If low<high, the arc will be drawn counterclockwise.
|
||||
// Otherwise it will be clockwise. The angles are not normalized by any means.
|
||||
//
|
||||
// imd.EllipseArc(pixel.V(100, 50), 0, 8*math.Pi, 0)
|
||||
//
|
||||
// This line will fill the whole ellipse 4 times.
|
||||
func (imd *IMDraw) EllipseArc(radius pixel.Vec, low, high, thickness float64) {
|
||||
if thickness == 0 {
|
||||
imd.fillEllipseArc(radius, low, high)
|
||||
} else {
|
||||
imd.outlineEllipseArc(radius, low, high, thickness, true)
|
||||
}
|
||||
}
|
||||
|
||||
func (imd *IMDraw) getAndClearPoints() []point {
|
||||
points := imd.points
|
||||
// use one of the existing pools so we don't reallocate as often
|
||||
if len(imd.pool) > 0 {
|
||||
pos := len(imd.pool) - 1
|
||||
imd.points = imd.pool[pos][:0]
|
||||
imd.pool = imd.pool[:pos]
|
||||
} else {
|
||||
imd.points = nil
|
||||
}
|
||||
return points
|
||||
}
|
||||
|
||||
func (imd *IMDraw) restorePoints(points []point) {
|
||||
imd.pool = append(imd.pool, imd.points)
|
||||
imd.points = points[:0]
|
||||
}
|
||||
|
||||
func (imd *IMDraw) applyMatrixAndMask(off int) {
|
||||
for i := range (*imd.tri)[off:] {
|
||||
(*imd.tri)[off+i].Position = imd.matrix.Project((*imd.tri)[off+i].Position)
|
||||
(*imd.tri)[off+i].Color = imd.mask.Mul((*imd.tri)[off+i].Color)
|
||||
}
|
||||
}
|
||||
|
||||
func (imd *IMDraw) fillRectangle() {
|
||||
points := imd.getAndClearPoints()
|
||||
|
||||
if len(points) < 2 {
|
||||
imd.restorePoints(points)
|
||||
return
|
||||
}
|
||||
|
||||
off := imd.tri.Len()
|
||||
imd.tri.SetLen(imd.tri.Len() + 6*(len(points)-1))
|
||||
|
||||
for i, j := 0, off; i+1 < len(points); i, j = i+1, j+6 {
|
||||
a, b := points[i], points[i+1]
|
||||
c := point{
|
||||
pos: pixel.V(a.pos.X, b.pos.Y),
|
||||
col: a.col.Add(b.col).Mul(pixel.Alpha(0.5)),
|
||||
pic: pixel.V(a.pic.X, b.pic.Y),
|
||||
in: (a.in + b.in) / 2,
|
||||
}
|
||||
d := point{
|
||||
pos: pixel.V(b.pos.X, a.pos.Y),
|
||||
col: a.col.Add(b.col).Mul(pixel.Alpha(0.5)),
|
||||
pic: pixel.V(b.pic.X, a.pic.Y),
|
||||
in: (a.in + b.in) / 2,
|
||||
}
|
||||
|
||||
for k, p := range [...]point{a, b, c, a, b, d} {
|
||||
(*imd.tri)[j+k].Position = p.pos
|
||||
(*imd.tri)[j+k].Color = p.col
|
||||
(*imd.tri)[j+k].Picture = p.pic
|
||||
(*imd.tri)[j+k].Intensity = p.in
|
||||
}
|
||||
}
|
||||
|
||||
imd.applyMatrixAndMask(off)
|
||||
imd.batch.Dirty()
|
||||
|
||||
imd.restorePoints(points)
|
||||
}
|
||||
|
||||
func (imd *IMDraw) outlineRectangle(thickness float64) {
|
||||
points := imd.getAndClearPoints()
|
||||
|
||||
if len(points) < 2 {
|
||||
imd.restorePoints(points)
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i+1 < len(points); i++ {
|
||||
a, b := points[i], points[i+1]
|
||||
mid := a
|
||||
mid.col = a.col.Add(b.col).Mul(pixel.Alpha(0.5))
|
||||
mid.in = (a.in + b.in) / 2
|
||||
|
||||
imd.pushPt(a.pos, a)
|
||||
imd.pushPt(pixel.V(a.pos.X, b.pos.Y), mid)
|
||||
imd.pushPt(b.pos, b)
|
||||
imd.pushPt(pixel.V(b.pos.X, a.pos.Y), mid)
|
||||
imd.polyline(thickness, true)
|
||||
}
|
||||
|
||||
imd.restorePoints(points)
|
||||
}
|
||||
|
||||
func (imd *IMDraw) fillPolygon() {
|
||||
points := imd.getAndClearPoints()
|
||||
|
||||
if len(points) < 3 {
|
||||
imd.restorePoints(points)
|
||||
return
|
||||
}
|
||||
|
||||
off := imd.tri.Len()
|
||||
imd.tri.SetLen(imd.tri.Len() + 3*(len(points)-2))
|
||||
|
||||
for i, j := 1, off; i+1 < len(points); i, j = i+1, j+3 {
|
||||
for k, p := range [...]int{0, i, i + 1} {
|
||||
tri := &(*imd.tri)[j+k]
|
||||
tri.Position = points[p].pos
|
||||
tri.Color = points[p].col
|
||||
tri.Picture = points[p].pic
|
||||
tri.Intensity = points[p].in
|
||||
}
|
||||
}
|
||||
|
||||
imd.applyMatrixAndMask(off)
|
||||
imd.batch.Dirty()
|
||||
|
||||
imd.restorePoints(points)
|
||||
}
|
||||
|
||||
func (imd *IMDraw) fillEllipseArc(radius pixel.Vec, low, high float64) {
|
||||
points := imd.getAndClearPoints()
|
||||
|
||||
for _, pt := range points {
|
||||
num := math.Ceil(math.Abs(high-low) / (2 * math.Pi) * float64(pt.precision))
|
||||
delta := (high - low) / num
|
||||
|
||||
off := imd.tri.Len()
|
||||
imd.tri.SetLen(imd.tri.Len() + 3*int(num))
|
||||
|
||||
for i := range (*imd.tri)[off:] {
|
||||
(*imd.tri)[off+i].Color = pt.col
|
||||
(*imd.tri)[off+i].Picture = pixel.ZV
|
||||
(*imd.tri)[off+i].Intensity = 0
|
||||
}
|
||||
|
||||
for i, j := 0.0, off; i < num; i, j = i+1, j+3 {
|
||||
angle := low + i*delta
|
||||
sin, cos := math.Sincos(angle)
|
||||
a := pt.pos.Add(pixel.V(
|
||||
radius.X*cos,
|
||||
radius.Y*sin,
|
||||
))
|
||||
|
||||
angle = low + (i+1)*delta
|
||||
sin, cos = math.Sincos(angle)
|
||||
b := pt.pos.Add(pixel.V(
|
||||
radius.X*cos,
|
||||
radius.Y*sin,
|
||||
))
|
||||
|
||||
(*imd.tri)[j+0].Position = pt.pos
|
||||
(*imd.tri)[j+1].Position = a
|
||||
(*imd.tri)[j+2].Position = b
|
||||
}
|
||||
|
||||
imd.applyMatrixAndMask(off)
|
||||
imd.batch.Dirty()
|
||||
}
|
||||
|
||||
imd.restorePoints(points)
|
||||
}
|
||||
|
||||
func (imd *IMDraw) outlineEllipseArc(radius pixel.Vec, low, high, thickness float64, doEndShape bool) {
|
||||
points := imd.getAndClearPoints()
|
||||
|
||||
for _, pt := range points {
|
||||
num := math.Ceil(math.Abs(high-low) / (2 * math.Pi) * float64(pt.precision))
|
||||
delta := (high - low) / num
|
||||
|
||||
off := imd.tri.Len()
|
||||
imd.tri.SetLen(imd.tri.Len() + 6*int(num))
|
||||
|
||||
for i := range (*imd.tri)[off:] {
|
||||
(*imd.tri)[off+i].Color = pt.col
|
||||
(*imd.tri)[off+i].Picture = pixel.ZV
|
||||
(*imd.tri)[off+i].Intensity = 0
|
||||
}
|
||||
|
||||
for i, j := 0.0, off; i < num; i, j = i+1, j+6 {
|
||||
angle := low + i*delta
|
||||
sin, cos := math.Sincos(angle)
|
||||
normalSin, normalCos := pixel.V(sin, cos).ScaledXY(radius).Unit().XY()
|
||||
a := pt.pos.Add(pixel.V(
|
||||
radius.X*cos-thickness/2*normalCos,
|
||||
radius.Y*sin-thickness/2*normalSin,
|
||||
))
|
||||
b := pt.pos.Add(pixel.V(
|
||||
radius.X*cos+thickness/2*normalCos,
|
||||
radius.Y*sin+thickness/2*normalSin,
|
||||
))
|
||||
|
||||
angle = low + (i+1)*delta
|
||||
sin, cos = math.Sincos(angle)
|
||||
normalSin, normalCos = pixel.V(sin, cos).ScaledXY(radius).Unit().XY()
|
||||
c := pt.pos.Add(pixel.V(
|
||||
radius.X*cos-thickness/2*normalCos,
|
||||
radius.Y*sin-thickness/2*normalSin,
|
||||
))
|
||||
d := pt.pos.Add(pixel.V(
|
||||
radius.X*cos+thickness/2*normalCos,
|
||||
radius.Y*sin+thickness/2*normalSin,
|
||||
))
|
||||
|
||||
(*imd.tri)[j+0].Position = a
|
||||
(*imd.tri)[j+1].Position = b
|
||||
(*imd.tri)[j+2].Position = c
|
||||
(*imd.tri)[j+3].Position = c
|
||||
(*imd.tri)[j+4].Position = b
|
||||
(*imd.tri)[j+5].Position = d
|
||||
}
|
||||
|
||||
imd.applyMatrixAndMask(off)
|
||||
imd.batch.Dirty()
|
||||
|
||||
if doEndShape {
|
||||
lowSin, lowCos := math.Sincos(low)
|
||||
lowCenter := pt.pos.Add(pixel.V(
|
||||
radius.X*lowCos,
|
||||
radius.Y*lowSin,
|
||||
))
|
||||
normalLowSin, normalLowCos := pixel.V(lowSin, lowCos).ScaledXY(radius).Unit().XY()
|
||||
normalLow := pixel.V(normalLowCos, normalLowSin).Angle()
|
||||
|
||||
highSin, highCos := math.Sincos(high)
|
||||
highCenter := pt.pos.Add(pixel.V(
|
||||
radius.X*highCos,
|
||||
radius.Y*highSin,
|
||||
))
|
||||
normalHighSin, normalHighCos := pixel.V(highSin, highCos).ScaledXY(radius).Unit().XY()
|
||||
normalHigh := pixel.V(normalHighCos, normalHighSin).Angle()
|
||||
|
||||
orientation := 1.0
|
||||
if low > high {
|
||||
orientation = -1.0
|
||||
}
|
||||
|
||||
switch pt.endshape {
|
||||
case NoEndShape:
|
||||
// nothing
|
||||
case SharpEndShape:
|
||||
thick := pixel.V(thickness/2, 0).Rotated(normalLow)
|
||||
imd.pushPt(lowCenter.Add(thick), pt)
|
||||
imd.pushPt(lowCenter.Sub(thick), pt)
|
||||
imd.pushPt(lowCenter.Sub(thick.Normal().Scaled(orientation)), pt)
|
||||
imd.fillPolygon()
|
||||
thick = pixel.V(thickness/2, 0).Rotated(normalHigh)
|
||||
imd.pushPt(highCenter.Add(thick), pt)
|
||||
imd.pushPt(highCenter.Sub(thick), pt)
|
||||
imd.pushPt(highCenter.Add(thick.Normal().Scaled(orientation)), pt)
|
||||
imd.fillPolygon()
|
||||
case RoundEndShape:
|
||||
imd.pushPt(lowCenter, pt)
|
||||
imd.fillEllipseArc(pixel.V(thickness/2, thickness/2), normalLow, normalLow-math.Pi*orientation)
|
||||
imd.pushPt(highCenter, pt)
|
||||
imd.fillEllipseArc(pixel.V(thickness/2, thickness/2), normalHigh, normalHigh+math.Pi*orientation)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
imd.restorePoints(points)
|
||||
}
|
||||
|
||||
func (imd *IMDraw) polyline(thickness float64, closed bool) {
|
||||
points := imd.getAndClearPoints()
|
||||
|
||||
if len(points) == 0 {
|
||||
imd.restorePoints(points)
|
||||
return
|
||||
}
|
||||
if len(points) == 1 {
|
||||
// one point special case
|
||||
points = append(points, points[0])
|
||||
}
|
||||
|
||||
// first point
|
||||
j, i := 0, 1
|
||||
ijNormal := points[0].pos.To(points[1].pos).Normal().Unit().Scaled(thickness / 2)
|
||||
|
||||
if !closed {
|
||||
switch points[j].endshape {
|
||||
case NoEndShape:
|
||||
// nothing
|
||||
case SharpEndShape:
|
||||
imd.pushPt(points[j].pos.Add(ijNormal), points[j])
|
||||
imd.pushPt(points[j].pos.Sub(ijNormal), points[j])
|
||||
imd.pushPt(points[j].pos.Add(ijNormal.Normal()), points[j])
|
||||
imd.fillPolygon()
|
||||
case RoundEndShape:
|
||||
imd.pushPt(points[j].pos, points[j])
|
||||
imd.fillEllipseArc(pixel.V(thickness/2, thickness/2), ijNormal.Angle(), ijNormal.Angle()+math.Pi)
|
||||
}
|
||||
}
|
||||
|
||||
imd.pushPt(points[j].pos.Add(ijNormal), points[j])
|
||||
imd.pushPt(points[j].pos.Sub(ijNormal), points[j])
|
||||
|
||||
// middle points
|
||||
for i := 0; i < len(points); i++ {
|
||||
j, k := i+1, i+2
|
||||
|
||||
closing := false
|
||||
if j >= len(points) {
|
||||
j %= len(points)
|
||||
closing = true
|
||||
}
|
||||
if k >= len(points) {
|
||||
if !closed {
|
||||
break
|
||||
}
|
||||
k %= len(points)
|
||||
}
|
||||
|
||||
jkNormal := points[j].pos.To(points[k].pos).Normal().Unit().Scaled(thickness / 2)
|
||||
|
||||
orientation := 1.0
|
||||
if ijNormal.Cross(jkNormal) > 0 {
|
||||
orientation = -1.0
|
||||
}
|
||||
|
||||
imd.pushPt(points[j].pos.Sub(ijNormal), points[j])
|
||||
imd.pushPt(points[j].pos.Add(ijNormal), points[j])
|
||||
imd.fillPolygon()
|
||||
|
||||
switch points[j].endshape {
|
||||
case NoEndShape:
|
||||
// nothing
|
||||
case SharpEndShape:
|
||||
imd.pushPt(points[j].pos, points[j])
|
||||
imd.pushPt(points[j].pos.Add(ijNormal.Scaled(orientation)), points[j])
|
||||
imd.pushPt(points[j].pos.Add(jkNormal.Scaled(orientation)), points[j])
|
||||
imd.fillPolygon()
|
||||
case RoundEndShape:
|
||||
imd.pushPt(points[j].pos, points[j])
|
||||
imd.fillEllipseArc(pixel.V(thickness/2, thickness/2), ijNormal.Angle(), ijNormal.Angle()-math.Pi)
|
||||
imd.pushPt(points[j].pos, points[j])
|
||||
imd.fillEllipseArc(pixel.V(thickness/2, thickness/2), jkNormal.Angle(), jkNormal.Angle()+math.Pi)
|
||||
}
|
||||
|
||||
if !closing {
|
||||
imd.pushPt(points[j].pos.Add(jkNormal), points[j])
|
||||
imd.pushPt(points[j].pos.Sub(jkNormal), points[j])
|
||||
}
|
||||
// "next" normal becomes previous normal
|
||||
ijNormal = jkNormal
|
||||
}
|
||||
|
||||
// last point
|
||||
i, j = len(points)-2, len(points)-1
|
||||
ijNormal = points[i].pos.To(points[j].pos).Normal().Unit().Scaled(thickness / 2)
|
||||
|
||||
imd.pushPt(points[j].pos.Sub(ijNormal), points[j])
|
||||
imd.pushPt(points[j].pos.Add(ijNormal), points[j])
|
||||
imd.fillPolygon()
|
||||
|
||||
if !closed {
|
||||
switch points[j].endshape {
|
||||
case NoEndShape:
|
||||
// nothing
|
||||
case SharpEndShape:
|
||||
imd.pushPt(points[j].pos.Add(ijNormal), points[j])
|
||||
imd.pushPt(points[j].pos.Sub(ijNormal), points[j])
|
||||
imd.pushPt(points[j].pos.Add(ijNormal.Normal().Scaled(-1)), points[j])
|
||||
imd.fillPolygon()
|
||||
case RoundEndShape:
|
||||
imd.pushPt(points[j].pos, points[j])
|
||||
imd.fillEllipseArc(pixel.V(thickness/2, thickness/2), ijNormal.Angle(), ijNormal.Angle()-math.Pi)
|
||||
}
|
||||
}
|
||||
|
||||
imd.restorePoints(points)
|
||||
}
|
133
vendor/github.com/faiface/pixel/interface.go
generated
vendored
Normal file
133
vendor/github.com/faiface/pixel/interface.go
generated
vendored
Normal file
@@ -0,0 +1,133 @@
|
||||
package pixel
|
||||
|
||||
import "image/color"
|
||||
|
||||
// Target is something that can be drawn onto, such as a window, a canvas, and so on.
|
||||
//
|
||||
// You can notice, that there are no "drawing" methods in a Target. That's because all drawing
|
||||
// happens indirectly through Triangles and Picture instances generated via MakeTriangles and
|
||||
// MakePicture method.
|
||||
type Target interface {
|
||||
// MakeTriangles generates a specialized copy of the provided Triangles.
|
||||
//
|
||||
// When calling Draw method on the returned TargetTriangles, the TargetTriangles will be
|
||||
// drawn onto the Target that generated them.
|
||||
//
|
||||
// Note, that not every Target has to recognize all possible types of Triangles. Some may
|
||||
// only recognize TrianglesPosition and TrianglesColor and ignore all other properties (if
|
||||
// present) when making new TargetTriangles. This varies from Target to Target.
|
||||
MakeTriangles(Triangles) TargetTriangles
|
||||
|
||||
// MakePicture generates a specialized copy of the provided Picture.
|
||||
//
|
||||
// When calling Draw method on the returned TargetPicture, the TargetPicture will be drawn
|
||||
// onto the Target that generated it together with the TargetTriangles supplied to the Draw
|
||||
// method.
|
||||
MakePicture(Picture) TargetPicture
|
||||
}
|
||||
|
||||
// BasicTarget is a Target with additional basic adjustment methods.
|
||||
type BasicTarget interface {
|
||||
Target
|
||||
|
||||
// SetMatrix sets a Matrix that every point will be projected by.
|
||||
SetMatrix(Matrix)
|
||||
|
||||
// SetColorMask sets a color that will be multiplied with the TrianglesColor property of all
|
||||
// Triangles.
|
||||
SetColorMask(color.Color)
|
||||
}
|
||||
|
||||
// Triangles represents a list of vertices, where each three vertices form a triangle. (First,
|
||||
// second and third is the first triangle, fourth, fifth and sixth is the second triangle, etc.)
|
||||
type Triangles interface {
|
||||
// Len returns the number of vertices. The number of triangles is the number of vertices
|
||||
// divided by 3.
|
||||
Len() int
|
||||
|
||||
// SetLen resizes Triangles to len vertices. If Triangles B were obtained by calling Slice
|
||||
// method on Triangles A, the relationship between A and B is undefined after calling SetLen
|
||||
// on either one of them.
|
||||
SetLen(len int)
|
||||
|
||||
// Slice returns a sub-Triangles of this Triangles, covering vertices in range [i, j).
|
||||
//
|
||||
// If Triangles B were obtained by calling Slice(4, 9) on Triangles A, then A and B must
|
||||
// share the same underlying data. Modifying B must change the contents of A in range
|
||||
// [4, 9). The vertex with index 0 at B is the vertex with index 4 in A, and so on.
|
||||
//
|
||||
// Returned Triangles must have the same underlying type.
|
||||
Slice(i, j int) Triangles
|
||||
|
||||
// Update copies vertex properties from the supplied Triangles into this Triangles.
|
||||
//
|
||||
// Properies not supported by these Triangles should be ignored. Properties not supported by
|
||||
// the supplied Triangles should be left untouched.
|
||||
//
|
||||
// The two Triangles must have the same Len.
|
||||
Update(Triangles)
|
||||
|
||||
// Copy creates an exact independent copy of this Triangles (with the same underlying type).
|
||||
Copy() Triangles
|
||||
}
|
||||
|
||||
// TargetTriangles are Triangles generated by a Target with MakeTriangles method. They can be drawn
|
||||
// onto that (no other) Target.
|
||||
type TargetTriangles interface {
|
||||
Triangles
|
||||
|
||||
// Draw draws Triangles onto an associated Target.
|
||||
Draw()
|
||||
}
|
||||
|
||||
// TrianglesPosition specifies Triangles with Position property.
|
||||
type TrianglesPosition interface {
|
||||
Triangles
|
||||
Position(i int) Vec
|
||||
}
|
||||
|
||||
// TrianglesColor specifies Triangles with Color property.
|
||||
type TrianglesColor interface {
|
||||
Triangles
|
||||
Color(i int) RGBA
|
||||
}
|
||||
|
||||
// TrianglesPicture specifies Triangles with Picture propery.
|
||||
//
|
||||
// The first value returned from Picture method is Picture coordinates. The second one specifies the
|
||||
// weight of the Picture. Value of 0 means, that Picture should be completely ignored, 1 means that
|
||||
// is should be fully included and anything in between means anything in between.
|
||||
type TrianglesPicture interface {
|
||||
Triangles
|
||||
Picture(i int) (pic Vec, intensity float64)
|
||||
}
|
||||
|
||||
// Picture represents a rectangular area of raster data, such as a color. It has Bounds which
|
||||
// specify the rectangle where data is located.
|
||||
type Picture interface {
|
||||
// Bounds returns the rectangle of the Picture. All data is located witih this rectangle.
|
||||
// Querying properties outside the rectangle should return default value of that property.
|
||||
Bounds() Rect
|
||||
}
|
||||
|
||||
// TargetPicture is a Picture generated by a Target using MakePicture method. This Picture can be drawn onto
|
||||
// that (no other) Target together with a TargetTriangles generated by the same Target.
|
||||
//
|
||||
// The TargetTriangles specify where, shape and how the Picture should be drawn.
|
||||
type TargetPicture interface {
|
||||
Picture
|
||||
|
||||
// Draw draws the supplied TargetTriangles (which must be generated by the same Target as
|
||||
// this TargetPicture) with this TargetPicture. The TargetTriangles should utilize the data
|
||||
// from this TargetPicture in some way.
|
||||
Draw(TargetTriangles)
|
||||
}
|
||||
|
||||
// PictureColor specifies Picture with Color property, so that every position inside the Picture's
|
||||
// Bounds has a color.
|
||||
//
|
||||
// Positions outside the Picture's Bounds must return full transparent (Alpha(0)).
|
||||
type PictureColor interface {
|
||||
Picture
|
||||
Color(at Vec) RGBA
|
||||
}
|
431
vendor/github.com/faiface/pixel/pixelgl/canvas.go
generated
vendored
Normal file
431
vendor/github.com/faiface/pixel/pixelgl/canvas.go
generated
vendored
Normal file
@@ -0,0 +1,431 @@
|
||||
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 *glhf.Shader
|
||||
|
||||
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.SetBounds(bounds)
|
||||
|
||||
var shader *glhf.Shader
|
||||
mainthread.Call(func() {
|
||||
var err error
|
||||
shader, err = glhf.NewShader(
|
||||
canvasVertexFormat,
|
||||
canvasUniformFormat,
|
||||
canvasVertexShader,
|
||||
canvasFragmentShader,
|
||||
)
|
||||
if err != nil {
|
||||
panic(errors.Wrap(err, "failed to create Canvas, there's a bug in the shader"))
|
||||
}
|
||||
})
|
||||
c.shader = shader
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// 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 {
|
||||
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
|
||||
mat := ct.dst.mat
|
||||
col := ct.dst.col
|
||||
smt := ct.dst.smooth
|
||||
|
||||
mainthread.CallNonBlock(func() {
|
||||
ct.dst.setGlhfBounds()
|
||||
setBlendFunc(cmp)
|
||||
|
||||
frame := ct.dst.gf.Frame()
|
||||
shader := ct.dst.shader
|
||||
|
||||
frame.Begin()
|
||||
shader.Begin()
|
||||
|
||||
dstBounds := ct.dst.Bounds()
|
||||
shader.SetUniformAttr(canvasBounds, mgl32.Vec4{
|
||||
float32(dstBounds.Min.X),
|
||||
float32(dstBounds.Min.Y),
|
||||
float32(dstBounds.W()),
|
||||
float32(dstBounds.H()),
|
||||
})
|
||||
shader.SetUniformAttr(canvasTransform, mat)
|
||||
shader.SetUniformAttr(canvasColorMask, col)
|
||||
|
||||
if tex == nil {
|
||||
ct.vs.Begin()
|
||||
ct.vs.Draw()
|
||||
ct.vs.End()
|
||||
} else {
|
||||
tex.Begin()
|
||||
|
||||
bx, by, bw, bh := intBounds(bounds)
|
||||
shader.SetUniformAttr(canvasTexBounds, mgl32.Vec4{
|
||||
float32(bx),
|
||||
float32(by),
|
||||
float32(bw),
|
||||
float32(bh),
|
||||
})
|
||||
|
||||
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())
|
||||
}
|
||||
|
||||
const (
|
||||
canvasPosition int = iota
|
||||
canvasColor
|
||||
canvasTexCoords
|
||||
canvasIntensity
|
||||
)
|
||||
|
||||
var canvasVertexFormat = glhf.AttrFormat{
|
||||
canvasPosition: {Name: "position", Type: glhf.Vec2},
|
||||
canvasColor: {Name: "color", Type: glhf.Vec4},
|
||||
canvasTexCoords: {Name: "texCoords", Type: glhf.Vec2},
|
||||
canvasIntensity: {Name: "intensity", Type: glhf.Float},
|
||||
}
|
||||
|
||||
const (
|
||||
canvasTransform int = iota
|
||||
canvasColorMask
|
||||
canvasBounds
|
||||
canvasTexBounds
|
||||
)
|
||||
|
||||
var canvasUniformFormat = glhf.AttrFormat{
|
||||
canvasTransform: {Name: "transform", Type: glhf.Mat3},
|
||||
canvasColorMask: {Name: "colorMask", Type: glhf.Vec4},
|
||||
canvasBounds: {Name: "bounds", Type: glhf.Vec4},
|
||||
canvasTexBounds: {Name: "texBounds", Type: glhf.Vec4},
|
||||
}
|
||||
|
||||
var canvasVertexShader = `
|
||||
#version 330 core
|
||||
|
||||
in vec2 position;
|
||||
in vec4 color;
|
||||
in vec2 texCoords;
|
||||
in float intensity;
|
||||
|
||||
out vec4 Color;
|
||||
out vec2 TexCoords;
|
||||
out float Intensity;
|
||||
|
||||
uniform mat3 transform;
|
||||
uniform vec4 bounds;
|
||||
|
||||
void main() {
|
||||
vec2 transPos = (transform * vec3(position, 1.0)).xy;
|
||||
vec2 normPos = (transPos - bounds.xy) / bounds.zw * 2 - vec2(1, 1);
|
||||
gl_Position = vec4(normPos, 0.0, 1.0);
|
||||
Color = color;
|
||||
TexCoords = texCoords;
|
||||
Intensity = intensity;
|
||||
}
|
||||
`
|
||||
|
||||
var canvasFragmentShader = `
|
||||
#version 330 core
|
||||
|
||||
in vec4 Color;
|
||||
in vec2 TexCoords;
|
||||
in float Intensity;
|
||||
|
||||
out vec4 color;
|
||||
|
||||
uniform vec4 colorMask;
|
||||
uniform vec4 texBounds;
|
||||
uniform sampler2D tex;
|
||||
|
||||
void main() {
|
||||
if (Intensity == 0) {
|
||||
color = colorMask * Color;
|
||||
} else {
|
||||
color = vec4(0, 0, 0, 0);
|
||||
color += (1 - Intensity) * Color;
|
||||
vec2 t = (TexCoords - texBounds.xy) / texBounds.zw;
|
||||
color += Intensity * Color * texture(tex, t);
|
||||
color *= colorMask;
|
||||
}
|
||||
}
|
||||
`
|
5
vendor/github.com/faiface/pixel/pixelgl/doc.go
generated
vendored
Normal file
5
vendor/github.com/faiface/pixel/pixelgl/doc.go
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
// 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
|
105
vendor/github.com/faiface/pixel/pixelgl/glframe.go
generated
vendored
Normal file
105
vendor/github.com/faiface/pixel/pixelgl/glframe.go
generated
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
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
|
||||
}
|
98
vendor/github.com/faiface/pixel/pixelgl/glpicture.go
generated
vendored
Normal file
98
vendor/github.com/faiface/pixel/pixelgl/glpicture.go
generated
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
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,
|
||||
}
|
||||
}
|
215
vendor/github.com/faiface/pixel/pixelgl/gltriangles.go
generated
vendored
Normal file
215
vendor/github.com/faiface/pixel/pixelgl/gltriangles.go
generated
vendored
Normal file
@@ -0,0 +1,215 @@
|
||||
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 *glhf.Shader
|
||||
}
|
||||
|
||||
var (
|
||||
_ pixel.TrianglesPosition = (*GLTriangles)(nil)
|
||||
_ pixel.TrianglesColor = (*GLTriangles)(nil)
|
||||
_ pixel.TrianglesPicture = (*GLTriangles)(nil)
|
||||
)
|
||||
|
||||
// NewGLTriangles returns GLTriangles initialized with the data from the supplied Triangles.
|
||||
//
|
||||
// Only draw the Triangles using the provided Shader.
|
||||
func NewGLTriangles(shader *glhf.Shader, t pixel.Triangles) *GLTriangles {
|
||||
var gt *GLTriangles
|
||||
mainthread.Call(func() {
|
||||
gt = &GLTriangles{
|
||||
vs: glhf.MakeVertexSlice(shader, 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() *glhf.Shader {
|
||||
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,
|
||||
)
|
||||
}
|
||||
case length < gt.Len():
|
||||
gt.data = gt.data[:length*gt.vs.Stride()]
|
||||
default:
|
||||
return
|
||||
}
|
||||
mainthread.CallNonBlock(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
|
||||
)
|
||||
d := gt.data[i*stride : i*stride+9]
|
||||
d[0] = float32(px)
|
||||
d[1] = float32(py)
|
||||
d[2] = float32(col.R)
|
||||
d[3] = float32(col.G)
|
||||
d[4] = float32(col.B)
|
||||
d[5] = float32(col.A)
|
||||
d[6] = float32(tx)
|
||||
d[7] = float32(ty)
|
||||
d[8] = float32(in)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if t, ok := t.(pixel.TrianglesPosition); ok {
|
||||
for i := 0; i < length; i++ {
|
||||
px, py := t.Position(i).XY()
|
||||
gt.data[i*stride+0] = float32(px)
|
||||
gt.data[i*stride+1] = float32(py)
|
||||
}
|
||||
}
|
||||
if t, ok := t.(pixel.TrianglesColor); ok {
|
||||
for i := 0; i < length; i++ {
|
||||
col := t.Color(i)
|
||||
gt.data[i*stride+2] = float32(col.R)
|
||||
gt.data[i*stride+3] = float32(col.G)
|
||||
gt.data[i*stride+4] = float32(col.B)
|
||||
gt.data[i*stride+5] = 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+6] = float32(pic.X)
|
||||
gt.data[i*stride+7] = float32(pic.Y)
|
||||
gt.data[i*stride+8] = float32(intensity)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// Position returns the Position property of the i-th vertex.
|
||||
func (gt *GLTriangles) Position(i int) pixel.Vec {
|
||||
px := gt.data[i*gt.vs.Stride()+0]
|
||||
py := gt.data[i*gt.vs.Stride()+1]
|
||||
return pixel.V(float64(px), float64(py))
|
||||
}
|
||||
|
||||
// Color returns the Color property of the i-th vertex.
|
||||
func (gt *GLTriangles) Color(i int) pixel.RGBA {
|
||||
r := gt.data[i*gt.vs.Stride()+2]
|
||||
g := gt.data[i*gt.vs.Stride()+3]
|
||||
b := gt.data[i*gt.vs.Stride()+4]
|
||||
a := gt.data[i*gt.vs.Stride()+5]
|
||||
return pixel.RGBA{
|
||||
R: float64(r),
|
||||
G: float64(g),
|
||||
B: float64(b),
|
||||
A: float64(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[i*gt.vs.Stride()+6]
|
||||
ty := gt.data[i*gt.vs.Stride()+7]
|
||||
intensity = float64(gt.data[i*gt.vs.Stride()+8])
|
||||
return pixel.V(float64(tx), float64(ty)), intensity
|
||||
}
|
388
vendor/github.com/faiface/pixel/pixelgl/input.go
generated
vendored
Normal file
388
vendor/github.com/faiface/pixel/pixelgl/input.go
generated
vendored
Normal file
@@ -0,0 +1,388 @@
|
||||
package pixelgl
|
||||
|
||||
import (
|
||||
"github.com/faiface/mainthread"
|
||||
"github.com/faiface/pixel"
|
||||
"github.com/go-gl/glfw/v3.2/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 just been pressed down.
|
||||
func (w *Window) JustPressed(button Button) bool {
|
||||
return w.currInp.buttons[button] && !w.prevInp.buttons[button]
|
||||
}
|
||||
|
||||
// JustReleased returns whether the Button has just been released up.
|
||||
func (w *Window) JustReleased(button Button) bool {
|
||||
return !w.currInp.buttons[button] && w.prevInp.buttons[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
|
||||
}
|
||||
|
||||
// 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.tempInp.buttons[Button(button)] = true
|
||||
case glfw.Release:
|
||||
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.tempInp.buttons[Button(key)] = true
|
||||
case glfw.Release:
|
||||
w.tempInp.buttons[Button(key)] = false
|
||||
case glfw.Repeat:
|
||||
w.tempInp.repeat[Button(key)] = true
|
||||
}
|
||||
})
|
||||
|
||||
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.prevInp = w.currInp
|
||||
w.currInp = w.tempInp
|
||||
|
||||
w.tempInp.repeat = [KeyLast + 1]bool{}
|
||||
w.tempInp.scroll = pixel.ZV
|
||||
w.tempInp.typed = ""
|
||||
}
|
97
vendor/github.com/faiface/pixel/pixelgl/monitor.go
generated
vendored
Normal file
97
vendor/github.com/faiface/pixel/pixelgl/monitor.go
generated
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
package pixelgl
|
||||
|
||||
import (
|
||||
"github.com/faiface/mainthread"
|
||||
"github.com/go-gl/glfw/v3.2/glfw"
|
||||
)
|
||||
|
||||
// Monitor represents a physical display attached to your computer.
|
||||
type Monitor struct {
|
||||
monitor *glfw.Monitor
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
33
vendor/github.com/faiface/pixel/pixelgl/run.go
generated
vendored
Normal file
33
vendor/github.com/faiface/pixel/pixelgl/run.go
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
package pixelgl
|
||||
|
||||
import (
|
||||
"github.com/faiface/mainthread"
|
||||
"github.com/go-gl/glfw/v3.2/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)
|
||||
}
|
15
vendor/github.com/faiface/pixel/pixelgl/util.go
generated
vendored
Normal file
15
vendor/github.com/faiface/pixel/pixelgl/util.go
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
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
|
||||
}
|
426
vendor/github.com/faiface/pixel/pixelgl/window.go
generated
vendored
Normal file
426
vendor/github.com/faiface/pixel/pixelgl/window.go
generated
vendored
Normal file
@@ -0,0 +1,426 @@
|
||||
package pixelgl
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/color"
|
||||
"runtime"
|
||||
|
||||
"github.com/faiface/glhf"
|
||||
"github.com/faiface/mainthread"
|
||||
"github.com/faiface/pixel"
|
||||
"github.com/go-gl/glfw/v3.2/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
|
||||
|
||||
// If set to nil, the Window will be windowed. Otherwise it will be fullscreen on the
|
||||
// specified Monitor.
|
||||
Monitor *Monitor
|
||||
|
||||
// Whether the Window is resizable.
|
||||
Resizable bool
|
||||
|
||||
// Undecorated Window ommits the borders and decorations (close button, etc.).
|
||||
Undecorated bool
|
||||
|
||||
// VSync (vertical synchronization) synchronizes Window's framerate with the framerate of
|
||||
// the monitor.
|
||||
VSync bool
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
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}
|
||||
|
||||
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])
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// enter the OpenGL context
|
||||
w.begin()
|
||||
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() {
|
||||
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()
|
||||
})
|
||||
|
||||
w.UpdateInput()
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 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()
|
||||
glhf.Init()
|
||||
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)
|
||||
}
|
117
vendor/github.com/faiface/pixel/sprite.go
generated
vendored
Normal file
117
vendor/github.com/faiface/pixel/sprite.go
generated
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
package pixel
|
||||
|
||||
import "image/color"
|
||||
|
||||
// Sprite is a drawable frame of a Picture. It's anchored by the center of it's Picture's frame.
|
||||
//
|
||||
// Frame specifies a rectangular portion of the Picture that will be drawn. For example, this
|
||||
// creates a Sprite that draws the whole Picture:
|
||||
//
|
||||
// sprite := pixel.NewSprite(pic, pic.Bounds())
|
||||
//
|
||||
// Note, that Sprite caches the results of MakePicture from Targets it's drawn to for each Picture
|
||||
// it's set to. What it means is that using a Sprite with an unbounded number of Pictures leads to a
|
||||
// memory leak, since Sprite caches them and never forgets. In such a situation, create a new Sprite
|
||||
// for each Picture.
|
||||
type Sprite struct {
|
||||
tri *TrianglesData
|
||||
frame Rect
|
||||
d Drawer
|
||||
|
||||
matrix Matrix
|
||||
mask RGBA
|
||||
}
|
||||
|
||||
// NewSprite creates a Sprite from the supplied frame of a Picture.
|
||||
func NewSprite(pic Picture, frame Rect) *Sprite {
|
||||
tri := MakeTrianglesData(6)
|
||||
s := &Sprite{
|
||||
tri: tri,
|
||||
d: Drawer{Triangles: tri},
|
||||
}
|
||||
s.matrix = IM
|
||||
s.mask = Alpha(1)
|
||||
s.Set(pic, frame)
|
||||
return s
|
||||
}
|
||||
|
||||
// Set sets a new frame of a Picture for this Sprite.
|
||||
func (s *Sprite) Set(pic Picture, frame Rect) {
|
||||
s.d.Picture = pic
|
||||
if frame != s.frame {
|
||||
s.frame = frame
|
||||
s.calcData()
|
||||
}
|
||||
}
|
||||
|
||||
// Picture returns the current Sprite's Picture.
|
||||
func (s *Sprite) Picture() Picture {
|
||||
return s.d.Picture
|
||||
}
|
||||
|
||||
// Frame returns the current Sprite's frame.
|
||||
func (s *Sprite) Frame() Rect {
|
||||
return s.frame
|
||||
}
|
||||
|
||||
// Draw draws the Sprite onto the provided Target. The Sprite will be transformed by the given Matrix.
|
||||
//
|
||||
// This method is equivalent to calling DrawColorMask with nil color mask.
|
||||
func (s *Sprite) Draw(t Target, matrix Matrix) {
|
||||
s.DrawColorMask(t, matrix, nil)
|
||||
}
|
||||
|
||||
// DrawColorMask draws the Sprite onto the provided Target. The Sprite will be transformed by the
|
||||
// given Matrix and all of it's color will be multiplied by the given mask.
|
||||
//
|
||||
// If the mask is nil, a fully opaque white mask will be used, which causes no effect.
|
||||
func (s *Sprite) DrawColorMask(t Target, matrix Matrix, mask color.Color) {
|
||||
dirty := false
|
||||
if matrix != s.matrix {
|
||||
s.matrix = matrix
|
||||
dirty = true
|
||||
}
|
||||
if mask == nil {
|
||||
mask = Alpha(1)
|
||||
}
|
||||
rgba := ToRGBA(mask)
|
||||
if rgba != s.mask {
|
||||
s.mask = rgba
|
||||
dirty = true
|
||||
}
|
||||
|
||||
if dirty {
|
||||
s.calcData()
|
||||
}
|
||||
|
||||
s.d.Draw(t)
|
||||
}
|
||||
|
||||
func (s *Sprite) calcData() {
|
||||
var (
|
||||
center = s.frame.Center()
|
||||
horizontal = V(s.frame.W()/2, 0)
|
||||
vertical = V(0, s.frame.H()/2)
|
||||
)
|
||||
|
||||
(*s.tri)[0].Position = Vec{}.Sub(horizontal).Sub(vertical)
|
||||
(*s.tri)[1].Position = Vec{}.Add(horizontal).Sub(vertical)
|
||||
(*s.tri)[2].Position = Vec{}.Add(horizontal).Add(vertical)
|
||||
(*s.tri)[3].Position = Vec{}.Sub(horizontal).Sub(vertical)
|
||||
(*s.tri)[4].Position = Vec{}.Add(horizontal).Add(vertical)
|
||||
(*s.tri)[5].Position = Vec{}.Sub(horizontal).Add(vertical)
|
||||
|
||||
for i := range *s.tri {
|
||||
(*s.tri)[i].Color = s.mask
|
||||
(*s.tri)[i].Picture = center.Add((*s.tri)[i].Position)
|
||||
(*s.tri)[i].Intensity = 1
|
||||
}
|
||||
|
||||
// matrix and mask
|
||||
for i := range *s.tri {
|
||||
(*s.tri)[i].Position = s.matrix.Project((*s.tri)[i].Position)
|
||||
(*s.tri)[i].Color = s.mask
|
||||
}
|
||||
|
||||
s.d.Dirty()
|
||||
}
|
Reference in New Issue
Block a user