-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsimulation.go
165 lines (135 loc) · 3.63 KB
/
simulation.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
package main
import (
"image"
"os"
_ "image/gif"
_ "image/jpeg"
_ "image/png"
_ "github.com/hexaflex/pnm"
"github.com/go-gl/gl/v4.2-core/gl"
"github.com/hexaflex/wireworld-gpu/math"
)
// Simulation implements the GPU driven wireworld simulation.
type Simulation struct {
shader Shader
input SimulationState
output SimulationState
vao uint32
vbo uint32
}
// NewSimulation creates a new, empty simulation with the given dimensions.
func NewSimulation(size math.Vec2) (*Simulation, error) {
var err error
var s Simulation
s.shader, err = SimulationShader.Compile()
if err != nil {
return nil, err
}
if err = s.input.Init(size); err != nil {
return nil, err
}
if err = s.output.Init(size); err != nil {
s.Release()
return nil, err
}
var verts = []float32{
// x,y,u,v
-1, -1, 0, 0,
1, -1, 1, 0,
-1, 1, 0, 1,
1, -1, 1, 0,
1, 1, 1, 1,
-1, 1, 0, 1}
gl.GenVertexArrays(1, &s.vao)
gl.BindVertexArray(s.vao)
gl.GenBuffers(1, &s.vbo)
gl.BindBuffer(gl.ARRAY_BUFFER, s.vbo)
gl.EnableVertexAttribArray(0)
gl.EnableVertexAttribArray(1)
gl.VertexAttribPointer(0, 2, gl.FLOAT, false, 4*4, gl.PtrOffset(0))
gl.VertexAttribPointer(1, 2, gl.FLOAT, false, 4*4, gl.PtrOffset(2*4))
gl.BufferData(gl.ARRAY_BUFFER, len(verts)*4, gl.Ptr(verts), gl.STATIC_DRAW)
gl.BindBuffer(gl.ARRAY_BUFFER, 0)
gl.BindVertexArray(0)
return &s, nil
}
// LoadSimulation loads a simulation from the given image file.
// Supported formats: PNG, JPG, GIF, PNM
//
// It uses the given color palette to recognize cell states.
func LoadSimulation(file string, pal *Palette) (*Simulation, error) {
fd, err := os.Open(file)
if err != nil {
return nil, err
}
img, _, err := image.Decode(fd)
fd.Close()
if err != nil {
return nil, err
}
b := img.Bounds()
size := math.Vec2{float32(b.Dx()), float32(b.Dy())}
sim, err := NewSimulation(size)
if err != nil {
return nil, err
}
// Set the input buffer to the image data.
pix, size := pal.toInternalFormat(img)
sim.input.SetData(pix, size)
return sim, nil
}
// Release unloads simulator resources.
func (s *Simulation) Release() {
gl.DeleteBuffers(1, &s.vbo)
gl.DeleteVertexArrays(1, &s.vao)
s.shader.Release()
s.input.Release()
s.output.Release()
}
// Size returns the cell dimensions of the simulation.
func (s *Simulation) Size() math.Vec2 {
return s.output.Size()
}
// Image returns the current simulation state as an image,
// colored using the given palette. Note that this uses
// glReadPixels and consequently is rather slow. Use it sparingly.
func (s *Simulation) Image(pal *Palette) image.Image {
// We read from input because the render function sets
// this to the most recent simulation state.
size := s.input.Size()
pix := s.input.Data()
return pal.fromInternalFormat(pix, size)
}
// Bind binds the current simulation state's texture, so it may be
// used in other rendering operations.
func (s *Simulation) Bind() {
s.input.BindTexture()
}
// Unbind unbinds the current texture.
func (s *Simulation) Unbind() {
s.input.UnbindTexture()
}
// Step runs the simulation n times.
func (s *Simulation) Step(n int) {
if n < 1 {
return
}
s.shader.Use()
size := s.input.Size()
gl.Viewport(0, 0, int32(size[0]), int32(size[1]))
gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
gl.BindVertexArray(s.vao)
gl.ActiveTexture(gl.TEXTURE0)
for i := 0; i < n; i++ {
s.output.BindBuffer()
s.input.BindTexture()
gl.DrawArrays(gl.TRIANGLES, 0, 6)
s.input.UnbindTexture()
s.output.UnbindBuffer()
// Swap the states around. So the output of this pass
// becomes the input of the next pass.
s.output, s.input = s.input, s.output
}
gl.BindVertexArray(0)
s.shader.Unuse()
}