camera.cpp

Go to the documentation of this file.
00001 /*
00002  * camera.cpp
00003  *
00004  * Copyright (C) 2008,2009  Thomas A. Vaughan
00005  * All rights reserved.
00006  *
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted provided that the following conditions are met:
00010  *     * Redistributions of source code must retain the above copyright
00011  *       notice, this list of conditions and the following disclaimer.
00012  *     * Redistributions in binary form must reproduce the above copyright
00013  *       notice, this list of conditions and the following disclaimer in the
00014  *       documentation and/or other materials provided with the distribution.
00015  *     * Neither the name of the <organization> nor the
00016  *       names of its contributors may be used to endorse or promote products
00017  *       derived from this software without specific prior written permission.
00018  *
00019  * THIS SOFTWARE IS PROVIDED BY THOMAS A. VAUGHAN ''AS IS'' AND ANY
00020  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00021  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00022  * DISCLAIMED. IN NO EVENT SHALL THOMAS A. VAUGHAN BE LIABLE FOR ANY
00023  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00024  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00025  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00026  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00027  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00028  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00029  *
00030  *
00031  * Implementation of simple GLUT-based camera.  See camera.h
00032  */
00033 
00034 // includes --------------------------------------------------------------------
00035 #include "camera.h"                     // always include our own header first!
00036 
00037 #include "glut.h"
00038 
00039 
00040 namespace glut {
00041 
00042 
00043 static const float s_twoPi              = 2.0 * M_PI;
00044 
00045 
00046 ////////////////////////////////////////////////////////////////////////////////
00047 //
00048 /// \ingroup glut
00049 /// \defgroup glut_camera Cameras and OpenGL
00050 ///
00051 ///
00052 /// Basically, use GL_PROJECTION to set up the camera properties but NOT
00053 /// position or rotation.  Use GL_MODELVIEW for those transformations.
00054 ///
00055 /// \n
00056 /// From http://www.opengl.org/resources/faq/technical/viewing.htm :
00057 ///
00058 /// The GL_PROJECTION matrix should contain only the projection transformation
00059 /// calls it needs to transform eye space coordinates into clip coordinates.
00060 ///
00061 /// The GL_MODELVIEW matrix, as its name implies, should contain modeling and
00062 /// viewing transformations, which transform object space coordinates into eye
00063 /// space coordinates.  Remember to place the camera transformations on the
00064 /// GL_MODELVIEW matrix and never on the GL_PROJECTION matrix.
00065 ///
00066 /// Think of the projection matrix as describing the attributes of your camera,
00067 /// such as field of view, focal length, fish eye lens, etc. Think of the
00068 /// ModelView matrix as where you stand with the camera and the direction you
00069 /// point it.
00070 ///
00071 ///
00072 /// \n
00073 /// From http://sjbaker.org/steve/omniv/projection_abuse.html :
00074 ///
00075 /// One of the most common OpenGL programming errors is misuse of the
00076 /// GL_PROJECTION matrix stack. This matrix stack is often perceived as being
00077 /// the correct place to put the position and orientation of the 'camera' -
00078 /// but this is not in fact the case...
00079 ///
00080 /// The Golden Rule.
00081 /// The rule is:
00082 ///
00083 /// <em>The only functions that should be called when the
00084 ///     glMatrixMode is set to GL_PROJECTION are:</em>
00085 ///
00086 /// - glLoadIdentity - to initialise the stack.
00087 /// - gluPerspective/glFrustum/glOrtho/gluOrtho2 -
00088 ///               to set the appropriate projection onto the stack.
00089 ///
00090 //
00091 ////////////////////////////////////////////////////////////////////////////////
00092 
00093 
00094 
00095 ////////////////////////////////////////////////////////////////////////////////
00096 //
00097 //      Viewer implementation
00098 //
00099 ////////////////////////////////////////////////////////////////////////////////
00100 
00101 Viewer::Viewer(void)
00102 throw()
00103 {
00104         this->clear();
00105 }
00106 
00107 
00108 
00109 void
00110 Viewer::clear
00111 (
00112 void
00113 )
00114 throw()
00115 {
00116         m_position.clear();
00117         m_yRot = m_upRot = 0.0;
00118 }
00119 
00120 
00121 
00122 void
00123 Viewer::rotateY
00124 (
00125 IN float dy
00126 )
00127 throw()
00128 {
00129         m_yRot += dy;
00130 
00131         // keep y-rotation in range 0 <= y_rot <= 2 PI
00132         if (m_yRot < 0.0) {
00133                 m_yRot += s_twoPi;
00134         } else if (m_yRot > s_twoPi) {
00135                 m_yRot -= s_twoPi;
00136         }
00137 }
00138 
00139 
00140 
00141 void
00142 Viewer::rotateUp
00143 (
00144 IN float dz
00145 )
00146 throw()
00147 {
00148         m_upRot += dz;
00149 
00150         // keep z-rotation in range - PI/2 <= z_rot <= +PI/2
00151         const float maxUpRot = 0.49 * M_PI;
00152         if (m_upRot < -maxUpRot) {
00153                 m_upRot = -maxUpRot;
00154         } else if (m_upRot > maxUpRot) {
00155                 m_upRot = maxUpRot;
00156         }
00157 }
00158 
00159 
00160 
00161 void
00162 Viewer::move
00163 (
00164 IN float forwards,
00165 IN float sideways,
00166 IN float up
00167 )
00168 throw()
00169 {
00170         m_position.x += -forwards * sin(m_yRot) - sideways * cos(m_yRot);
00171         m_position.z += -forwards * cos(m_yRot) + sideways * sin(m_yRot);
00172         m_position.y += up;
00173 }
00174 
00175 
00176 
00177 point3d_t
00178 Viewer::getFacing
00179 (
00180 void
00181 )
00182 const
00183 throw()
00184 {
00185         point3d_t facing;
00186 
00187         facing.x = sin(m_yRot) * cos(m_upRot);
00188         facing.y = sin(m_upRot);
00189         facing.z = cos(m_yRot) * cos(m_upRot);
00190 
00191         return facing;
00192 }
00193 
00194 
00195 
00196 point3d_t
00197 Viewer::getUp
00198 (
00199 void
00200 )
00201 const
00202 throw()
00203 {
00204         point3d_t up;
00205 
00206         up.x = sin(m_yRot) * sin(m_upRot);
00207         up.y = cos(m_upRot);
00208         up.z = cos(m_yRot) * sin(m_upRot);
00209 
00210         return up;
00211 }
00212 
00213 
00214 
00215 ////////////////////////////////////////////////////////////////////////////////
00216 //
00217 //      public API
00218 //
00219 ////////////////////////////////////////////////////////////////////////////////
00220 
00221 void
00222 getTrailingViewer
00223 (
00224 IN const Viewer& subject,
00225 IN float dist,
00226 OUT Viewer& view
00227 )
00228 throw()
00229 {
00230         ASSERT(dist > 0.0, "Bad trailing distance: %f", dist);
00231 
00232         point3d_t pos = subject.getPosition();
00233         point3d_t facing = subject.getFacing();
00234 
00235         pos.x += -dist * facing.x;
00236         pos.y += -dist * facing.y;
00237         pos.z += -dist * facing.z;
00238 
00239         view.clear();
00240         view.setPosition(pos);
00241         view.rotateY(subject.getYRotation());
00242         view.rotateUp(subject.getUpRotation());
00243 }
00244 
00245 
00246 
00247 void
00248 setOpenGLProjection
00249 (
00250 IN const camera_t& camera
00251 )
00252 throw()
00253 {
00254         // see the big comment block above.
00255         //      GL_PROJECTION: basic camera properties
00256         //      GL_MODELVIEW:  camera position + rotation (inverted)
00257 
00258         // set up the projection matrix
00259         glMatrixMode(GL_PROJECTION);
00260         glLoadIdentity();
00261         gluPerspective(camera.getFovy(),
00262                        camera.getAspect(),
00263                        camera.getZNear(),
00264                        camera.getZFar());
00265 }
00266 
00267 
00268 
00269 void
00270 setOpenGLViewer
00271 (
00272 IN const Viewer& viewer
00273 )
00274 throw()
00275 {
00276         // which way is up?
00277         point3d_t up(0, 1, 0);
00278 
00279         // where are we?
00280         point3d_t position = viewer.getPosition();
00281 
00282         // where are we looking?
00283         point3d_t facing = viewer.getFacing();
00284         facing.increment(position);     // looking at this point
00285 
00286         // set up transformation
00287         glMatrixMode(GL_MODELVIEW);
00288         glLoadIdentity();
00289         gluLookAt(position.x, position.y, position.z,
00290                   facing.x, facing.y, facing.z,
00291                   up.x, up.y, up.z);
00292 }
00293 
00294 
00295 
00296 void
00297 addQuaternionRotation
00298 (
00299 IN const quaternion_t& q
00300 )
00301 throw()
00302 {
00303         // first convert quaternion into OpenGL column-major 4x4 matrix
00304         // see http://nehe.gamedev.net/data/lessons/lesson.asp?lesson=Quaternion_Camera_Class
00305         // or http://gpwiki.org/index.php/OpenGL:Tutorials:Using_Quaternions_to_represent_rotation
00306         // for examples
00307         // Watch out for row-major vs. column-major representations! 
00308         // We want column-major.
00309         // Note on 10 aug 09: I flipped back to row-major!  I must have mis-copied
00310         //   one of the above sources.
00311         float m[16];    // 4x4 matrix
00312 
00313         // first row
00314         m[ 0] = 1.0 - 2.0 * (q.y * q.y + q.z * q.z);
00315         m[ 4] = 2.0 * (q.x * q.y - q.z * q.w);
00316         m[ 8] = 2.0 * (q.x * q.z + q.y * q.w);
00317         m[12] = 0.0;
00318 
00319         // second row
00320         m[ 1] = 2.0 * (q.x * q.y + q.w * q.z);
00321         m[ 5] = 1.0 - 2.0 * (q.x * q.x + q.z * q.z);
00322         m[ 9] = 2.0 * (q.y * q.z - q.w * q.x);
00323         m[13] = 0.0;
00324 
00325         // third row
00326         m[ 2] = 2.0 * (q.x * q.z - q.w * q.y);
00327         m[ 6] = 2.0 * (q.y * q.z + q.w * q.x);
00328         m[10] = 1.0 - 2.0 * (q.x * q.x + q.y * q.y);
00329         m[14] = 0.0;
00330 
00331         // fourth row
00332         m[ 3] = 0.0;
00333         m[ 7] = 0.0;
00334         m[11] = 0.0;
00335         m[15] = 1.0;
00336 
00337         // okay, multiply current OpenGL matrix
00338         glMultMatrixf(m);
00339 }
00340 
00341 
00342 
00343 void
00344 setPlacement
00345 (
00346 IN const placement_t& p
00347 )
00348 throw()
00349 {
00350         glMatrixMode(GL_MODELVIEW);
00351         glTranslatef(p.position.x, p.position.y, p.position.z);
00352         addQuaternionRotation(p.rotation);
00353 }
00354 
00355 
00356 
00357 };      // glut namespace
00358