home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
rtsi.com
/
2014.01.www.rtsi.com.tar
/
www.rtsi.com
/
OS9
/
OSK
/
GRAPHICS
/
rayshade.lzh
/
cone.c
< prev
next >
Wrap
Text File
|
1990-05-08
|
7KB
|
296 lines
/*
* cone.c
*
* Copyright (C) 1989, Craig E. Kolb
*
* This software may be freely copied, modified, and redistributed,
* provided that this copyright notice is preserved on all copies.
*
* There is no warranty or other guarantee of fitness for this software,
* it is provided solely . Bug reports or fixes may be sent
* to the author, who may or may not act on them as he desires.
*
* You may not include this software in a program or other software product
* without supplying the source, or without informing the end-user that the
* source is available for no extra charge.
*
* If you modify this software, you should include a notice giving the
* name of the person performing the modification, the date of modification,
* and the reason for such modification.
*
* $Id: cone.c,v 3.0.1.5 90/04/09 14:30:08 craig Exp $
*
* $Log: cone.c,v $
* Revision 3.0.1.5 90/04/09 14:30:08 craig
* patch5: Transformation information now stored locally.
* patch5: Canonical cone now truly canonical.
*
* Revision 3.0.1.4 90/04/04 14:51:25 craig
* patch5: Fixed divide by zero problem in intcone().
*
* Revision 3.0.1.3 90/02/12 13:20:58 craig
* patch4: Changes to avoid rotation about null axis.
*
* Revision 3.0.1.2 89/12/06 16:33:44 craig
* patch2: Added calls to new error/warning routines.
*
* Revision 3.0.1.1 89/11/18 14:08:09 craig
* patch1: Changes to reflect new names of transformation routines.
*
* Revision 3.0 89/10/27 02:05:47 craig
* Baseline for first official release.
*
*/
#include <stdio.h>
#include <math.h>
#include "typedefs.h"
#include "funcdefs.h"
#include "constants.h"
Object *
makcone(surf, cent, ax, br, ar)
char *surf;
Vector *cent, *ax;
double br, ar;
{
Cone *cone;
Primitive *prim;
Object *newobj;
double tantheta, lprime, tlen, len, dtmp;
Vector axis, base, tmp;
prim = mallocprim();
prim->surf = find_surface(surf);
prim->type = CONE;
newobj = new_object(NULL, CONE, (char *)prim, (Trans *)NULL);
cone = (Cone *)Malloc(sizeof(Cone));
prim->objpnt.p_cone = cone;
/*
* To render a cone, we transform the desired cone into
* a canonical, Z-axis aligned, unit length, unit radius
* at the apex cone.
*
* Here, we construct the transformation matrix to map
* from cone<-->world space.
*/
/*
* The passed basepoint must be closer to the origin of the
* cone than the apex point, implying that the base radius
* must be smaller than the apex radius. If the values passed
* reflect the opposite, we switch everything.
*/
if(ar < br) {
tmp = *cent;
*cent = *ax;
*ax = tmp;
dtmp = br;
br = ar;
ar = dtmp;
} else if (equal(ar, br)) {
/*
* If the base and apex radii are equal, then we
* can treat the cone as a cylinder.
*/
return makcyl(surf, cent, ax, br);
}
/*
* Find the axis and axis length.
*/
vecsub(*ax, *cent, &axis);
len = normalize(&axis);
if (len < EPSILON) {
yywarning("Degenerate cone.\n");
free((char *)cone);
free((char *)prim);
free((char *)newobj);
return (Object *)0;
}
/*
* "tantheta" is the change in radius per unit length along
* the cone axis.
*/
tantheta = (ar - br) / len;
/*
* lprime defines the distance along the axis where the first
* endcap should be placed.
*/
lprime = br / tantheta;
/*
* Find the true base (origin) of the cone.
*/
scalar_prod(-lprime, axis, &base);
vecadd(base, *cent, &base);
/*
* tlen is the total length of the cone.
*/
tlen = lprime + len;
/*
* start_dist is the distance from the origin of the canonical
* cone at which the first endcap appears.
*/
cone->start_dist = lprime / tlen;
/*
* Calculate transformation to map from cone space to world space.
*/
if (abs(axis.z) == 1.) {
tmp.x = 1;
tmp.y = tmp.z = 0.;
} else {
tmp.x = axis.y;
tmp.y = -axis.x;
tmp.z = 0.;
}
init_trans(&cone->trans.obj2world);
RS_scale(&cone->trans.obj2world, ar, ar, tlen);
RS_rotate(&cone->trans.obj2world, &tmp, acos(axis.z));
RS_translate(&cone->trans.obj2world, &base);
invert_trans(&cone->trans.world2obj, &cone->trans.obj2world);
return newobj;
}
/*
* Ray-cone intersection test. This routine is far from optimal, but
* it's straight-forward and it works...
*/
double
intcone(pos, ray, obj)
Vector *pos, *ray;
Primitive *obj;
{
double t1, t2, a, b, c, disc, zpos, et1, et2;
double x, y, distfact;
Ray newray;
Vector nray, npos;
extern double TransformRay();
extern unsigned long primtests[];
Cone *cone;
primtests[CONE]++;
cone = obj->objpnt.p_cone;
/*
* Transform ray from world to cone space.
*/
newray.pos = *pos;
newray.dir = *ray;
distfact = TransformRay(&newray, &cone->trans.world2obj);
nray = newray.dir;
npos = newray.pos;
a = nray.x * nray.x + nray.y * nray.y - nray.z*nray.z;
b = nray.x * npos.x + nray.y * npos.y - nray.z*npos.z;
c = npos.x*npos.x + npos.y*npos.y - npos.z*npos.z;
if (equal(a, 0.)) {
/*
* Only one intersection point...
*/
t1 = -c / b;
zpos = npos.z + t1 * nray.z;
if (t1 < EPSILON || zpos < cone->start_dist || zpos > 1.)
t1 = FAR_AWAY;
t2 = FAR_AWAY;
} else {
disc = b*b - a*c;
if(disc < 0.)
return 0.; /* No possible intersection */
disc = sqrt(disc);
t1 = (-b + disc) / a;
t2 = (-b - disc) / a;
/*
* Clip intersection points.
*/
zpos = npos.z + t1 * nray.z;
if (t1 < EPSILON || zpos < cone->start_dist || zpos > 1.)
t1 = FAR_AWAY;
zpos = npos.z + t2 * nray.z;
if (t2 < EPSILON || zpos < cone->start_dist || zpos > 1.)
t2 = FAR_AWAY;
}
if (equal(nray.z, 0.)) {
t1 = min(t1, t2);
return (t1 == FAR_AWAY ? 0. : t1 / distfact);
}
/*
* Find t for both endcaps.
*/
et1 = (cone->start_dist - npos.z) / nray.z;
x = npos.x + et1 * nray.x;
y = npos.y + et1 * nray.y;
if (x*x + y*y > cone->start_dist*cone->start_dist)
et1 = FAR_AWAY;
et2 = (1. - npos.z) / nray.z;
x = npos.x + et2 * nray.x;
y = npos.y + et2 * nray.y;
if (x*x + y*y > 1.)
et2 = FAR_AWAY;
t1 = min(t1, min(t2, min(et1, et2)));
return (t1 == FAR_AWAY ? 0. : t1 / distfact);
}
/*
* Compute the normal to a cone at a given location on its surface.
*/
nrmcone(pos, obj, nrm)
Vector *pos, *nrm;
Primitive *obj;
{
Cone *cone;
Vector npos;
cone = obj->objpnt.p_cone;
/*
* Transform intersection point to cone space.
*/
npos = *pos;
transform_point(&npos, &cone->trans.world2obj);
if (equal(npos.z, cone->start_dist)) {
nrm->x = nrm->y = 0.;
nrm->z = -1.;
} else if (equal(npos.z, 1)) {
nrm->x = nrm->y = 0.;
nrm->z = 1.;
} else {
/*
* The following is equal to
* (pos X (0, 0, 1)) X pos
*/
nrm->x = npos.x * npos.z;
nrm->y = npos.y * npos.z;
nrm->z = -npos.x * npos.x - npos.y * npos.y;
}
/*
* Transform normal back to world space.
*/
TransformNormal(nrm, &cone->trans.world2obj);
}
/*
* Return the extent of a cone.
*/
coneextent(o, bounds)
Primitive *o;
double bounds[2][3];
{
Cone *cone;
cone = o->objpnt.p_cone;
bounds[LOW][X] = bounds[LOW][Y] = -1;
bounds[HIGH][X] = bounds[HIGH][Y] = 1;
bounds[LOW][Z] = cone->start_dist;
bounds[HIGH][Z] = 1;
/*
* Transform bounding box to world space.
*/
transform_bounds(&cone->trans.obj2world, bounds);
}