home *** CD-ROM | disk | FTP | other *** search
- /*
- * Raymath.c -- simple math routines.
- *
- * (c) 1993, 1994 by Han-Wen Nienhuys <hanwen@stack.urc.tue.nl>
- *
- * based on work by George Kyriazis, 1988
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation;
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
- /*
- * math and vector operations
- */
-
- #include <stdlib.h>
- #include "ray.h"
- #include "proto.h"
- #include "extern.h"
- #include "random.h"
-
- /*
- * floating point random number generator, from K&R chap 7.8.7 [deleted]
- * now with table lookup.
- */
-
- PRIVATE randidx;
-
- PUBLIC double
- frand(void)
- {
- if (++randidx > RANDOMTABSIZE)
- randidx = rand() % RANDOMTABSIZE;
-
- return randtable[randidx];
- }
-
-
-
- /*
- * Random number between -1 and 1
- */
- PUBLIC double
- rand1(void)
- {
- return (frand() - 0.5) * 2;
- }
-
-
- /*
- * approximate a gaussian random number generator between -1 and 1
- */
- PUBLIC double
- Gauss_rand(void)
- {
- double t;
-
- /* get a random number between -1 and 1 */
- t = rand1();
-
- /* and then square it. Don't lose the sign! */
- return (t * t * SGN(t));
- }
-
-
- /* check wether a vector is the nullvector */
- PUBLIC bool
- isvnull(vector a)
- {
- if (quickveclen(a) < EPSILON)
- return TRUE;
- else
- return FALSE;
- }
-
- /*
- * evaluate a polynomial given by coef, ord at the point x. Turbo C has a
- * function poly() to do this, but it is not ANSI-C, so this one is
- * included, just to be on the safe side.
- *
- * It's taken from Vort.
- */
-
- PUBLIC double
- poly_eval(register double x, int ord, double *coef)
- {
- register double *fp,
- f;
-
- fp = &coef[ord];
- f = *fp;
-
- for (fp--; fp >= coef; fp--)
- f = x * f + *fp;
-
- return f;
- }
-
- /****************************************************************************
- Performs a safe arctangent calculation for the two provided numbers. If the
- denominator is 0.o, atan2 is not called (it results in a floating exception
- on some machines). Instead, + or - PI/2 is returned.
- ****************************************************************************/
-
- PUBLIC double
- safe_arctangent(double a, double b)
- {
- if (b != 0.0)
- return atan2(a, b);
- else if (a > 0.0)
- return M_PI_2;
- else
- return -M_PI_2;
- }
-
-
-
- /* These are not used, so they are commented out. */
- #ifdef UNDEFINED
- PUBLIC double
- quickcos(double x)
- {
- double val;
-
- val = 1 - x * x / 2.4684;
-
- return val;
- }
-
- /* not used */
- PUBLIC double
- quickinvcos(double x)
- {
- double val;
-
- val = sqrt(2.4684 * (1 - x));
-
- return val;
- }
-
- #endif
-
- /* just a standard routine ... */
- PUBLIC void
- copy_vector(vector *dst, vector *src)
- {
- assert(src != NULL && src != NULL);
-
- *dst = *src;
- }
-
- /* transpose a matrix. WATCH IT! don't do transpose_matrix(A,A); */
- PUBLIC void
- transpose_matrix(matrix tr, matrix org)
- {
- int i,
- j;
-
- for (i = 0; i < 4; i++)
- for (j = 0; j < 4; j++)
- tr[i][j] = org[j][i];
-
- }
-
- PUBLIC void
- copy_matrix(matrix dst, matrix src)
- {
- int i,
- j;
-
- for (i = 0; i < 4; i++)
- for (j = 0; j < 4; j++)
- dst[i][j] = src[i][j];
- }
-
- /*
- * multiply matrix a with matrix, in that order. mmproduct(A,A,B) will
- * give valid results
- */
- PUBLIC void
- mmproduct(matrix c, matrix a, matrix b)
- {
- int i,
- j,
- k;
- matrix tmp;
-
- for (i = 0; i < 4; i++)
- for (j = 0; j < 4; j++) {
- tmp[i][j] = 0;
- for (k = 0; k < 4; k++)
- tmp[i][j] += a[i][k] * b[k][j];
- }
-
- copy_matrix(c, tmp);
- }
-
- /*
- * matrix vector product: multiply a 3x3 (or 4x4) matrix with
- * columnvector x
- */
- PUBLIC vector
- mvproduct(matrix A, vector x)
- {
- vector v;
-
- v.x = A[0][0] * x.x + A[0][1] * x.y + A[0][2] * x.z + A[0][3];
- v.y = A[1][0] * x.x + A[1][1] * x.y + A[1][2] * x.z + A[1][3];
- v.z = A[2][0] * x.x + A[2][1] * x.y + A[2][2] * x.z + A[2][3];
-
- return v;
- }
-
- /* multiply with transpose of A */
- PUBLIC vector
- transform_normal(matrix A, vector n)
- {
- vector v;
-
- v.x = n.x * A[0][0] + n.y * A[1][0] + n.z * A[2][0];
- v.y = n.x * A[0][1] + n.y * A[1][1] + n.z * A[2][1];
- v.z = n.x * A[0][2] + n.y * A[1][2] + n.z * A[2][2];
-
- return v;
- }
-
- /*
- * matrix-vector multiply, but don't use translation.
- */
- PUBLIC vector
- transform_direction(matrix A, vector x)
- {
- vector v;
-
- v.x = A[0][0] * x.x + A[0][1] * x.y + A[0][2] * x.z;
- v.y = A[1][0] * x.x + A[1][1] * x.y + A[1][2] * x.z;
- v.z = A[2][0] * x.x + A[2][1] * x.y + A[2][2] * x.z;
-
- return v;
- }
-
- /*
- * transform a ray using the object's inverse transform
- */
- PUBLIC void
- transform_ray(struct ray *r, object *o)
- {
-
- if (o->inv_trans == NULL)
- return;
- r->dir = transform_direction(*o->inv_trans, r->dir);
-
- /* remember! r->dir is NOT normalized */
-
- r->pos = mvproduct(*o->inv_trans, r->pos);
- }
-
-
- /* initialize m to the null matrix */
- PUBLIC void
- null_matrix(matrix m)
- {
- int i,
- j;
-
- for (i = 0; i < 4; i++)
- for (j = 0; j < 4; j++)
- m[i][j] = 0;
- }
-
- /* m := diag(1,1,..) */
- PUBLIC void
- unit_matrix(matrix m)
- {
- int i,
- j;
-
- for (i = 0; i < 4; i++)
- for (j = 0; j < 4; j++)
- if (i != j)
- m[i][j] = 0;
- else
- m[i][j] = 1;
-
- }
-
- /* ?rotate() generates matrix for lefthanded rotation about ? axis. */
- PRIVATE void
- xrotate(matrix m, double angle)
- {
- unit_matrix(m);
- m[1][1] = m[2][2] = cos(angle);
- m[1][2] = -sin(angle);
- m[2][1] = -m[1][2];
- }
-
- PRIVATE void
- yrotate(matrix m, double angle)
- {
- unit_matrix(m);
- m[0][0] = m[2][2] = cos(angle);
- m[0][2] = sin(angle);
- m[2][0] = -m[0][2];
- }
-
- PRIVATE void
- zrotate(matrix m, double angle)
- {
- unit_matrix(m);
- m[0][0] = m[1][1] = cos(angle);
- m[1][0] = sin(angle);
- m[0][1] = -m[1][0];
- }
-
- /*
- * generate matrix for rotation around x (angle xa), y (angle ya), z
- * (angle za), in that order.
- */
- PUBLIC void
- rotate_matrix(matrix m, vector r)
- {
- matrix a,
- b,
- c,
- d;
-
- xrotate(a, r.x);
- yrotate(b, r.y);
- mmproduct(d, b, a);
-
- zrotate(c, r.z);
- mmproduct(m, c, d);
- }
-
-
- /* generate a matrix which is the inverse of the rotation matrix of r */
- PUBLIC void
- inv_rotate_matrix(matrix m, vector r)
- {
- matrix rm;
-
- rotate_matrix(rm, r);
- transpose_matrix(m, rm);
- }
-
- /* generate a matrix for scaling with the vector s */
- PUBLIC void
- scale_matrix(matrix m, vector s)
- {
- unit_matrix(m);
- m[0][0] = s.x;
- m[1][1] = s.y;
- m[2][2] = s.z;
- }
-
-
- /* inverse of scale_matrix() */
- PUBLIC void
- inv_scale_matrix(matrix m, vector s)
- {
- s.x = 1 / s.x;
- s.y = 1 / s.y;
- s.z = 1 / s.z;
- scale_matrix(m, s);
- }
-
- /* translate_matrix */
- PUBLIC void
- translate_matrix(matrix m, vector t)
- {
- unit_matrix(m);
- m[0][3] = t.x;
- m[1][3] = t.y;
- m[2][3] = t.z;
- }
-
- /* inverse translation */
- PUBLIC void
- inv_translate_matrix(matrix m, vector t)
- {
- vneg(t, t);
- translate_matrix(m, t);
- }
-
- /* rotate a vector, just a standard routine */
- PUBLIC void
- rotate_vector(vector *v, matrix rotmat)
- {
- *v = mvproduct(rotmat, *v);
- }
-
- /* conversion. Not really used. */
- PUBLIC double
- degtorad(double deg)
- {
- return M_PI * deg / 180.0;
- }
-
- PUBLIC double
- radtodeg(double rad)
- {
- return 180.0 * rad / M_PI;
- }
-
- /*
- * convert a vector in degrees to a vector in radians, generate rotation
- * matrix, put it in m
- */
- PUBLIC void
- make_rotation_matrix(matrix m, vector v)
- {
- v.x = degtorad(v.x);
- v.y = degtorad(v.y);
- v.z = degtorad(v.z);
- rotate_matrix(m, v);
- }
-
-
- /*
- * decompose an inverse transform into the orginal simple transformations
- * Every combination of transforms should be writeable as
- *
- * TRS
- *
- * Translation, Rotation, Scale So any inverse transform is writeable as
- *
- * S^-1 R^-1 T^-1
- *
- */
-
- /*
- * this routine could be a lot faster, but this isn't critical, so I leave
- * at this.
- */
- PUBLIC void
- invert_trans(matrix out, matrix A)
- {
- vector row;
- matrix temp,
- rot,
- tm,
- sm;
- vector scal;
-
- unit_matrix(rot);
- row.x = A[0][0];
- row.y = A[0][1];
- row.z = A[0][2];
-
- scal.x = 1 / veclen(row);
- rot[0][0] = row.x * scal.x;
- rot[1][0] = row.y * scal.x;
- rot[2][0] = row.z * scal.x;
-
- row.x = A[1][0];
- row.y = A[1][1];
- row.z = A[1][2];
-
- scal.y = 1 / veclen(row);
- rot[0][1] = row.x * scal.y;
- rot[1][1] = row.y * scal.y;
- rot[2][1] = row.z * scal.y;
-
- row.x = A[2][0];
- row.y = A[2][1];
- row.z = A[2][2];
-
- scal.z = 1 / veclen(row);
- rot[0][2] = row.x * scal.z;
- rot[1][2] = row.y * scal.z;
- rot[2][2] = row.z * scal.z;
- scale_matrix(sm, scal);
-
- mmproduct(temp, sm, A);
- mmproduct(tm, rot, temp);
- tm[0][3] = -tm[0][3];
- tm[1][3] = -tm[1][3];
- tm[2][3] = -tm[2][3];
-
- mmproduct(temp, rot, sm);
- mmproduct(out, tm, temp);
- }
-
-
- /*
- * The provided point is compared against the current minimum and maximum
- * values in X, Y, and Z. If a new maximum or minimum is found, the min
- * and max variables are updated.
- */
- /* from GGems III */
- PUBLIC void
- update_min_and_max(vector *min, vector *max, vector point)
- {
- if (point.x < min->x)
- min->x = point.x;
- if (point.x > max->x)
- max->x = point.x;
- if (point.y < min->y)
- min->y = point.y;
- if (point.y > max->y)
- max->y = point.y;
- if (point.z < min->z)
- min->z = point.z;
- if (point.z > max->z)
- max->z = point.z;
- }
-
-
- /*
- * MEMORY MANAGEMENT
- */
-
-
- PUBLIC matrix *
- get_new_matrix(void)
- {
- matrix *p;
- p = malloc(sizeof(matrix));
-
- CHECK_MEM(p, "transform");
- unit_matrix(*p);
- return p;
- }
-
- PUBLIC void
- free_float(double *f)
- {
- free((void *) f);
- }
-
- PUBLIC void
- free_vector(vector *v)
- {
- free((void *) v);
- }
-
- PUBLIC vector *
- get_new_vector(void)
- {
- vector *p;
- p = malloc(sizeof(vector));
-
- CHECK_MEM(p, "vector");
- setvector(*p, 0, 0, 0);
- return p;
- }
-
- PUBLIC double *
- get_new_float(void)
- {
- double *p;
-
- p = ALLOC(double);
-
- CHECK_MEM(p, "float");
-
- *p = 0.0;
- return p;
- }
-
- /*
- * DEBUGGING
- */
-
- PUBLIC void
- print_v(char *s, vector v)
- {
- #ifdef DEBUG
- printf("%s: <%lf, %lf, %lf>\n", s, v.x, v.y, v.z);
- #endif
- }
-
-
- PUBLIC void
- print_matrix(matrix c)
- {
- #ifdef DEBUG
- int i,
- j;
-
- printf("\n");
- for (i = 0; i < 4; i++) {
- for (j = 0; j < 4; j++)
- printf("%10lf ", c[i][j]);
- printf("\n");
- }
-
- #endif
- }
-