light-path.cpp

Go to the documentation of this file.
00001 /*
00002  * light-path.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  * See light-path.h
00032  */
00033 
00034 // includes --------------------------------------------------------------------
00035 #include "light-path.h"         // always include our own header first!
00036 
00037 #include <math.h>
00038 
00039 #include "perf/perf.h"
00040 
00041 
00042 namespace glut {
00043 
00044 
00045 
00046 static const float s_eps                = 1.0e-6;
00047 
00048 struct sort_seg_t {
00049         int             index;
00050         float           d2;     // distance^2 from viewer
00051 };
00052 
00053 
00054 ////////////////////////////////////////////////////////////////////////////////
00055 //
00056 //      static helper methods
00057 //
00058 ////////////////////////////////////////////////////////////////////////////////
00059 
00060 static void
00061 setSegment
00062 (
00063 IN const point3d_t& p0,         // starting point of ray
00064 IN const point3d_t& p1,         // ending point of ray
00065 IN const matrix4_t& T,          // transform to viewer coordinates
00066 IN int genTextureId,            // generating texture ID
00067 IN int outTextureId,            // destination for offscreen drawing
00068 IN Framebuffer * framebuffer,   // for offscreen drawing
00069 IN float size,                  // size along tangent
00070 IN const glut::render_context_t& rc, // viewer information
00071 OUT poly_request_t * pr         // output vertices here
00072 )
00073 {
00074         ASSERT(genTextureId, "null");
00075         ASSERT(outTextureId, "null");
00076         ASSERT(framebuffer, "null");
00077         ASSERT(size > 0, "bad size: %f", size);
00078         ASSERT(pr, "null");
00079 
00080         p0.dump("  ray p0");
00081         p1.dump("  ray p1");
00082 
00083         const point3d_t& pos = rc.viewer.getPosition();
00084         const point3d_t& facing = rc.viewer.getFacing();
00085 
00086         pos.dump("  viewer position");
00087         facing.dump("  viewer facing");
00088 
00089         point3d_t mid = 0.5 * (p0 + p1);
00090         point3d_t to = mid - pos;       // vector from viewer to midpoint
00091         normalize(to);
00092         mid.dump("  ray midpoint");
00093         to.dump("  from viewer to ray midpoint");
00094 
00095         point3d_t ray = p1 - p0;
00096         float ray2 = dotProduct(ray, ray);
00097         if (ray2 < s_eps) {
00098                 DPRINTF("null ray?");
00099                 return;
00100         }
00101         float rayLength = sqrt(ray2);
00102         DPRINTF("  rayLength = %f", rayLength);
00103         ray = (1.0 / rayLength) * ray;  //  normalize
00104         ray.dump("  ray");
00105 
00106         float delta = dotProduct(ray, to) - 1.0;
00107         if (delta > -s_eps && delta < s_eps) {
00108                 // segment is exactly aligned with facing direction
00109                 DPRINTF("Aligned?");
00110                 return;
00111         }
00112 
00113         // determine the tangent to the ray, given the facing direction
00114         point3d_t tang = crossProduct(ray, to);
00115         normalize(tang);
00116         tang.dump("  tangent");
00117 
00118         // determine normal to plane: ray x tangent
00119         point3d_t norm = crossProduct(ray, tang);
00120         normalize(norm);
00121         norm.dump("  normal");
00122 
00123         // now draw to an offscreen buffer
00124         glMatrixMode(GL_MODELVIEW);
00125         glPushMatrix();
00126         {
00127                 FramebufferContext fc(framebuffer, outTextureId, rc);
00128 
00129                 Viewer viewer2 = rc.viewer;     // same orientation
00130                 point3d_t newPos = mid - 5.0 * rayLength * facing;
00131                 viewer2.setPosition(newPos);
00132                 setOpenGLViewer(viewer2);
00133                 viewer2.getPosition().dump("  temporary viewer position");
00134 
00135                 glEnable(GL_BLEND);
00136                 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
00137                 glClearColor(0.0, 0.0, 0.0, 0.0);
00138                 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
00139 
00140                 // here we use the generating texture
00141                 glEnable(GL_TEXTURE_2D);
00142                 glBindTexture(GL_TEXTURE_2D, genTextureId);
00143                 glNormal3fv(&norm.x);
00144                 glBegin(GL_POLYGON);
00145 
00146                         // top left corner
00147                         point3d_t vtx = p0 - size * ray + size * tang;
00148                         vtx.dump("  temp top left");
00149                         glTexCoord2f(0, 0);
00150                         glVertex3fv(&vtx.x);
00151 
00152                         // bottom left corner
00153                         vtx = p0 - size * ray - size * tang;
00154                         vtx.dump("  temp bottom left");
00155                         glTexCoord2f(0, 1);
00156                         glVertex3fv(&vtx.x);
00157 
00158                         // bottom right corner
00159                         vtx = p1 + size * ray - size * tang;
00160                         vtx.dump("  temp bottom right");
00161                         glTexCoord2f(1, 1);
00162                         glVertex3fv(&vtx.x);
00163 
00164                         // top right corner
00165                         vtx = p1 + size * ray + size * tang;
00166                         vtx.dump("  temp top right");
00167                         glTexCoord2f(1, 0);
00168                         glVertex3fv(&vtx.x);
00169 
00170                 glEnd();
00171 
00172                 glDisable(GL_BLEND);
00173                 glDisable(GL_TEXTURE_2D);
00174         }
00175         glMatrixMode(GL_MODELVIEW);
00176         glPopMatrix();
00177 
00178         // now we set up a request to draw a polygon that is in the viewer's
00179         //   z-plane
00180         const point3d_t& up = rc.viewer.getUp();
00181         point3d_t right = crossProduct(facing, up);
00182         normalize(right);
00183         point3d_t realUp = crossProduct(right, facing);
00184         normalize(realUp);
00185 
00186         right.dump("  viewer right");
00187         realUp.dump("  viewer up");
00188 
00189         // set up output request
00190         pr->nVertices = 4;
00191         pr->textureId = genTextureId;
00192         pr->normal = -facing;
00193 
00194 /*
00195         // draw now!
00196         glEnable(GL_TEXTURE_2D);
00197         glEnable(GL_BLEND);
00198         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
00199         glBindTexture(GL_TEXTURE_2D, genTextureId);
00200         glBegin(GL_POLYGON);
00201 */
00202         // top left corner
00203         point3d_t vtx = mid + 0.5 * rayLength * (realUp - right);
00204         vtx.dump("  top left");
00205 //      glTexCoord2f(0, 0);
00206 //      glVertex3fv(&vtx.x);
00207         pr->vertex[0] = T * vtx;        // transformed vertex
00208         pr->u[0] = 0;
00209         pr->v[0] = 0;
00210 
00211         // bottom left corner
00212         vtx = mid + 0.5 * rayLength * (-realUp - right);
00213         vtx.dump("  bottom left");
00214 //      glTexCoord2f(0, 1);
00215 //      glVertex3fv(&vtx.x);
00216         pr->vertex[1] = T * vtx;
00217         pr->u[1] = 0;
00218         pr->v[1] = 1;
00219 
00220         // bottom right corner
00221         vtx = mid + 0.5 * rayLength * (-realUp + right);
00222         vtx.dump("  bottom right");
00223 //      glTexCoord2f(1, 1);
00224 //      glVertex3fv(&vtx.x);
00225         pr->vertex[2] = T * vtx;
00226         pr->u[2] = 1;
00227         pr->v[2] = 1;
00228 
00229         // top right corner
00230         vtx = mid + 0.5 * rayLength * (realUp + right);
00231         vtx.dump("  top right");
00232 //      glTexCoord2f(1, 0);
00233 //      glVertex3fv(&vtx.x);
00234         pr->vertex[3] = T * vtx;
00235         pr->u[3] = 1;
00236         pr->v[3] = 0;
00237 
00238 //      glEnd();
00239 //      glDisable(GL_TEXTURE_2D);
00240 
00241         for (int i = 0; i < pr->nVertices; ++i) {
00242                 pr->vertex[i].dump("  transformed");
00243         }
00244 }
00245 
00246 
00247 
00248 static int
00249 compareSegments
00250 (
00251 IN const void * p1,
00252 IN const void * p2
00253 )
00254 throw()
00255 {
00256         const sort_seg_t * pss1 = (const sort_seg_t *) p1;
00257         const sort_seg_t * pss2 = (const sort_seg_t *) p2;
00258 
00259         ASSERT(pss1 && pss2, "null");
00260 
00261         if (pss1->d2 > pss2->d2) {
00262                 // sort segment 1 is farther: it should be later in the list
00263                 return +1;
00264         } else if (pss1->d2 < pss2->d2) {
00265                 // sort segment 2 is farther
00266                 return -1;
00267         }
00268 
00269         // must be equidistant!
00270         return 0;
00271 }
00272 
00273 
00274 
00275 ////////////////////////////////////////////////////////////////////////////////
00276 //
00277 //      public API
00278 //
00279 ////////////////////////////////////////////////////////////////////////////////
00280 
00281 void
00282 drawTransparentPath
00283 (
00284 IN const point3d_t * pointArray,
00285 IN int * textureArray,
00286 IN int nSegments,
00287 IN Framebuffer * framebuffer,
00288 IN float size,
00289 IN int textureId,
00290 IN const render_context_t& rc,
00291 IN RenderQueue * rq
00292 )
00293 {
00294         perf::Timer timer("drawTransparentPath");
00295         ASSERT(pointArray, "null");
00296         ASSERT(textureArray, "null");
00297         ASSERT(nSegments > 0, "bad segment count: %d", nSegments);
00298         ASSERT(framebuffer, "null");
00299         ASSERT(size > 0, "bad size: %f", size);
00300         ASSERT(textureId > 0, "Bad texture ID");
00301         ASSERT(rq, "null");
00302 
00303         // get basics for rendering
00304         point3d_t pos = rc.viewer.getPosition();
00305 
00306 //      DPRINTF("Drawing light path");
00307 //      pos.dump("  viewer position");
00308 //      facing.dump("  viewer facing");
00309 
00310         matrix4_t T;
00311         getModelViewMatrix(T);
00312 //      T.dump("  modelview");
00313 
00314         const int s_maxSegments = 64;
00315         if (nSegments > s_maxSegments) {
00316                 DPRINTF("WARNING: support a max of %d segments", s_maxSegments);
00317                 nSegments = s_maxSegments;
00318         }
00319 
00320         // first walk through segments: sort in order of closest to farthest
00321         sort_seg_t array[s_maxSegments];
00322         for (int i = 0; i < nSegments; ++i) {
00323                 point3d_t mid = 0.5 * (pointArray[i] + pointArray[i + 1]);
00324                 sort_seg_t& ss = array[i];
00325                 ss.index = i;
00326                 point3d_t diff = mid - pos; // distance from segement to viewer
00327                 ss.d2 = dotProduct(diff, diff);
00328         }
00329 
00330         // sort from closest to farthest
00331         qsort(array, nSegments, sizeof(sort_seg_t), compareSegments);
00332 
00333         // walk through path segments from farthest to closest
00334         for (int i = 0; i < nSegments; ++i) {
00335                 const sort_seg_t& ss = array[i];
00336                 int idx = ss.index;
00337                 poly_request_t * pr = rq->grabRequestSlot();
00338                 if (!pr)
00339                         return; // can't draw anymore
00340                 setSegment(pointArray[idx], pointArray[idx + 1], T,
00341                     textureId, textureArray[idx], framebuffer, size, rc, pr);
00342         }
00343 }
00344 
00345 
00346 
00347 };      // glut namespace
00348