Import from neverball-1.3.1.tar.gz
[neverball-archive.git] / share / solid.c
blobd287983ceaf20ee78a1a8e4d2c83ab7c14f70069
1 /*
2 * Copyright (C) 2003 Robert Kooima
4 * NEVERBALL is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published
6 * by the Free Software Foundation; either version 2 of the License,
7 * or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
15 #include <SDL.h>
16 #include <SDL_rwops.h>
17 #include <SDL_image.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <math.h>
24 #include "glext.h"
25 #include "vec3.h"
26 #include "geom.h"
27 #include "solid.h"
28 #include "config.h"
29 #include "binary.h"
31 #define SMALL 1.0e-5f
32 #define LARGE 1.0e+5f
34 /*---------------------------------------------------------------------------*/
36 static float erp(float t)
38 return 3.0f * t * t - 2.0f * t * t * t;
41 static float derp(float t)
43 return 6.0f * t - 6.0f * t * t;
46 static void sol_body_v(float v[3],
47 const struct s_file *fp,
48 const struct s_body *bp)
50 if (bp->pi >= 0 && fp->pv[bp->pi].f)
52 const struct s_path *pp = fp->pv + bp->pi;
53 const struct s_path *pq = fp->pv + pp->pi;
55 v_sub(v, pq->p, pp->p);
56 v_scl(v, v, 1.0f / pp->t);
58 v_scl(v, v, derp(bp->t / pp->t));
60 else
62 v[0] = 0.0f;
63 v[1] = 0.0f;
64 v[2] = 0.0f;
68 static void sol_body_p(float p[3],
69 const struct s_file *fp,
70 const struct s_body *bp)
72 float v[3];
74 if (bp->pi >= 0)
76 const struct s_path *pp = fp->pv + bp->pi;
77 const struct s_path *pq = fp->pv + pp->pi;
79 v_sub(v, pq->p, pp->p);
80 v_mad(p, pp->p, v, erp(bp->t / pp->t));
82 else
84 p[0] = 0.0f;
85 p[1] = 0.0f;
86 p[2] = 0.0f;
90 /*---------------------------------------------------------------------------*/
92 static short sol_enum_mtrl(const struct s_file *fp,
93 const struct s_body *bp, short mi)
95 short li, gi, c = 0;
97 /* Count all lump geoms with this material. */
99 for (li = 0; li < bp->lc; li++)
101 short g0 = fp->lv[bp->l0 + li].g0;
102 short gc = fp->lv[bp->l0 + li].gc;
104 for (gi = 0; gi < gc; gi++)
105 if (fp->gv[fp->iv[g0 + gi]].mi == mi)
106 c++;
109 /* Count all body geoms with this material. */
111 for (gi = 0; gi < bp->gc; gi++)
112 if (fp->gv[fp->iv[bp->g0 + gi]].mi == mi)
113 c++;
115 return c;
118 static short sol_enum_body(const struct s_file *fp,
119 const struct s_body *bp, short fl)
121 short mi, c = 0;
123 /* Count all geoms with this flag. */
125 for (mi = 0; mi < fp->mc; mi++)
126 if (fp->mv[mi].fl & fl)
127 c = c + sol_enum_mtrl(fp, bp, mi);
129 return c;
132 /*---------------------------------------------------------------------------*/
134 * The following code renders a body in a ludicrously inefficient
135 * manner. It iterates the materials and scans the data structure for
136 * geometry using each. This has the effect of absolutely minimizing
137 * material changes, texture bindings, and Begin/End pairs, but
138 * maximizing trips through the data.
140 * However, this is only done once for each level. The results are
141 * stored in display lists. Thus, it is well worth it.
144 static void sol_draw_mtrl(const struct s_file *fp, short i)
146 const struct s_mtrl *mp = fp->mv + i;
148 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, mp->a);
149 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, mp->d);
150 glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mp->s);
151 glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, mp->e);
152 glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mp->h);
154 if (mp->fl & M_ENVIRONMENT)
156 glEnable(GL_TEXTURE_GEN_S);
157 glEnable(GL_TEXTURE_GEN_T);
159 glBindTexture(GL_TEXTURE_2D, mp->o);
161 glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
162 glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
164 else
166 glDisable(GL_TEXTURE_GEN_S);
167 glDisable(GL_TEXTURE_GEN_T);
169 glBindTexture(GL_TEXTURE_2D, mp->o);
172 if (mp->fl & M_ADDITIVE)
173 glBlendFunc(GL_ONE, GL_ONE);
174 else
175 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
179 static void sol_draw_geom(const struct s_file *fp,
180 const struct s_geom *gp, short mi, int s)
182 if (gp->mi == mi)
184 const float *ui = fp->tv[gp->ti].u;
185 const float *uj = fp->tv[gp->tj].u;
186 const float *uk = fp->tv[gp->tk].u;
188 const float *ni = fp->sv[gp->si].n;
189 const float *nj = fp->sv[gp->sj].n;
190 const float *nk = fp->sv[gp->sk].n;
192 const float *vi = fp->vv[gp->vi].p;
193 const float *vj = fp->vv[gp->vj].p;
194 const float *vk = fp->vv[gp->vk].p;
196 if (s)
198 glMultiTexCoord2f(GL_TEXTURE0, ui[0], ui[1]);
199 glMultiTexCoord2f(GL_TEXTURE1, vi[0], vi[2]);
200 glNormal3fv(ni);
201 glVertex3fv(vi);
203 glMultiTexCoord2f(GL_TEXTURE0, uj[0], uj[1]);
204 glMultiTexCoord2f(GL_TEXTURE1, vj[0], vj[2]);
205 glNormal3fv(nj);
206 glVertex3fv(vj);
208 glMultiTexCoord2f(GL_TEXTURE0, uk[0], uk[1]);
209 glMultiTexCoord2f(GL_TEXTURE1, vk[0], vk[2]);
210 glNormal3fv(nk);
211 glVertex3fv(vk);
213 else
215 glTexCoord2fv(ui);
216 glNormal3fv(ni);
217 glVertex3fv(vi);
219 glTexCoord2fv(uj);
220 glNormal3fv(nj);
221 glVertex3fv(vj);
223 glTexCoord2fv(uk);
224 glNormal3fv(nk);
225 glVertex3fv(vk);
230 static void sol_draw_lump(const struct s_file *fp,
231 const struct s_lump *lp, short mi, int s)
233 short i;
235 for (i = 0; i < lp->gc; i++)
237 short gi = fp->iv[lp->g0 + i];
239 sol_draw_geom(fp, fp->gv + gi, mi, s);
243 static void sol_draw_body(const struct s_file *fp,
244 const struct s_body *bp, short fl, int s)
246 short mi, li, gi;
248 /* Iterate all materials of the correct opacity. */
250 for (mi = 0; mi < fp->mc; mi++)
251 if (fp->mv[mi].fl & fl)
253 if (sol_enum_mtrl(fp, bp, mi))
255 /* Set the material state. */
257 sol_draw_mtrl(fp, mi);
259 /* Render all geometry of that material. */
261 glBegin(GL_TRIANGLES);
263 for (li = 0; li < bp->lc; li++)
264 sol_draw_lump(fp, fp->lv + bp->l0 + li, mi, s);
265 for (gi = 0; gi < bp->gc; gi++)
266 sol_draw_geom(fp, fp->gv + fp->iv[bp->g0 + gi], mi, s);
268 glEnd();
273 static void sol_draw_bill(const struct s_file *fp,
274 const struct s_bill *rp, float t)
276 float T = fmodf(t, rp->t) - rp->t / 2;
278 float w = rp->w[0] + rp->w[1] * T + rp->w[2] * T * T;
279 float h = rp->h[0] + rp->h[1] * T + rp->h[2] * T * T;
281 if (w > 0 && h > 0)
283 float rx = rp->rx[0] + rp->rx[1] * T + rp->rx[2] * T * T;
284 float ry = rp->ry[0] + rp->ry[1] * T + rp->ry[2] * T * T;
285 float rz = rp->rz[0] + rp->rz[1] * T + rp->rz[2] * T * T;
287 glPushMatrix();
289 float y0 = (rp->fl & B_EDGE) ? 0 : -h / 2;
290 float y1 = (rp->fl & B_EDGE) ? h : +h / 2;
292 glRotatef(ry, 0.0f, 1.0f, 0.0f);
293 glRotatef(rx, 1.0f, 0.0f, 0.0f);
294 glTranslatef(0.0f, 0.0f, -rp->d);
296 if (rp->fl & B_FLAT)
298 glRotatef(-rx - 90.0f, 1.0f, 0.0f, 0.0f);
299 glRotatef(-ry, 0.0f, 0.0f, 1.0f);
301 if (rp->fl & B_EDGE)
302 glRotatef(-rx, 1.0f, 0.0f, 0.0f);
304 glRotatef(rz, 0.0f, 0.0f, 1.0f);
306 sol_draw_mtrl(fp, rp->mi);
308 glBegin(GL_QUADS);
310 glTexCoord2f(0.0f, 1.0f); glVertex2f(-w / 2, y0);
311 glTexCoord2f(1.0f, 1.0f); glVertex2f(+w / 2, y0);
312 glTexCoord2f(1.0f, 0.0f); glVertex2f(+w / 2, y1);
313 glTexCoord2f(0.0f, 0.0f); glVertex2f(-w / 2, y1);
315 glEnd();
317 glPopMatrix();
321 static void sol_draw_list(const struct s_file *fp,
322 const struct s_body *bp, GLuint list, int s)
324 float p[3];
326 sol_body_p(p, fp, bp);
328 glPushMatrix();
330 /* Translate a moving body. */
332 glTranslatef(p[0], p[1], p[2]);
334 /* Translate the shadow on a moving body. */
336 if (s)
338 glActiveTexture(GL_TEXTURE1);
339 glMatrixMode(GL_TEXTURE);
341 glPushMatrix();
342 glTranslatef(p[0], p[2], 0.0f);
344 glMatrixMode(GL_MODELVIEW);
345 glActiveTexture(GL_TEXTURE0);
348 /* Draw the body. */
350 glCallList(list);
352 /* Pop the shadow translation. */
354 if (s)
356 glActiveTexture(GL_TEXTURE1);
357 glMatrixMode(GL_TEXTURE);
359 glPopMatrix();
361 glMatrixMode(GL_MODELVIEW);
362 glActiveTexture(GL_TEXTURE0);
365 glPopMatrix();
368 /*---------------------------------------------------------------------------*/
370 void sol_back(const struct s_file *fp, float n, float f, float t)
372 int ri;
374 glPushAttrib(GL_LIGHTING_BIT | GL_DEPTH_BUFFER_BIT);
376 /* Render all billboards in the given range. */
378 glDisable(GL_LIGHTING);
379 glDepthMask(GL_FALSE);
381 for (ri = 0; ri < fp->rc; ri++)
382 if (n <= fp->rv[ri].d && fp->rv[ri].d < f)
383 sol_draw_bill(fp, fp->rv + ri, t);
385 glPopAttrib();
388 void sol_refl(const struct s_file *fp, int s)
390 short bi;
392 glPushAttrib(GL_LIGHTING_BIT);
394 /* Render all reflective geometry into the color and depth buffers. */
396 glEnable(GL_BLEND);
397 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
399 for (bi = 0; bi < fp->bc; bi++)
400 if (fp->bv[bi].rl)
401 sol_draw_list(fp, fp->bv + bi, fp->bv[bi].rl, s);
403 glPopAttrib();
406 void sol_draw(const struct s_file *fp, int s)
408 short i;
410 glPushAttrib(GL_TEXTURE_BIT |
411 GL_LIGHTING_BIT |
412 GL_COLOR_BUFFER_BIT |
413 GL_DEPTH_BUFFER_BIT);
415 /* Render all obaque geometry into the color and depth buffers. */
417 for (i = 0; i < fp->bc; i++)
418 if (fp->bv[i].ol)
419 sol_draw_list(fp, fp->bv + i, fp->bv[i].ol, s);
421 /* Render all translucent geometry into only the color buffer. */
423 glDepthMask(GL_FALSE);
425 glEnable(GL_BLEND);
426 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
428 for (i = 0; i < fp->bc; i++)
429 if (fp->bv[i].tl)
430 sol_draw_list(fp, fp->bv + i, fp->bv[i].tl, s);
432 glPopAttrib();
435 /*---------------------------------------------------------------------------*/
437 static void sol_load_objects(struct s_file *fp, int s)
439 short i;
441 for (i = 0; i < fp->bc; i++)
443 struct s_body *bp = fp->bv + i;
445 /* Draw all opaque geometry. */
447 if (sol_enum_body(fp, bp, M_OPAQUE | M_ENVIRONMENT))
449 fp->bv[i].ol = glGenLists(1);
451 glNewList(fp->bv[i].ol, GL_COMPILE);
453 sol_draw_body(fp, fp->bv + i, M_OPAQUE | M_ENVIRONMENT, s);
455 glEndList();
457 else fp->bv[i].ol = 0;
459 /* Draw all translucent geometry. */
461 if (sol_enum_body(fp, bp, M_TRANSPARENT))
463 fp->bv[i].tl = glGenLists(1);
465 glNewList(fp->bv[i].tl, GL_COMPILE);
467 sol_draw_body(fp, fp->bv + i, M_TRANSPARENT, s);
469 glEndList();
471 else fp->bv[i].tl = 0;
473 /* Draw all reflective geometry. */
475 if (sol_enum_body(fp, bp, M_REFLECTIVE))
477 fp->bv[i].rl = glGenLists(1);
479 glNewList(fp->bv[i].rl, GL_COMPILE);
481 sol_draw_body(fp, fp->bv + i, M_REFLECTIVE, s);
483 glEndList();
485 else fp->bv[i].rl = 0;
489 static SDL_Surface *sol_find_texture(const char *name, GLenum *f0, GLenum *f1)
491 char png[MAXSTR];
492 char tga[MAXSTR];
493 char jpg[MAXSTR];
494 SDL_Surface *s;
496 strncpy(png, name, PATHMAX); strcat(png, ".png");
497 strncpy(tga, name, PATHMAX); strcat(tga, ".tga");
498 strncpy(jpg, name, PATHMAX); strcat(jpg, ".jpg");
500 /* Prefer a lossless copy of the texture over a lossy compression. */
502 if ((s = IMG_Load(config_data(png))))
504 *f0 = (s->format->BitsPerPixel == 32) ? GL_RGBA : GL_RGB;
505 *f1 = (s->format->BitsPerPixel == 32) ? GL_RGBA : GL_RGB;
506 return s;
508 if ((s = IMG_Load(config_data(tga))))
510 *f0 = (s->format->BitsPerPixel == 32) ? GL_RGBA : GL_RGB;
511 *f1 = (s->format->BitsPerPixel == 32) ? GL_BGRA : GL_RGB; /* swab */
512 return s;
514 if ((s = IMG_Load(config_data(jpg))))
516 *f0 = (s->format->BitsPerPixel == 32) ? GL_RGBA : GL_RGB;
517 *f1 = (s->format->BitsPerPixel == 32) ? GL_RGBA : GL_RGB;
518 return s;
521 return NULL;
524 static void sol_load_textures(struct s_file *fp, int k)
526 SDL_Surface *s;
527 SDL_Surface *d;
528 GLenum f0;
529 GLenum f1;
531 int i;
533 for (i = 0; i < fp->mc; i++)
534 if ((s = sol_find_texture(fp->mv[i].f, &f0, &f1)))
536 glGenTextures(1, &fp->mv[i].o);
537 glBindTexture(GL_TEXTURE_2D, fp->mv[i].o);
539 if (k > 1)
541 /* Create a new buffer and copy the scaled image to it. */
543 d = SDL_CreateRGBSurface(SDL_SWSURFACE, s->w / k, s->h / k,
544 s->format->BitsPerPixel,
545 RMASK, GMASK, BMASK, AMASK);
546 if (d)
548 SDL_LockSurface(s);
549 SDL_LockSurface(d);
551 gluScaleImage(f1,
552 s->w, s->h, GL_UNSIGNED_BYTE, s->pixels,
553 d->w, d->h, GL_UNSIGNED_BYTE, d->pixels);
555 SDL_UnlockSurface(d);
556 SDL_UnlockSurface(s);
558 /* Load the scaled image. */
560 gluBuild2DMipmaps(GL_TEXTURE_2D, f0, d->w, d->h, f1,
561 GL_UNSIGNED_BYTE, d->pixels);
563 SDL_FreeSurface(d);
566 else
567 gluBuild2DMipmaps(GL_TEXTURE_2D, f0, s->w, s->h, f1,
568 GL_UNSIGNED_BYTE, s->pixels);
570 glTexParameteri(GL_TEXTURE_2D,
571 GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
572 glTexParameteri(GL_TEXTURE_2D,
573 GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR);
575 if (fp->mv[i].fl & M_CLAMPED)
577 glTexParameteri(GL_TEXTURE_2D,
578 GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
579 glTexParameteri(GL_TEXTURE_2D,
580 GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
582 else
584 glTexParameteri(GL_TEXTURE_2D,
585 GL_TEXTURE_WRAP_S, GL_REPEAT);
586 glTexParameteri(GL_TEXTURE_2D,
587 GL_TEXTURE_WRAP_T, GL_REPEAT);
590 SDL_FreeSurface(s);
594 /*---------------------------------------------------------------------------*/
596 static void sol_load_mtrl(FILE *fin, struct s_mtrl *mp)
598 get_array(fin, mp->a, 4);
599 get_array(fin, mp->d, 4);
600 get_array(fin, mp->s, 4);
601 get_array(fin, mp->e, 4);
602 get_array(fin, mp->h, 1);
603 get_short(fin, &mp->fl);
605 fread(mp->f, 1, PATHMAX, fin);
608 static void sol_load_vert(FILE *fin, struct s_vert *vp)
610 get_array(fin, vp->p, 3);
613 static void sol_load_edge(FILE *fin, struct s_edge *ep)
615 get_short(fin, &ep->vi);
616 get_short(fin, &ep->vj);
619 static void sol_load_side(FILE *fin, struct s_side *sp)
621 get_array(fin, sp->n, 3);
622 get_float(fin, &sp->d);
625 static void sol_load_texc(FILE *fin, struct s_texc *tp)
627 get_array(fin, tp->u, 2);
630 static void sol_load_geom(FILE *fin, struct s_geom *gp)
632 get_short(fin, &gp->mi);
633 get_short(fin, &gp->ti);
634 get_short(fin, &gp->si);
635 get_short(fin, &gp->vi);
636 get_short(fin, &gp->tj);
637 get_short(fin, &gp->sj);
638 get_short(fin, &gp->vj);
639 get_short(fin, &gp->tk);
640 get_short(fin, &gp->sk);
641 get_short(fin, &gp->vk);
644 static void sol_load_lump(FILE *fin, struct s_lump *lp)
646 get_short(fin, &lp->fl);
647 get_short(fin, &lp->v0);
648 get_short(fin, &lp->vc);
649 get_short(fin, &lp->e0);
650 get_short(fin, &lp->ec);
651 get_short(fin, &lp->g0);
652 get_short(fin, &lp->gc);
653 get_short(fin, &lp->s0);
654 get_short(fin, &lp->sc);
657 static void sol_load_node(FILE *fin, struct s_node *np)
659 get_short(fin, &np->si);
660 get_short(fin, &np->ni);
661 get_short(fin, &np->nj);
662 get_short(fin, &np->l0);
663 get_short(fin, &np->lc);
666 static void sol_load_path(FILE *fin, struct s_path *pp)
668 get_array(fin, pp->p, 3);
669 get_float(fin, &pp->t);
670 get_short(fin, &pp->pi);
671 get_short(fin, &pp->f);
674 static void sol_load_body(FILE *fin, struct s_body *bp)
676 get_short(fin, &bp->pi);
677 get_short(fin, &bp->ni);
678 get_short(fin, &bp->l0);
679 get_short(fin, &bp->lc);
680 get_short(fin, &bp->g0);
681 get_short(fin, &bp->gc);
684 static void sol_load_coin(FILE *fin, struct s_coin *cp)
686 get_array(fin, cp->p, 3);
687 get_short(fin, &cp->n);
690 static void sol_load_goal(FILE *fin, struct s_goal *zp)
692 get_array(fin, zp->p, 3);
693 get_float(fin, &zp->r);
696 static void sol_load_swch(FILE *fin, struct s_swch *xp)
698 get_array(fin, xp->p, 3);
699 get_float(fin, &xp->r);
700 get_short(fin, &xp->pi);
701 get_float(fin, &xp->t0);
702 get_float(fin, &xp->t);
703 get_short(fin, &xp->f0);
704 get_short(fin, &xp->f);
707 static void sol_load_bill(FILE *fin, struct s_bill *rp)
709 get_short(fin, &rp->fl);
710 get_short(fin, &rp->mi);
711 get_float(fin, &rp->t);
712 get_float(fin, &rp->d);
713 get_array(fin, rp->w, 3);
714 get_array(fin, rp->h, 3);
715 get_array(fin, rp->rx, 3);
716 get_array(fin, rp->ry, 3);
717 get_array(fin, rp->rz, 3);
720 static void sol_load_jump(FILE *fin, struct s_jump *jp)
722 get_array(fin, jp->p, 3);
723 get_array(fin, jp->q, 3);
724 get_float(fin, &jp->r);
727 static void sol_load_ball(FILE *fin, struct s_ball *bp)
729 get_array(fin, bp->e[0], 3);
730 get_array(fin, bp->e[1], 3);
731 get_array(fin, bp->e[2], 3);
732 get_array(fin, bp->p, 3);
733 get_float(fin, &bp->r);
736 static void sol_load_view(FILE *fin, struct s_view *wp)
738 get_array(fin, wp->p, 3);
739 get_array(fin, wp->q, 3);
742 static void sol_load_file(FILE *fin, struct s_file *fp)
744 int i;
746 get_short(fin, &fp->mc);
747 get_short(fin, &fp->vc);
748 get_short(fin, &fp->ec);
749 get_short(fin, &fp->sc);
750 get_short(fin, &fp->tc);
751 get_short(fin, &fp->gc);
752 get_short(fin, &fp->lc);
753 get_short(fin, &fp->nc);
754 get_short(fin, &fp->pc);
755 get_short(fin, &fp->bc);
756 get_short(fin, &fp->cc);
757 get_short(fin, &fp->zc);
758 get_short(fin, &fp->jc);
759 get_short(fin, &fp->xc);
760 get_short(fin, &fp->rc);
761 get_short(fin, &fp->uc);
762 get_short(fin, &fp->wc);
763 get_short(fin, &fp->ic);
764 get_short(fin, &fp->ac);
766 fp->mv = (struct s_mtrl *) calloc(fp->mc, sizeof (struct s_mtrl));
767 fp->vv = (struct s_vert *) calloc(fp->vc, sizeof (struct s_vert));
768 fp->ev = (struct s_edge *) calloc(fp->ec, sizeof (struct s_edge));
769 fp->sv = (struct s_side *) calloc(fp->sc, sizeof (struct s_side));
770 fp->tv = (struct s_texc *) calloc(fp->tc, sizeof (struct s_texc));
771 fp->gv = (struct s_geom *) calloc(fp->gc, sizeof (struct s_geom));
772 fp->lv = (struct s_lump *) calloc(fp->lc, sizeof (struct s_lump));
773 fp->nv = (struct s_node *) calloc(fp->nc, sizeof (struct s_node));
774 fp->pv = (struct s_path *) calloc(fp->pc, sizeof (struct s_path));
775 fp->bv = (struct s_body *) calloc(fp->bc, sizeof (struct s_body));
776 fp->cv = (struct s_coin *) calloc(fp->cc, sizeof (struct s_coin));
777 fp->zv = (struct s_goal *) calloc(fp->zc, sizeof (struct s_goal));
778 fp->jv = (struct s_jump *) calloc(fp->jc, sizeof (struct s_jump));
779 fp->xv = (struct s_swch *) calloc(fp->xc, sizeof (struct s_swch));
780 fp->rv = (struct s_bill *) calloc(fp->rc, sizeof (struct s_bill));
781 fp->uv = (struct s_ball *) calloc(fp->uc, sizeof (struct s_ball));
782 fp->wv = (struct s_view *) calloc(fp->wc, sizeof (struct s_view));
783 fp->iv = (short *) calloc(fp->ic, sizeof (short));
784 fp->av = (char *) calloc(fp->ac, sizeof (char));
786 for (i = 0; i < fp->mc; i++) sol_load_mtrl(fin, fp->mv + i);
787 for (i = 0; i < fp->vc; i++) sol_load_vert(fin, fp->vv + i);
788 for (i = 0; i < fp->ec; i++) sol_load_edge(fin, fp->ev + i);
789 for (i = 0; i < fp->sc; i++) sol_load_side(fin, fp->sv + i);
790 for (i = 0; i < fp->tc; i++) sol_load_texc(fin, fp->tv + i);
791 for (i = 0; i < fp->gc; i++) sol_load_geom(fin, fp->gv + i);
792 for (i = 0; i < fp->lc; i++) sol_load_lump(fin, fp->lv + i);
793 for (i = 0; i < fp->nc; i++) sol_load_node(fin, fp->nv + i);
794 for (i = 0; i < fp->pc; i++) sol_load_path(fin, fp->pv + i);
795 for (i = 0; i < fp->bc; i++) sol_load_body(fin, fp->bv + i);
796 for (i = 0; i < fp->cc; i++) sol_load_coin(fin, fp->cv + i);
797 for (i = 0; i < fp->zc; i++) sol_load_goal(fin, fp->zv + i);
798 for (i = 0; i < fp->jc; i++) sol_load_jump(fin, fp->jv + i);
799 for (i = 0; i < fp->xc; i++) sol_load_swch(fin, fp->xv + i);
800 for (i = 0; i < fp->rc; i++) sol_load_bill(fin, fp->rv + i);
801 for (i = 0; i < fp->uc; i++) sol_load_ball(fin, fp->uv + i);
802 for (i = 0; i < fp->wc; i++) sol_load_view(fin, fp->wv + i);
803 for (i = 0; i < fp->ic; i++) get_short(fin, fp->iv + i);
805 fread(fp->av, 1, fp->ac, fin);
808 int sol_load(struct s_file *fp, const char *filename, int k, int s)
810 FILE *fin;
812 if ((fin = fopen(filename, FMODE_RB)))
814 sol_load_file(fin, fp);
815 sol_load_textures(fp, k);
816 sol_load_objects (fp, s);
818 fclose(fin);
820 return 1;
822 return 0;
825 /*---------------------------------------------------------------------------*/
827 static void sol_stor_mtrl(FILE *fout, struct s_mtrl *mp)
829 put_array(fout, mp->a, 4);
830 put_array(fout, mp->d, 4);
831 put_array(fout, mp->s, 4);
832 put_array(fout, mp->e, 4);
833 put_array(fout, mp->h, 1);
834 put_short(fout, &mp->fl);
836 fwrite(mp->f, 1, PATHMAX, fout);
839 static void sol_stor_vert(FILE *fout, struct s_vert *vp)
841 put_array(fout, vp->p, 3);
844 static void sol_stor_edge(FILE *fout, struct s_edge *ep)
846 put_short(fout, &ep->vi);
847 put_short(fout, &ep->vj);
850 static void sol_stor_side(FILE *fout, struct s_side *sp)
852 put_array(fout, sp->n, 3);
853 put_float(fout, &sp->d);
856 static void sol_stor_texc(FILE *fout, struct s_texc *tp)
858 put_array(fout, tp->u, 2);
861 static void sol_stor_geom(FILE *fout, struct s_geom *gp)
863 put_short(fout, &gp->mi);
864 put_short(fout, &gp->ti);
865 put_short(fout, &gp->si);
866 put_short(fout, &gp->vi);
867 put_short(fout, &gp->tj);
868 put_short(fout, &gp->sj);
869 put_short(fout, &gp->vj);
870 put_short(fout, &gp->tk);
871 put_short(fout, &gp->sk);
872 put_short(fout, &gp->vk);
875 static void sol_stor_lump(FILE *fout, struct s_lump *lp)
877 put_short(fout, &lp->fl);
878 put_short(fout, &lp->v0);
879 put_short(fout, &lp->vc);
880 put_short(fout, &lp->e0);
881 put_short(fout, &lp->ec);
882 put_short(fout, &lp->g0);
883 put_short(fout, &lp->gc);
884 put_short(fout, &lp->s0);
885 put_short(fout, &lp->sc);
888 static void sol_stor_node(FILE *fout, struct s_node *np)
890 put_short(fout, &np->si);
891 put_short(fout, &np->ni);
892 put_short(fout, &np->nj);
893 put_short(fout, &np->l0);
894 put_short(fout, &np->lc);
897 static void sol_stor_path(FILE *fout, struct s_path *pp)
899 put_array(fout, pp->p, 3);
900 put_float(fout, &pp->t);
901 put_short(fout, &pp->pi);
902 put_short(fout, &pp->f);
905 static void sol_stor_body(FILE *fout, struct s_body *bp)
907 put_short(fout, &bp->pi);
908 put_short(fout, &bp->ni);
909 put_short(fout, &bp->l0);
910 put_short(fout, &bp->lc);
911 put_short(fout, &bp->g0);
912 put_short(fout, &bp->gc);
915 static void sol_stor_coin(FILE *fout, struct s_coin *cp)
917 put_array(fout, cp->p, 3);
918 put_short(fout, &cp->n);
921 static void sol_stor_goal(FILE *fout, struct s_goal *zp)
923 put_array(fout, zp->p, 3);
924 put_float(fout, &zp->r);
927 static void sol_stor_swch(FILE *fout, struct s_swch *xp)
929 put_array(fout, xp->p, 3);
930 put_float(fout, &xp->r);
931 put_short(fout, &xp->pi);
932 put_float(fout, &xp->t0);
933 put_float(fout, &xp->t);
934 put_short(fout, &xp->f0);
935 put_short(fout, &xp->f);
938 static void sol_stor_bill(FILE *fout, struct s_bill *rp)
940 put_short(fout, &rp->fl);
941 put_short(fout, &rp->mi);
942 put_float(fout, &rp->t);
943 put_float(fout, &rp->d);
944 put_array(fout, rp->w, 3);
945 put_array(fout, rp->h, 3);
946 put_array(fout, rp->rx, 3);
947 put_array(fout, rp->ry, 3);
948 put_array(fout, rp->rz, 3);
951 static void sol_stor_jump(FILE *fout, struct s_jump *jp)
953 put_array(fout, jp->p, 3);
954 put_array(fout, jp->q, 3);
955 put_float(fout, &jp->r);
958 static void sol_stor_ball(FILE *fout, struct s_ball *bp)
960 put_array(fout, bp->e[0], 3);
961 put_array(fout, bp->e[1], 3);
962 put_array(fout, bp->e[2], 3);
963 put_array(fout, bp->p, 3);
964 put_float(fout, &bp->r);
967 static void sol_stor_view(FILE *fout, struct s_view *wp)
969 put_array(fout, wp->p, 3);
970 put_array(fout, wp->q, 3);
973 static void sol_stor_file(FILE *fin, struct s_file *fp)
975 int i;
977 put_short(fin, &fp->mc);
978 put_short(fin, &fp->vc);
979 put_short(fin, &fp->ec);
980 put_short(fin, &fp->sc);
981 put_short(fin, &fp->tc);
982 put_short(fin, &fp->gc);
983 put_short(fin, &fp->lc);
984 put_short(fin, &fp->nc);
985 put_short(fin, &fp->pc);
986 put_short(fin, &fp->bc);
987 put_short(fin, &fp->cc);
988 put_short(fin, &fp->zc);
989 put_short(fin, &fp->jc);
990 put_short(fin, &fp->xc);
991 put_short(fin, &fp->rc);
992 put_short(fin, &fp->uc);
993 put_short(fin, &fp->wc);
994 put_short(fin, &fp->ic);
995 put_short(fin, &fp->ac);
997 for (i = 0; i < fp->mc; i++) sol_stor_mtrl(fin, fp->mv + i);
998 for (i = 0; i < fp->vc; i++) sol_stor_vert(fin, fp->vv + i);
999 for (i = 0; i < fp->ec; i++) sol_stor_edge(fin, fp->ev + i);
1000 for (i = 0; i < fp->sc; i++) sol_stor_side(fin, fp->sv + i);
1001 for (i = 0; i < fp->tc; i++) sol_stor_texc(fin, fp->tv + i);
1002 for (i = 0; i < fp->gc; i++) sol_stor_geom(fin, fp->gv + i);
1003 for (i = 0; i < fp->lc; i++) sol_stor_lump(fin, fp->lv + i);
1004 for (i = 0; i < fp->nc; i++) sol_stor_node(fin, fp->nv + i);
1005 for (i = 0; i < fp->pc; i++) sol_stor_path(fin, fp->pv + i);
1006 for (i = 0; i < fp->bc; i++) sol_stor_body(fin, fp->bv + i);
1007 for (i = 0; i < fp->cc; i++) sol_stor_coin(fin, fp->cv + i);
1008 for (i = 0; i < fp->zc; i++) sol_stor_goal(fin, fp->zv + i);
1009 for (i = 0; i < fp->jc; i++) sol_stor_jump(fin, fp->jv + i);
1010 for (i = 0; i < fp->xc; i++) sol_stor_swch(fin, fp->xv + i);
1011 for (i = 0; i < fp->rc; i++) sol_stor_bill(fin, fp->rv + i);
1012 for (i = 0; i < fp->uc; i++) sol_stor_ball(fin, fp->uv + i);
1013 for (i = 0; i < fp->wc; i++) sol_stor_view(fin, fp->wv + i);
1014 for (i = 0; i < fp->ic; i++) put_short(fin, fp->iv + i);
1016 fwrite(fp->av, 1, fp->ac, fin);
1019 /*---------------------------------------------------------------------------*/
1021 int sol_stor(struct s_file *fp, const char *filename)
1023 FILE *fout;
1025 if ((fout = fopen(filename, FMODE_WB)))
1027 sol_stor_file(fout, fp);
1028 fclose(fout);
1030 return 1;
1032 return 0;
1035 void sol_free(struct s_file *fp)
1037 short i;
1039 for (i = 0; i < fp->mc; i++)
1041 if (glIsTexture(fp->mv[i].o))
1042 glDeleteTextures(1, &fp->mv[i].o);
1045 for (i = 0; i < fp->bc; i++)
1047 if (glIsList(fp->bv[i].ol))
1048 glDeleteLists(fp->bv[i].ol, 1);
1049 if (glIsList(fp->bv[i].tl))
1050 glDeleteLists(fp->bv[i].tl, 1);
1051 if (glIsList(fp->bv[i].rl))
1052 glDeleteLists(fp->bv[i].rl, 1);
1055 if (fp->mv) free(fp->mv);
1056 if (fp->vv) free(fp->vv);
1057 if (fp->ev) free(fp->ev);
1058 if (fp->sv) free(fp->sv);
1059 if (fp->tv) free(fp->tv);
1060 if (fp->gv) free(fp->gv);
1061 if (fp->lv) free(fp->lv);
1062 if (fp->nv) free(fp->nv);
1063 if (fp->pv) free(fp->pv);
1064 if (fp->bv) free(fp->bv);
1065 if (fp->cv) free(fp->cv);
1066 if (fp->zv) free(fp->zv);
1067 if (fp->jv) free(fp->jv);
1068 if (fp->xv) free(fp->xv);
1069 if (fp->rv) free(fp->rv);
1070 if (fp->uv) free(fp->uv);
1071 if (fp->wv) free(fp->wv);
1072 if (fp->av) free(fp->av);
1073 if (fp->iv) free(fp->iv);
1075 memset(fp, 0, sizeof (struct s_file));
1078 /*---------------------------------------------------------------------------*/
1079 /* Solves (p + v * t) . (p + v * t) == r * r for smallest t. */
1081 static float v_sol(const float p[3], const float v[3], float r)
1083 float a = v_dot(v, v);
1084 float b = v_dot(v, p) * 2.0f;
1085 float c = v_dot(p, p) - r * r;
1086 float d = b * b - 4.0f * a * c;
1088 if (a == 0.0f) return LARGE;
1089 if (d < 0.0f) return LARGE;
1091 if (d == 0.0f)
1092 return -b * 0.5f / a;
1093 else
1095 float t0 = 0.5f * (-b - fsqrtf(d)) / a;
1096 float t1 = 0.5f * (-b + fsqrtf(d)) / a;
1097 float t = (t0 < t1) ? t0 : t1;
1099 return (t < 0.0f) ? LARGE : t;
1103 /*---------------------------------------------------------------------------*/
1106 * Compute the earliest time and position of the intersection of a
1107 * sphere and a vertex.
1109 * The sphere has radius R and moves along vector V from point P. The
1110 * vertex moves along vector W from point Q in a coordinate system
1111 * based at O.
1113 static float v_vert(float Q[3],
1114 const float o[3],
1115 const float q[3],
1116 const float w[3],
1117 const float p[3],
1118 const float v[3], float r)
1120 float O[3], P[3], V[3];
1121 float t = LARGE;
1123 v_add(O, o, q);
1124 v_sub(P, p, O);
1125 v_sub(V, v, w);
1127 if (v_dot(P, V) < 0.0f)
1129 t = v_sol(P, V, r);
1131 if (t < LARGE)
1132 v_mad(Q, O, w, t);
1134 return t;
1138 * Compute the earliest time and position of the intersection of a
1139 * sphere and an edge.
1141 * The sphere has radius R and moves along vector V from point P. The
1142 * edge moves along vector W from point Q in a coordinate system based
1143 * at O. The edge extends along the length of vector U.
1145 static float v_edge(float Q[3],
1146 const float o[3],
1147 const float q[3],
1148 const float u[3],
1149 const float w[3],
1150 const float p[3],
1151 const float v[3], float r)
1153 float d[3], e[3];
1154 float P[3], V[3];
1155 float du, eu, uu, s, t;
1157 v_sub(d, p, o);
1158 v_sub(d, d, q);
1159 v_sub(e, v, w);
1161 du = v_dot(d, u);
1162 eu = v_dot(e, u);
1163 uu = v_dot(u, u);
1165 v_mad(P, d, u, -du / uu);
1166 v_mad(V, e, u, -eu / uu);
1168 t = v_sol(P, V, r);
1169 s = (du + eu * t) / uu;
1171 if (0.0f < t && t < LARGE && 0.0f < s && s < 1.0f)
1173 v_mad(d, o, w, t);
1174 v_mad(e, q, u, s);
1175 v_add(Q, e, d);
1177 else
1178 t = LARGE;
1180 return t;
1184 * Compute the earlist time and position of the intersection of a
1185 * sphere and a plane.
1187 * The sphere has radius R and moves along vector V from point P. The
1188 * plane oves along vector W. The plane has normal N and is
1189 * positioned at distance D from the origin O along that normal.
1191 static float v_side(float Q[3],
1192 const float o[3],
1193 const float w[3],
1194 const float n[3], float d,
1195 const float p[3],
1196 const float v[3], float r)
1198 float vn = v_dot(v, n);
1199 float wn = v_dot(w, n);
1200 float t = LARGE;
1202 if (vn - wn <= 0.0f)
1204 float on = v_dot(o, n);
1205 float pn = v_dot(p, n);
1207 float u = (r + d + on - pn) / (vn - wn);
1208 float a = ( d + on - pn) / (vn - wn);
1210 if (0.0f <= u)
1212 t = u;
1214 v_mad(Q, p, v, +t);
1215 v_mad(Q, Q, n, -r);
1217 else if (0.0f <= a)
1219 t = 0;
1221 v_mad(Q, p, v, +t);
1222 v_mad(Q, Q, n, -r);
1225 return t;
1228 /*---------------------------------------------------------------------------*/
1231 * Compute the new linear and angular velocities of a bouncing ball.
1232 * Q gives the position of the point of impact and W gives the
1233 * velocity of the object being impacted.
1235 static float sol_bounce(struct s_ball *up,
1236 const float q[3],
1237 const float w[3], float dt)
1239 const float kb = 1.10f;
1240 const float ke = 0.70f;
1241 const float km = 0.20f;
1243 float n[3], r[3], d[3], u[3], vn, wn, xn, yn;
1244 float *p = up->p;
1245 float *v = up->v;
1247 /* Find the normal of the impact. */
1249 v_sub(r, p, q);
1250 v_sub(d, v, w);
1251 v_nrm(n, r);
1253 /* Find the new angular velocity. */
1255 v_crs(up->w, d, r);
1256 v_scl(up->w, up->w, -1.0f / (up->r * up->r));
1258 /* Find the new linear velocity. */
1260 vn = v_dot(v, n);
1261 wn = v_dot(w, n);
1262 xn = (vn < 0.0f) ? -vn * ke : vn;
1263 yn = (wn > 0.0f) ? wn * kb : wn;
1265 v_mad(u, w, n, -wn);
1266 v_mad(v, v, n, -vn);
1267 v_mad(v, v, u, +km * dt);
1268 v_mad(v, v, n, xn + yn);
1270 v_mad(p, q, n, up->r);
1272 /* Return the "energy" of the impact, to determine the sound amplitude. */
1274 return fabsf(v_dot(n, d));
1277 /*---------------------------------------------------------------------------*/
1280 * Compute the states of all switches after DT seconds have passed.
1282 static void sol_swch_step(struct s_file *fp, float dt)
1284 short xi;
1286 for (xi = 0; xi < fp->xc; xi++)
1288 struct s_swch *xp = fp->xv + xi;
1290 if (xp->t > 0)
1292 xp->t -= dt;
1294 if (xp->t <= 0)
1296 short pi = xp->pi;
1297 short pj = xp->pi;
1299 do /* Tortoise and hare cycle traverser. */
1301 fp->pv[pi].f = xp->f0;
1302 fp->pv[pj].f = xp->f0;
1304 pi = fp->pv[pi].pi;
1305 pj = fp->pv[pj].pi;
1306 pj = fp->pv[pj].pi;
1308 while (pi != pj);
1310 xp->f = xp->f0;
1317 * Compute the positions of all bodies after DT seconds have passed.
1319 static void sol_body_step(struct s_file *fp, float dt)
1321 short i;
1323 for (i = 0; i < fp->bc; i++)
1325 struct s_body *bp = fp->bv + i;
1326 struct s_path *pp = fp->pv + bp->pi;
1328 if (bp->pi >= 0 && pp->f)
1330 bp->t += dt;
1332 if (bp->t >= pp->t)
1334 bp->t -= pp->t;
1335 bp->pi = pp->pi;
1342 * Compute the positions of all balls after DT seconds have passed.
1344 static void sol_ball_step(struct s_file *fp, float dt)
1346 short i;
1348 for (i = 0; i < fp->uc; i++)
1350 struct s_ball *up = fp->uv + i;
1352 v_mad(up->p, up->p, up->v, dt);
1354 if (v_len(up->w) > 0.0f)
1356 float M[16];
1357 float w[3];
1358 float e[3][3];
1360 v_nrm(w, up->w);
1361 m_rot(M, w, v_len(up->w) * dt);
1363 m_vxfm(e[0], M, up->e[0]);
1364 m_vxfm(e[1], M, up->e[1]);
1365 m_vxfm(e[2], M, up->e[2]);
1367 v_crs(up->e[2], e[0], e[1]);
1368 v_crs(up->e[1], e[2], e[0]);
1369 v_crs(up->e[0], e[1], e[2]);
1371 v_nrm(up->e[0], up->e[0]);
1372 v_nrm(up->e[1], up->e[1]);
1373 v_nrm(up->e[2], up->e[2]);
1378 /*---------------------------------------------------------------------------*/
1380 static float sol_test_vert(float dt,
1381 float T[3],
1382 const struct s_ball *up,
1383 const struct s_vert *vp,
1384 const float o[3],
1385 const float w[3])
1387 return v_vert(T, o, vp->p, w, up->p, up->v, up->r);
1390 static float sol_test_edge(float dt,
1391 float T[3],
1392 const struct s_ball *up,
1393 const struct s_file *fp,
1394 const struct s_edge *ep,
1395 const float o[3],
1396 const float w[3])
1398 float q[3];
1399 float u[3];
1401 v_cpy(q, fp->vv[ep->vi].p);
1402 v_sub(u, fp->vv[ep->vj].p,
1403 fp->vv[ep->vi].p);
1405 return v_edge(T, o, q, u, w, up->p, up->v, up->r);
1408 static float sol_test_side(float dt,
1409 float T[3],
1410 const struct s_ball *up,
1411 const struct s_file *fp,
1412 const struct s_lump *lp,
1413 const struct s_side *sp,
1414 const float o[3],
1415 const float w[3])
1417 float t = v_side(T, o, w, sp->n, sp->d, up->p, up->v, up->r);
1418 short i;
1420 if (t < dt)
1421 for (i = 0; i < lp->sc; i++)
1423 const struct s_side *sq = fp->sv + fp->iv[lp->s0 + i];
1425 if (sp != sq &&
1426 v_dot(T, sq->n) -
1427 v_dot(o, sq->n) -
1428 v_dot(w, sq->n) * t > sq->d)
1429 return LARGE;
1431 return t;
1434 /*---------------------------------------------------------------------------*/
1436 static float sol_test_fore(float dt,
1437 const struct s_ball *up,
1438 const struct s_side *sp,
1439 const float o[3],
1440 const float w[3])
1442 float q[3];
1444 /* If the ball is not behind the plane, the test passes. */
1446 v_sub(q, up->p, o);
1448 if (v_dot(q, sp->n) - sp->d + up->r >= 0)
1449 return 1;
1451 /* if the ball is behind the plane but will hit before dt, test passes. */
1453 if (v_side(q, o, w, sp->n, sp->d, up->p, up->v, up->r) < dt)
1454 return 1;
1456 /* If the ball is behind but moving toward the plane, test passes. */
1458 if (v_dot(up->v, sp->n) > 0)
1459 return 1;
1462 /* Else, test fails. */
1464 return 0;
1467 static float sol_test_back(float dt,
1468 const struct s_ball *up,
1469 const struct s_side *sp,
1470 const float o[3],
1471 const float w[3])
1473 float q[3];
1475 /* If the ball is not in front of the plane, the test passes. */
1477 v_sub(q, up->p, o);
1479 if (v_dot(q, sp->n) - sp->d - up->r <= 0)
1480 return 1;
1482 /* if the ball is behind the plane but will hit before dt, test passes. */
1484 if (v_side(q, o, w, sp->n, sp->d, up->p, up->v, up->r) < dt)
1485 return 1;
1487 /* If the ball is in front but moving toward the plane, test passes. */
1489 if (v_dot(up->v, sp->n) < 0)
1490 return 1;
1493 /* Else, test fails. */
1495 return 0;
1498 /*---------------------------------------------------------------------------*/
1500 static float sol_test_lump(float dt,
1501 float T[3],
1502 const struct s_ball *up,
1503 const struct s_file *fp,
1504 const struct s_lump *lp,
1505 const float o[3],
1506 const float w[3])
1508 float U[3], u, t = dt;
1509 short i;
1511 /* Short circuit a non-solid lump. */
1513 if (lp->fl & L_DETAIL) return t;
1515 /* Test all verts */
1517 if (up->r > 0.0f)
1518 for (i = 0; i < lp->vc; i++)
1520 const struct s_vert *vp = fp->vv + fp->iv[lp->v0 + i];
1522 if ((u = sol_test_vert(t, U, up, vp, o, w)) < t)
1524 v_cpy(T, U);
1525 t = u;
1529 /* Test all edges */
1531 if (up->r > 0.0f)
1532 for (i = 0; i < lp->ec; i++)
1534 const struct s_edge *ep = fp->ev + fp->iv[lp->e0 + i];
1536 if ((u = sol_test_edge(t, U, up, fp, ep, o, w)) < t)
1538 v_cpy(T, U);
1539 t = u;
1543 /* Test all sides */
1545 for (i = 0; i < lp->sc; i++)
1547 const struct s_side *sp = fp->sv + fp->iv[lp->s0 + i];
1549 if ((u = sol_test_side(t, U, up, fp, lp, sp, o, w)) < t)
1551 v_cpy(T, U);
1552 t = u;
1555 return t;
1558 static float sol_test_node(float dt,
1559 float T[3],
1560 const struct s_ball *up,
1561 const struct s_file *fp,
1562 const struct s_node *np,
1563 const float o[3],
1564 const float w[3])
1566 float U[3], u, t = dt;
1567 short i;
1569 /* Test all lumps */
1571 for (i = 0; i < np->lc; i++)
1573 const struct s_lump *lp = fp->lv + np->l0 + i;
1575 if ((u = sol_test_lump(t, U, up, fp, lp, o, w)) < t)
1577 v_cpy(T, U);
1578 t = u;
1582 /* Test in front of this node */
1584 if (np->ni >= 0 && sol_test_fore(t, up, fp->sv + np->si, o, w))
1586 const struct s_node *nq = fp->nv + np->ni;
1588 if ((u = sol_test_node(t, U, up, fp, nq, o, w)) < t)
1590 v_cpy(T, U);
1591 t = u;
1595 /* Test behind this node */
1597 if (np->nj >= 0 && sol_test_back(t, up, fp->sv + np->si, o, w))
1599 const struct s_node *nq = fp->nv + np->nj;
1601 if ((u = sol_test_node(t, U, up, fp, nq, o, w)) < t)
1603 v_cpy(T, U);
1604 t = u;
1608 return t;
1611 static float sol_test_body(float dt,
1612 float T[3], float V[3],
1613 const struct s_ball *up,
1614 const struct s_file *fp,
1615 const struct s_body *bp)
1617 float U[3], O[3], W[3], u, t = dt;
1619 const struct s_node *np = fp->nv + bp->ni;
1621 sol_body_p(O, fp, bp);
1622 sol_body_v(W, fp, bp);
1624 if ((u = sol_test_node(t, U, up, fp, np, O, W)) < t)
1626 v_cpy(T, U);
1627 v_cpy(V, W);
1628 t = u;
1630 return t;
1633 static float sol_test_file(float dt,
1634 float T[3], float V[3],
1635 const struct s_ball *up,
1636 const struct s_file *fp)
1638 float U[3], W[3], u, t = dt;
1639 short i;
1641 for (i = 0; i < fp->bc; i++)
1643 const struct s_body *bp = fp->bv + i;
1645 if ((u = sol_test_body(t, U, W, up, fp, bp)) < t)
1647 v_cpy(T, U);
1648 v_cpy(V, W);
1649 t = u;
1652 return t;
1655 /*---------------------------------------------------------------------------*/
1658 * Step the physics forward DT seconds under the influence of gravity
1659 * vector G. If the ball gets pinched between two moving solids, this
1660 * loop might not terminate. It is better to do something physically
1661 * impossible than to lock up the game. So, if we make more than C
1662 * iterations, punt it.
1665 float sol_step(struct s_file *fp, const float *g, float dt, short ui, int *m)
1667 float P[3], V[3], v[3], r[3], d, e, nt, b = 0.0f, tt = dt;
1668 int c = 16;
1670 if (ui < fp->uc)
1672 struct s_ball *up = fp->uv + ui;
1674 /* If the ball is in contact with a surface, apply friction. */
1676 v_cpy(v, up->v);
1677 v_cpy(up->v, g);
1679 if (m && sol_test_file(tt, P, V, up, fp) < 0.0005f)
1681 v_cpy(up->v, v);
1682 v_sub(r, P, up->p);
1684 if ((d = v_dot(r, g) / (v_len(r) * v_len(g))) > 0.999f)
1686 if ((e = (v_len(up->v) - dt)) > 0.0f)
1688 /* Scale the linear velocity. */
1690 v_nrm(up->v, up->v);
1691 v_scl(up->v, up->v, e);
1693 /* Scale the angular velocity. */
1695 v_sub(v, V, up->v);
1696 v_crs(up->w, v, r);
1697 v_scl(up->w, up->w, -1.0f / (up->r * up->r));
1699 else
1701 /* Friction has brought the ball to a stop. */
1703 up->v[0] = 0.0f;
1704 up->v[1] = 0.0f;
1705 up->v[2] = 0.0f;
1707 (*m)++;
1710 else v_mad(up->v, v, g, tt);
1712 else v_mad(up->v, v, g, tt);
1714 /* Test for collision. */
1716 while (c > 0 && tt > 0 && tt > (nt = sol_test_file(tt, P, V, up, fp)))
1718 sol_body_step(fp, nt);
1719 sol_swch_step(fp, nt);
1720 sol_ball_step(fp, nt);
1722 tt -= nt;
1724 if (b < (d = sol_bounce(up, P, V, nt)))
1725 b = d;
1727 c--;
1730 sol_body_step(fp, tt);
1731 sol_swch_step(fp, tt);
1732 sol_ball_step(fp, tt);
1734 return b;
1737 /*---------------------------------------------------------------------------*/
1739 int sol_coin_test(struct s_file *fp, float *p, float coin_r)
1741 const float *ball_p = fp->uv->p;
1742 const float ball_r = fp->uv->r;
1743 short ci, n;
1745 for (ci = 0; ci < fp->cc; ci++)
1747 float r[3];
1749 r[0] = ball_p[0] - fp->cv[ci].p[0];
1750 r[1] = ball_p[1] - fp->cv[ci].p[1];
1751 r[2] = ball_p[2] - fp->cv[ci].p[2];
1753 if (fp->cv[ci].n > 0 && v_len(r) < ball_r + coin_r)
1755 p[0] = fp->cv[ci].p[0];
1756 p[1] = fp->cv[ci].p[1];
1757 p[2] = fp->cv[ci].p[2];
1759 n = fp->cv[ci].n;
1760 fp->cv[ci].n = 0;
1762 return n;
1765 return 0;
1768 int sol_goal_test(struct s_file *fp, float *p, short ui)
1770 const float *ball_p = fp->uv[ui].p;
1771 const float ball_r = fp->uv[ui].r;
1772 short zi;
1774 for (zi = 0; zi < fp->zc; zi++)
1776 float r[3];
1778 r[0] = ball_p[0] - fp->zv[zi].p[0];
1779 r[1] = ball_p[2] - fp->zv[zi].p[2];
1780 r[2] = 0;
1782 if (v_len(r) < fp->zv[zi].r * 1.1 - ball_r &&
1783 ball_p[1] > fp->zv[zi].p[1] &&
1784 ball_p[1] < fp->zv[zi].p[1] + GOAL_HEIGHT / 2)
1786 p[0] = fp->zv[zi].p[0];
1787 p[1] = fp->zv[zi].p[1];
1788 p[2] = fp->zv[zi].p[2];
1790 return 1;
1793 return 0;
1796 int sol_jump_test(struct s_file *fp, float *p, short ui)
1798 const float *ball_p = fp->uv[ui].p;
1799 const float ball_r = fp->uv[ui].r;
1800 short ji;
1802 for (ji = 0; ji < fp->jc; ji++)
1804 float r[3];
1806 r[0] = ball_p[0] - fp->jv[ji].p[0];
1807 r[1] = ball_p[2] - fp->jv[ji].p[2];
1808 r[2] = 0;
1810 if (v_len(r) < fp->jv[ji].r - ball_r &&
1811 ball_p[1] > fp->jv[ji].p[1] &&
1812 ball_p[1] < fp->jv[ji].p[1] + JUMP_HEIGHT / 2)
1814 p[0] = fp->jv[ji].q[0] + (ball_p[0] - fp->jv[ji].p[0]);
1815 p[1] = fp->jv[ji].q[1] + (ball_p[1] - fp->jv[ji].p[1]);
1816 p[2] = fp->jv[ji].q[2] + (ball_p[2] - fp->jv[ji].p[2]);
1818 return 1;
1821 return 0;
1824 int sol_swch_test(struct s_file *fp, int flag, short ui)
1826 const float *ball_p = fp->uv[ui].p;
1827 const float ball_r = fp->uv[ui].r;
1828 short xi;
1829 int f = 1;
1831 for (xi = 0; xi < fp->xc; xi++)
1833 struct s_swch *xp = fp->xv + xi;
1835 if (xp->t0 == 0 || xp->f == xp->f0)
1837 float r[3];
1839 r[0] = ball_p[0] - xp->p[0];
1840 r[1] = ball_p[2] - xp->p[2];
1841 r[2] = 0;
1843 if (v_len(r) < xp->r - ball_r &&
1844 ball_p[1] > xp->p[1] &&
1845 ball_p[1] < xp->p[1] + SWCH_HEIGHT / 2)
1847 if (flag)
1849 short pi = xp->pi;
1850 short pj = xp->pi;
1852 /* Toggle the state, update the path. */
1854 xp->f = xp->f ? 0 : 1;
1856 do /* Tortoise and hare cycle traverser. */
1858 fp->pv[pi].f = xp->f;
1859 fp->pv[pj].f = xp->f;
1861 pi = fp->pv[pi].pi;
1862 pj = fp->pv[pj].pi;
1863 pj = fp->pv[pj].pi;
1865 while (pi != pj);
1867 /* It toggled to non-default state, start the timer. */
1869 if (xp->f != xp->f0)
1870 xp->t = xp->t0;
1872 f = 0;
1876 return f;
1879 /*---------------------------------------------------------------------------*/
1881 void put_file_state(FILE *fout, struct s_file *fp)
1883 /* Write the position and orientation of the ball. */
1885 put_array(fout, fp->uv[0].p, 3);
1886 put_array(fout, fp->uv[0].e[0], 3);
1887 put_array(fout, fp->uv[0].e[1], 3);
1890 void get_file_state(FILE *fin, struct s_file *fp)
1892 /* Read the position and orientation of the ball. */
1894 get_array(fin, fp->uv[0].p, 3);
1895 get_array(fin, fp->uv[0].e[0], 3);
1896 get_array(fin, fp->uv[0].e[1], 3);
1898 /* Compute the 3rd vector of the ball orientatian basis. */
1900 v_crs(fp->uv[0].e[2], fp->uv[0].e[0], fp->uv[0].e[1]);
1903 /*---------------------------------------------------------------------------*/