3 #include <gmath/gmath.h>
11 * - whistle hhgg music
12 * - colliding particles merge
13 * - select objects and center camera on them
37 #define GRID_SIZE 2048
39 #define GRID_X(idx) (((idx) >> GRID_BITS) & (GRID_SIZE - 1))
40 #define GRID_Y(idx) ((idx) & (GRID_SIZE - 1))
41 #define GRID_DELTA ((float)FIELD_SIZE / (float)GRID_SIZE)
43 #define FIELD_SIZE 2048
44 #define MIN_CAM_DIST 1.0f
45 #define MAX_CAM_DIST 350.0f
47 #define MASS_TO_RAD(m) log((m) + 1.0)
49 #define CONTRIB_THRES 0.005
50 #define CONTRIB_RANGE(m) sqrt((m) / CONTRIB_THRES)
52 static int pos_to_grid(float x, float y);
53 static Vec2 grid_to_pos(int gx, int gy);
55 static float grid[GRID_SIZE * GRID_SIZE];
56 static Particle grid_part[GRID_SIZE * GRID_SIZE];
57 static Texture *grid_tex;
59 static std::vector<Emitter*> emitters;
61 static Texture *gvis_tex; // texture tile for visualizing a grid
62 static unsigned int field_sdr;
64 static float cam_theta;
65 static float cam_dist = 100.0f;
66 static Vec2 *targ_pos;
67 static Mat4 view_matrix, proj_matrix;
69 // emitter placement data (filled by event handlers, completed in update)
70 static bool emit_place_pending;
71 static Vec2 emit_place_pos;
74 bool GameScreen::init()
76 grid_tex = new Texture;
77 grid_tex->create(GRID_SIZE, GRID_SIZE, TEX_2D, GL_LUMINANCE32F_ARB);
78 grid_tex->set_anisotropy(glcaps.max_aniso);
80 gvis_tex = new Texture;
81 if(!gvis_tex->load("data/purple_grid.png")) {
84 gvis_tex->set_anisotropy(glcaps.max_aniso);
86 unsigned int vsdr, tcsdr, tesdr, psdr;
88 if(!(vsdr = load_vertex_shader("sdr/field.v.glsl")) ||
89 !(tcsdr = load_tessctl_shader("sdr/field.tc.glsl")) ||
90 !(tesdr = load_tesseval_shader("sdr/field.te.glsl")) ||
91 !(psdr = load_pixel_shader("sdr/field.p.glsl"))) {
95 if(!(field_sdr = create_program_link(vsdr, tcsdr, tesdr, psdr, 0))) {
98 set_uniform_int(field_sdr, "gvis_tex", 0);
99 set_uniform_int(field_sdr, "field_tex", 1);
100 set_uniform_int(field_sdr, "tess_level", 2);
103 emit_place_pos = Vec2(0, 0);
104 emit_place_pending = true;
109 void GameScreen::destroy()
114 static void calc_contrib_bounds(const Emitter *em, Rect *rect)
116 int gidx = pos_to_grid(em->pos.x, em->pos.y);
117 int gx = GRID_X(gidx);
118 int gy = GRID_Y(gidx);
119 int maxrange = (int)ceil(CONTRIB_RANGE(em->mass));
121 int sx = gx - maxrange;
122 int sy = gy - maxrange;
123 int ex = gx + maxrange;
124 int ey = gy + maxrange;
126 if(ex > GRID_SIZE) ex = GRID_SIZE;
127 if(ey > GRID_SIZE) ey = GRID_SIZE;
129 rect->x = sx < 0 ? 0 : sx;
130 rect->y = sy < 0 ? 0 : sy;
131 rect->width = ex - sx;
132 rect->height = ey - sy;
135 static void simstep()
137 // calculate gravitational field - assume field within radius constant: m / r^2
139 // first clear the field, and then add contributions
140 memset(grid, 0, sizeof grid);
142 // contribution of emitters
143 int num_emitters = emitters.size();
144 for(int i=0; i<num_emitters; i++) {
145 Emitter *em = emitters[i];
147 calc_contrib_bounds(em, &cbox);
148 float emradius = MASS_TO_RAD(em->mass);
150 float *gptr = grid + cbox.y * GRID_SIZE + cbox.x;
151 Vec2 startpos = grid_to_pos(cbox.x, cbox.y);
153 for(int y=0; y<cbox.height; y++) {
154 for(int x=0; x<cbox.width; x++) {
155 Vec2 cellpos = Vec2(startpos.x + (float)x * GRID_DELTA, startpos.y);
157 Vec2 dir = cellpos - em->pos;
158 float dsq = dot(dir, dir);
159 float radsq = emradius * emradius;
164 gptr[x] -= em->mass / dsq;
167 startpos.y += GRID_DELTA;
173 assert(glGetError() == GL_NO_ERROR);
175 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, GRID_SIZE, GRID_SIZE, GL_LUMINANCE,
177 assert(glGetError() == GL_NO_ERROR);
182 if(emit_place_pending) {
183 emit_place_pending = false;
184 Emitter *em = new Emitter;
185 em->pos = emit_place_pos;
191 emitters.push_back(em);
194 calc_contrib_bounds(em, &cbox);
195 printf("bounds: %d,%d %dx%d\n", cbox.x, cbox.y, cbox.width, cbox.height);
199 static float interval;
200 interval += frame_dt;
201 if(interval >= SIM_DT) {
204 assert(glGetError() == GL_NO_ERROR);
207 // update projection matrix
208 proj_matrix.perspective(deg_to_rad(60.0f), win_aspect, 0.5, 5000.0);
210 // update view matrix
213 targ.x = targ_pos->x;
214 targ.z = targ_pos->y;
217 float theta = -deg_to_rad(cam_theta);
218 Vec3 camdir = Vec3(sin(theta) * cam_dist, pow(cam_dist * 0.1, 2.0) + 0.5, cos(theta) * cam_dist);
219 Vec3 campos = targ + camdir;
221 view_matrix.inv_lookat(campos, targ, Vec3(0, 1, 0));
224 void GameScreen::draw()
228 glClearColor(0.01, 0.01, 0.01, 1);
229 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
231 glMatrixMode(GL_PROJECTION);
232 glLoadMatrixf(proj_matrix[0]);
233 glMatrixMode(GL_MODELVIEW);
234 glLoadMatrixf(view_matrix[0]);
236 glPushAttrib(GL_ENABLE_BIT);
237 glDisable(GL_LIGHTING);
238 glDisable(GL_CULL_FACE);
240 glEnable(GL_TEXTURE_2D);
241 bind_texture(gvis_tex, 0);
242 bind_texture(grid_tex, 1);
244 glUseProgram(field_sdr);
246 float maxu = FIELD_SIZE / 32.0f;
247 float maxv = FIELD_SIZE / 32.0f;
248 float hsz = FIELD_SIZE * 0.5f;
253 glMultiTexCoord2f(0, 0, 0);
254 glMultiTexCoord2f(1, 0, 0);
255 glVertex3f(-hsz, 0, -hsz);
257 glMultiTexCoord2f(0, maxu, 0);
258 glMultiTexCoord2f(1, 1, 0);
259 glVertex3f(hsz, 0, -hsz);
261 glMultiTexCoord2f(0, maxu, maxv);
262 glMultiTexCoord2f(1, 1, 1);
263 glVertex3f(hsz, 0, hsz);
265 glMultiTexCoord2f(0, 0, maxv);
266 glMultiTexCoord2f(1, 0, 1);
267 glVertex3f(-hsz, 0, hsz);
275 void GameScreen::reshape(int x, int y)
281 void GameScreen::keyboard(int key, bool pressed)
295 static int prev_x, prev_y;
297 void GameScreen::mbutton(int bn, bool pressed, int x, int y)
303 void GameScreen::mmotion(int x, int y)
310 if(game_bnstate(2)) {
311 cam_theta += dx * 0.5;
315 void GameScreen::mwheel(int dir, int x, int y)
317 cam_dist -= dir * cam_dist * 0.05f;
318 if(cam_dist <= MIN_CAM_DIST) cam_dist = MIN_CAM_DIST;
319 if(cam_dist > MAX_CAM_DIST) cam_dist = MAX_CAM_DIST;
323 static int pos_to_grid(float x, float y)
325 int gx = ((x / (float)FIELD_SIZE) + 0.5f) * (float)GRID_SIZE;
326 int gy = ((y / (float)FIELD_SIZE) + 0.5f) * (float)GRID_SIZE;
329 if(gx >= GRID_SIZE) gx = GRID_SIZE - 1;
331 if(gy >= GRID_SIZE) gy = GRID_SIZE - 1;
333 return (gx << GRID_BITS) | gy;
336 static Vec2 grid_to_pos(int gx, int gy)
338 float x = (((float)gx / (float)GRID_SIZE) - 0.5f) * (float)FIELD_SIZE;
339 float y = (((float)gy / (float)GRID_SIZE) - 0.5f) * (float)FIELD_SIZE;