-
Notifications
You must be signed in to change notification settings - Fork 2
/
arcball.cpp
executable file
·280 lines (244 loc) · 7.53 KB
/
arcball.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
/* Arcball, written by Bradley Smith, March 24, 2006
*
* See arcball.h for usage details.
*/
#include "arcball.h"
arcball::arcball()
{
for(int i = 0; i < 16;i++)
{
if(i == 0)
{
ab_quat[i] = 1;
ab_last[i] = 1;
ab_next[i] = 1;
ab_glp[i] = 1;
ab_glm[i] = 1;
}
else if(i == 5)
{
ab_quat[i] = 1;
ab_last[i] = 1;
ab_next[i] = 1;
ab_glp[i] = 1;
ab_glm[i] = 1;
}
else if(i == 10)
{
ab_quat[i] = 1;
ab_last[i] = 1;
ab_next[i] = 1;
ab_glp[i] = 1;
ab_glm[i] = 1;
}
else if(i == 15)
{
ab_quat[i] = 1;
ab_last[i] = 1;
ab_next[i] = 1;
ab_glp[i] = 1;
ab_glm[i] = 1;
}
else
{
ab_quat[i] = 0;
ab_last[i] = 0;
ab_next[i] = 0;
ab_glp[i] = 0;
ab_glm[i] = 0;
}
}
//ab_quat = {1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1};
//ab_last = {1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1};
//ab_next = {1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1};
//ab_inv = {1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1};
// the distance from the origin to the eye
ab_zoom = 1.0;
ab_zoom2 = 1.0;
// the radius of the arcball
ab_sphere = 1.0;
ab_sphere2 = 1.0;
// the distance from the origin of the plane that intersects
// the edge of the visible sphere (tangent to a ray from the eye)
ab_edge = 1.0;
// whether we are using a sphere or plane
ab_planar = false;
ab_planedist = 0.5;
ab_start = vec(0,0,1);
ab_curr = vec(0,0,1);
ab_eye = vec(0,0,1);
ab_eyedir = vec(0,0,1);
ab_up = vec(0,1,0);
ab_out = vec(1,0,0);
//ab_glp = {1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1};
//ab_glm = {1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1};
ab_glv[0] = 0;
ab_glv[1] = 0;
ab_glv[2] = 640;
ab_glv[3] = 480;
//ab_glv = {0,0,640,480};
}
void arcball::arcball_setzoom(float radius, vec eye, vec up)
{
ab_eye = eye; // store eye vector
ab_zoom2 = ab_eye * ab_eye;
ab_zoom = sqrt(ab_zoom2); // store eye distance
ab_sphere = radius; // sphere radius
ab_sphere2 = ab_sphere * ab_sphere;
ab_eyedir = ab_eye * (1.0 / ab_zoom); // distance to eye
ab_edge = ab_sphere2 / ab_zoom; // plane of visible edge
if(ab_sphere <= 0.0) // trackball mode
{
ab_planar = true;
ab_up = up;
ab_out = ( ab_eyedir ^ ab_up );
ab_planedist = (0.0 - ab_sphere) * ab_zoom;
} else
ab_planar = false;
glGetDoublev(GL_PROJECTION_MATRIX,ab_glp);
glGetIntegerv(GL_VIEWPORT,ab_glv);
}
// affect the arcball's orientation on openGL
GLfloat* arcball::arcball_rotate()
{
return ab_quat;
}
// convert the quaternion into a rotation matrix
void arcball::quaternion(GLfloat* q, GLfloat x, GLfloat y, GLfloat z, GLfloat w)
{
GLfloat x2 = x*x;
GLfloat y2 = y*y;
GLfloat z2 = z*z;
GLfloat xy = x*y;
GLfloat xz = x*z;
GLfloat yz = y*z;
GLfloat wx = w*x;
GLfloat wy = w*y;
GLfloat wz = w*z;
q[0] = 1 - 2*y2 - 2*z2;
q[1] = 2*xy + 2*wz;
q[2] = 2*xz - 2*wy;
q[4] = 2*xy - 2*wz;
q[5] = 1 - 2*x2 - 2*z2;
q[6] = 2*yz + 2*wx;
q[8] = 2*xz + 2*wy;
q[9] = 2*yz - 2*wx;
q[10]= 1 - 2*x2 - 2*y2;
}
// reset the rotation matrix
void arcball::quatidentity(GLfloat* q)
{ q[0]=1; q[1]=0; q[2]=0; q[3]=0;
q[4]=0; q[5]=1; q[6]=0; q[7]=0;
q[8]=0; q[9]=0; q[10]=1; q[11]=0;
q[12]=0; q[13]=0; q[14]=0; q[15]=1; }
// copy a rotation matrix
void arcball::quatcopy(GLfloat* dst, GLfloat* src)
{ dst[0]=src[0]; dst[1]=src[1]; dst[2]=src[2];
dst[4]=src[4]; dst[5]=src[5]; dst[6]=src[6];
dst[8]=src[8]; dst[9]=src[9]; dst[10]=src[10]; }
// multiply two rotation matrices
void arcball::quatnext(GLfloat* dest, GLfloat* left, GLfloat* right)
{
dest[0] = left[0]*right[0] + left[1]*right[4] + left[2] *right[8];
dest[1] = left[0]*right[1] + left[1]*right[5] + left[2] *right[9];
dest[2] = left[0]*right[2] + left[1]*right[6] + left[2] *right[10];
dest[4] = left[4]*right[0] + left[5]*right[4] + left[6] *right[8];
dest[5] = left[4]*right[1] + left[5]*right[5] + left[6] *right[9];
dest[6] = left[4]*right[2] + left[5]*right[6] + left[6] *right[10];
dest[8] = left[8]*right[0] + left[9]*right[4] + left[10]*right[8];
dest[9] = left[8]*right[1] + left[9]*right[5] + left[10]*right[9];
dest[10]= left[8]*right[2] + left[9]*right[6] + left[10]*right[10];
}
// find the intersection with the plane through the visible edge
vec arcball::edge_coords(vec m)
{
// find the intersection of the edge plane and the ray
float t = (ab_edge - ab_zoom) / (ab_eyedir * m);
vec a = ab_eye + (m*t);
// find the direction of the eye-axis from that point
// along the edge plane
vec c = (ab_eyedir * ab_edge) - a;
// find the intersection of the sphere with the ray going from
// the plane outside the sphere toward the eye-axis.
float ac = (a*c);
float c2 = (c*c);
float q = ( 0.0 - ac - sqrt( ac*ac - c2*((a*a)-ab_sphere2) ) ) / c2;
return (a+(c*q)).unit();
}
// find the intersection with the sphere
vec arcball::sphere_coords(GLdouble mx, GLdouble my)
{
GLdouble ax,ay,az;
gluUnProject(mx,my,0,ab_glm,ab_glp,ab_glv,&ax,&ay,&az);
vec m = vec((float)ax,(float)ay,(float)az) - ab_eye;
// mouse position represents ray: eye + t*m
// intersecting with a sphere centered at the origin
GLfloat a = m*m;
GLfloat b = (ab_eye*m);
GLfloat root = (b*b) - a*(ab_zoom2 - ab_sphere2);
if(root <= 0) return edge_coords(m);
GLfloat t = (0.0 - b - sqrt(root)) / a;
return (ab_eye+(m*t)).unit();
}
// get intersection with plane for "trackball" style rotation
vec arcball::planar_coords(GLdouble mx, GLdouble my)
{
GLdouble ax,ay,az;
gluUnProject(mx,my,0,ab_glm,ab_glp,ab_glv,&ax,&ay,&az);
vec m = vec((float)ax,(float)ay,(float)az) - ab_eye;
// intersect the point with the trackball plane
GLfloat t = (ab_planedist - ab_zoom) / (ab_eyedir * m);
vec d = ab_eye + m*t;
return vec(d*ab_up,d*ab_out,0.0);
}
// reset the arcball
void arcball::arcball_reset()
{
quatidentity(ab_quat);
quatidentity(ab_last);
}
// begin arcball rotation
void arcball::arcball_start(int mx, int my)
{
// saves a copy of the current rotation for comparison
quatcopy(ab_last,ab_quat);
if(ab_planar) ab_start = planar_coords((GLdouble)mx,(GLdouble)my);
else ab_start = sphere_coords((GLdouble)mx,(GLdouble)my);
}
// update current arcball rotation
void arcball::arcball_move(int mx, int my)
{
if(ab_planar)
{
ab_curr = planar_coords((GLdouble)mx,(GLdouble)my);
if(ab_curr.equals(ab_start)) return;
// d is motion since the last position
vec d = ab_curr - ab_start;
GLfloat angle = d.length() * 0.5;
GLfloat cosa = cos( angle );
GLfloat sina = sin( angle );
// p is perpendicular to d
vec p = ((ab_out*d.x)-(ab_up*d.y)).unit() * sina;
quaternion(ab_next,p.x,p.y,p.z,cosa);
quatnext(ab_quat,ab_last,ab_next);
// planar style only ever relates to the last point
quatcopy(ab_last,ab_quat);
ab_start = ab_curr;
} else {
ab_curr = sphere_coords((GLdouble)mx,(GLdouble)my);
if(ab_curr.equals(ab_start))
{ // avoid potential rare divide by tiny
quatcopy(ab_quat,ab_last);
return;
}
// use a dot product to get the angle between them
// use a cross product to get the vector to rotate around
GLfloat cos2a = ab_start*ab_curr;
GLfloat sina = sqrt((1.0 - cos2a)*0.5);
GLfloat cosa = sqrt((1.0 + cos2a)*0.5);
vec cross = (ab_start^ab_curr).unit() * sina;
quaternion(ab_next,cross.x,cross.y,cross.z,cosa);
// update the rotation matrix
quatnext(ab_quat,ab_last,ab_next);
}
}