tesselation
[ld42_outofspace] / src / gamescr.cc
1 #include <assert.h>
2 #include <vector>
3 #include <gmath/gmath.h>
4 #include "game.h"
5 #include "screen.h"
6 #include "opengl.h"
7 #include "texture.h"
8 #include "sdr.h"
9
10 /* NOTES:
11  * - whistle hhgg music
12  * - colliding particles merge
13  * - select objects and center camera on them
14  */
15
16 struct Particle {
17         float radius;
18         float mass;
19         Vec2 pos;
20         Vec2 vel;
21 };
22
23 struct Emitter {
24         Vec2 pos;
25         float mass;
26         float rate, chunk;
27         float angle, spread;
28 };
29
30 struct Rect {
31         int x, y;
32         int width, height;
33 };
34
35 struct QuadMesh {
36         Vec3 *v;
37         Vec2 *uv;
38         uint16_t *idx;
39
40         int num_verts, num_idx, num_quads;
41
42         unsigned int vbo_v, vbo_uv, ibo;
43 };
44
45 #define SIM_DT          0.016
46
47 #define GRID_SIZE       2048
48 #define GRID_BITS       12
49 #define GRID_X(idx)     (((idx) >> GRID_BITS) & (GRID_SIZE - 1))
50 #define GRID_Y(idx)     ((idx) & (GRID_SIZE - 1))
51 #define GRID_DELTA      ((float)FIELD_SIZE / (float)GRID_SIZE)
52
53 #define FIELD_SIZE      2048
54 #define MIN_CAM_DIST    1.0f
55 #define MAX_CAM_DIST    350.0f
56
57 #define MASS_TO_RAD(m)  log((m) + 1.0)
58
59 #define CONTRIB_THRES   0.005
60 #define CONTRIB_RANGE(m) sqrt((m) / CONTRIB_THRES)
61
62 static int pos_to_grid(float x, float y);
63 static Vec2 grid_to_pos(int gx, int gy);
64
65 static void destroy_quadmesh(QuadMesh *m);
66 static void draw_quadmesh(const QuadMesh *m, unsigned int prim = GL_QUADS);
67 static void gen_quad_plane(QuadMesh *mesh, float width, float height, int usub, int vsub);
68
69 static float grid[GRID_SIZE * GRID_SIZE];
70 static Particle grid_part[GRID_SIZE * GRID_SIZE];
71 static Texture *grid_tex;
72
73 static std::vector<Emitter*> emitters;
74
75 static Texture *gvis_tex;       // texture tile for visualizing a grid
76 static unsigned int field_sdr;
77 static int tess_level = 32;
78 static float field_scale = 16.0f;
79
80 static QuadMesh field_mesh;
81
82 static float cam_theta;
83 static float cam_dist = 100.0f;
84 static Vec2 *targ_pos;
85 static Mat4 view_matrix, proj_matrix;
86
87 // emitter placement data (filled by event handlers, completed in update)
88 static bool emit_place_pending;
89 static Vec2 emit_place_pos;
90
91
92 bool GameScreen::init()
93 {
94         grid_tex = new Texture;
95         grid_tex->create(GRID_SIZE, GRID_SIZE, TEX_2D, GL_LUMINANCE32F_ARB);
96         grid_tex->set_anisotropy(glcaps.max_aniso);
97
98         gvis_tex = new Texture;
99         if(!gvis_tex->load("data/purple_grid.png")) {
100                 return false;
101         }
102         gvis_tex->set_anisotropy(glcaps.max_aniso);
103
104         unsigned int vsdr, tcsdr, tesdr, psdr;
105
106         if(!(vsdr = load_vertex_shader("sdr/field.v.glsl")) ||
107                         !(tcsdr = load_tessctl_shader("sdr/field.tc.glsl")) ||
108                         !(tesdr = load_tesseval_shader("sdr/field.te.glsl")) ||
109                         !(psdr = load_pixel_shader("sdr/field.p.glsl"))) {
110                 return false;
111         }
112
113         if(!(field_sdr = create_program_link(vsdr, tcsdr, tesdr, psdr, 0))) {
114                 return false;
115         }
116         set_uniform_int(field_sdr, "gvis_tex", 0);
117         set_uniform_int(field_sdr, "field_tex", 1);
118         set_uniform_float(field_sdr, "gvis_scale", FIELD_SIZE / 32.0f);
119         set_uniform_int(field_sdr, "tess_level", tess_level);
120         set_uniform_float(field_sdr, "field_scale", field_scale);
121
122         gen_quad_plane(&field_mesh, FIELD_SIZE, FIELD_SIZE, 32, 32);
123
124         // XXX DBG
125         emit_place_pos = Vec2(0, 0);
126         emit_place_pending = true;
127
128         assert(glGetError() == GL_NO_ERROR);
129         return true;
130 }
131
132 void GameScreen::destroy()
133 {
134         free_program(field_sdr);
135         delete gvis_tex;
136         delete grid_tex;
137         destroy_quadmesh(&field_mesh);
138 }
139
140 static void calc_contrib_bounds(const Emitter *em, Rect *rect)
141 {
142         int gidx = pos_to_grid(em->pos.x, em->pos.y);
143         int gx = GRID_X(gidx);
144         int gy = GRID_Y(gidx);
145         int maxrange = (int)ceil(CONTRIB_RANGE(em->mass));
146
147         int sx = gx - maxrange;
148         int sy = gy - maxrange;
149         int ex = gx + maxrange;
150         int ey = gy + maxrange;
151
152         if(ex > GRID_SIZE) ex = GRID_SIZE;
153         if(ey > GRID_SIZE) ey = GRID_SIZE;
154
155         rect->x = sx < 0 ? 0 : sx;
156         rect->y = sy < 0 ? 0 : sy;
157         rect->width = ex - sx;
158         rect->height = ey - sy;
159 }
160
161 static void simstep()
162 {
163         // calculate gravitational field - assume field within radius constant: m / r^2
164
165         // first clear the field, and then add contributions
166         memset(grid, 0, sizeof grid);
167
168         // contribution of emitters
169         int num_emitters = emitters.size();
170         for(int i=0; i<num_emitters; i++) {
171                 Emitter *em = emitters[i];
172                 Rect cbox;
173                 calc_contrib_bounds(em, &cbox);
174                 float emradius = MASS_TO_RAD(em->mass);
175
176                 float *gptr = grid + cbox.y * GRID_SIZE + cbox.x;
177                 Vec2 startpos = grid_to_pos(cbox.x, cbox.y);
178
179                 for(int y=0; y<cbox.height; y++) {
180                         for(int x=0; x<cbox.width; x++) {
181                                 Vec2 cellpos = Vec2(startpos.x + (float)x * GRID_DELTA, startpos.y);
182
183                                 Vec2 dir = cellpos - em->pos;
184                                 float dsq = dot(dir, dir);
185                                 float radsq = emradius * emradius;
186                                 if(dsq < radsq) {
187                                         dsq = radsq;
188                                 }
189
190                                 gptr[x] -= em->mass / dsq;
191                         }
192
193                         startpos.y += GRID_DELTA;
194                         gptr += GRID_SIZE;
195                 }
196         }
197
198         // update texture
199         assert(glGetError() == GL_NO_ERROR);
200         grid_tex->bind();
201         glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, GRID_SIZE, GRID_SIZE, GL_LUMINANCE,
202                         GL_FLOAT, grid);
203         assert(glGetError() == GL_NO_ERROR);
204 }
205
206 static void update()
207 {
208         if(emit_place_pending) {
209                 emit_place_pending = false;
210                 Emitter *em = new Emitter;
211                 em->pos = emit_place_pos;
212                 em->mass = 100;
213                 em->rate = 1;
214                 em->chunk = 0.05;
215                 em->angle = -1;
216                 em->spread = 0;
217                 emitters.push_back(em);
218
219                 Rect cbox;
220                 calc_contrib_bounds(em, &cbox);
221                 printf("bounds: %d,%d %dx%d\n", cbox.x, cbox.y, cbox.width, cbox.height);
222         }
223
224         // update simulation
225         static float interval;
226         interval += frame_dt;
227         if(interval >= SIM_DT) {
228                 interval -= SIM_DT;
229                 simstep();
230                 assert(glGetError() == GL_NO_ERROR);
231         }
232
233         // update projection matrix
234         proj_matrix.perspective(deg_to_rad(60.0f), win_aspect, 0.5, 5000.0);
235
236         // update view matrix
237         Vec3 targ;
238         if(targ_pos) {
239                 targ.x = targ_pos->x;
240                 targ.z = targ_pos->y;
241         }
242
243         float theta = -deg_to_rad(cam_theta);
244         Vec3 camdir = Vec3(sin(theta) * cam_dist, pow(cam_dist * 0.1, 2.0) + 0.5, cos(theta) * cam_dist);
245         Vec3 campos = targ + camdir;
246
247         view_matrix.inv_lookat(campos, targ, Vec3(0, 1, 0));
248 }
249
250 void GameScreen::draw()
251 {
252         update();
253
254         glClearColor(0.01, 0.01, 0.01, 1);
255         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
256
257         glMatrixMode(GL_PROJECTION);
258         glLoadMatrixf(proj_matrix[0]);
259         glMatrixMode(GL_MODELVIEW);
260         glLoadMatrixf(view_matrix[0]);
261
262         glPushAttrib(GL_ENABLE_BIT);
263         glDisable(GL_LIGHTING);
264         glDisable(GL_CULL_FACE);
265
266         glEnable(GL_TEXTURE_2D);
267         bind_texture(gvis_tex, 0);
268         bind_texture(grid_tex, 1);
269
270         glUseProgram(field_sdr);
271         assert(glGetError() == GL_NO_ERROR);
272
273         glPatchParameteri(GL_PATCH_VERTICES, 4);
274         draw_quadmesh(&field_mesh, GL_PATCHES);
275
276         glUseProgram(0);
277
278         glPopAttrib();
279
280         assert(glGetError() == GL_NO_ERROR);
281 }
282
283 void GameScreen::reshape(int x, int y)
284 {
285 }
286
287
288
289 void GameScreen::keyboard(int key, bool pressed)
290 {
291         if(pressed) {
292                 switch(key) {
293                 case KEY_ESC:
294                         pop_screen();
295                         break;
296
297                 case ']':
298                         if(tess_level < glcaps.max_tess_level) {
299                                 tess_level++;
300                                 printf("tessellation level: %d\n", tess_level);
301                                 set_uniform_int(field_sdr, "tess_level", tess_level);
302                                 glUseProgram(0);
303                         }
304                         break;
305
306                 case '[':
307                         if(tess_level > 1) {
308                                 tess_level--;
309                                 printf("tessellation level: %d\n", tess_level);
310                                 set_uniform_int(field_sdr, "tess_level", tess_level);
311                                 glUseProgram(0);
312                         }
313                         break;
314
315                 case '=':
316                         field_scale += 0.5;
317                         printf("field scale: %f\n", field_scale);
318                         set_uniform_float(field_sdr, "field_scale", field_scale);
319                         break;
320
321                 case '-':
322                         field_scale -= 0.5;
323                         if(field_scale < 0.0f) {
324                                 field_scale = 0.0f;
325                         }
326                         printf("field scale: %f\n", field_scale);
327                         set_uniform_float(field_sdr, "field_scale", field_scale);
328                         break;
329
330                 default:
331                         break;
332                 }
333         }
334 }
335
336 static int prev_x, prev_y;
337
338 void GameScreen::mbutton(int bn, bool pressed, int x, int y)
339 {
340         prev_x = x;
341         prev_y = y;
342 }
343
344 void GameScreen::mmotion(int x, int y)
345 {
346         int dx = x - prev_x;
347         int dy = y - prev_y;
348         prev_x = x;
349         prev_y = y;
350
351         if(game_bnstate(2)) {
352                 cam_theta += dx * 0.5;
353         }
354 }
355
356 void GameScreen::mwheel(int dir, int x, int y)
357 {
358         cam_dist -= dir * cam_dist * 0.05f;
359         if(cam_dist <= MIN_CAM_DIST) cam_dist = MIN_CAM_DIST;
360         if(cam_dist > MAX_CAM_DIST) cam_dist = MAX_CAM_DIST;
361 }
362
363
364 static int pos_to_grid(float x, float y)
365 {
366         int gx = ((x / (float)FIELD_SIZE) + 0.5f) * (float)GRID_SIZE;
367         int gy = ((y / (float)FIELD_SIZE) + 0.5f) * (float)GRID_SIZE;
368
369         if(gx < 0) gx = 0;
370         if(gx >= GRID_SIZE) gx = GRID_SIZE - 1;
371         if(gy < 0) gy = 0;
372         if(gy >= GRID_SIZE) gy = GRID_SIZE - 1;
373
374         return (gx << GRID_BITS) | gy;
375 }
376
377 static Vec2 grid_to_pos(int gx, int gy)
378 {
379         float x = (((float)gx / (float)GRID_SIZE) - 0.5f) * (float)FIELD_SIZE;
380         float y = (((float)gy / (float)GRID_SIZE) - 0.5f) * (float)FIELD_SIZE;
381
382         return Vec2(x, y);
383 }
384
385 static void destroy_quadmesh(QuadMesh *m)
386 {
387         glDeleteBuffers(1, &m->vbo_v);
388         glDeleteBuffers(1, &m->vbo_uv);
389         glDeleteBuffers(1, &m->ibo);
390
391         delete [] m->v;
392         delete [] m->uv;
393         delete [] m->idx;
394 }
395
396 static void draw_quadmesh(const QuadMesh *m, unsigned int prim)
397 {
398         glEnableClientState(GL_VERTEX_ARRAY);
399         glEnableClientState(GL_TEXTURE_COORD_ARRAY);
400
401         glBindBuffer(GL_ARRAY_BUFFER, m->vbo_v);
402         glVertexPointer(3, GL_FLOAT, 0, 0);
403
404         glBindBuffer(GL_ARRAY_BUFFER, m->vbo_uv);
405         glTexCoordPointer(2, GL_FLOAT, 0, 0);
406
407         glBindBuffer(GL_ARRAY_BUFFER, 0);
408
409         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m->ibo);
410         glDrawElements(prim, m->num_idx, GL_UNSIGNED_SHORT, 0);
411         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
412
413         glDisableClientState(GL_VERTEX_ARRAY);
414         glDisableClientState(GL_TEXTURE_COORD_ARRAY);
415 }
416
417 static void gen_quad_plane(QuadMesh *m, float width, float height, int usub, int vsub)
418 {
419         Vec3 *vptr;
420         Vec2 *uvptr;
421         uint16_t *iptr;
422
423         if(usub < 1) usub = 1;
424         if(vsub < 1) vsub = 1;
425
426         int uverts = usub + 1;
427         int vverts = vsub + 1;
428         m->num_verts = uverts * vverts;
429         m->num_quads = usub * vsub;
430         m->num_idx = m->num_quads * 4;
431
432         vptr = m->v = new Vec3[m->num_verts];
433         uvptr = m->uv = new Vec2[m->num_verts];
434         iptr = m->idx = new uint16_t[m->num_idx];
435
436         float du = 1.0f / (float)usub;
437         float dv = 1.0f / (float)vsub;
438
439         float u = 0.0f;
440         for(int i=0; i<uverts; i++) {
441                 float x = (u - 0.5f) * width;
442                 float v = 0.0f;
443                 for(int j=0; j<vverts; j++) {
444                         float y = (v - 0.5f) * height;
445
446                         *vptr++ = Vec3(x, 0, y);
447                         *uvptr++ = Vec2(u, v);
448
449                         if(i < usub && j < vsub) {
450                                 int idx = i * vverts + j;
451
452                                 *iptr++ = idx;
453                                 *iptr++ = idx + vverts;
454                                 *iptr++ = idx + vverts + 1;
455                                 *iptr++ = idx + 1;
456                         }
457
458                         v += dv;
459                 }
460                 u += du;
461         }
462
463         glGenBuffers(1, &m->vbo_v);
464         glBindBuffer(GL_ARRAY_BUFFER, m->vbo_v);
465         glBufferData(GL_ARRAY_BUFFER, m->num_verts * 3 * sizeof(float), m->v, GL_STATIC_DRAW);
466
467         glGenBuffers(1, &m->vbo_uv);
468         glBindBuffer(GL_ARRAY_BUFFER, m->vbo_uv);
469         glBufferData(GL_ARRAY_BUFFER, m->num_verts * 2 * sizeof(float), m->uv, GL_STATIC_DRAW);
470
471         glGenBuffers(1, &m->ibo);
472         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m->ibo);
473         glBufferData(GL_ELEMENT_ARRAY_BUFFER, m->num_idx * sizeof(uint16_t), m->idx, GL_STATIC_DRAW);
474
475         glBindBuffer(GL_ARRAY_BUFFER, 0);
476         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
477 }