1 /*
2  * Adapted to D by Antonio Monteiro
3  */
4 /*
5  * (c) Copyright 1993, 1994, Silicon Graphics, Inc.
6  * ALL RIGHTS RESERVED
7  * Permission to use, copy, modify, and distribute this software for
8  * any purpose and without fee is hereby granted, provided that the above
9  * copyright notice appear in all copies and that both the copyright notice
10  * and this permission notice appear in supporting documentation, and that
11  * the name of Silicon Graphics, Inc. not be used in advertising
12  * or publicity pertaining to distribution of the software without specific,
13  * written prior permission.
14  *
15  * THE MATERIAL EMBODIED ON THIS SOFTWARE IS PROVIDED TO YOU "AS-IS"
16  * AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE,
17  * INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR
18  * FITNESS FOR A PARTICULAR PURPOSE.  IN NO EVENT SHALL SILICON
19  * GRAPHICS, INC.  BE LIABLE TO YOU OR ANYONE ELSE FOR ANY DIRECT,
20  * SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY
21  * KIND, OR ANY DAMAGES WHATSOEVER, INCLUDING WITHOUT LIMITATION,
22  * LOSS OF PROFIT, LOSS OF USE, SAVINGS OR REVENUE, OR THE CLAIMS OF
23  * THIRD PARTIES, WHETHER OR NOT SILICON GRAPHICS, INC.  HAS BEEN
24  * ADVISED OF THE POSSIBILITY OF SUCH LOSS, HOWEVER CAUSED AND ON
25  * ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE
26  * POSSESSION, USE OR PERFORMANCE OF THIS SOFTWARE.
27  *
28  * US Government Users Restricted Rights
29  * Use, duplication, or disclosure by the Government is subject to
30  * restrictions set forth in FAR 52.227.19(c)(2) or subparagraph
31  * (c)(1)(ii) of the Rights in Technical Data and Computer Software
32  * clause at DFARS 252.227-7013 and/or in similar or successor
33  * clauses in the FAR or the DOD or NASA FAR Supplement.
34  * Unpublished-- rights reserved under the copyright laws of the
35  * United States.  Contractor/manufacturer is Silicon Graphics,
36  * Inc., 2011 N.  Shoreline Blvd., Mountain View, CA 94039-7311.
37  *
38  * OpenGL(TM) is a trademark of Silicon Graphics, Inc.
39  */
40 /*
41  * trackball.h
42  * A virtual trackball implementation
43  * Written by Gavin Bell for Silicon Graphics, November 1988.
44  */
45 
46 /*
47  * This size should really be based on the distance from the center of
48  * rotation to the point on the object underneath the mouse.  That
49  * point would then track the mouse as closely as possible.  This is a
50  * simple example, though, so that is left as an Exercise for the
51  * Programmer.
52  */
53 
54 module TrackBall;
55 
56 import std.math;
57 
58 public:
59 const float TRACKBALLSIZE = 0.8;
60 const int RENORMCOUNT = 97;
61  
62 /**
63  * Pass the x and y coordinates of the last and current positions of
64  * the mouse, scaled so they are from (-1.0 ... 1.0).
65  *
66  * The resulting rotation is returned as a quaternion rotation in the
67  * first paramater.
68  */
69 void
70 trackball(ref float q[4], float p1x, float p1y, float p2x, float p2y)
71 {
72 /*
73  * Ok, simulate a track-ball.  Project the points onto the virtual
74  * trackball, then figure out the axis of rotation, which is the cross
75  * product of P1 P2 and O P1 (O is the center of the ball, 0,0,0)
76  * Note:  This is a deformed trackball-- is a trackball in the center,
77  * but is deformed into a hyperbolic sheet of rotation away from the
78  * center.  This particular function was chosen after trying out
79  * several variations.
80  *
81  * It is assumed that the arguments to this routine are in the range
82  * (-1.0 ... 1.0)
83  */
84     float[3] a; /* Axis of rotation */
85     float phi;  /* how much to rotate about axis */
86     float[3] p1;
87 	float[3] p2;
88 	float[3] d;
89     float t;
90 
91     if (p1x == p2x && p1y == p2y) {
92         /* Zero rotation */
93         vzero(q.ptr);
94         q[3] = 1.0;
95         return;
96     }
97 
98     /*
99      * First, figure out z-coordinates for projection of P1 and P2 to
100      * deformed sphere
101      */
102     vset(p1,p1x,p1y,tb_project_to_sphere(TRACKBALLSIZE,p1x,p1y));
103     vset(p2,p2x,p2y,tb_project_to_sphere(TRACKBALLSIZE,p2x,p2y));
104 
105     /*
106      *  Now, we want the cross product of P1 and P2
107      */
108     vcross(p2.ptr,p1.ptr,a.ptr);
109 
110     /*
111      *  Figure out how much to rotate around that axis.
112      */
113     vsub(p1,p2,d);
114     t = vlength(d) / (2.0*TRACKBALLSIZE);
115 
116     /*
117      * Avoid problems with out-of-control values...
118      */
119     if (t > 1.0) t = 1.0;
120     if (t < -1.0) t = -1.0;
121     phi = 2.0 * asin(t);//std.math.asin(t);
122 
123     axis_to_quat(a,phi,q);
124 }
125 
126 
127 /*
128  * Given two quaternions, add them together to get a third quaternion.
129  * Adding quaternions to get a compound rotation is analagous to adding
130  * translations to get a compound translation.  When incrementally
131  * adding rotations, the first argument here should be the new
132  * rotation, the second and third the total rotation (which will be
133  * over-written with the resulting new total rotation).
134  */
135 void
136 add_quats(ref float[4] q1, ref float[4] q2, ref float[4] dest)
137 {
138 /*
139  * Given two rotations, e1 and e2, expressed as quaternion rotations,
140  * figure out the equivalent single rotation and stuff it into dest.
141  *
142  * This routine also normalizes the result every RENORMCOUNT times it is
143  * called, to keep error from creeping in.
144  *
145  * NOTE: This routine is written so that q1 or q2 may be the same
146  * as dest (or each other).
147  */
148     static int count=0;
149     float[4] t1;
150     float[4] t2;
151     float[4] t3;
152     float tf[4];
153 
154     vcopy(q1.ptr,t1.ptr);
155     vscale(t1.ptr,q2[3]);
156 
157     vcopy(q2.ptr,t2.ptr);
158     vscale(t2.ptr,q1[3]);
159 
160     vcross(q2.ptr,q1.ptr,t3.ptr);
161     vadd(t1.ptr,t2.ptr,tf.ptr);
162     vadd(t3.ptr,tf.ptr,tf.ptr);
163     tf[3] = q1[3] * q2[3] - vdot(q1.ptr,q2.ptr);
164 
165     dest[0] = tf[0];
166     dest[1] = tf[1];
167     dest[2] = tf[2];
168     dest[3] = tf[3];
169 
170     if (++count > RENORMCOUNT) {
171         count = 0;
172         normalize_quat(dest);
173     }
174 }
175 
176 
177 /*
178  * A useful function, builds a rotation matrix in Matrix based on
179  * given quaternion.
180  */
181 void
182 build_rotmatrix(ref float[4][4] m, ref float[4] q)
183 {
184     m[0][0] = 1.0 - 2.0 * (q[1] * q[1] + q[2] * q[2]);
185     m[0][1] = 2.0 * (q[0] * q[1] - q[2] * q[3]);
186     m[0][2] = 2.0 * (q[2] * q[0] + q[1] * q[3]);
187     m[0][3] = 0.0;
188 
189     m[1][0] = 2.0 * (q[0] * q[1] + q[2] * q[3]);
190     m[1][1]= 1.0 - 2.0 * (q[2] * q[2] + q[0] * q[0]);
191     m[1][2] = 2.0 * (q[1] * q[2] - q[0] * q[3]);
192     m[1][3] = 0.0;
193 
194     m[2][0] = 2.0 * (q[2] * q[0] - q[1] * q[3]);
195     m[2][1] = 2.0 * (q[1] * q[2] + q[0] * q[3]);
196     m[2][2] = 1.0 - 2.0 * (q[1] * q[1] + q[0] * q[0]);
197     m[2][3] = 0.0;
198 
199     m[3][0] = 0.0;
200     m[3][1] = 0.0;
201     m[3][2] = 0.0;
202     m[3][3] = 1.0;
203 }
204 
205 
206 /*
207  * This function computes a quaternion based on an axis (defined by
208  * the given vector) and an angle about which to rotate.  The angle is
209  * expressed in radians.  The result is put into the third argument.
210  */
211 void
212 axis_to_quat(ref float[3] a, ref float phi, ref float[4] q)
213 {
214     vnormal(a);
215     vcopy(a.ptr,q.ptr);
216     vscale(q.ptr,sin(phi/2.0));
217     q[3] = cos(phi/2.0);
218 }
219 
220 /*
221  * Quaternions always obey:  a^2 + b^2 + c^2 + d^2 = 1.0
222  * If they don't add up to 1.0, dividing by their magnitued will
223  * renormalize them.
224  *
225  * Note: See the following for more information on quaternions:
226  *
227  * - Shoemake, K., Animating rotation with quaternion curves, Computer
228  *   Graphics 19, No 3 (Proc. SIGGRAPH'85), 245-254, 1985.
229  * - Pletinckx, D., Quaternion calculus as a basic tool in computer
230  *   graphics, The Visual Computer 5, 2-13, 1989.
231  */
232 static void
233 normalize_quat(ref float[4] q)
234 {
235     int i;
236     float mag;
237 
238     mag = (q[0]*q[0] + q[1]*q[1] + q[2]*q[2] + q[3]*q[3]);
239     for (i = 0; i < 4; i++) q[i] /= mag;
240 }
241 
242 /*
243  * Project an x,y pair onto a sphere of radius r OR a hyperbolic sheet
244  * if we are away from the center of the sphere.
245  */
246 static float
247 tb_project_to_sphere(float r, float x, float y)
248 {
249     float d, t, z;
250 
251     d = sqrt(x*x + y*y);
252     if (d < r * 0.70710678118654752440) {    /* Inside sphere */
253         z = sqrt(r*r - d*d);
254     } else {           /* On hyperbola */
255         t = r / 1.41421356237309504880;
256         z = t*t / d;
257     }
258     return z;
259 }
260 
261 void
262 vzero(float* v)
263 {
264     v[0] = 0.0;
265     v[1] = 0.0;
266     v[2] = 0.0;
267 }
268 
269 void
270 vset(ref float[3] v, float x, float y, float z)
271 {
272     v[0] = x;
273     v[1] = y;
274     v[2] = z;
275 }
276 
277 void
278 vsub(ref float[3] src1, ref float[3] src2, ref float[3] dst)
279 {
280     dst[0] = src1[0] - src2[0];
281     dst[1] = src1[1] - src2[1];
282     dst[2] = src1[2] - src2[2];
283 }
284 
285 void
286 vcopy(float* v1, float* v2)
287 {
288     int i;
289     for (i = 0 ; i < 3 ; i++)
290         v2[i] = v1[i];
291 }
292 
293 void
294 vcross(float* v1, float* v2, float* cross)
295 {
296     float[3] temp;
297 
298     temp[0] = (v1[1] * v2[2]) - (v1[2] * v2[1]);
299     temp[1] = (v1[2] * v2[0]) - (v1[0] * v2[2]);
300     temp[2] = (v1[0] * v2[1]) - (v1[1] * v2[0]);
301     vcopy(temp.ptr, cross);
302 }
303 
304 float
305 vlength(ref float[3] v)
306 {
307     return sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
308 }
309 
310 void
311 vscale(float* v, float div)
312 {
313     v[0] *= div;
314     v[1] *= div;
315     v[2] *= div;
316 }
317 
318 void
319 vnormal(ref float[3] v)
320 {
321     vscale(v.ptr,1.0/vlength(v));
322 }
323 
324 float
325 vdot(float* v1, float* v2)
326 {
327     return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2];
328 }
329 
330 void
331 vadd(float* src1, float* src2, float* dst)
332 {
333     dst[0] = src1[0] + src2[0];
334     dst[1] = src1[1] + src2[1];
335     dst[2] = src1[2] + src2[2];
336 }
337