metaballs
[dosdemo] / src / metaball.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <math.h>
5 #include <assert.h>
6 #include "screen.h"
7 #include "demo.h"
8 #include "3dgfx.h"
9 #include "gfxutil.h"
10 #include "util.h"
11 #include "metasurf.h"
12 #include "dynarr.h"
13
14 struct mesh {
15         int prim;
16         struct g3d_vertex *varr;
17         int16_t *iarr;
18         int vcount, icount;
19 };
20
21 struct metaball {
22         float energy;
23         float pos[3];
24 };
25
26 static int init(void);
27 static void destroy(void);
28 static void start(long trans_time);
29 static void draw(void);
30 static void draw_mesh(struct mesh *mesh);
31 static void zsort(struct mesh *m);
32
33 static void calc_voxel_field(void);
34 static float eval(struct metasurface *ms, float x, float y, float z);
35 static void emit_vertex(struct metasurface *ms, float x, float y, float z);
36
37 static struct screen scr = {
38         "metaballs",
39         init,
40         destroy,
41         start, 0,
42         draw
43 };
44
45 static float cam_theta, cam_phi = 25;
46 static float cam_dist = 10;
47 static struct mesh mmesh;
48
49 static struct metasurface *msurf;
50
51 #define VOL_SZ  32
52 #define VOL_SCALE       10.0f
53 #define VOX_DIST        (VOL_SCALE / VOL_SZ)
54 #define VOL_HALF_SCALE  (VOL_SCALE * 0.5f)
55 static float *volume;
56 #define VOXEL(x, y, z)  (volume[(z) * VOL_SZ * VOL_SZ + (y) * VOL_SZ + (x)])
57
58 #define NUM_MBALLS      3
59 static struct metaball mball[NUM_MBALLS];
60
61 static int dbg;
62
63 struct screen *metaballs_screen(void)
64 {
65         return &scr;
66 }
67
68 static int init(void)
69 {
70         if(!(volume = malloc(VOL_SZ * VOL_SZ * VOL_SZ * sizeof *volume))) {
71                 fprintf(stderr, "failed to allocate %dx%dx%d voxel field\n", VOL_SZ, VOL_SZ, VOL_SZ);
72                 return -1;
73         }
74
75         mball[0].energy = 1.2;
76         mball[1].energy = 0.8;
77         mball[2].energy = 1.0;
78
79         if(!(msurf = msurf_create())) {
80                 fprintf(stderr, "failed to initialize metasurf\n");
81                 return -1;
82         }
83         msurf_set_resolution(msurf, VOL_SZ, VOL_SZ, VOL_SZ);
84         msurf_set_bounds(msurf, 0, 0, 0, VOL_SCALE, VOL_SCALE, VOL_SCALE);
85         msurf_eval_func(msurf, eval);
86         msurf_set_threshold(msurf, 1.7);
87         msurf_set_inside(msurf, MSURF_GREATER);
88         msurf_vertex_func(msurf, emit_vertex);
89
90         mmesh.prim = G3D_TRIANGLES;
91         mmesh.varr = 0;
92         mmesh.iarr = 0;
93         mmesh.vcount = mmesh.icount = 0;
94
95         return 0;
96 }
97
98 static void destroy(void)
99 {
100         dynarr_free(mmesh.varr);
101 }
102
103 static void start(long trans_time)
104 {
105         g3d_matrix_mode(G3D_PROJECTION);
106         g3d_load_identity();
107         g3d_perspective(50.0, 1.3333333, 0.5, 100.0);
108
109         g3d_enable(G3D_CULL_FACE);
110         g3d_enable(G3D_LIGHTING);
111         g3d_enable(G3D_LIGHT0);
112
113         g3d_polygon_mode(G3D_GOURAUD);
114 }
115
116 static void update(void)
117 {
118         static int prev_mx, prev_my;
119         static unsigned int prev_bmask;
120
121         if(mouse_bmask) {
122                 if((mouse_bmask ^ prev_bmask) == 0) {
123                         int dx = mouse_x - prev_mx;
124                         int dy = mouse_y - prev_my;
125
126                         if(dx || dy) {
127                                 if(mouse_bmask & 1) {
128                                         cam_theta += dx * 1.0;
129                                         cam_phi += dy * 1.0;
130
131                                         if(cam_phi < -90) cam_phi = -90;
132                                         if(cam_phi > 90) cam_phi = 90;
133                                 }
134                                 if(mouse_bmask & 4) {
135                                         cam_dist += dy * 0.5;
136
137                                         if(cam_dist < 0) cam_dist = 0;
138                                 }
139                         }
140                 }
141         }
142         prev_mx = mouse_x;
143         prev_my = mouse_y;
144         prev_bmask = mouse_bmask;
145
146         {
147                 int i, j;
148                 float tsec = time_msec / 1000.0f;
149                 static float phase[] = {0.0, M_PI / 3.0, M_PI * 0.8};
150                 static float speed[] = {0.8, 1.4, 1.0};
151                 static float scale[][3] = {{1, 2, 0.8}, {0.5, 1.6, 0.6}, {1.5, 0.7, 0.5}};
152                 static float offset[][3] = {{0, 0, 0}, {0.25, 0, 0}, {-0.2, 0.15, 0.2}};
153
154                 for(i=0; i<NUM_MBALLS; i++) {
155                         float t = (tsec + phase[i]) * speed[i];
156
157                         for(j=0; j<3; j++) {
158                                 float x = sin(t + j * M_PI / 2.0);
159                                 mball[i].pos[j] = offset[i][j] + x * scale[i][j];
160                         }
161                 }
162         }
163
164         calc_voxel_field();
165
166         dynarr_free(mmesh.varr);
167         mmesh.vcount = 0;
168         mmesh.varr = dynarr_alloc(0, sizeof *mmesh.varr);
169         msurf_polygonize(msurf);
170 }
171
172 static void draw(void)
173 {
174         update();
175
176         memset(fb_pixels, 0, fb_width * fb_height * 2);
177
178         g3d_matrix_mode(G3D_MODELVIEW);
179         g3d_load_identity();
180         g3d_translate(0, 0, -cam_dist);
181         g3d_rotate(cam_phi, 1, 0, 0);
182         g3d_rotate(cam_theta, 0, 1, 0);
183
184         g3d_light_pos(0, -10, 10, 20);
185
186         zsort(&mmesh);
187
188         g3d_mtl_diffuse(0.6, 0.6, 0.6);
189
190         draw_mesh(&mmesh);
191
192         swap_buffers(fb_pixels);
193 }
194
195 static void draw_mesh(struct mesh *mesh)
196 {
197         if(mesh->iarr) {
198                 g3d_draw_indexed(mesh->prim, mesh->varr, mesh->vcount, mesh->iarr, mesh->icount);
199         } else {
200                 g3d_draw(mesh->prim, mesh->varr, mesh->vcount);
201         }
202 }
203
204 static struct {
205         struct g3d_vertex *varr;
206         const float *xform;
207 } zsort_cls;
208
209 static int zsort_cmp(const void *aptr, const void *bptr)
210 {
211         const int16_t *a = (const int16_t*)aptr;
212         const int16_t *b = (const int16_t*)bptr;
213
214         const float *m = zsort_cls.xform;
215
216         const struct g3d_vertex *va = zsort_cls.varr + a[0];
217         const struct g3d_vertex *vb = zsort_cls.varr + b[0];
218
219         float za = m[2] * va->x + m[6] * va->y + m[10] * va->z + m[14];
220         float zb = m[2] * vb->x + m[6] * vb->y + m[10] * vb->z + m[14];
221
222         va = zsort_cls.varr + a[2];
223         vb = zsort_cls.varr + b[2];
224
225         za += m[2] * va->x + m[6] * va->y + m[10] * va->z + m[14];
226         zb += m[2] * vb->x + m[6] * vb->y + m[10] * vb->z + m[14];
227
228         return za - zb;
229 }
230
231 static void zsort(struct mesh *m)
232 {
233         int nfaces = m->icount / m->prim;
234
235         zsort_cls.varr = m->varr;
236         zsort_cls.xform = g3d_get_matrix(G3D_MODELVIEW, 0);
237
238         qsort(m->iarr, nfaces, m->prim * sizeof *m->iarr, zsort_cmp);
239 }
240
241 static void calc_voxel_field(void)
242 {
243         int i, j, k, b;
244         float *voxptr = volume;
245
246         for(i=0; i<VOL_SZ; i++) {
247                 float z = VOL_SCALE * ((float)i / (float)VOL_SZ - 0.5);
248
249                 for(j=0; j<VOL_SZ; j++) {
250                         float y = VOL_SCALE * ((float)j / (float)VOL_SZ - 0.5);
251
252                         for(k=0; k<VOL_SZ; k++) {
253                                 float x = VOL_SCALE * ((float)k / (float)VOL_SZ - 0.5);
254
255                                 float val = 0.0f;
256                                 for(b=0; b<NUM_MBALLS; b++) {
257                                         float dx = mball[b].pos[0] - x;
258                                         float dy = mball[b].pos[1] - y;
259                                         float dz = mball[b].pos[2] - z;
260
261                                         float lensq = dx * dx + dy * dy + dz * dz;
262
263                                         val += lensq == 0.0f ? 1024.0f : mball[b].energy / lensq;
264                                 }
265
266                                 *voxptr++ = val;
267                         }
268                 }
269         }
270         ++dbg;
271 }
272
273 static float eval(struct metasurface *ms, float x, float y, float z)
274 {
275         int xidx = cround64(VOL_SZ * x / VOL_SCALE);
276         int yidx = cround64(VOL_SZ * y / VOL_SCALE);
277         int zidx = cround64(VOL_SZ * z / VOL_SCALE);
278
279         assert(xidx >= 0 && xidx < VOL_SZ);
280         assert(yidx >= 0 && yidx < VOL_SZ);
281         assert(zidx >= 0 && zidx < VOL_SZ);
282
283         return VOXEL(xidx, yidx, zidx);
284 }
285
286 static void emit_vertex(struct metasurface *ms, float x, float y, float z)
287 {
288         struct g3d_vertex v;
289         float val, len;
290
291         v.x = x - VOL_HALF_SCALE;
292         v.y = y - VOL_HALF_SCALE;
293         v.z = z - VOL_HALF_SCALE;
294         v.r = cround64(255.0 * x / VOL_SCALE);
295         v.g = cround64(255.0 * y / VOL_SCALE);
296         v.b = cround64(255.0 * z / VOL_SCALE);
297
298         val = eval(ms, x, y, z);
299         v.nx = eval(ms, x + VOX_DIST, y, z) - val;
300         v.ny = eval(ms, x, y + VOX_DIST, z) - val;
301         v.nz = eval(ms, x, y, z - VOX_DIST) - val;
302
303         if((len = sqrt(v.nx * v.nx + v.ny * v.ny + v.nz * v.nz)) != 0.0f) {
304                 v.nx /= len;
305                 v.ny /= len;
306                 v.nz /= len;
307         }
308
309         mmesh.varr = dynarr_push(mmesh.varr, &v);
310         assert(mmesh.varr);
311         ++mmesh.vcount;
312 }
313
314