sphere.cpp

Go to the documentation of this file.
00001 /*
00002  * sphere.cpp
00003  *
00004  * Copyright (C) 2010  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  * A glowing colored sphere with lightning.  Oooohhh.
00032  */
00033 
00034 // includes --------------------------------------------------------------------
00035 #include "opengl-effects.h"             // always include our own header first!
00036 
00037 #include "glut/texture.h"
00038 #include "perf/perf.h"
00039 
00040 
00041 namespace glut {
00042 
00043 
00044 
00045 ////////////////////////////////////////////////////////////////////////////////
00046 //
00047 //      static helper methods
00048 //
00049 ////////////////////////////////////////////////////////////////////////////////
00050 
00051 static float
00052 randomX
00053 (
00054 IN float x
00055 )
00056 {
00057         return (x * rand()) / RAND_MAX;
00058 }
00059 
00060 
00061 
00062 static void
00063 jitter
00064 (
00065 IO point3d_t& p,
00066 IN float dx
00067 )
00068 throw()
00069 {
00070         p.x += randomX(dx) - 0.5 * dx;
00071         p.y += randomX(dx) - 0.5 * dx;
00072         p.z += randomX(dx) - 0.5 * dx;
00073 }
00074 
00075 
00076 
00077 struct ring_t {
00078         ring_t(void) throw() {
00079                         gamma = 1.01 + randomX(0.06);
00080 
00081                         axis.x = -1.0 + randomX(2.0);
00082                         axis.y = -1.0 + randomX(2.0);
00083                         axis.z = -1.0 + randomX(2.0);
00084                         normalize(axis);
00085 
00086                         phi = randomX(2.0 * M_PI);
00087                         dPhi = 0.2 * (5 + randomX(10));
00088                 }
00089 
00090         void tick(void) throw() {
00091                         phi += dPhi;
00092                 }
00093 
00094         // data fields
00095         float           gamma;
00096         point3d_t       axis;
00097         float           phi;
00098         float           dPhi;
00099 };
00100 
00101 
00102 
00103 static void
00104 drawLightningRing
00105 (
00106 IN float radius,
00107 IN const point3d_t& axis,
00108 IN float phi,
00109 IN const glut_color_t& color
00110 )
00111 throw()
00112 {
00113         ASSERT(radius > 0, "bad radius: %f", radius);
00114         int nPoints = 32;
00115         ASSERT(nPoints > 1, "need at least 2!");
00116 
00117         // translate to center, and rotate by specified axis
00118         glMatrixMode(GL_MODELVIEW);
00119         glPushMatrix();
00120         glRotatef(phi, axis.x, axis.y, axis.z);
00121 
00122         float theta = 0;
00123         float dTheta = 2.0 * M_PI / (nPoints - 1);
00124 
00125         const float dJitter = 0.3;
00126 
00127         glDisable(GL_LIGHTING);
00128         glColor4fv(&color.red);
00129         glBegin(GL_LINE_LOOP);
00130         for (int i = 0; i < nPoints; ++i) {
00131                 float mu = theta + randomX(dJitter) - 0.5 * dJitter;
00132                 float nu = randomX(dJitter) - 0.5 * dJitter;
00133                 point3d_t p(cos(mu) * cos(nu), sin(nu), sin(mu) * cos(nu));
00134                 p *= radius;
00135                 glVertex3fv(&p.x);
00136                 theta += dTheta;
00137         }
00138         glEnd();
00139         glEnable(GL_LIGHTING);
00140 
00141         glPopMatrix();
00142 }
00143 
00144 
00145 
00146 class Sphere : public Renderable {
00147 public:
00148         // constructor, destructor ---------------------------------------------
00149         ~Sphere(void) throw();
00150 
00151         // public class methods ------------------------------------------------
00152         void initialize(IN const sphere_init_t& si);
00153 
00154         // glut::Renderable class interface methods ----------------------------
00155         void render(IN const render_context_t& rc,
00156                                 IN RenderQueue * rq);
00157         rect3d_t getBoundingBox(void) const throw();
00158 
00159 private:
00160         // private typedefs ----------------------------------------------------
00161         // private helper methods ----------------------------------------------
00162         // private member data -------------------------------------------------
00163         float           m_radius;
00164         float           m_phi;
00165         float           m_dPhi;
00166         int             m_nRings;
00167         ring_t          m_rings[sphere_init_t::eMaxRings];
00168         GLuint          m_textureId;
00169 };
00170 
00171 
00172 
00173 Sphere::~Sphere(void)
00174 throw()
00175 {
00176         if (m_textureId) {
00177                 glDeleteTextures(1, &m_textureId);
00178         }
00179 }
00180 
00181 
00182 
00183 void
00184 Sphere::initialize
00185 (
00186 IN const sphere_init_t& si
00187 )
00188 {
00189         ASSERT(si.isValid(), "invalid sphere initialization data");
00190 
00191         // copy from input
00192         m_radius = si.radius;
00193         m_nRings = si.nRings;
00194 
00195         // set random parameters
00196         m_phi = randomX(2.0 * M_PI);
00197         m_dPhi = 5 * (5 + randomX(10));
00198         if (randomX(10) < 5) {
00199                 m_dPhi = -m_dPhi;
00200         }
00201 
00202         // create density map
00203         media::image_t img;
00204         //createSphericalDensityMap(40, si.color, img);
00205         createCircularDensityMap(64, si.color, img);
00206 
00207         // create texture
00208         m_textureId = createTextureFromImage(img);
00209         DPRINTF("Texture ID: %d", m_textureId);
00210 }
00211 
00212 
00213 
00214 void
00215 Sphere::render
00216 (
00217 IN const render_context_t& rc,
00218 IN RenderQueue * rq
00219 )
00220 {
00221         perf::Timer timer("Sphere::render");
00222         ASSERT(rq, "null");
00223 
00224         m_phi += m_dPhi;
00225 
00226         // first, render a standard sphere at position
00227         glMatrixMode(GL_MODELVIEW);
00228         glPushMatrix();
00229         glRotatef(m_phi, 0, 1, 0);
00230         glutSolidSphere(m_radius, 12, 12);
00231         glPopMatrix();
00232 
00233         // now draw lightning rings
00234         for (int i = 0; i < m_nRings; ++i) {
00235                 ring_t& r = m_rings[i];
00236                 r.tick();
00237                 drawLightningRing(r.gamma * m_radius,
00238                     r.axis, r.phi, glut_color_t(1, 1, 1, 1));
00239         }
00240 
00241         // ray from viewer to center of sphere
00242         point3d_t to = rc.placement.position - rc.viewer.getPosition();
00243         float z2 = dotProduct(to, to);
00244         if (z2 < 1.0e-4)
00245                 return;         // too close!
00246         float z = sqrt(z2);
00247         to = (1.0 / z) * to;
00248 
00249         // we can choose an arbitrary "up" direction
00250         point3d_t bogusUp = rc.viewer.getUp();
00251         point3d_t right = crossProduct(to, bogusUp);
00252         normalize(right);
00253 
00254         // get a proper "up" direction now
00255         point3d_t up = -crossProduct(to, right);
00256         normalize(up);
00257 
00258         // queue request
00259         glut::poly_request_t * pr = rq->grabRequestSlot();
00260         if (!pr) {
00261                 DPRINTF("No available request slots?");
00262                 return;
00263         }
00264 
00265         matrix4_t T;
00266         glut::getModelViewMatrix(T);
00267 
00268         // regardless of how far away (or how close) the viewer is, we want the
00269         // "glow" effect to appear to be a constant radius
00270         // we perform some simple geometry calculations so that, even though the
00271         //      glow plane is closer to the viewer and therefore more sensitive
00272         //      to perspective effects, the overall glow always looks like it is
00273         //      an enclosing sphere of constant radius
00274         float beta = 1.17;      // desired radius scaling
00275         float gamma = 1.1;      // distance from sphere center to glow plane
00276         float alpha = beta * (z - gamma * m_radius) / z;
00277 //      DPRINTF("z=%f  beta=%f  gamma=%f  -->  alpha=%f", z, beta, gamma, alpha);
00278 
00279         // midpoint of new polygon
00280         float scale = alpha * m_radius; // half width/height of glow plane
00281         point3d_t mid = -gamma * m_radius * to;
00282 
00283         // set up
00284         pr->nVertices = 4;
00285         pr->textureId = m_textureId;
00286         pr->normal = -to;
00287 
00288         pr->vertex[0] = T * (mid + scale * (-up + right));
00289         pr->u[0] = 0;
00290         pr->v[0] = 0;
00291 
00292         pr->vertex[1] = T * (mid + scale * ( up + right));
00293         pr->u[1] = 0;
00294         pr->v[1] = 1;
00295 
00296         pr->vertex[2] = T * (mid + scale * ( up - right));
00297         pr->u[2] = 1;
00298         pr->v[2] = 1;
00299 
00300         pr->vertex[3] = T * (mid + scale * (-up - right));
00301         pr->u[3] = 1;
00302         pr->v[3] = 0;
00303 }
00304 
00305 
00306 
00307 rect3d_t
00308 Sphere::getBoundingBox
00309 (
00310 void
00311 )
00312 const
00313 throw()
00314 {
00315         rect3d_t r;
00316         r.clear();
00317         r.inflate(m_radius);
00318         return r;
00319 }
00320 
00321 
00322 
00323 ////////////////////////////////////////////////////////////////////////////////
00324 //
00325 //      public API
00326 //
00327 ////////////////////////////////////////////////////////////////////////////////
00328 
00329 smart_ptr<Renderable>
00330 createCoolSphere
00331 (
00332 IN const sphere_init_t& si
00333 )
00334 {
00335         ASSERT(si.isValid(), "invalid sphere initializer");
00336 
00337         smart_ptr<Sphere> local = new Sphere;
00338         ASSERT(local, "out of memory");
00339 
00340         local->initialize(si);
00341 
00342         return local;
00343 }
00344 
00345 
00346 
00347 };      // end of glut namespace
00348