132001f49Smrg#include <stdio.h> 232001f49Smrg/* 332001f49Smrg * (c) Copyright 1993, 1994, Silicon Graphics, Inc. 432001f49Smrg * ALL RIGHTS RESERVED 532001f49Smrg * Permission to use, copy, modify, and distribute this software for 632001f49Smrg * any purpose and without fee is hereby granted, provided that the above 732001f49Smrg * copyright notice appear in all copies and that both the copyright notice 832001f49Smrg * and this permission notice appear in supporting documentation, and that 932001f49Smrg * the name of Silicon Graphics, Inc. not be used in advertising 1032001f49Smrg * or publicity pertaining to distribution of the software without specific, 1132001f49Smrg * written prior permission. 1232001f49Smrg * 1332001f49Smrg * THE MATERIAL EMBODIED ON THIS SOFTWARE IS PROVIDED TO YOU "AS-IS" 1432001f49Smrg * AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE, 1532001f49Smrg * INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR 1632001f49Smrg * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON 1732001f49Smrg * GRAPHICS, INC. BE LIABLE TO YOU OR ANYONE ELSE FOR ANY DIRECT, 1832001f49Smrg * SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY 1932001f49Smrg * KIND, OR ANY DAMAGES WHATSOEVER, INCLUDING WITHOUT LIMITATION, 2032001f49Smrg * LOSS OF PROFIT, LOSS OF USE, SAVINGS OR REVENUE, OR THE CLAIMS OF 2132001f49Smrg * THIRD PARTIES, WHETHER OR NOT SILICON GRAPHICS, INC. HAS BEEN 2232001f49Smrg * ADVISED OF THE POSSIBILITY OF SUCH LOSS, HOWEVER CAUSED AND ON 2332001f49Smrg * ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE 2432001f49Smrg * POSSESSION, USE OR PERFORMANCE OF THIS SOFTWARE. 2532001f49Smrg * 2632001f49Smrg * US Government Users Restricted Rights 2732001f49Smrg * Use, duplication, or disclosure by the Government is subject to 2832001f49Smrg * restrictions set forth in FAR 52.227.19(c)(2) or subparagraph 2932001f49Smrg * (c)(1)(ii) of the Rights in Technical Data and Computer Software 3032001f49Smrg * clause at DFARS 252.227-7013 and/or in similar or successor 3132001f49Smrg * clauses in the FAR or the DOD or NASA FAR Supplement. 3232001f49Smrg * Unpublished-- rights reserved under the copyright laws of the 3332001f49Smrg * United States. Contractor/manufacturer is Silicon Graphics, 3432001f49Smrg * Inc., 2011 N. Shoreline Blvd., Mountain View, CA 94039-7311. 3532001f49Smrg * 3632001f49Smrg * OpenGL(TM) is a trademark of Silicon Graphics, Inc. 3732001f49Smrg */ 3832001f49Smrg/* 3932001f49Smrg * Trackball code: 4032001f49Smrg * 4132001f49Smrg * Implementation of a virtual trackball. 4232001f49Smrg * Implemented by Gavin Bell, lots of ideas from Thant Tessman and 4332001f49Smrg * the August '88 issue of Siggraph's "Computer Graphics," pp. 121-129. 4432001f49Smrg * 4532001f49Smrg * Vector manip code: 4632001f49Smrg * 4732001f49Smrg * Original code from: 4832001f49Smrg * David M. Ciemiewicz, Mark Grossman, Henry Moreton, and Paul Haeberli 4932001f49Smrg * 5032001f49Smrg * Much mucking with by: 5132001f49Smrg * Gavin Bell 5232001f49Smrg */ 537ec3b29aSmrg#if defined(_MSC_VER) 5432001f49Smrg#pragma warning (disable:4244) /* disable bogus conversion warnings */ 5532001f49Smrg#endif 5632001f49Smrg#include <math.h> 5732001f49Smrg#include "trackball.h" 5832001f49Smrg 5932001f49Smrg/* 6032001f49Smrg * This size should really be based on the distance from the center of 6132001f49Smrg * rotation to the point on the object underneath the mouse. That 6232001f49Smrg * point would then track the mouse as closely as possible. This is a 6332001f49Smrg * simple example, though, so that is left as an Exercise for the 6432001f49Smrg * Programmer. 6532001f49Smrg */ 6632001f49Smrg#define TRACKBALLSIZE (0.8f) 6732001f49Smrg 6832001f49Smrg/* 6932001f49Smrg * Local function prototypes (not defined in trackball.h) 7032001f49Smrg */ 7132001f49Smrgstatic float tb_project_to_sphere(float, float, float); 7232001f49Smrgstatic void normalize_quat(float [4]); 7332001f49Smrg 7432001f49Smrgstatic void 7532001f49Smrgvzero(float v[3]) 7632001f49Smrg{ 7732001f49Smrg v[0] = 0.0; 7832001f49Smrg v[1] = 0.0; 7932001f49Smrg v[2] = 0.0; 8032001f49Smrg} 8132001f49Smrg 8232001f49Smrgstatic void 8332001f49Smrgvset(float v[3], float x, float y, float z) 8432001f49Smrg{ 8532001f49Smrg v[0] = x; 8632001f49Smrg v[1] = y; 8732001f49Smrg v[2] = z; 8832001f49Smrg} 8932001f49Smrg 9032001f49Smrgstatic void 9132001f49Smrgvsub(const float src1[3], const float src2[3], float dst[3]) 9232001f49Smrg{ 9332001f49Smrg dst[0] = src1[0] - src2[0]; 9432001f49Smrg dst[1] = src1[1] - src2[1]; 9532001f49Smrg dst[2] = src1[2] - src2[2]; 9632001f49Smrg} 9732001f49Smrg 9832001f49Smrgstatic void 9932001f49Smrgvcopy(const float v1[3], float v2[3]) 10032001f49Smrg{ 10132001f49Smrg register int i; 10232001f49Smrg for (i = 0 ; i < 3 ; i++) 10332001f49Smrg v2[i] = v1[i]; 10432001f49Smrg} 10532001f49Smrg 10632001f49Smrgstatic void 10732001f49Smrgvcross(const float v1[3], const float v2[3], float cross[3]) 10832001f49Smrg{ 10932001f49Smrg float temp[3]; 11032001f49Smrg 11132001f49Smrg temp[0] = (v1[1] * v2[2]) - (v1[2] * v2[1]); 11232001f49Smrg temp[1] = (v1[2] * v2[0]) - (v1[0] * v2[2]); 11332001f49Smrg temp[2] = (v1[0] * v2[1]) - (v1[1] * v2[0]); 11432001f49Smrg vcopy(temp, cross); 11532001f49Smrg} 11632001f49Smrg 11732001f49Smrgstatic float 11832001f49Smrgvlength(const float v[3]) 11932001f49Smrg{ 12032001f49Smrg return sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); 12132001f49Smrg} 12232001f49Smrg 12332001f49Smrgstatic void 12432001f49Smrgvscale(float v[3], float div) 12532001f49Smrg{ 12632001f49Smrg v[0] *= div; 12732001f49Smrg v[1] *= div; 12832001f49Smrg v[2] *= div; 12932001f49Smrg} 13032001f49Smrg 13132001f49Smrgstatic void 13232001f49Smrgvnormal(float v[3]) 13332001f49Smrg{ 13432001f49Smrg vscale(v,1.0/vlength(v)); 13532001f49Smrg} 13632001f49Smrg 13732001f49Smrgstatic float 13832001f49Smrgvdot(const float v1[3], const float v2[3]) 13932001f49Smrg{ 14032001f49Smrg return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2]; 14132001f49Smrg} 14232001f49Smrg 14332001f49Smrgstatic void 14432001f49Smrgvadd(const float src1[3], const float src2[3], float dst[3]) 14532001f49Smrg{ 14632001f49Smrg dst[0] = src1[0] + src2[0]; 14732001f49Smrg dst[1] = src1[1] + src2[1]; 14832001f49Smrg dst[2] = src1[2] + src2[2]; 14932001f49Smrg} 15032001f49Smrg 15132001f49Smrg/* 15232001f49Smrg * Ok, simulate a track-ball. Project the points onto the virtual 15332001f49Smrg * trackball, then figure out the axis of rotation, which is the cross 15432001f49Smrg * product of P1 P2 and O P1 (O is the center of the ball, 0,0,0) 15532001f49Smrg * Note: This is a deformed trackball-- is a trackball in the center, 15632001f49Smrg * but is deformed into a hyperbolic sheet of rotation away from the 15732001f49Smrg * center. This particular function was chosen after trying out 15832001f49Smrg * several variations. 15932001f49Smrg * 16032001f49Smrg * It is assumed that the arguments to this routine are in the range 16132001f49Smrg * (-1.0 ... 1.0) 16232001f49Smrg */ 16332001f49Smrgvoid 16432001f49Smrgtrackball(float q[4], float p1x, float p1y, float p2x, float p2y) 16532001f49Smrg{ 16632001f49Smrg float a[3]; /* Axis of rotation */ 16732001f49Smrg float phi; /* how much to rotate about axis */ 16832001f49Smrg float p1[3], p2[3], d[3]; 16932001f49Smrg float t; 17032001f49Smrg 17132001f49Smrg if (p1x == p2x && p1y == p2y) { 17232001f49Smrg /* Zero rotation */ 17332001f49Smrg vzero(q); 17432001f49Smrg q[3] = 1.0; 17532001f49Smrg return; 17632001f49Smrg } 17732001f49Smrg 17832001f49Smrg /* 17932001f49Smrg * First, figure out z-coordinates for projection of P1 and P2 to 18032001f49Smrg * deformed sphere 18132001f49Smrg */ 18232001f49Smrg vset(p1,p1x,p1y,tb_project_to_sphere(TRACKBALLSIZE,p1x,p1y)); 18332001f49Smrg vset(p2,p2x,p2y,tb_project_to_sphere(TRACKBALLSIZE,p2x,p2y)); 18432001f49Smrg 18532001f49Smrg /* 18632001f49Smrg * Now, we want the cross product of P1 and P2 18732001f49Smrg */ 18832001f49Smrg vcross(p2,p1,a); 18932001f49Smrg 19032001f49Smrg /* 19132001f49Smrg * Figure out how much to rotate around that axis. 19232001f49Smrg */ 19332001f49Smrg vsub(p1,p2,d); 19432001f49Smrg t = vlength(d) / (2.0*TRACKBALLSIZE); 19532001f49Smrg 19632001f49Smrg /* 19732001f49Smrg * Avoid problems with out-of-control values... 19832001f49Smrg */ 19932001f49Smrg if (t > 1.0) t = 1.0; 20032001f49Smrg if (t < -1.0) t = -1.0; 20132001f49Smrg phi = 2.0 * asin(t); 20232001f49Smrg 20332001f49Smrg axis_to_quat(a,phi,q); 20432001f49Smrg} 20532001f49Smrg 20632001f49Smrg/* 20732001f49Smrg * Given an axis and angle, compute quaternion. 20832001f49Smrg */ 20932001f49Smrgvoid 21032001f49Smrgaxis_to_quat(const float a[3], float phi, float q[4]) 21132001f49Smrg{ 21232001f49Smrg vcopy(a,q); 21332001f49Smrg vnormal(q); 21432001f49Smrg vscale(q, sin(phi/2.0)); 21532001f49Smrg q[3] = cos(phi/2.0); 21632001f49Smrg} 21732001f49Smrg 21832001f49Smrg/* 21932001f49Smrg * Project an x,y pair onto a sphere of radius r OR a hyperbolic sheet 22032001f49Smrg * if we are away from the center of the sphere. 22132001f49Smrg */ 22232001f49Smrgstatic float 22332001f49Smrgtb_project_to_sphere(float r, float x, float y) 22432001f49Smrg{ 22532001f49Smrg float d, t, z; 22632001f49Smrg 22732001f49Smrg d = sqrt(x*x + y*y); 22832001f49Smrg if (d < r * 0.70710678118654752440) { /* Inside sphere */ 22932001f49Smrg z = sqrt(r*r - d*d); 23032001f49Smrg } else { /* On hyperbola */ 23132001f49Smrg t = r / 1.41421356237309504880; 23232001f49Smrg z = t*t / d; 23332001f49Smrg } 23432001f49Smrg return z; 23532001f49Smrg} 23632001f49Smrg 23732001f49Smrg/* 23832001f49Smrg * Given two rotations, e1 and e2, expressed as quaternion rotations, 23932001f49Smrg * figure out the equivalent single rotation and stuff it into dest. 24032001f49Smrg * 24132001f49Smrg * This routine also normalizes the result every RENORMCOUNT times it is 24232001f49Smrg * called, to keep error from creeping in. 24332001f49Smrg * 24432001f49Smrg * NOTE: This routine is written so that q1 or q2 may be the same 24532001f49Smrg * as dest (or each other). 24632001f49Smrg */ 24732001f49Smrg 24832001f49Smrg#define RENORMCOUNT 97 24932001f49Smrg 25032001f49Smrgvoid 25132001f49Smrgadd_quats(const float q1[4], const float q2[4], float dest[4]) 25232001f49Smrg{ 25332001f49Smrg static int count=0; 25432001f49Smrg float t1[4], t2[4], t3[4]; 25532001f49Smrg float tf[4]; 25632001f49Smrg 25732001f49Smrg#if 0 25832001f49Smrgprintf("q1 = %f %f %f %f\n", q1[0], q1[1], q1[2], q1[3]); 25932001f49Smrgprintf("q2 = %f %f %f %f\n", q2[0], q2[1], q2[2], q2[3]); 26032001f49Smrg#endif 26132001f49Smrg 26232001f49Smrg vcopy(q1,t1); 26332001f49Smrg vscale(t1,q2[3]); 26432001f49Smrg 26532001f49Smrg vcopy(q2,t2); 26632001f49Smrg vscale(t2,q1[3]); 26732001f49Smrg 26832001f49Smrg vcross(q2,q1,t3); 26932001f49Smrg vadd(t1,t2,tf); 27032001f49Smrg vadd(t3,tf,tf); 27132001f49Smrg tf[3] = q1[3] * q2[3] - vdot(q1,q2); 27232001f49Smrg 27332001f49Smrg#if 0 27432001f49Smrgprintf("tf = %f %f %f %f\n", tf[0], tf[1], tf[2], tf[3]); 27532001f49Smrg#endif 27632001f49Smrg 27732001f49Smrg dest[0] = tf[0]; 27832001f49Smrg dest[1] = tf[1]; 27932001f49Smrg dest[2] = tf[2]; 28032001f49Smrg dest[3] = tf[3]; 28132001f49Smrg 28232001f49Smrg if (++count > RENORMCOUNT) { 28332001f49Smrg count = 0; 28432001f49Smrg normalize_quat(dest); 28532001f49Smrg } 28632001f49Smrg} 28732001f49Smrg 28832001f49Smrg/* 28932001f49Smrg * Quaternions always obey: a^2 + b^2 + c^2 + d^2 = 1.0 29032001f49Smrg * If they don't add up to 1.0, dividing by their magnitued will 29132001f49Smrg * renormalize them. 29232001f49Smrg * 29332001f49Smrg * Note: See the following for more information on quaternions: 29432001f49Smrg * 29532001f49Smrg * - Shoemake, K., Animating rotation with quaternion curves, Computer 29632001f49Smrg * Graphics 19, No 3 (Proc. SIGGRAPH'85), 245-254, 1985. 29732001f49Smrg * - Pletinckx, D., Quaternion calculus as a basic tool in computer 29832001f49Smrg * graphics, The Visual Computer 5, 2-13, 1989. 29932001f49Smrg */ 30032001f49Smrgstatic void 30132001f49Smrgnormalize_quat(float q[4]) 30232001f49Smrg{ 30332001f49Smrg int i; 30432001f49Smrg float mag; 30532001f49Smrg 30632001f49Smrg mag = sqrt(q[0]*q[0] + q[1]*q[1] + q[2]*q[2] + q[3]*q[3]); 30732001f49Smrg for (i = 0; i < 4; i++) 30832001f49Smrg q[i] /= mag; 30932001f49Smrg} 31032001f49Smrg 31132001f49Smrg/* 31232001f49Smrg * Build a rotation matrix, given a quaternion rotation. 31332001f49Smrg * 31432001f49Smrg */ 31532001f49Smrgvoid 31632001f49Smrgbuild_rotmatrix(float m[4][4], const float q[4]) 31732001f49Smrg{ 31832001f49Smrg m[0][0] = 1.0 - 2.0 * (q[1] * q[1] + q[2] * q[2]); 31932001f49Smrg m[0][1] = 2.0 * (q[0] * q[1] - q[2] * q[3]); 32032001f49Smrg m[0][2] = 2.0 * (q[2] * q[0] + q[1] * q[3]); 32132001f49Smrg m[0][3] = 0.0; 32232001f49Smrg 32332001f49Smrg m[1][0] = 2.0 * (q[0] * q[1] + q[2] * q[3]); 32432001f49Smrg m[1][1]= 1.0 - 2.0 * (q[2] * q[2] + q[0] * q[0]); 32532001f49Smrg m[1][2] = 2.0 * (q[1] * q[2] - q[0] * q[3]); 32632001f49Smrg m[1][3] = 0.0; 32732001f49Smrg 32832001f49Smrg m[2][0] = 2.0 * (q[2] * q[0] - q[1] * q[3]); 32932001f49Smrg m[2][1] = 2.0 * (q[1] * q[2] + q[0] * q[3]); 33032001f49Smrg m[2][2] = 1.0 - 2.0 * (q[1] * q[1] + q[0] * q[0]); 33132001f49Smrg m[2][3] = 0.0; 33232001f49Smrg 33332001f49Smrg m[3][0] = 0.0; 33432001f49Smrg m[3][1] = 0.0; 33532001f49Smrg m[3][2] = 0.0; 33632001f49Smrg m[3][3] = 1.0; 33732001f49Smrg} 33832001f49Smrg 339