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