1 /*
2 * cylinder.c
3 *
4 * Copyright (C) 1989, 1991, Craig E. Kolb
5 * All rights reserved.
6 *
7 * This software may be freely copied, modified, and redistributed
8 * provided that this copyright notice is preserved on all copies.
9 *
10 * You may not distribute this software, in whole or in part, as part of
11 * any commercial product without the express consent of the authors.
12 *
13 * There is no warranty or other guarantee of fitness of this software
14 * for any purpose. It is provided solely "as is".
15 *
16 * $Id: cylinder.c,v 4.0 91/07/17 14:37:12 kolb Exp Locker: kolb $
17 *
18 * $Log: cylinder.c,v $
19 * Revision 4.0 91/07/17 14:37:12 kolb
20 * Initial version.
21 *
22 */
23 #include "geom.h"
24 #include "cylinder.h"
25
26 static Methods *iCylinderMethods = NULL;
27 static char cylName[] = "cylinder";
28
29 unsigned long CylTests, CylHits;
30
31 Cylinder *
CylinderCreate(r,bot,top)32 CylinderCreate(r, bot, top)
33 Float r;
34 Vector *bot, *top;
35 {
36 Cylinder *cyl;
37 Float len;
38 Vector axis;
39
40 if (r <= 0.) {
41 RLerror(RL_WARN, "Invalid cylinder radius.\n");
42 return (Cylinder*)NULL;
43 }
44
45 VecSub(*top, *bot, &axis);
46
47 len = VecNormalize(&axis);
48
49 if (len < EPSILON) {
50 RLerror(RL_WARN, "Degenerate cylinder.\n");
51 return (Cylinder *)NULL;
52 }
53
54 cyl = (Cylinder *)share_malloc(sizeof(Cylinder));
55 CoordSysTransform(bot, &axis, r, len, &cyl->trans);
56 return cyl;
57 }
58
59 Methods *
CylinderMethods()60 CylinderMethods()
61 {
62 if (iCylinderMethods == (Methods *)NULL) {
63 iCylinderMethods = MethodsCreate();
64 iCylinderMethods->name = CylinderName;
65 iCylinderMethods->create = (GeomCreateFunc *)CylinderCreate;
66 iCylinderMethods->methods = CylinderMethods;
67 iCylinderMethods->intersect = CylinderIntersect;
68 iCylinderMethods->normal = CylinderNormal;
69 iCylinderMethods->uv = CylinderUV;
70 iCylinderMethods->bounds = CylinderBounds;
71 iCylinderMethods->stats = CylinderStats;
72 iCylinderMethods->checkbounds = TRUE;
73 iCylinderMethods->closed = FALSE;
74 }
75 return iCylinderMethods;
76 }
77
78 /*
79 * Ray-cylinder intersection test.
80 */
81 int
CylinderIntersect(cyl,ray,mindist,maxdist)82 CylinderIntersect(cyl, ray, mindist, maxdist)
83 Cylinder *cyl;
84 Ray *ray;
85 Float mindist, *maxdist;
86 {
87 Float t1, t2, a, b, c, zpos1, zpos2, disc;
88 Float distfact;
89 Ray newray;
90 Vector nray, npos;
91 Float nmin;
92
93 CylTests++;
94
95 /*
96 * Transform ray into canonical cylinder space.
97 */
98 newray = *ray;
99 distfact = RayTransform(&newray, &cyl->trans.itrans);
100 nray = newray.dir;
101 npos = newray.pos;
102 nmin = mindist * distfact;
103
104 a = nray.x * nray.x + nray.y * nray.y;
105 if (a < EPSILON*EPSILON)
106 /* |nray.z| == 1. */
107 return FALSE;
108
109 b = nray.x * npos.x + nray.y * npos.y;
110 c = npos.x*npos.x + npos.y*npos.y - 1;
111 disc = b*b - a*c;
112 if(disc < 0.)
113 return FALSE;
114 disc = sqrt(disc);
115 t1 = (-b + disc) / a;
116 t2 = (-b - disc) / a;
117 if (t1 < nmin && t2 < nmin)
118 return FALSE;
119 zpos1 = npos.z + t1 * nray.z;
120 zpos2 = npos.z + t2 * nray.z;
121
122 if (t1 < nmin || zpos1 < 0. || zpos1 > 1.) {
123 if (t2 < nmin || zpos2 < 0. || zpos2 > 1.)
124 return FALSE;
125 else
126 t1 = t2 / distfact;
127
128 } else {
129 if (t2 < nmin || zpos2 < 0. || zpos2 > 1.)
130 t1 /= distfact;
131 else {
132 t1 = min(t1, t2) / distfact;
133 }
134 }
135
136 if (t1 < *maxdist) {
137 *maxdist = t1;
138 CylHits++;
139 return TRUE;
140 }
141 return FALSE;
142 }
143
144 int
CylinderNormal(cyl,pos,nrm,gnrm)145 CylinderNormal(cyl, pos, nrm, gnrm)
146 Cylinder *cyl;
147 Vector *pos, *nrm, *gnrm;
148 {
149 /*
150 * Transform position into cylinder space.
151 */
152 *nrm = *pos;
153 PointTransform(nrm, &cyl->trans.itrans);
154 /*
155 * The normal is equal to the point of intersection in cylinder
156 * space, but with Z = 0.;
157 */
158 nrm->z = 0.;
159
160 /*
161 * Tranform normal back to world space.
162 */
163 NormalTransform(nrm, &cyl->trans.itrans);
164 *gnrm = *nrm;
165 return FALSE;
166 }
167
168 void
CylinderUV(cyl,pos,norm,uv,dpdu,dpdv)169 CylinderUV(cyl, pos, norm, uv, dpdu, dpdv)
170 Cylinder *cyl;
171 Vector *pos, *norm, *dpdu, *dpdv;
172 Vec2d *uv;
173 {
174 Vector npos;
175
176 npos = *pos;
177 PointTransform(&npos, &cyl->trans.itrans);
178
179 uv->v = npos.z;
180 /*
181 * Due to roundoff error, |npos.x| may be > 1.
182 */
183 if (npos.x > 1.)
184 uv->u = 0.;
185 else if (npos.x < -1.)
186 uv->u = 0.5;
187 else
188 uv->u = acos(npos.x) / TWOPI;
189 if (npos.y < 0.)
190 uv->u = 1. - uv->u;
191
192 if (dpdu) {
193 dpdv->x = dpdv->y = 0.;
194 dpdv->z = 1.;
195 dpdu->x = -npos.y;
196 dpdu->y = npos.x;
197 dpdu->z = 0.;
198 VecTransform(dpdu, &cyl->trans.trans);
199 VecTransform(dpdv, &cyl->trans.trans);
200 (void)VecNormalize(dpdu);
201 (void)VecNormalize(dpdv);
202 }
203 }
204
205 void
CylinderBounds(cyl,bounds)206 CylinderBounds(cyl, bounds)
207 Cylinder *cyl;
208 Float bounds[2][3];
209 {
210 bounds[LOW][X] = bounds[LOW][Y] = -1;
211 bounds[HIGH][X] = bounds[HIGH][Y] = 1;
212 bounds[LOW][Z] = 0.;
213 bounds[HIGH][Z] = 1;
214 /*
215 * Transform bounding box to world space.
216 */
217 BoundsTransform(&cyl->trans.trans, bounds);
218 }
219
220 char *
CylinderName()221 CylinderName()
222 {
223 return cylName;
224 }
225
226 void
CylinderStats(tests,hits)227 CylinderStats(tests, hits)
228 unsigned long *tests, *hits;
229 {
230 *tests = CylTests;
231 *hits = CylHits;
232 }
233
234 void
CylinderMethodRegister(meth)235 CylinderMethodRegister(meth)
236 UserMethodType meth;
237 {
238 if (iCylinderMethods)
239 iCylinderMethods->user = meth;
240 }
241