initial commit
[liquidmodel] / src / metaobj.c
1 #include <stdio.h>
2 #include <string.h>
3 #include "cgmath/cgmath.h"
4 #include "demo.h"
5 #include "metasurf.h"
6 #include "metaobj.h"
7 #include "util.h"
8
9 static struct mobject *mobj_create(int num);
10 static void swstate(struct mobject *mobj, int st);
11 static void update(struct mobject *mobj, float tsec);
12 static float eval(struct mobject *mobj, cgm_vec3 *pos);
13
14 static void upd_sflake_ball(struct mobject *mobj, struct mball *ball, float tsec, float t);
15 static int calc_num_balls(int depth);
16 static int gen_sflake(cgm_vec4 *sarr, int num, int depth, float x, float y, float z, float rad);
17
18 static void upd_sgi_caps(struct mobject *mobj, struct mcapsule *caps, float tsec, float t);
19
20 static float capsule_distsq(struct mcapsule *c, cgm_vec3 *pos);
21 static float easein(float x);
22 static float easeout(float x);
23
24
25 static struct mobject *mobj_create(int num)
26 {
27         int i;
28         struct mobject *mobj;
29
30         mobj = calloc_nf(1, sizeof *mobj);
31
32         mobj->idlepos = malloc_nf(num * sizeof *mobj->idlepos);
33         mobj->mot = malloc_nf(num * sizeof *mobj->mot);
34
35         for(i=0; i<num; i++) {
36                 mobj->mot[i].x = 2.0f * ((float)rand() / (float)RAND_MAX) - 1.0f;
37                 mobj->mot[i].y = 2.0f * ((float)rand() / (float)RAND_MAX) - 1.0f;
38                 mobj->mot[i].z = 2.0f * ((float)rand() / (float)RAND_MAX) - 1.0f;
39                 mobj->mot[i].w = 0.8;
40         }
41
42         mobj->swstate = swstate;
43         mobj->update = update;
44         mobj->eval = eval;
45
46         mobj->state = -1;
47         swstate(mobj, MOBJ_IDLE);
48         return mobj;
49 }
50
51 static void swstate(struct mobject *mobj, int st)
52 {
53         if(st == mobj->state) return;
54         if(st == MOBJ_GRABING && mobj->state != MOBJ_IDLE) return;
55         if(st == MOBJ_DROPPING && mobj->state != MOBJ_HELD && mobj->state != MOBJ_GRABING) {
56                 return;
57         }
58
59         switch(st) {
60         case MOBJ_GRABING:
61                 if(mobj->state != MOBJ_IDLE) return;
62                 break;
63
64         case MOBJ_DROPPING:
65                 if(mobj->state != MOBJ_HELD && mobj->state != MOBJ_GRABING) {
66                         return;
67                 }
68                 break;
69
70         case MOBJ_IDLE:
71                 mobj->pos.x = mobj->pos.z = 0.0f;
72                 mobj->pos.y = -BBOX_YSZ * 0.5f;
73                 break;
74         }
75
76         mobj->state = st;
77         mobj->tstart = (float)time_msec / 1000.0f;
78 }
79
80 static void update(struct mobject *mobj, float tsec)
81 {
82         int i, count;
83         struct mball *ball;
84         struct mcapsule *caps;
85         float t, elapsed;
86         cgm_vec3 *idleptr;
87         cgm_vec4 *motptr;
88
89         count = mobj->num_balls + mobj->num_caps;
90
91         if(mobj->state != MOBJ_IDLE) {
92                 cgm_midentity(mobj->xform);
93                 cgm_mrotate_x(mobj->xform, tsec);
94                 cgm_mrotate_y(mobj->xform, tsec);
95                 cgm_mtranslate(mobj->xform, mobj->pos.x, mobj->pos.y, mobj->pos.z);
96         }
97         if(mobj->state != MOBJ_HELD) {
98                 for(i=0; i<count; i++) {
99                         mobj->idlepos[i].x = sin(tsec * mobj->mot[i].x + mobj->mot[i].y) * mobj->mot[i].z * 4.0f;
100                         mobj->idlepos[i].z = cos(tsec * mobj->mot[i].z + mobj->mot[i].y) * mobj->mot[i].x * 4.0f;
101                         mobj->idlepos[i].y = -BBOX_YSZ * 0.45f;
102                 }
103         }
104
105         idleptr = mobj->idlepos;
106         motptr = mobj->mot;
107         ball = mobj->balls;
108         caps = mobj->caps;
109
110         switch(mobj->state) {
111         case MOBJ_IDLE:
112                 if(mobj->balls) {
113                         for(i=0; i<mobj->num_balls; i++) {
114                                 ball->pos = idleptr[i];
115                                 ball->energy = motptr[i].w;
116                                 ball++;
117                         }
118                         idleptr += mobj->num_balls;
119                         motptr += mobj->num_balls;
120                 }
121                 if(mobj->caps) {
122                         for(i=0; i<mobj->num_caps; i++) {
123                                 caps->end[0] = caps->end[1] = idleptr[i];
124                                 caps->energy = motptr[i].w;
125                                 caps++;
126                         }
127                 }
128                 break;
129
130         case MOBJ_GRABING:
131                 if((elapsed = tsec - mobj->tstart) >= TRANSDUR) {
132                         mobj->swstate(mobj, MOBJ_HELD);
133                         t = 1.0f;
134                 } else {
135                         t = easeout((tsec - mobj->tstart) / TRANSDUR);
136                 }
137                 if(0) {
138         case MOBJ_DROPPING:
139                         if((elapsed = tsec - mobj->tstart) >= TRANSDUR) {
140                                 mobj->swstate(mobj, MOBJ_IDLE);
141                                 t = 1.0f;
142                         } else {
143                                 t = easein((tsec - mobj->tstart) / TRANSDUR);
144                         }
145                 }
146                 for(i=0; i<mobj->num_balls; i++) {
147                         mobj->upd_ball(mobj, ball++, tsec, t);
148                 }
149                 for(i=0; i<mobj->num_caps; i++) {
150                         mobj->upd_caps(mobj, caps++, tsec, t);
151                 }
152                 break;
153
154         case MOBJ_HELD:
155                 for(i=0; i<mobj->num_balls; i++) {
156                         mobj->upd_ball(mobj, ball++, tsec, 0);
157                 }
158                 for(i=0; i<mobj->num_caps; i++) {
159                         mobj->upd_caps(mobj, caps++, tsec, 0);
160                 }
161                 break;
162         }
163 }
164
165 static float eval(struct mobject *mobj, cgm_vec3 *pos)
166 {
167         int i;
168         float dsq, energy = 0.0f;
169         struct mball *ball = mobj->balls;
170         struct mcapsule *caps = mobj->caps;
171
172         for(i=0; i<mobj->num_balls; i++) {
173                 dsq = cgm_vdist_sq(&ball->pos, pos);
174                 energy += ball->energy / dsq;
175                 ball++;
176         }
177
178         for(i=0; i<mobj->num_caps; i++) {
179                 dsq = capsule_distsq(caps, pos);
180                 energy += caps->energy / dsq;
181                 caps++;
182         }
183         return energy;
184 }
185
186
187 /* ---- sphereflake ---- */
188 #define SF_MAX_DEPTH    2
189 static cgm_vec4 *sfsph;
190
191 struct mobject *metaobj_sflake(void)
192 {
193         int num_balls;
194         struct mobject *mobj;
195
196         num_balls = calc_num_balls(SF_MAX_DEPTH);
197
198         mobj = mobj_create(num_balls);
199
200         mobj->num_balls = num_balls;
201         mobj->balls = malloc_nf(num_balls * sizeof *mobj->balls);
202         sfsph = malloc_nf(num_balls * sizeof *sfsph);
203
204         gen_sflake(sfsph, 0, SF_MAX_DEPTH, 0, 0, 0, 20);
205
206         mobj->upd_ball = upd_sflake_ball;
207         return mobj;
208 }
209
210 static void upd_sflake_ball(struct mobject *mobj, struct mball *ball, float tsec, float t)
211 {
212         int idx = ball - mobj->balls;
213         cgm_vec3 pos;
214
215         switch(mobj->state) {
216         case MOBJ_DROPPING:
217                 t = 1.0f - t;
218         case MOBJ_GRABING:
219                 cgm_vcons(&pos, sfsph[idx].x, sfsph[idx].y, sfsph[idx].z);
220                 cgm_vmul_m4v3(&pos, mobj->xform);
221                 cgm_vlerp(&ball->pos, mobj->idlepos + idx, &pos, t);
222                 ball->energy = cgm_lerp(mobj->mot[idx].w, sfsph[idx].w, t);
223                 break;
224
225         case MOBJ_HELD:
226                 cgm_vcons(&ball->pos, sfsph[idx].x, sfsph[idx].y, sfsph[idx].z);
227                 cgm_vmul_m4v3(&ball->pos, mobj->xform);
228                 ball->energy = sfsph[idx].w;
229                 break;
230         }
231 }
232
233 static int calc_num_balls(int depth)
234 {
235         if(!depth) return 0;
236         return calc_num_balls(depth - 1) * 6 + 1;
237 }
238
239 static int gen_sflake(cgm_vec4 *sarr, int num, int depth, float x, float y, float z, float rad)
240 {
241         int subnum;
242         float subrad, offs;
243
244         if(!depth) return 0;
245
246         sarr[num].x = x;
247         sarr[num].y = y;
248         sarr[num].z = z;
249         sarr[num].w = rad;
250         num++;
251
252         subrad = rad * 0.2f;
253         offs = rad * 0.16f;
254
255         subnum = 0;
256         subnum += gen_sflake(sarr, num + subnum, depth - 1, x + offs, y, z, subrad);
257         subnum += gen_sflake(sarr, num + subnum, depth - 1, x - offs, y, z, subrad);
258         subnum += gen_sflake(sarr, num + subnum, depth - 1, x, y + offs, z, subrad);
259         subnum += gen_sflake(sarr, num + subnum, depth - 1, x, y - offs, z, subrad);
260         subnum += gen_sflake(sarr, num + subnum, depth - 1, x, y, z + offs, subrad);
261         subnum += gen_sflake(sarr, num + subnum, depth - 1, x, y, z - offs, subrad);
262         return subnum + 1;
263 }
264
265 /* ---- SGI logo ---- */
266
267 static const cgm_vec3 sgiv[] = {
268         {2.794170, 4.254175, 2.738066},
269         {2.794170, 4.254174, -4.358471},
270         {-2.173414, 4.254174, -4.358471},
271         {-2.173414, -2.842363, -4.358470},
272         {4.923134, -2.842363, -4.358471},
273         {4.923134, 2.125212, -4.358471},
274         {4.923134, 2.125212, 2.738066},
275         {4.923134, -4.971326, 2.738067},
276         {4.923134, -4.971326, -2.229511},
277         {-2.173413, -4.971326, -2.229511},
278         {-2.173413, -4.971325, 4.867042},
279         {2.794170, -4.971325, 4.867042},
280         {2.794170, 2.125213, 4.867042},
281         {-4.302382, 2.125213, 4.867042},
282         {-4.302383, -2.842362, 4.867042},
283         {-4.302382, -2.842363, -2.229511},
284         {-4.302382, 4.254175, -2.229512},
285         {-4.302383, 4.254175, 2.738066}
286 };
287 #define NUM_SGI_VERTS   (sizeof sgiv / sizeof *sgiv)
288 static float sgimat[16];
289
290 struct mobject *metaobj_sgi(void)
291 {
292         int i;
293         struct mobject *mobj;
294
295         cgm_midentity(sgimat);
296         cgm_mrotate_y(sgimat, -M_PI / 4.0f);
297         cgm_mrotate_x(sgimat, M_PI / 4.0f);
298         cgm_mtranslate(sgimat, 0, -4, 0);
299
300         mobj = mobj_create(NUM_SGI_VERTS);
301
302         mobj->num_caps = NUM_SGI_VERTS;
303         mobj->caps = calloc_nf(mobj->num_caps, sizeof *mobj->caps);
304
305         for(i=0; i<mobj->num_caps; i++) {
306                 mobj->caps[i].energy = 0.7;
307         }
308
309         mobj->swstate = swstate;
310         mobj->upd_caps = upd_sgi_caps;
311         return mobj;
312 }
313
314 #define LOGOSCALE       0.55f
315 static void upd_sgi_caps(struct mobject *mobj, struct mcapsule *caps, float tsec, float t)
316 {
317         int idx0, idx1;
318         cgm_vec3 pos[2];
319         static cgm_vec3 prev_pos;
320
321         idx0 = caps - mobj->caps;
322         idx1 = idx0 >= mobj->num_caps - 1 ? 0 : idx0 + 1;
323
324         switch(mobj->state) {
325         case MOBJ_DROPPING:
326                 t = 1.0f - t;
327         case MOBJ_GRABING:
328                 if(idx0 == 0) {
329                         pos[0] = sgiv[idx0];
330                         cgm_vscale(pos, LOGOSCALE);
331                         cgm_vmul_m4v3(pos, mobj->xform);
332                         cgm_vlerp(caps->end, mobj->idlepos + idx0, pos, t);
333                 } else {
334                         caps->end[0] = prev_pos;
335                 }
336                 pos[1] = sgiv[idx1];
337                 cgm_vscale(pos + 1, LOGOSCALE);
338                 cgm_vmul_m4v3(pos + 1, mobj->xform);
339                 cgm_vlerp(caps->end + 1, mobj->idlepos + idx1, pos + 1, t);
340                 prev_pos = caps->end[1];
341                 /*caps->energy = cgm_lerp(mobj->mot[idx].w, sfsph[idx].w, t);*/
342                 break;
343
344         case MOBJ_HELD:
345                 if(idx0 == 0) {
346                         pos[0] = sgiv[0];
347                         cgm_vscale(pos, LOGOSCALE);
348                         cgm_vmul_m4v3(pos, mobj->xform);
349                         caps->end[0] = pos[0];
350                 } else {
351                         caps->end[0] = prev_pos;
352                 }
353                 pos[1] = sgiv[idx1];
354                 cgm_vscale(pos + 1, LOGOSCALE);
355                 cgm_vmul_m4v3(pos + 1, mobj->xform);
356                 prev_pos = caps->end[1] = pos[1];
357                 break;
358         }
359         caps->len = cgm_vdist(caps->end, caps->end + 1);
360 }
361
362 static float capsule_distsq(struct mcapsule *c, cgm_vec3 *pos)
363 {
364         float t;
365         cgm_vec3 pp, dir, pdir;
366
367         dir = c->end[1]; cgm_vsub(&dir, c->end);
368         if(c->len != 0.0f) {
369                 float s = 1.0f / c->len;
370                 dir.x *= s;
371                 dir.y *= s;
372                 dir.z *= s;
373         }
374         pdir = *pos; cgm_vsub(&pdir, c->end);
375         t = cgm_vdot(&dir, &pdir);
376
377         if(t < 0.0f) {
378                 return cgm_vdist_sq(c->end, pos);
379         }
380         if(t > c->len) {
381                 return cgm_vdist_sq(c->end + 1, pos);
382         }
383
384         pp = c->end[0];
385         cgm_vadd_scaled(&pp, &dir, t);
386         return cgm_vdist_sq(&pp, pos);
387 }
388
389 static float easein(float x)
390 {
391         return x * x * x;
392 }
393
394 static float easeout(float x)
395 {
396         return 1.0f - pow(1.0f - x, 3.0f);
397 }