home *** CD-ROM | disk | FTP | other *** search
- /*
- * Copyright 1992, 1993, 1994, Silicon Graphics, Inc.
- * All Rights Reserved.
- *
- * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
- * the contents of this file may not be disclosed to third parties, copied or
- * duplicated in any form, in whole or in part, without the prior written
- * permission of Silicon Graphics, Inc.
- *
- * RESTRICTED RIGHTS LEGEND:
- * Use, duplication or disclosure by the Government is subject to restrictions
- * as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
- * and Computer Software clause at DFARS 252.227-7013, and/or in similar or
- * successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
- * rights reserved under the Copyright Laws of the United States.
- */
-
- /* Tom Davis -- 1992 */
-
- #include "3d.h"
- #include <stdio.h>
- #include <math.h>
- #define PI 3.1415926535
-
- curve_t *newcurve()
- {
- curve_t *c = (curve_t *)malloc(sizeof(curve_t));
- c->type = 0;
- c->data = c->cache = 0;
- return c;
- }
-
- typedef struct {
- float (*x)(float), (*y)(float), (*z)(float);
- float (*x1)(float), (*y1)(float), (*z1)(float);
- float (*x2)(float), (*y2)(float), (*z2)(float);
- } analyticdata_t;
-
- typedef struct {
- float a[3], b[3], c[3], d[3];
- } cubicdata_t;
-
- void freecurve(curve_t *c)
- {
- (*c->fctns->cleanup)(c);
- free(c);
- }
-
- static void analyticpoint(curve_t *c, float t, float p[3])
- {
- analyticdata_t *cd = (analyticdata_t *)c->data;
-
- p[0] = (*cd->x)(t);
- p[1] = (*cd->y)(t);
- p[2] = (*cd->z)(t);
- }
-
- static void analytictangent(curve_t *c, float t, float p[3])
- {
- analyticdata_t *cd = (analyticdata_t *)c->data;
-
- p[0] = (*cd->x1)(t);
- p[1] = (*cd->y1)(t);
- p[2] = (*cd->z1)(t);
- }
-
- static void analyticaccel(curve_t *c, float t, float p[3])
- {
- analyticdata_t *cd = (analyticdata_t *)c->data;
-
- p[0] = (*cd->x2)(t);
- p[1] = (*cd->y2)(t);
- p[2] = (*cd->z2)(t);
- }
-
- static void analyticcleanup(curve_t *c)
- {
- free(c->data);
- if (c->cache) free(c->cache);
- }
-
- cblock_t analyticblock = {
- analyticpoint,
- analytictangent,
- analyticaccel,
- analyticcleanup
- };
-
- curve_t *newanalyticcurve(float (*x)(float), float (*y)(float), float (*z)(float),
- float (*x1)(float), float (*y1)(float), float (*z1)(float),
- float (*x2)(float), float (*y2)(float), float (*z2)(float))
- {
- curve_t *cur = newcurve();
- analyticdata_t *cdata = (analyticdata_t *)malloc(sizeof(analyticdata_t));
-
- cur->type = CURVE_ANALYTIC;
- cur->fctns = &analyticblock;
- cur->data = (long *)cdata;
- cdata->x = x; cdata->y = y; cdata->z = z;
- cdata->x1 = x1; cdata->y1 = y1; cdata->z1 = z1;
- cdata->x2 = x2; cdata->y2 = y2; cdata->z2 = z2;
- return cur;
- }
-
- static void cubicpoint(curve_t *c, float t, float p[3])
- {
- cubicdata_t *cd = (cubicdata_t *)c->data;
- long i;
-
- for (i = 0; i < 3; i++)
- p[i] = cd->d[i] + t*(cd->c[i] + t*(cd->b[i] + t*cd->a[i]));
- }
-
- static void cubictangent(curve_t *c, float t, float p[3])
- {
- cubicdata_t *cd = (cubicdata_t *)c->data;
- long i;
-
- for (i = 0; i < 3; i++)
- p[i] = cd->c[i] + t*(2.0*cd->b[i] + t*3.0*cd->a[i]);
- }
-
- static void cubicaccel(curve_t *c, float t, float p[3])
- {
- cubicdata_t *cd = (cubicdata_t *)c->data;
- long i;
-
- for (i = 0; i < 3; i++)
- p[i] = 2.0*cd->b[i] + 6.0*t*cd->a[i];
- }
-
- static void cubiccleanup(curve_t *c)
- {
- free(c->data);
- if (c->cache) free(c->cache);
- }
-
- cblock_t cubicblock = {
- cubicpoint,
- cubictangent,
- cubicaccel,
- cubiccleanup
- };
-
- curve_t *newcubiccurve(float a[3], float b[3], float c[3], float d[3])
- {
- curve_t *cur = newcurve();
- cubicdata_t *cdata = (cubicdata_t *)malloc(sizeof(cubicdata_t));
-
- cur->type = CURVE_CUBIC;
- cur->fctns = &cubicblock;
- cur->data = (long *)cdata;
- copy3(a, cdata->a);
- copy3(b, cdata->b);
- copy3(c, cdata->c);
- copy3(d, cdata->d);
- return cur;
- }
-
- /* For the Bezier, B Spline, and Cardinal splines, the following
- * code converts them to cubic splines so that they can be expanded
- * as polynomials. The conversion is simply a change of basis
- * accomplished by a matrix multiplication. See, for example,
- * "Geometric Modeling", by Michael Mortensen.
- */
-
- static float bez[4][4] = {
- -1.0, 3.0, -3.0, 1.0,
- 3.0, -6.0, 3.0, 0.0,
- -3.0, 3.0, 0.0, 0.0,
- 1.0, 0.0, 0.0, 0.0
- };
-
- curve_t *newbeziercurve(float p[3], float q[3], float r[3], float s[3])
- {
- curve_t *cur = newcurve();
- cubicdata_t *cdata = (cubicdata_t *)malloc(sizeof(cubicdata_t));
- long i;
-
- cur->type = CURVE_CUBIC;
- cur->fctns = &cubicblock;
- cur->data = (long *)cdata;
- for (i = 0; i < 3; i++) {
- cdata->a[i] = bez[0][0]*p[i]+bez[0][1]*q[i]+bez[0][2]*r[i]+bez[0][3]*s[i];
- cdata->b[i] = bez[1][0]*p[i]+bez[1][1]*q[i]+bez[1][2]*r[i]+bez[1][3]*s[i];
- cdata->c[i] = bez[2][0]*p[i]+bez[2][1]*q[i]+bez[2][2]*r[i]+bez[2][3]*s[i];
- cdata->d[i] = bez[3][0]*p[i]+bez[3][1]*q[i]+bez[3][2]*r[i]+bez[3][3]*s[i];
- }
- return cur;
- }
-
- static float bsp[4][4] = {
- {-1.0/6.0, 3.0/6.0, -3.0/6.0, 1.0/6.0},
- {3.0/6.0, -6.0/6.0, 3.0/6.0, 0.0},
- {-3.0/6.0, 0.0, 3.0/6.0, 0.0},
- {1.0/6.0, 4.0/6.0, 1.0/6.0, 0.0}
- };
-
- curve_t *newbsplinecurve(float p[3], float q[3], float r[3], float s[3])
- {
- curve_t *cur = newcurve();
- cubicdata_t *cdata = (cubicdata_t *)malloc(sizeof(cubicdata_t));
- long i;
-
- cur->type = CURVE_CUBIC;
- cur->fctns = &cubicblock;
- cur->data = (long *)cdata;
- for (i = 0; i < 3; i++) {
- cdata->a[i] = bsp[0][0]*p[i]+bsp[0][1]*q[i]+bsp[0][2]*r[i]+bsp[0][3]*s[i];
- cdata->b[i] = bsp[1][0]*p[i]+bsp[1][1]*q[i]+bsp[1][2]*r[i]+bsp[1][3]*s[i];
- cdata->c[i] = bsp[2][0]*p[i]+bsp[2][1]*q[i]+bsp[2][2]*r[i]+bsp[2][3]*s[i];
- cdata->d[i] = bsp[3][0]*p[i]+bsp[3][1]*q[i]+bsp[3][2]*r[i]+bsp[3][3]*s[i];
- }
- return cur;
- }
-
- static float card[4][4];
-
- static void makecardinal(float a)
- {
- card[0][0] = card[1][3] = card[2][0] = -a;
- card[0][3] = card[2][2] = a;
- card[2][1] = card[2][3] = card[3][0] =
- card[3][2] = card[3][3] = 0.0;
- card[3][1] = 1.0;
- card[0][1] = 2.0 - a;
- card[0][2] = -2.0 + a;
- card[1][0] = 2.0 * a;
- card[1][1] = -3.0 + a;
- card[1][2] = 3.0 - 2.0 * a;
- }
-
- curve_t *newcardinalcurve(float p[3], float q[3], float r[3], float s[3])
- {
- curve_t *cur = newcurve();
- cubicdata_t *cdata = (cubicdata_t *)malloc(sizeof(cubicdata_t));
- long i;
- static long inited = 0;
-
- if (inited == 0) {
- inited = 1;
- makecardinal(.5);
- }
-
- cur->type = CURVE_CUBIC;
- cur->fctns = &cubicblock;
- cur->data = (long *)cdata;
- for (i = 0; i < 3; i++) {
- cdata->a[i] =card[0][0]*p[i]+card[0][1]*q[i]+card[0][2]*r[i]+card[0][3]*s[i];
- cdata->b[i] =card[1][0]*p[i]+card[1][1]*q[i]+card[1][2]*r[i]+card[1][3]*s[i];
- cdata->c[i] =card[2][0]*p[i]+card[2][1]*q[i]+card[2][2]*r[i]+card[2][3]*s[i];
- cdata->d[i] =card[3][0]*p[i]+card[3][1]*q[i]+card[3][2]*r[i]+card[3][3]*s[i];
- }
- return cur;
- }
-
- #define SSTEPS 400
-
- /* curvelength() breaks the curve into SSTEPS steps in parameter
- * space, and then approximates it with a broken line segment
- * whose length is calculated. It would be better to do this
- * adaptively, with more steps where the curvature is higher.
- */
-
- float curvelength(curve_t *c)
- {
- float len = 0.0, *cache;
- float p[3], q[3];
- long i;
-
- (*c->fctns->point)(c, 0.0, p);
- cache = (float *)malloc((1+SSTEPS)*sizeof(float));
- cache[0] = 0.0;
- c->cache = (long *)cache;
- for (i = 1; i <= SSTEPS; i++) {
- (*c->fctns->point)(c, (float)i/(float)(SSTEPS), q);
- len += dist3(p, q);
- cache[i] = len;
- copy3(q, p);
- }
- return len;
- }
-
- /* curvestep returns in p the point on the curve n steps into it --
- * i.e. at the point (float)(n/nsegs).
- */
-
- static float curvestep(curve_t *c, long n, long nsegs, float len, float p[3])
- {
- long i;
- float limit, l1, l2;
- float p1[3], q1[3], val;
- float *cache = (float *)c->cache;
- static long nlast = -1, ilast;
- static float plast[3];
- static float vallast;
-
- if (n == nlast) {
- p[0] = plast[0]; p[1] = plast[1]; p[2] = plast[2];
- return vallast;
- }
- if (n == 0) {
- nlast = -1;
- (*c->fctns->point)(c, 0.0, p);
- return 0.0;
- }
- if (n == nsegs) {
- nlast = -1;
- (*c->fctns->point)(c, 1.0, p);
- return 1.0;
- }
- limit = len*(float)n/(float)nsegs;
- for (i = 1; i < SSTEPS; i++)
- if (cache[i] > limit) {
- val = ((float)(i-1) +
- (limit-cache[i-1])/(cache[i]-cache[i-1]))/((float)SSTEPS);
- (*c->fctns->point)(c, val, p);
- nlast = n;
- plast[0] = p[0]; plast[1] = p[1]; plast[2] = p[2];
- vallast = val;
- return val;
- }
- }
-
- pwlin_t *pwlinfromcurve(curve_t *cur, long n)
- {
- pwlin_t *pwl = newpwlin();
- float *f, len;
- long i;
-
- pwl->n = n+1;
- pwl->verts = f = (float *)malloc((n+1)*3*sizeof(float));
- len = curvelength(cur);
- for (i = 0; i <= n; i++)
- (void) curvestep(cur, i, n, len, &f[i*3]);
- return pwl;
- }
-
- void makesmoothpwlworm(curve_t *cur, pwlin_t *pw, long nsegs, void (*savefunc)())
- {
- makepwlworm(cur, pw, nsegs, smoothsave);
- doverts();
- doquads(savefunc);
- }
-
- /* Makes a "worm" by moving a Fresnel frame along the curve. The
- * tangent points along the curve, the normal is the derivative of
- * the tangent, and the binormal is perpendicular to both. See any
- * elementary book on differential geometry for more information.
- * The code in the following routine, makeworm(), is similar.
- */
-
- void makepwlworm(curve_t *cur, pwlin_t *pw, long nsegs, void (*savefunc)())
- {
- static float lasttan[3] = { 1, 0, 0, };
- static float lastnorm[3] = { 0, 1, 0, };
-
- float slen;
- float p[3], t, tan[3], norm[3], binorm[3];
- float tan1[3], norm1[3], binorm1[3];
- float p0[3], p1[3], p2[3], p3[3], pp[3];
- float n0[3];
- long i, j;
- float s, c, tt, s1, c1, t1, dot;
- long nsides = pw->n - 1;
-
- slen = curvelength(cur);
- for (i = 0; i < nsegs; i++) {
- t = curvestep(cur, i, nsegs, slen, p);
- (*cur->fctns->tangent)(cur, t, tan);
- if (tan[0] == 0.0 && tan[1] == 0.0 && tan[2] == 0.0) {
- error("makepwlworm: zero tangent");
- copy3( lasttan, tan );
- }
- copy3( tan, lasttan );
- (*cur->fctns->accel)(cur, t, norm);
- if (norm[0] == 0.0 && norm[1] == 0.0 && norm[2] == 0.0) {
- error("makepwlworm: zero acceleration");
- copy3( lastnorm, norm );
- }
- copy3( norm, lastnorm );
- crossprod(tan, norm, norm);
- crossprod(tan, norm, binorm);
- normalize(tan);
- normalize(norm);
- normalize(binorm);
- t = curvestep(cur, i+1, nsegs, slen, pp);
- (*cur->fctns->tangent)(cur, t, tan1);
- if (tan1[0] == 0.0 && tan1[1] == 0.0 && tan1[2] == 0.0) {
- error("makepwlworm: zero tangent");
- copy3( lasttan, tan1 );
- }
- copy3( tan1, lasttan );
- (*cur->fctns->accel)(cur, t, norm1);
- if (norm1[0] == 0.0 && norm1[1] == 0.0 && norm1[2] == 0.0) {
- error("makepwlworm: zero acceleration");
- copy3( lastnorm, norm1 );
- }
- copy3( norm1, lastnorm );
- crossprod(tan1, norm1, norm1);
- crossprod(tan1, norm1, binorm1);
- normalize(tan1);
- normalize(norm1);
- normalize(binorm1);
- for (j = 0; j < nsides; j++) {
- tt = pw->verts[3*j + 2];
- s = pw->verts[3*j + 1];
- c = pw->verts[3*j];
- t1 = pw->verts[3*j + 5];
- s1 = pw->verts[3*j + 4];
- c1 = pw->verts[3*j + 3];
- p0[0] = c*norm[0] + s*binorm[0] + tt*tan[0] + p[0];
- p0[1] = c*norm[1] + s*binorm[1] + tt*tan[1] + p[1];
- p0[2] = c*norm[2] + s*binorm[2] + tt*tan[2] + p[2];
- p1[0] = c1*norm[0] + s1*binorm[0] + t1*tan[0] + p[0];
- p1[1] = c1*norm[1] + s1*binorm[1] + t1*tan[1] + p[1];
- p1[2] = c1*norm[2] + s1*binorm[2] + t1*tan[2] + p[2];
- p2[0] = c1*norm1[0] + s1*binorm1[0] + t1*tan1[0] + pp[0];
- p2[1] = c1*norm1[1] + s1*binorm1[1] + t1*tan1[1] + pp[1];
- p2[2] = c1*norm1[2] + s1*binorm1[2] + t1*tan1[2] + pp[2];
- p3[0] = c*norm1[0] + s*binorm1[0] + tt*tan1[0] + pp[0];
- p3[1] = c*norm1[1] + s*binorm1[1] + tt*tan1[1] + pp[1];
- p3[2] = c*norm1[2] + s*binorm1[2] + tt*tan1[2] + pp[2];
-
- if (samepoint(p0, p1) || samepoint(p1, p2))
- perpnorm(p0, p2, p3, n0);
- else
- perpnorm(p0, p1, p2, n0);
-
- scalarmult(-1.0, n0, n0);
- m_xformpt(p0, p0, n0, n0);
- m_xformptonly(p1, p1);
- m_xformptonly(p2, p2);
- m_xformptonly(p3, p3);
-
- (*savefunc)(ADD_QUAD, n0, p0, n0, p1, n0, p2, n0, p3);
- }
- }
- }
-
- void makeworm(curve_t *cur, float radius, long nsides, long nsegs, void (*savefunc)())
- {
- float slen;
- float p[3], t, tan[3], norm[3], binorm[3];
- float tan1[3], norm1[3], binorm1[3];
- float p0[3], p1[3], p2[3], p3[3], pp[3];
- float n0[3], n1[3], n2[3], n3[3];
- long i, j;
- float s, c, s1, c1, dot;
-
- slen = curvelength(cur);
- for (i = 0; i < nsegs; i++) {
- t = curvestep(cur, i, nsegs, slen, p);
- (*cur->fctns->tangent)(cur, t, tan);
- if (tan[0] == 0.0 && tan[1] == 0.0 && tan[2] == 0.0) {
- error("makeworm: zero tangent");
- return;
- }
- (*cur->fctns->accel)(cur, t, norm);
- if (norm[0] == 0.0 && norm[1] == 0.0 && norm[2] == 0.0) {
- error("makeworm: zero acceleration");
- return;
- }
- crossprod(tan, norm, norm);
- crossprod(tan, norm, binorm);
- normalize(norm);
- normalize(binorm);
- t = curvestep(cur, i+1, nsegs, slen, pp);
- (*cur->fctns->tangent)(cur, t, tan1);
- if (tan1[0] == 0.0 && tan1[1] == 0.0 && tan1[2] == 0.0) {
- error("makeworm: zero tangent");
- return;
- }
- (*cur->fctns->accel)(cur, t, norm1);
- if (norm1[0] == 0.0 && norm1[1] == 0.0 && norm1[2] == 0.0) {
- error("makeworm: zero acceleration");
- return;
- }
- crossprod(tan1, norm1, norm1);
- crossprod(tan1, norm1, binorm1);
- normalize(norm1);
- normalize(binorm1);
- for (j = 0; j < nsides; j++) {
- s = sin(2*j*PI/nsides);
- c = cos(2*j*PI/nsides);
- if (j+1 == nsides) {
- s1 = sin(0.0);
- c1 = cos(0.0);
- } else {
- s1 = sin(2*(j+1)*PI/nsides);
- c1 = cos(2*(j+1)*PI/nsides);
- }
- n0[0] = c*norm[0] + s*binorm[0];
- n0[1] = c*norm[1] + s*binorm[1];
- n0[2] = c*norm[2] + s*binorm[2];
- p0[0] = radius*n0[0] + p[0];
- p0[1] = radius*n0[1] + p[1];
- p0[2] = radius*n0[2] + p[2];
- n1[0] = c1*norm[0] + s1*binorm[0];
- n1[1] = c1*norm[1] + s1*binorm[1];
- n1[2] = c1*norm[2] + s1*binorm[2];
- p1[0] = radius*n1[0] + p[0];
- p1[1] = radius*n1[1] + p[1];
- p1[2] = radius*n1[2] + p[2];
- n2[0] = c1*norm1[0] + s1*binorm1[0];
- n2[1] = c1*norm1[1] + s1*binorm1[1];
- n2[2] = c1*norm1[2] + s1*binorm1[2];
- p2[0] = radius*n2[0] + pp[0];
- p2[1] = radius*n2[1] + pp[1];
- p2[2] = radius*n2[2] + pp[2];
- n3[0] = c*norm1[0] + s*binorm1[0];
- n3[1] = c*norm1[1] + s*binorm1[1];
- n3[2] = c*norm1[2] + s*binorm1[2];
- p3[0] = radius*n3[0] + pp[0];
- p3[1] = radius*n3[1] + pp[1];
- p3[2] = radius*n3[2] + pp[2];
-
- m_xformpt(p0, p0, n0, n0);
- m_xformpt(p1, p1, n1, n1);
- m_xformpt(p2, p2, n2, n2);
- m_xformpt(p3, p3, n3, n3);
-
- (*savefunc)(ADD_QUAD, n0, p0, n1, p1, n2, p2, n3, p3);
- }
- }
- }
-
-