transitions for both shapes
[metatoy] / src / metaobj.c
1 #include <stdio.h>
2 #include <string.h>
3 #include "cgmath/cgmath.h"
4 #include "game.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;
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                 t = easeout((tsec - mobj->tstart) / TRANSDUR);
132                 if(t >= 1.0f) mobj->swstate(mobj, MOBJ_HELD);
133                 if(0) {
134         case MOBJ_DROPPING:
135                         t = easein((tsec - mobj->tstart) / TRANSDUR);
136                         if(t >= 1.0f) mobj->swstate(mobj, MOBJ_IDLE);
137                 }
138                 for(i=0; i<mobj->num_balls; i++) {
139                         mobj->upd_ball(mobj, ball++, tsec, t);
140                 }
141                 for(i=0; i<mobj->num_caps; i++) {
142                         mobj->upd_caps(mobj, caps++, tsec, t);
143                 }
144                 break;
145
146         case MOBJ_HELD:
147                 for(i=0; i<mobj->num_balls; i++) {
148                         mobj->upd_ball(mobj, ball++, tsec, 0);
149                 }
150                 for(i=0; i<mobj->num_caps; i++) {
151                         mobj->upd_caps(mobj, caps++, tsec, 0);
152                 }
153                 break;
154         }
155 }
156
157 static float eval(struct mobject *mobj, cgm_vec3 *pos)
158 {
159         int i;
160         float dsq, energy = 0.0f;
161         struct mball *ball = mobj->balls;
162         struct mcapsule *caps = mobj->caps;
163
164         for(i=0; i<mobj->num_balls; i++) {
165                 dsq = cgm_vdist_sq(&ball->pos, pos);
166                 energy += ball->energy / dsq;
167                 ball++;
168         }
169
170         for(i=0; i<mobj->num_caps; i++) {
171                 dsq = capsule_distsq(mobj->caps + i, pos);
172                 energy += caps->energy / dsq;
173         }
174         return energy;
175 }
176
177
178 /* ---- sphereflake ---- */
179 #define SF_MAX_DEPTH    2
180 static cgm_vec4 *sfsph;
181
182 struct mobject *metaobj_sflake(void)
183 {
184         int num_balls;
185         struct mobject *mobj;
186
187         num_balls = calc_num_balls(SF_MAX_DEPTH);
188
189         mobj = mobj_create(num_balls);
190
191         mobj->num_balls = num_balls;
192         mobj->balls = malloc_nf(num_balls * sizeof *mobj->balls);
193         sfsph = malloc_nf(num_balls * sizeof *sfsph);
194
195         gen_sflake(sfsph, 0, SF_MAX_DEPTH, 0, 0, 0, 20);
196
197         mobj->upd_ball = upd_sflake_ball;
198         return mobj;
199 }
200
201 static void upd_sflake_ball(struct mobject *mobj, struct mball *ball, float tsec, float t)
202 {
203         int idx = ball - mobj->balls;
204         cgm_vec3 pos;
205
206         switch(mobj->state) {
207         case MOBJ_DROPPING:
208                 t = 1.0f - t;
209         case MOBJ_GRABING:
210                 cgm_vcons(&pos, sfsph[idx].x, sfsph[idx].y, sfsph[idx].z);
211                 cgm_vmul_m4v3(&pos, mobj->xform);
212                 cgm_vlerp(&ball->pos, mobj->idlepos + idx, &pos, t);
213                 ball->energy = cgm_lerp(mobj->mot[idx].w, sfsph[idx].w, t);
214                 break;
215
216         case MOBJ_HELD:
217                 cgm_vcons(&ball->pos, sfsph[idx].x, sfsph[idx].y, sfsph[idx].z);
218                 cgm_vmul_m4v3(&ball->pos, mobj->xform);
219                 ball->energy = sfsph[idx].w;
220                 break;
221         }
222 }
223
224 static int calc_num_balls(int depth)
225 {
226         if(!depth) return 0;
227         return calc_num_balls(depth - 1) * 6 + 1;
228 }
229
230 static int gen_sflake(cgm_vec4 *sarr, int num, int depth, float x, float y, float z, float rad)
231 {
232         int subnum;
233         float subrad, offs;
234
235         if(!depth) return 0;
236
237         sarr[num].x = x;
238         sarr[num].y = y;
239         sarr[num].z = z;
240         sarr[num].w = rad;
241         num++;
242
243         subrad = rad * 0.2f;
244         offs = rad * 0.16f;
245
246         subnum = 0;
247         subnum += gen_sflake(sarr, num + subnum, depth - 1, x + offs, y, z, subrad);
248         subnum += gen_sflake(sarr, num + subnum, depth - 1, x - offs, y, z, subrad);
249         subnum += gen_sflake(sarr, num + subnum, depth - 1, x, y + offs, z, subrad);
250         subnum += gen_sflake(sarr, num + subnum, depth - 1, x, y - offs, z, subrad);
251         subnum += gen_sflake(sarr, num + subnum, depth - 1, x, y, z + offs, subrad);
252         subnum += gen_sflake(sarr, num + subnum, depth - 1, x, y, z - offs, subrad);
253         return subnum + 1;
254 }
255
256 /* ---- SGI logo ---- */
257
258 static const cgm_vec3 sgiv[] = {
259         {2.794170, 4.254175, 2.738066},
260         {2.794170, 4.254174, -4.358471},
261         {-2.173414, 4.254174, -4.358471},
262         {-2.173414, -2.842363, -4.358470},
263         {4.923134, -2.842363, -4.358471},
264         {4.923134, 2.125212, -4.358471},
265         {4.923134, 2.125212, 2.738066},
266         {4.923134, -4.971326, 2.738067},
267         {4.923134, -4.971326, -2.229511},
268         {-2.173413, -4.971326, -2.229511},
269         {-2.173413, -4.971325, 4.867042},
270         {2.794170, -4.971325, 4.867042},
271         {2.794170, 2.125213, 4.867042},
272         {-4.302382, 2.125213, 4.867042},
273         {-4.302383, -2.842362, 4.867042},
274         {-4.302382, -2.842363, -2.229511},
275         {-4.302382, 4.254175, -2.229512},
276         {-4.302383, 4.254175, 2.738066}
277 };
278 #define NUM_SGI_VERTS   (sizeof sgiv / sizeof *sgiv)
279 static float sgimat[16];
280
281 struct mobject *metaobj_sgi(void)
282 {
283         int i;
284         struct mobject *mobj;
285
286         cgm_midentity(sgimat);
287         cgm_mrotate_y(sgimat, -M_PI / 4.0f);
288         cgm_mrotate_x(sgimat, M_PI / 4.0f);
289         cgm_mtranslate(sgimat, 0, -4, 0);
290
291         mobj = mobj_create(NUM_SGI_VERTS);
292
293         mobj->num_caps = NUM_SGI_VERTS;
294         mobj->caps = calloc_nf(mobj->num_caps, sizeof *mobj->caps);
295
296         for(i=0; i<mobj->num_caps; i++) {
297                 mobj->caps[i].energy = 0.7;
298         }
299
300         mobj->swstate = swstate;
301         mobj->upd_caps = upd_sgi_caps;
302         return mobj;
303 }
304
305 #define LOGOSCALE       0.55f
306 static void upd_sgi_caps(struct mobject *mobj, struct mcapsule *caps, float tsec, float t)
307 {
308         int idx0, idx1;
309         cgm_vec3 pos[2];
310         static cgm_vec3 prev_pos;
311
312         idx0 = caps - mobj->caps;
313         idx1 = idx0 >= mobj->num_caps - 1 ? 0 : idx0 + 1;
314
315         switch(mobj->state) {
316         case MOBJ_DROPPING:
317                 t = 1.0f - t;
318         case MOBJ_GRABING:
319                 if(idx0 == 0) {
320                         pos[0] = sgiv[idx0];
321                         cgm_vscale(pos, LOGOSCALE);
322                         cgm_vmul_m4v3(pos, mobj->xform);
323                         cgm_vlerp(caps->end, mobj->idlepos + idx0, pos, t);
324                 } else {
325                         caps->end[0] = prev_pos;
326                 }
327                 pos[1] = sgiv[idx1];
328                 cgm_vscale(pos + 1, LOGOSCALE);
329                 cgm_vmul_m4v3(pos + 1, mobj->xform);
330                 cgm_vlerp(caps->end + 1, mobj->idlepos + idx1, pos + 1, t);
331                 prev_pos = caps->end[1];
332                 /*caps->energy = cgm_lerp(mobj->mot[idx].w, sfsph[idx].w, t);*/
333                 break;
334
335         case MOBJ_HELD:
336                 if(idx0 == 0) {
337                         pos[0] = sgiv[idx0];
338                         cgm_vscale(pos, LOGOSCALE);
339                         cgm_vmul_m4v3(pos, mobj->xform);
340                         caps->end[0] = pos[0];
341                 } else {
342                         caps->end[0] = prev_pos;
343                 }
344                 pos[1] = sgiv[idx0];
345                 cgm_vscale(pos + 1, LOGOSCALE);
346                 cgm_vmul_m4v3(pos + 1, mobj->xform);
347                 prev_pos = caps->end[1] = pos[1];
348                 break;
349         }
350         caps->len = cgm_vdist(caps->end, caps->end + 1);
351 }
352
353 static float capsule_distsq(struct mcapsule *c, cgm_vec3 *pos)
354 {
355         float t;
356         cgm_vec3 pp, dir, pdir;
357
358         dir = c->end[1]; cgm_vsub(&dir, c->end);
359         if(c->len != 0.0f) {
360                 float s = 1.0f / c->len;
361                 dir.x *= s;
362                 dir.y *= s;
363                 dir.z *= s;
364         }
365         pdir = *pos; cgm_vsub(&pdir, c->end);
366         t = cgm_vdot(&dir, &pdir);
367
368         if(t < 0.0f) {
369                 return cgm_vdist_sq(c->end, pos);
370         }
371         if(t > c->len) {
372                 return cgm_vdist_sq(c->end + 1, pos);
373         }
374
375         pp = c->end[0];
376         cgm_vadd_scaled(&pp, &dir, t);
377         return cgm_vdist_sq(&pp, pos);
378 }
379
380 static float easein(float x)
381 {
382         return x * x * x;
383 }
384
385 static float easeout(float x)
386 {
387         return 1.0f - pow(1.0f - x, 3.0f);
388 }