topical media & game development
basic-visual-06-slerp.c
? /
basic-visual-06-slerp.c
// ------------------------------------------------------------------------
// This program is complementary material for the book:
//
// Frank Nielsen
//
// Visual Computing: Geometry, Graphics, and Vision
//
// ISBN: 1-58450-427-7
//
// Charles River Media, Inc.
//
//
// All programs are available at www.charlesriver.com/visualcomputing/
//
// You may use this program for ACADEMIC and PERSONAL purposes ONLY.
//
//
// The use of this program in a commercial product requires EXPLICITLY
// written permission from the author. The author is NOT responsible or
// liable for damage or loss that may be caused by the use of this program.
//
// Copyright (c) 2005. Frank Nielsen. All rights reserved.
// ------------------------------------------------------------------------
// ------------------------------------------------------------------------
// File: slerp.cpp
//
// Description: Spherical linear interpolation demo program in OpenGL(R)
// ------------------------------------------------------------------------
include <stdafx.h>
include <windows.h>
include <math.h>
include <GL/gl.h>
include <GL/glut.h>
using namespace std;
define W 800
define H 800
inline double drand() {return rand()/(double)RAND_MAX;}
define M_PI 3.14159265
define toRad(x) ((x)*(M_PI/180.0))
class Point3D
{
public:
double x,y,z;
};
class Quaternion{
public:
double w;
Point3D u;
inline void Multiply(const Quaternion q)
{
Quaternion tmp;
tmp.u.x = ((w * q.u.x) + (u.x * q.w) + (u.y * q.u.z) - (u.z * q.u.y));
tmp.u.y = ((w * q.u.y) - (u.x * q.u.z) + (u.y * q.w) + (u.z * q.u.x));
tmp.u.z = ((w * q.u.z) + (u.x * q.u.y) - (u.y * q.u.x) + (u.z * q.w));
tmp.w = ((w * q.w) - (u.x * q.u.x) - (u.y * q.u.y) - (u.z * q.u.z));
*this = tmp;
}
inline double Norm()
{return sqrt(u.x*u.x+u.y*u.y+u.z*u.z+w*w);}
inline void Normalize()
{
double norm=Norm();
u.x/=norm;u.y/=norm;u.z/=norm;
}
inline void Conjugate()
{
u.x=-u.x;
u.y=-u.y;
u.z=-u.z;
}
inline void Inverse()
{
double norm=Norm();
Conjugate();
u.x/=norm;
u.y/=norm;
u.z/=norm;
w/=norm;
}
void ExportToMatrix(float matrix[16])
{
float wx, wy, wz, xx, yy, yz, xy, xz, zz;
// adapted from Shoemake
xx = u.x * u.x;
xy = u.x * u.y;
xz = u.x * u.z;
yy = u.y * u.y;
zz = u.z * u.z;
yz = u.y * u.z;
wx = w * u.x;
wy = w * u.y;
wz = w * u.z;
matrix[0] = 1.0f - 2.0f*(yy + zz);
matrix[4] = 2.0f*(xy - wz);
matrix[8] = 2.0f*(xz + wy);
matrix[12] = 0.0;
matrix[1] = 2.0f*(xy + wz);
matrix[5] = 1.0f - 2.0f*(xx + zz);
matrix[9] = 2.0f*(yz - wx);
matrix[13] = 0.0;
matrix[2] = 2.0f*(xz - wy);
matrix[6] = 2.0f*(yz + wx);
matrix[10] = 1.0f - 2.0f*(xx + yy);
matrix[14] = 0.0;
matrix[3] = 0;
matrix[7] = 0;
matrix[11] = 0;
matrix[15] = 1;
}
};
Quaternion RotateAboutAxis(Point3D pt, double angle, Point3D axis)
{
Quaternion q,p, qinv;
q.w=cos(0.5*angle);
q.u.x=sin(0.5*angle)*axis.x;
q.u.y=sin(0.5*angle)*axis.y;
q.u.z=sin(0.5*angle)*axis.z;
p.w=0;
p.u=pt;
qinv=q;
qinv.Inverse();
q.Multiply(p);
q.Multiply(qinv);
return q;
}
void Slerp(Quaternion q1, Quaternion q2, Quaternion &qr , double lambda)
{
float dotproduct = q1.u.x * q2.u.x + q1.u.y * q2.u.y + q1.u.z * q2.u.z + q1.w * q2.w;
float theta, st, sut, sout, coeff1, coeff2;
// algorithm adapted from Shoemake's paper
lambda=lambda/2.0;
theta = (float) acos(dotproduct);
if (theta<0.0) theta=-theta;
st = (float) sin(theta);
sut = (float) sin(lambda*theta);
sout = (float) sin((1-lambda)*theta);
coeff1 = sout/st;
coeff2 = sut/st;
qr.u.x = coeff1*q1.u.x + coeff2*q2.u.x;
qr.u.y = coeff1*q1.u.y + coeff2*q2.u.y;
qr.u.z = coeff1*q1.u.z + coeff2*q2.u.z;
qr.w = coeff1*q1.w + coeff2*q2.w;
qr.Normalize();
}
Point3D p,q,Rp;
double lambdaanim=0.0;
void MultiplyPointMatrix(float m[16], Point3D p, Point3D& rotp)
{
rotp.x=m[0]*p.x+m[1]*p.y+m[2]*p.z;
rotp.y=m[4]*p.x+m[5]*p.y+m[6]*p.z;
rotp.z=m[8]*p.x+m[9]*p.y+m[10]*p.z;
}
void display(void)
{
GLfloat m[16];
char buffer[256];
double s=1.0;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glColor3f(0.8,0.8,0.8);
glutSolidSphere(0.99,32,32);
glColor3f(1,1,0);
glBegin(GL_LINES);
glVertex3f(0,0,0);
glVertex3f(0,2,0);
glVertex3f(0,0,0);
glVertex3f(2,0,0);
glVertex3f(0,0,0);
glVertex3f(0,0,2);
glEnd();
glColor3f(0,0,1);
glPointSize(10);
glBegin(GL_POINTS);
glVertex3f(p.x,p.y,p.z);
glEnd();
glColor3f(1,0,1);
glBegin(GL_POINTS);
glVertex3f(q.x,q.y,q.z);
glEnd();
Quaternion quatp,quatq, quatr;
quatp.u=p;quatp.w=0;
quatq.u=q;quatq.w=0;
glColor3f(0,1,0);
glPointSize(1.0);
glBegin(GL_LINE_STRIP);
for(double lambda=0;lambda<=1.01;lambda+=0.01)
{
Slerp(quatp, quatq, quatr,lambda);
quatr.ExportToMatrix(m);
MultiplyPointMatrix(m,p,Rp);
glVertex3f(s*Rp.x,s*Rp.y,s*Rp.z);
}
glEnd();
lambdaanim+=0.01;
if (lambdaanim>1.0) lambdaanim=0.0;
Slerp(quatp, quatq, quatr,lambdaanim);
quatr.ExportToMatrix(m);
MultiplyPointMatrix(m,p,Rp);
glPointSize(5);
glColor3f(1,0,0);
glBegin(GL_POINTS);
glVertex3f(s*Rp.x,s*Rp.y,s*Rp.z);
glEnd();
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glOrtho (0.0, W, 0.0, H, -1.0, 1.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glColor3f (0, 0, 0);
glRasterPos2f(50,30);
sprintf(buffer,"Spherical Linear Interpolation of Quaternions (L=%.3f)",lambdaanim);
for(int i=0;buffer[i]!=0;i++)
glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, buffer[i]);
glRasterPos2f(50,H-30);
sprintf(buffer,"Press any key to initialize another pair of points.");
for(int i=0;buffer[i]!=0;i++)
glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, buffer[i]);
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glFlush();
glutSwapBuffers();
}
void reshape(int w, int h)
{
GLfloat aspect = (GLfloat) w / (GLfloat) h;
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if (w <= h)
glOrtho(-1.25, 1.25, -1.25 * aspect, 1.25 * aspect, -2.0, 2.0);
else
glOrtho(-1.25 * aspect, 1.25 * aspect, -1.25, 1.25, -2.0, 2.0);
glutPostRedisplay();
}
inline void Spherical2Cartesian(double t,double p,double &X, double &Y, double &Z)
{
X=cos(p)*sin(t);
Y=sin(p);
Z=cos(p)*cos(t);
}
void keyboard(unsigned char key, int x, int y)
{
if (key=='q') exit(0);
// theta phi of point p
Spherical2Cartesian(M_PI*drand()*2.0,-M_PI/2.0+M_PI*drand(),p.x,p.y,p.z);
// theta, phi of point q
Spherical2Cartesian(M_PI*drand()*2.0,-M_PI/2.0+M_PI*drand(),q.x,q.y,q.z);
glutPostRedisplay();
}
int _tmain(int argc, _TCHAR* argv[])
{
srand(1234);
cout<<"Visual Computing: Geometry, Graphics, and Vision (ISBN:1-58450-427-7)"<<endl;
cout<<"Demo program\n\n"<<endl;
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE| GLUT_DEPTH);
glutInitWindowSize(W,H);
glutCreateWindow("Quaternion SLERP");
glutReshapeFunc(reshape);
glutDisplayFunc(display);
glutKeyboardFunc(keyboard);
glClearColor(1.0, 1.0, 1.0, 0.0);
glEnable(GL_DEPTH_TEST);
glutIdleFunc(display);
keyboard(0,0,0);
glutMainLoop();
return 0;
}
(C) Æliens
20/2/2008
You may not copy or print any of this material without explicit permission of the author or the publisher.
In case of other copyright issues, contact the author.