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