1 /*
2 * sphere.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: sphere.c,v 4.0 91/07/17 14:39:17 kolb Exp Locker: kolb $
17 *
18 * $Log: sphere.c,v $
19 * Revision 4.0 91/07/17 14:39:17 kolb
20 * Initial version.
21 *
22 */
23 #include "geom.h"
24 #include "sphere.h"
25
26 static Methods *iSphereMethods = NULL;
27 static char sphereName[] = "sphere";
28
29 unsigned long SphTests, SphHits;
30
31 /*
32 * Create & return reference to a sphere.
33 */
34 Sphere *
SphereCreate(r,pos)35 SphereCreate(r, pos)
36 Float r;
37 Vector *pos;
38 {
39 Sphere *sphere;
40
41 if (r < EPSILON) {
42 RLerror(RL_WARN, "Degenerate sphere.\n");
43 return (Sphere *)NULL;
44 }
45
46 sphere = (Sphere *)share_malloc(sizeof(Sphere));
47 /*
48 * sphere->rsq holds the square of the radius.
49 */
50 sphere->r = r;
51 sphere->rsq = r*r;
52 sphere->x = pos->x;
53 sphere->y = pos->y;
54 sphere->z = pos->z;
55
56 return sphere;
57 }
58
59 Methods *
SphereMethods()60 SphereMethods()
61 {
62 if (iSphereMethods == (Methods *)NULL) {
63 iSphereMethods = MethodsCreate();
64 iSphereMethods->create = (GeomCreateFunc *)SphereCreate;
65 iSphereMethods->methods = SphereMethods;
66 iSphereMethods->name = SphereName;
67 iSphereMethods->intersect = SphereIntersect;
68 iSphereMethods->normal = SphereNormal;
69 iSphereMethods->uv = SphereUV;
70 iSphereMethods->enter = SphereEnter;
71 iSphereMethods->bounds = SphereBounds;
72 iSphereMethods->stats = SphereStats;
73 iSphereMethods->checkbounds = TRUE;
74 iSphereMethods->closed = TRUE;
75 }
76 return iSphereMethods;
77 }
78
79 /*
80 * Ray/sphere intersection test.
81 */
82 int
SphereIntersect(sph,ray,mindist,maxdist)83 SphereIntersect(sph, ray, mindist, maxdist)
84 Sphere *sph;
85 Ray *ray;
86 Float mindist, *maxdist;
87 {
88 Float xadj, yadj, zadj;
89 Float b, t, s;
90
91 SphTests++;
92 /*
93 * Translate ray origin to object space and negate everything.
94 * (Thus, we translate the sphere into ray space, which saves
95 * us a couple of negations below.)
96 */
97 xadj = sph->x - ray->pos.x;
98 yadj = sph->y - ray->pos.y;
99 zadj = sph->z - ray->pos.z;
100
101 /*
102 * Solve quadratic equation.
103 */
104 b = xadj * ray->dir.x + yadj * ray->dir.y + zadj * ray->dir.z;
105 t = b * b - xadj * xadj - yadj * yadj - zadj * zadj + sph->rsq;
106 if (t < 0.)
107 return FALSE;
108 t = (Float)sqrt((double)t);
109 s = b - t;
110 if (s > mindist) {
111 if (s < *maxdist) {
112 *maxdist = s;
113 SphHits++;
114 return TRUE;
115 }
116 return FALSE;
117 }
118 s = b + t;
119 if (s > mindist && s < *maxdist) {
120 *maxdist = s;
121 SphHits++;
122 return TRUE;
123 }
124 return FALSE;
125 }
126
127 /*
128 * Compute normal to sphere at pos
129 */
130 int
SphereNormal(sphere,pos,nrm,gnrm)131 SphereNormal(sphere, pos, nrm, gnrm)
132 Sphere *sphere;
133 Vector *pos, *nrm, *gnrm;
134 {
135 nrm->x = (pos->x - sphere->x) / sphere->r;
136 nrm->y = (pos->y - sphere->y) / sphere->r;
137 nrm->z = (pos->z - sphere->z) / sphere->r;
138 *gnrm = *nrm;
139 return FALSE;
140 }
141
142 /*
143 * Determine if ray enters (TRUE) or leaves (FALSE) sphere at pos
144 */
145 int
SphereEnter(sphere,ray,mind,hitd)146 SphereEnter(sphere, ray, mind, hitd)
147 Sphere *sphere;
148 Ray *ray;
149 Float mind, hitd;
150 {
151 Vector pos;
152
153 VecAddScaled(ray->pos, mind, ray->dir, &pos);
154 pos.x -= sphere->x;
155 pos.y -= sphere->y;
156 pos.z -= sphere->z;
157
158 return dotp(&pos, &pos) > sphere->rsq;
159 }
160
161 /*ARGSUSED*/
162 void
SphereUV(sphere,pos,norm,uv,dpdu,dpdv)163 SphereUV(sphere, pos, norm, uv, dpdu, dpdv)
164 Sphere *sphere;
165 Vector *pos, *norm, *dpdu, *dpdv;
166 Vec2d *uv;
167 {
168 Float phi, theta;
169 Vector realnorm;
170
171 realnorm.x = pos->x - sphere->x;
172 realnorm.y = pos->y - sphere->y;
173 realnorm.z = pos->z - sphere->z;
174 VecNormalize( &realnorm );
175 if (realnorm.z > 1.) /* roundoff */
176 phi = PI;
177 else if (realnorm.z < -1.)
178 phi = 0;
179 else
180 phi = acos(-realnorm.z);
181
182 uv->v = phi / PI;
183
184 if (fabs(uv->v) < EPSILON || equal(uv->v, 1.))
185 uv->u = 0.;
186 else {
187 theta = realnorm.x / sin(phi);
188 if (theta > 1.)
189 theta = 0.;
190 else if (theta < -1.)
191 theta = 0.5;
192 else
193 theta = acos(theta) / TWOPI;
194
195 if (realnorm.y > 0)
196 uv->u = theta;
197 else
198 uv->u = 1 - theta;
199 }
200 if (dpdu != (Vector *)0) {
201 dpdu->x = -realnorm.y;
202 dpdu->y = realnorm.x;
203 dpdu->z = 0.;
204 (void)VecNormalize(dpdu);
205 (void)VecNormCross(&realnorm, dpdu, dpdv);
206 }
207 }
208
209 void
SphereBounds(s,bounds)210 SphereBounds(s, bounds)
211 Sphere *s;
212 Float bounds[2][3];
213 {
214 bounds[LOW][X] = s->x - s->r;
215 bounds[HIGH][X] = s->x + s->r;
216 bounds[LOW][Y] = s->y - s->r;
217 bounds[HIGH][Y] = s->y + s->r;
218 bounds[LOW][Z] = s->z - s->r;
219 bounds[HIGH][Z] = s->z + s->r;
220 }
221
222 char *
SphereName()223 SphereName()
224 {
225 return sphereName;
226 }
227
228 void
SphereStats(tests,hits)229 SphereStats(tests, hits)
230 unsigned long *tests, *hits;
231 {
232 *tests = SphTests;
233 *hits = SphHits;
234 }
235
236 void
SphereMethodRegister(meth)237 SphereMethodRegister(meth)
238 UserMethodType meth;
239 {
240 if (iSphereMethods)
241 iSphereMethods->user = meth;
242 }
243