glcanvas.cc

Go to the documentation of this file.
00001 
00003 
00004 //
00005 // Copyright (c) 1999-2004 California Institute of Technology
00006 // Copyright (c) 2004-2007 University of Southern California
00007 // Rob Peters <rjpeters at usc dot edu>
00008 //
00009 // created: Mon Dec  6 20:28:36 1999
00010 // commit: $Id: glcanvas.cc 10065 2007-04-12 05:54:56Z rjpeters $
00011 // $HeadURL: file:///lab/rjpeters/svnrepo/code/trunk/groovx/src/gfx/glcanvas.cc $
00012 //
00013 // --------------------------------------------------------------------
00014 //
00015 // This file is part of GroovX.
00016 //   [http://ilab.usc.edu/rjpeters/groovx/]
00017 //
00018 // GroovX is free software; you can redistribute it and/or modify it
00019 // under the terms of the GNU General Public License as published by
00020 // the Free Software Foundation; either version 2 of the License, or
00021 // (at your option) any later version.
00022 //
00023 // GroovX is distributed in the hope that it will be useful, but
00024 // WITHOUT ANY WARRANTY; without even the implied warranty of
00025 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00026 // General Public License for more details.
00027 //
00028 // You should have received a copy of the GNU General Public License
00029 // along with GroovX; if not, write to the Free Software Foundation,
00030 // Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
00031 //
00033 
00034 #ifndef GROOVX_GFX_GLCANVAS_CC_UTC20050626084024_DEFINED
00035 #define GROOVX_GFX_GLCANVAS_CC_UTC20050626084024_DEFINED
00036 
00037 #include "glcanvas.h"
00038 
00039 #include "geom/projection.h"
00040 #include "geom/rect.h"
00041 #include "geom/txform.h"
00042 #include "geom/vec2.h"
00043 #include "geom/vec3.h"
00044 
00045 #include "gfx/glwindowinterface.h"
00046 #include "gfx/glxopts.h"
00047 #include "gfx/gxrasterfont.h"
00048 #include "gfx/gxvectorfont.h"
00049 #include "gfx/rgbacolor.h"
00050 
00051 #include "media/bmapdata.h"
00052 
00053 #include "rutz/error.h"
00054 #include "rutz/sfmt.h"
00055 #include "rutz/shared_ptr.h"
00056 
00057 #include <vector>
00058 
00059 #if defined(GVX_GL_PLATFORM_GLX)
00060 #  include <GL/gl.h>
00061 #  include <GL/glu.h>
00062 #elif defined(GVX_GL_PLATFORM_AGL)
00063 #  include <AGL/gl.h>
00064 #  include <AGL/glu.h>
00065 #endif
00066 
00067 #include "rutz/trace.h"
00068 #include "rutz/debug.h"
00069 GVX_DBG_REGISTER
00070 
00071 using geom::recti;
00072 using geom::rectd;
00073 using geom::txform;
00074 using geom::vec2i;
00075 using geom::vec2d;
00076 using geom::vec3i;
00077 using geom::vec3d;
00078 
00079 using rutz::shared_ptr;
00080 
00081 namespace
00082 {
00083   nub::soft_ref<GLCanvas> theCurrentCanvas;
00084 
00085   GLint attribStackDepth()
00086   {
00087     GLint d = -1;
00088     glGetIntegerv(GL_ATTRIB_STACK_DEPTH, &d);
00089     return d;
00090   }
00091 
00092   bool rasterPositionValid() throw()
00093   {
00094     GLboolean value = GL_FALSE;
00095     glGetBooleanv(GL_CURRENT_RASTER_POSITION_VALID, &value);
00096     return (value == GL_TRUE);
00097   }
00098 
00099   geom::span<double> rawGepthRange()
00100   {
00101     GVX_TRACE("<glcanvas.cc>::depthRange");
00102     GLdouble vals[2];
00103     glGetDoublev(GL_DEPTH_RANGE, &vals[0]);
00104     return geom::span<double>(vals[0], vals[1]);
00105   }
00106 
00107   txform rawGetModelview()
00108   {
00109     GVX_TRACE("<glcanvas.cc>::rawGetModelview");
00110     GLdouble m[16];
00111     glGetDoublev(GL_MODELVIEW_MATRIX, &m[0]);
00112     return txform::copy_of(&m[0]);
00113   }
00114 
00115   txform rawGetProjection()
00116   {
00117     GVX_TRACE("<glcanvas.cc>::rawGetProjection");
00118     GLdouble m[16];
00119     glGetDoublev(GL_PROJECTION_MATRIX, &m[0]);
00120     return txform::copy_of(&m[0]);
00121   }
00122 
00123   vec3d unproject1(const txform& modelview,
00124                    const txform& projection,
00125                    const recti& viewport,
00126                    const vec3d& screen)
00127   {
00128     GVX_TRACE("<glcanvas.cc>::unproject1");
00129 
00130     const GLint v[4] = { viewport.left(), viewport.bottom(),
00131                          viewport.width(), viewport.height() };
00132 
00133     vec3d world_pos;
00134 
00135     GLint status =
00136       gluUnProject(screen.x(), screen.y(), screen.z(),
00137                    modelview.col_major_data(),
00138                    projection.col_major_data(),
00139                    &v[0],
00140                    &world_pos.x(), &world_pos.y(), &world_pos.z());
00141 
00142     dbg_eval_nl(3, status);
00143 
00144     if (status == GL_FALSE)
00145       throw rutz::error("gluUnProject error", SRC_POS);
00146 
00147     return world_pos;
00148   }
00149 
00150   vec3d project1(const txform& modelview,
00151                  const txform& projection,
00152                  const recti& viewport,
00153                  const vec3d& world_pos)
00154   {
00155     GVX_TRACE("<glcanvas.cc>::project1");
00156 
00157     const GLint v[4] = { viewport.left(), viewport.bottom(),
00158                          viewport.width(), viewport.height() };
00159 
00160     vec3d screen_pos;
00161 
00162     GLint status =
00163       gluProject(world_pos.x(), world_pos.y(), world_pos.z(),
00164                  modelview.col_major_data(),
00165                  projection.col_major_data(),
00166                  &v[0],
00167                  &screen_pos.x(), &screen_pos.y(), &screen_pos.z());
00168 
00169     dbg_eval_nl(3, status);
00170 
00171     if (status == GL_FALSE)
00172       throw rutz::error("GLCanvas::screenFromWorld3(): gluProject error",
00173                         SRC_POS);
00174 
00175     return screen_pos;
00176   }
00177 }
00178 
00179 class GLCanvas::Impl
00180 {
00181 public:
00182   Impl(shared_ptr<GlxOpts> opts_, shared_ptr<GlWindowInterface> glx_) :
00183     opts(opts_),
00184     glx(glx_),
00185     modelviewCache(),
00186     projectionCache(),
00187     viewportCache(),
00188     depthrangeCache(0.0, 1.0)
00189   {
00190     modelviewCache.push_back(txform::identity());
00191     projectionCache.push_back(txform::identity());
00192 
00193     // The following glLoadIdentity() calls are implied here; we
00194     // assume that the modelview and projection matrices are both
00195     // intialized to the identity matrix upon program
00196     // startup. However, we CANNOT safely call them here, since the GL
00197     // context may not yet be current. If it turns out that we do need
00198     // this explicit initialization, then it probably should go in
00199     // GlxWrapper::makeCurrent() under a flag that checks whether it's
00200     // the first time.
00201 #if 0
00202     glMatrixMode(GL_PROJECTION); glLoadIdentity();
00203     glMatrixMode(GL_MODELVIEW); glLoadIdentity();
00204 #endif
00205 
00206     dbg_dump(4, modelviewCache.back());
00207   }
00208 
00209   const txform& getModelview() const
00210   {
00211     GVX_TRACE("GLCanvas::getModelview");
00212     GVX_ASSERT(modelviewCache.size() > 0);
00213     if (GVX_DBG_LEVEL() >= 8)
00214       {
00215         const txform ref = rawGetModelview();
00216         const double sse = ref.debug_sse(modelviewCache.back());
00217         if (sse > 1e-10)
00218           {
00219             dbg_eval_nl(0, sse);
00220             dbg_dump(0, ref);
00221             dbg_dump(0, modelviewCache.back());
00222             GVX_PANIC("numerical error in modelview matrix cache");
00223           }
00224       }
00225     return modelviewCache.back();
00226   }
00227 
00228   const txform& getProjection() const
00229   {
00230     GVX_TRACE("GLCanvas::getProjection");
00231     GVX_ASSERT(projectionCache.size() > 0);
00232     if (GVX_DBG_LEVEL() >= 8)
00233       {
00234         const txform ref = rawGetProjection();
00235         const double sse = ref.debug_sse(projectionCache.back());
00236         if (sse > 1e-10)
00237           {
00238             dbg_eval_nl(0, sse);
00239             dbg_dump(0, ref);
00240             dbg_dump(0, projectionCache.back());
00241             GVX_PANIC("numerical error in projection matrix cache");
00242           }
00243       }
00244     return projectionCache.back();
00245   }
00246 
00247   shared_ptr<GlxOpts>           opts;
00248   shared_ptr<GlWindowInterface> glx;
00249   std::vector<txform>           modelviewCache;
00250   std::vector<txform>           projectionCache;
00251   recti                         viewportCache;
00252   const geom::span<double>      depthrangeCache;
00253 };
00254 
00255 GLCanvas::GLCanvas(shared_ptr<GlxOpts> opts,
00256                    shared_ptr<GlWindowInterface> glx) :
00257   rep(new Impl(opts, glx))
00258 {
00259 GVX_TRACE("GLCanvas::GLCanvas");
00260 }
00261 
00262 GLCanvas* GLCanvas::make(shared_ptr<GlxOpts> opts,
00263                          shared_ptr<GlWindowInterface> glx)
00264 {
00265 GVX_TRACE("GLCanvas::make");
00266   return new GLCanvas(opts, glx);
00267 }
00268 
00269 GLCanvas::~GLCanvas() throw()
00270 {
00271 GVX_TRACE("GLCanvas::~GLCanvas");
00272   delete rep;
00273   rep = 0;
00274 }
00275 
00276 void GLCanvas::makeCurrent()
00277 {
00278 GVX_TRACE("GLCanvas::makeCurrent");
00279   rep->glx->makeCurrent();
00280   if (!rep->opts->doubleFlag && this->isDoubleBuffered())
00281     {
00282       // We requested single buffering but had to accept a double
00283       // buffered visual. Set the GL draw buffer to be the front
00284       // buffer to simulate single buffering.
00285       this->drawBufferFront();
00286     }
00287   theCurrentCanvas = nub::soft_ref<GLCanvas>(this);
00288 }
00289 
00290 nub::soft_ref<GLCanvas> GLCanvas::getCurrent()
00291 {
00292 GVX_TRACE("GLCanvas::getCurrent");
00293   return theCurrentCanvas;
00294 }
00295 
00296 void GLCanvas::drawBufferFront() throw()
00297 {
00298 GVX_TRACE("GLCanvas::drawBufferFront");
00299   glDrawBuffer(GL_FRONT);
00300 }
00301 
00302 void GLCanvas::drawBufferBack() throw()
00303 {
00304 GVX_TRACE("GLCanvas::drawBufferBack");
00305   glDrawBuffer(GL_BACK);
00306 }
00307 
00308 vec3d GLCanvas::screenFromWorld3(const vec3d& world_pos) const
00309 {
00310 GVX_TRACE("GLCanvas::screenFromWorld3");
00311 
00312   const txform m = rep->getModelview();
00313   const txform p = rep->getProjection();
00314   const recti v = this->getScreenViewport();
00315 
00316   const vec3d screen_pos = geom::project(m, p, v, world_pos);
00317 
00318   dbg_dump(3, world_pos);
00319   dbg_dump(3, screen_pos);
00320 
00321   if (GVX_DBG_LEVEL() >= 8)
00322     {
00323       const vec3d screen_pos1 = project1(m, p, v, world_pos);
00324       const vec3d diff = screen_pos - screen_pos1;
00325 
00326       if (diff.length() > 1e-10)
00327         {
00328           dbg_eval_nl(0, diff.length());
00329           dbg_dump(0, p);
00330           dbg_dump(0, m);
00331           dbg_dump(0, screen_pos1);
00332           dbg_dump(0, screen_pos);
00333           GVX_PANIC("numerical error during world->screen projection");
00334         }
00335     }
00336 
00337   return screen_pos;
00338 }
00339 
00340 vec3d GLCanvas::worldFromScreen3(const vec3d& screen_pos) const
00341 {
00342 GVX_TRACE("GLCanvas::worldFromScreen3");
00343 
00344   dbg_dump(3, screen_pos);
00345 
00346   const txform m = rep->getModelview();
00347   const txform p = rep->getProjection();
00348   const recti v = getScreenViewport();
00349 
00350   dbg_dump(5, m);
00351   dbg_dump(5, p);
00352 
00353   const vec3d world2 = geom::unproject(m, p, v, screen_pos);
00354 
00355   if (GVX_DBG_LEVEL() >= 8)
00356     {
00357       const vec3d world1 = unproject1(m, p, v, screen_pos);
00358       const vec3d diff = world2 - world1;
00359 
00360       if (diff.length() > 1e-10)
00361         {
00362           dbg_eval_nl(0, diff.length());
00363           dbg_dump(0, p);
00364           dbg_dump(0, m);
00365           dbg_dump(0, world1);
00366           dbg_dump(0, world2);
00367           GVX_PANIC("numerical error during screen->world reverse projection");
00368         }
00369     }
00370 
00371   return world2;
00372 }
00373 
00374 
00375 recti GLCanvas::getScreenViewport() const
00376 {
00377 GVX_TRACE("GLCanvas::getScreenViewport");
00378 
00379   return rep->viewportCache;
00380 }
00381 
00382 
00383 bool GLCanvas::isRgba() const
00384 {
00385 GVX_TRACE("GLCanvas::isRgba");
00386 
00387   return rep->opts->rgbaFlag;
00388 }
00389 
00390 bool GLCanvas::isColorIndex() const
00391 {
00392 GVX_TRACE("GLCanvas::isColorIndex");
00393 
00394   return !(rep->opts->rgbaFlag);
00395 }
00396 
00397 bool GLCanvas::isDoubleBuffered() const
00398 {
00399 GVX_TRACE("GLCanvas::isDoubleBuffered");
00400 
00401   return rep->glx->isDoubleBuffered();
00402 }
00403 
00404 unsigned int GLCanvas::bitsPerPixel() const
00405 {
00406 GVX_TRACE("GLCanvas::bitsPerPixel");
00407 
00408   return rep->glx->bitsPerPixel();
00409 }
00410 
00411 void GLCanvas::throwIfError(const char* where,
00412                             const rutz::file_pos& pos) const
00413 {
00414 GVX_TRACE("GLCanvas::throwIfError");
00415   GLenum status = glGetError();
00416   if (status != GL_NO_ERROR)
00417     {
00418       const char* msg =
00419         reinterpret_cast<const char*>(gluErrorString(status));
00420       throw rutz::error(rutz::sfmt("GL error: %s %s", msg, where), pos);
00421     }
00422 }
00423 
00424 void GLCanvas::pushAttribs(const char* /*comment*/)
00425 {
00426 GVX_TRACE("GLCanvas::pushAttribs");
00427   glPushAttrib(GL_ALL_ATTRIB_BITS);
00428   dbg_eval_nl(3, attribStackDepth());
00429 }
00430 
00431 void GLCanvas::popAttribs()
00432 {
00433 GVX_TRACE("GLCanvas::popAttribs");
00434   glPopAttrib();
00435   dbg_eval_nl(3, attribStackDepth());
00436 }
00437 
00438 void GLCanvas::drawOnFrontBuffer()
00439 {
00440 GVX_TRACE("GLCanvas::drawOnFrontBuffer");
00441   glDrawBuffer(GL_FRONT);
00442 }
00443 
00444 void GLCanvas::drawOnBackBuffer()
00445 {
00446 GVX_TRACE("GLCanvas::drawOnBackBuffer");
00447   glDrawBuffer(GL_BACK);
00448 }
00449 
00450 void GLCanvas::setColor(const Gfx::RgbaColor& rgba)
00451 {
00452 GVX_TRACE("GLCanvas::setColor");
00453   glColor4dv(rgba.data());
00454 }
00455 
00456 void GLCanvas::setClearColor(const Gfx::RgbaColor& rgba)
00457 {
00458 GVX_TRACE("GLCanvas::setClearColor");
00459   glClearColor(rgba.r(), rgba.g(), rgba.b(), rgba.a());
00460 }
00461 
00462 void GLCanvas::setColorIndex(unsigned int index)
00463 {
00464 GVX_TRACE("GLCanvas::setColorIndex");
00465   glIndexi(index);
00466 }
00467 
00468 void GLCanvas::setClearColorIndex(unsigned int index)
00469 {
00470 GVX_TRACE("GLCanvas::setClearColorIndex");
00471   glClearIndex(index);
00472 }
00473 
00474 void GLCanvas::swapForeBack()
00475 {
00476 GVX_TRACE("GLCanvas::swapForeBack");
00477   if ( this->isRgba() )
00478     {
00479       GLdouble foreground[4];
00480       GLdouble background[4];
00481       glGetDoublev(GL_CURRENT_COLOR, &foreground[0]);
00482       glGetDoublev(GL_COLOR_CLEAR_VALUE, &background[0]);
00483 
00484       glColor4d(background[0],
00485                 background[1],
00486                 background[2],
00487                 foreground[3]); // <-- note we keep the foreground alpha value
00488 
00489       glClearColor(foreground[0],
00490                    foreground[1],
00491                    foreground[2],
00492                    background[3]); // <-- note we keep the background alpha value
00493     }
00494   else
00495     {
00496       GLint foreground, background;
00497       glGetIntegerv(GL_CURRENT_INDEX, &foreground);
00498       glGetIntegerv(GL_INDEX_CLEAR_VALUE, &background);
00499       glIndexi(background);
00500       glClearIndex(foreground);
00501     }
00502 }
00503 
00504 void GLCanvas::setPolygonFill(bool on)
00505 {
00506 GVX_TRACE("GLCanvas::setPolygonFill");
00507 
00508   if (on)
00509     {
00510       glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
00511     }
00512   else
00513     {
00514       glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
00515     }
00516 }
00517 
00518 void GLCanvas::setPointSize(double size)
00519 {
00520 GVX_TRACE("GLCanvas::setPointSize");
00521 
00522   glPointSize(size);
00523 }
00524 
00525 void GLCanvas::setLineWidth(double width)
00526 {
00527 GVX_TRACE("GLCanvas::setLineWidth");
00528   glLineWidth(width);
00529 }
00530 
00531 void GLCanvas::setLineStipple(unsigned short bit_pattern)
00532 {
00533 GVX_TRACE("GLCanvas::setLineStipple");
00534   glEnable(GL_LINE_STIPPLE);
00535   glLineStipple(1, bit_pattern);
00536 }
00537 
00538 void GLCanvas::enableAntialiasing()
00539 {
00540 GVX_TRACE("GLCanvas::enableAntialiasing");
00541 
00542   if (isRgba()) // antialiasing does not work well except in RGBA mode
00543     {
00544       glEnable(GL_BLEND); // blend incoming RGBA values with old RGBA values
00545 
00546       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // use transparency
00547 
00548       // use anti-aliasing for lines (but not polygons):
00549       glEnable(GL_LINE_SMOOTH);
00550     }
00551 }
00552 
00553 
00554 
00555 void GLCanvas::viewport(int x, int y, int w, int h)
00556 {
00557 GVX_TRACE("GLCanvas::viewport");
00558 
00559   glViewport(x, y, w, h);
00560   dbg_eval(2, x); dbg_eval(2, y); dbg_eval(2, w); dbg_eval_nl(2, h);
00561   if (GVX_DBG_LEVEL() >= 8)
00562     {
00563       GLint viewport[4];
00564       glGetIntegerv(GL_VIEWPORT, viewport);
00565       dbg_eval(2, viewport[0]);
00566       dbg_eval(2, viewport[1]);
00567       dbg_eval(2, viewport[2]);
00568       dbg_eval_nl(2, viewport[3]);
00569     }
00570 
00571   rep->viewportCache = recti::lbwh(x, y, w, h);
00572 }
00573 
00574 void GLCanvas::orthographic(const rectd& bounds,
00575                             double zNear, double zFar)
00576 {
00577 GVX_TRACE("GLCanvas::orthographic");
00578 
00579   dbg_eval(3, bounds.left()); dbg_eval_nl(3, bounds.right());
00580   dbg_eval(3, bounds.bottom()); dbg_eval_nl(3, bounds.top());
00581   dbg_eval(3, zNear); dbg_eval_nl(3, zFar);
00582 
00583   glMatrixMode(GL_PROJECTION);
00584   glLoadIdentity();
00585   glOrtho(bounds.left(), bounds.right(),
00586           bounds.bottom(), bounds.top(),
00587           zNear, zFar);
00588   rep->projectionCache.back() = txform::orthographic(bounds, zNear, zFar);
00589   glMatrixMode(GL_MODELVIEW);
00590 }
00591 
00592 void GLCanvas::perspective(double fovy, double aspect,
00593                            double zNear, double zFar)
00594 {
00595 GVX_TRACE("GLCanvas::perspective");
00596 
00597   glMatrixMode(GL_PROJECTION);
00598   glLoadIdentity();
00599   gluPerspective(fovy, aspect, zNear, zFar);
00600   rep->projectionCache.back() = rawGetProjection();
00601   glMatrixMode(GL_MODELVIEW);
00602 }
00603 
00604 void GLCanvas::pushMatrix(const char* /*comment*/)
00605 {
00606 GVX_TRACE("GLCanvas::pushMatrix");
00607   glMatrixMode(GL_MODELVIEW);
00608   glPushMatrix();
00609   rep->modelviewCache.push_back(rep->modelviewCache.back());
00610   dbg_dump(4, rep->modelviewCache.back());
00611 }
00612 
00613 void GLCanvas::popMatrix()
00614 {
00615 GVX_TRACE("GLCanvas::popMatrix");
00616   glMatrixMode(GL_MODELVIEW);
00617   glPopMatrix();
00618   GVX_ASSERT(rep->modelviewCache.size() > 0);
00619   rep->modelviewCache.pop_back();
00620   dbg_dump(4, rep->modelviewCache.back());
00621 }
00622 
00623 void GLCanvas::translate(const vec3d& v)
00624 {
00625 GVX_TRACE("GLCanvas::translate");
00626   glTranslated(v.x(), v.y(), v.z());
00627   rep->modelviewCache.back().translate(v);
00628   dbg_dump(4, rep->modelviewCache.back());
00629 }
00630 
00631 void GLCanvas::scale(const vec3d& v)
00632 {
00633 GVX_TRACE("GLCanvas::scale");
00634   if (v.x() == 0.0)
00635     {
00636       throw rutz::error("invalid x scaling factor", SRC_POS);
00637     }
00638   if (v.y() == 0.0)
00639     {
00640       throw rutz::error("invalid y scaling factor", SRC_POS);
00641     }
00642   if (v.z() == 0.0)
00643     {
00644       throw rutz::error("invalid z scaling factor", SRC_POS);
00645     }
00646   glScaled(v.x(), v.y(), v.z());
00647   rep->modelviewCache.back().scale(v);
00648   dbg_dump(4, rep->modelviewCache.back());
00649 }
00650 
00651 void GLCanvas::rotate(const vec3d& v, double angle_in_degrees)
00652 {
00653 GVX_TRACE("GLCanvas::rotate");
00654   glRotated(angle_in_degrees, v.x(), v.y(), v.z());
00655   rep->modelviewCache.back().rotate(v, angle_in_degrees);
00656   dbg_dump(4, rep->modelviewCache.back());
00657 }
00658 
00659 void GLCanvas::transform(const geom::txform& tx)
00660 {
00661 GVX_TRACE("GLCanvas::transform");
00662   glMultMatrixd(tx.col_major_data());
00663   rep->modelviewCache.back().transform(tx);
00664   dbg_dump(4, rep->modelviewCache.back());
00665 }
00666 
00667 void GLCanvas::loadMatrix(const geom::txform& tx)
00668 {
00669 GVX_TRACE("GLCanvas::loadMatrix");
00670   glLoadMatrixd(tx.col_major_data());
00671   rep->modelviewCache.back() = tx;
00672   dbg_dump(4, rep->modelviewCache.back());
00673 }
00674 
00675 void GLCanvas::rasterPos(const geom::vec3<double>& world_pos)
00676 {
00677 GVX_TRACE("GLCanvas::rasterPos");
00678 
00679   const rectd viewport = rectd(getScreenViewport());
00680 
00681   const geom::span<double> depth = rep->depthrangeCache;
00682 
00683   const vec3d screen_pos = screenFromWorld3(world_pos);
00684 
00685   dbg_dump(3, world_pos);
00686   dbg_dump(3, screen_pos);
00687 
00688   if (viewport.contains(screen_pos.as_vec2()) &&
00689       depth.contains(screen_pos.z()))
00690     {
00691       GVX_TRACE("GLCanvas::rasterPos::branch-1");
00692       glRasterPos3d(world_pos.x(), world_pos.y(), world_pos.z());
00693     }
00694   else
00695     {
00696       GVX_TRACE("GLCanvas::rasterPos::branch-2");
00697       // OK... in this case, our desired raster position actually
00698       // falls outside the onscreen viewport. If we just called
00699       // glRasterPos() with that position, it would recognize it as an
00700       // invalid point and then subsequent glDrawPixels() calls would
00701       // fail. To trick OpenGL in using the position we want, we first
00702       // do a glRasterPos() to some valid position -- in this case, we
00703       // pick a point near at the center of the viewport and depth
00704       // range. Then we do a glBitmap() call whose only purpose is to
00705       // use the "xmove" and "ymove" arguments to adjust the raster
00706       // position to the desired location.
00707       const vec3d safe_screen = vec3d(viewport.center_x(),
00708                                       viewport.center_y(),
00709                                       depth.center());
00710       const vec3d safe_world = worldFromScreen3(safe_screen);
00711 
00712       dbg_dump(3, safe_screen);
00713       dbg_dump(3, safe_world);
00714 
00715       glRasterPos3d(safe_world.x(), safe_world.y(), safe_world.z());
00716 
00717       glBitmap(0, 0, 0.0f, 0.0f,
00718                screen_pos.x()-safe_screen.x(),
00719                screen_pos.y()-safe_screen.y(),
00720                static_cast<const GLubyte*>(0));
00721     }
00722 
00723   if (GVX_DBG_LEVEL() >= 8)
00724     {
00725       // This operation is slow because it involves a glGet*()
00726       GVX_POSTCONDITION(rasterPositionValid());
00727     }
00728 }
00729 
00730 void GLCanvas::drawPixels(const media::bmap_data& data,
00731                           const vec3d& world_pos,
00732                           const vec2d& zoom)
00733 {
00734 GVX_TRACE("GLCanvas::drawPixels");
00735 
00736   data.set_row_order(media::bmap_data::BOTTOM_FIRST);
00737 
00738   rasterPos(world_pos);
00739 
00740   glPixelZoom(zoom.x(), zoom.y());
00741 
00742   glPixelStorei(GL_UNPACK_ALIGNMENT, data.byte_alignment());
00743 
00744   if (data.bits_per_pixel() == 32)
00745     {
00746       GVX_TRACE("GLCanvas::drawPixels[32]");
00747       glDrawPixels(data.width(), data.height(), GL_RGBA, GL_UNSIGNED_BYTE,
00748                    static_cast<GLvoid*>(data.bytes_ptr()));
00749     }
00750   else if (data.bits_per_pixel() == 24)
00751     {
00752       GVX_TRACE("GLCanvas::drawPixels[24]");
00753       glDrawPixels(data.width(), data.height(), GL_RGB, GL_UNSIGNED_BYTE,
00754                    static_cast<GLvoid*>(data.bytes_ptr()));
00755     }
00756   else if (data.bits_per_pixel() == 8)
00757     {
00758       GVX_TRACE("GLCanvas::drawPixels[8]");
00759       if (isRgba())
00760         {
00761           glDrawPixels(data.width(), data.height(),
00762                        GL_LUMINANCE, GL_UNSIGNED_BYTE,
00763                        static_cast<GLvoid*>(data.bytes_ptr()));
00764         }
00765       else
00766         {
00767           glDrawPixels(data.width(), data.height(),
00768                        GL_COLOR_INDEX, GL_UNSIGNED_BYTE,
00769                        static_cast<GLvoid*>(data.bytes_ptr()));
00770         }
00771     }
00772   else if (data.bits_per_pixel() == 1)
00773     {
00774       GVX_TRACE("GLCanvas::drawPixels[1]");
00775 
00776 #if 0
00777       if (isRgba())
00778         {
00779           GLfloat simplemap[] = {0.0, 1.0};
00780 
00781           glPixelMapfv(GL_PIXEL_MAP_I_TO_R, 2, simplemap);
00782           glPixelMapfv(GL_PIXEL_MAP_I_TO_G, 2, simplemap);
00783           glPixelMapfv(GL_PIXEL_MAP_I_TO_B, 2, simplemap);
00784           glPixelMapfv(GL_PIXEL_MAP_I_TO_A, 2, simplemap);
00785         }
00786 
00787       glDrawPixels(data.width(), data.height(), GL_COLOR_INDEX,
00788                    GL_BITMAP, static_cast<GLvoid*>(data.bytes_ptr()));
00789 #else
00790 
00791       // for nvidia driver; it's much faster (~125x faster) to first
00792       // unpack the 1-bit-per-pixel bitmap into an 8-bits-per-pixel
00793       // image before calling glDrawPixels()
00794 
00795       media::bmap_data unpacked(data.size(), 8, 1);
00796 
00797       const unsigned char* sptr = data.bytes_ptr();
00798       const unsigned char* sstop = sptr + data.byte_count();
00799       unsigned char* dptr = unpacked.bytes_ptr();
00800       unsigned char* dstop = unpacked.bytes_ptr() + unpacked.byte_count();
00801 
00802       while (sptr != sstop)
00803         {
00804           GVX_ASSERT((dptr+7) < dstop);
00805 
00806           *dptr++ = ((*sptr) & (1 << 7)) ? 255 : 0;
00807           *dptr++ = ((*sptr) & (1 << 6)) ? 255 : 0;
00808           *dptr++ = ((*sptr) & (1 << 5)) ? 255 : 0;
00809           *dptr++ = ((*sptr) & (1 << 4)) ? 255 : 0;
00810           *dptr++ = ((*sptr) & (1 << 3)) ? 255 : 0;
00811           *dptr++ = ((*sptr) & (1 << 2)) ? 255 : 0;
00812           *dptr++ = ((*sptr) & (1 << 1)) ? 255 : 0;
00813           *dptr++ = ((*sptr) & (1 << 0)) ? 255 : 0;
00814 
00815           ++sptr;
00816         }
00817 
00818       glPixelStorei(GL_UNPACK_ALIGNMENT, unpacked.byte_alignment());
00819 
00820       glDrawPixels(unpacked.width(), unpacked.height(),
00821                    GL_LUMINANCE, GL_UNSIGNED_BYTE,
00822                    static_cast<GLvoid*>(unpacked.bytes_ptr()));
00823 #endif
00824     }
00825 }
00826 
00827 void GLCanvas::drawBitmap(const media::bmap_data& data,
00828                           const vec3d& world_pos)
00829 {
00830 GVX_TRACE("GLCanvas::drawBitmap");
00831 
00832   data.set_row_order(media::bmap_data::BOTTOM_FIRST);
00833 
00834   rasterPos(world_pos);
00835 
00836   glBitmap(data.width(), data.height(), 0.0, 0.0, 0.0, 0.0,
00837            static_cast<GLubyte*>(data.bytes_ptr()));
00838 }
00839 
00840 void GLCanvas::grabPixels(const recti& bounds,
00841                           media::bmap_data& data_out)
00842 {
00843 GVX_TRACE("GLCanvas::grabPixels");
00844 
00845   const int pixel_alignment = 1;
00846 
00847   // NOTE: we can't just use GLCanvas::bitsPerPixel() here, since that
00848   // won't work in the case of a 16-bit color buffer; in that case, we
00849   // are still in RGBA mode, so glReadPixels() will return one byte
00850   // per color component (i.e. 24 bits per pixel) regardless of the
00851   // actual color buffer depth.
00852   const int bmap_bits_per_pixel = isRgba() ? 24 : 8;
00853 
00854   media::bmap_data new_data(bounds.size(),
00855                             bmap_bits_per_pixel, pixel_alignment);
00856 
00857   glPixelStorei(GL_PACK_ALIGNMENT, pixel_alignment);
00858 
00859   glPushAttrib(GL_PIXEL_MODE_BIT);
00860   {
00861     glReadBuffer(GL_FRONT);
00862     glReadPixels(bounds.left(), bounds.bottom(),
00863                  bounds.width(), bounds.height(),
00864                  (isRgba() ? GL_RGB : GL_COLOR_INDEX),
00865                  GL_UNSIGNED_BYTE, new_data.bytes_ptr());
00866   }
00867   glPopAttrib();
00868 
00869   new_data.specify_row_order(media::bmap_data::BOTTOM_FIRST);
00870 
00871   data_out.swap(new_data);
00872 }
00873 
00874 void GLCanvas::clearColorBuffer()
00875 {
00876 GVX_TRACE("GLCanvas::clearColorBuffer");
00877   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
00878 }
00879 
00880 void GLCanvas::clearColorBuffer(const recti& screen_rect)
00881 {
00882 GVX_TRACE("GLCanvas::clearColorBuffer(geom::recti)");
00883 
00884   glPushAttrib(GL_SCISSOR_BIT);
00885   {
00886     glEnable(GL_SCISSOR_TEST);
00887 
00888     glScissor(screen_rect.left(), screen_rect.bottom(),
00889               screen_rect.width(), screen_rect.height());
00890 
00891     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
00892     glDisable(GL_SCISSOR_TEST);
00893   }
00894   glPopAttrib();
00895 }
00896 
00897 void GLCanvas::drawRect(const rectd& rect)
00898 {
00899 GVX_TRACE("GLCanvas::drawRect");
00900 
00901   glRectd(rect.left(),
00902           rect.bottom(),
00903           rect.right(),
00904           rect.top());
00905 }
00906 
00907 void GLCanvas::drawCircle(double inner_radius, double outer_radius,
00908                           bool fill,
00909                           unsigned int slices, unsigned int loops)
00910 {
00911 GVX_TRACE("GLCanvas::drawCircle");
00912 
00913   GLUquadricObj* qobj = gluNewQuadric();
00914 
00915   if (qobj == 0)
00916     throw rutz::error("couldn't allocate GLUquadric object", SRC_POS);
00917 
00918   gluQuadricDrawStyle(qobj, fill ? GLU_FILL : GLU_SILHOUETTE);
00919   gluDisk(qobj, inner_radius, outer_radius, slices, loops);
00920   gluDeleteQuadric(qobj);
00921 }
00922 
00923 void GLCanvas::drawCylinder(double base_radius, double top_radius,
00924                             double height, int slices, int stacks,
00925                             bool fill)
00926 {
00927 GVX_TRACE("GLCanvas::drawCylinder");
00928 
00929   GLUquadric* qobj = gluNewQuadric();
00930 
00931   if (qobj == 0)
00932     throw rutz::error("couldn't allocate GLUquadric object", SRC_POS);
00933 
00934   gluQuadricDrawStyle(qobj, fill ? GLU_FILL : GLU_LINE);
00935   gluCylinder(qobj, base_radius, top_radius, height, slices, stacks);
00936   gluDeleteQuadric(qobj);
00937 }
00938 
00939 void GLCanvas::drawSphere(double radius, int slices, int stacks,
00940                           bool fill)
00941 {
00942 GVX_TRACE("GLCanvas::drawSphere");
00943 
00944   GLUquadric* qobj = gluNewQuadric();
00945 
00946   if (qobj == 0)
00947     throw rutz::error("couldn't allocate GLUquadric object", SRC_POS);
00948 
00949   gluQuadricDrawStyle(qobj, fill ? GLU_FILL : GLU_LINE);
00950   gluSphere(qobj, radius, slices, stacks);
00951   gluDeleteQuadric(qobj);
00952 }
00953 
00954 void GLCanvas::drawBezier4(const vec3d& p1,
00955                            const vec3d& p2,
00956                            const vec3d& p3,
00957                            const vec3d& p4,
00958                            unsigned int subdivisions)
00959 {
00960 GVX_TRACE("GLCanvas::drawBezier4");
00961 
00962 #if 1
00963   // Use the generic version, since it seems to actually be faster than the
00964   // glEvalMesh() approach anyway
00965   Canvas::drawBezier4(p1, p2, p3, p4, subdivisions);
00966 
00967 #else
00968 
00969   vec3d points[] =
00970   {
00971     p1, p2, p3, p4
00972   };
00973 
00974   glEnable(GL_MAP1_VERTEX_3);
00975   glMap1d(GL_MAP1_VERTEX_3, 0.0, 1.0, 3, 4, points[0].data());
00976 
00977   glMapGrid1d(subdivisions, 0.0, 1.0);
00978   glEvalMesh1(GL_LINE, 0, subdivisions);
00979 #endif
00980 }
00981 
00982 void GLCanvas::drawBezierFill4(const vec3d& center,
00983                                const vec3d& p1,
00984                                const vec3d& p2,
00985                                const vec3d& p3,
00986                                const vec3d& p4,
00987                                unsigned int subdivisions)
00988 {
00989 GVX_TRACE("GLCanvas::drawBezierFill4");
00990 
00991   // Looks like the the OpenGL-specific version beats the generic version
00992   // here, unlike for drawBezier4().
00993 
00994 #if 0
00995   Canvas::drawBezierFill4(center, p1, p2, p3, p4, subdivisions);
00996 #else
00997   vec3d points[] =
00998   {
00999     p1, p2, p3, p4
01000   };
01001 
01002   glEnable(GL_MAP1_VERTEX_3);
01003   glMap1d(GL_MAP1_VERTEX_3,
01004           0.0,                  // beginning of domain range
01005           double(subdivisions), // end of domain range
01006           3,                    // stride (i.e. skip between array points)
01007           4,                    // order (i.e. number of control points)
01008           points[0].data());
01009 
01010   glMapGrid1d(subdivisions, 0.0, 1.0);
01011 
01012   glBegin(GL_TRIANGLE_FAN);
01013   vertex3(center);
01014   for (unsigned int d = 0; d <= subdivisions; ++d)
01015     {
01016       glEvalCoord1d(double(d));
01017     }
01018   glEnd();
01019 #endif
01020 }
01021 
01022 void GLCanvas::beginPoints(const char* /*comment*/)
01023 { GVX_TRACE("GLCanvas::beginPoints"); glBegin(GL_POINTS); }
01024 
01025 void GLCanvas::beginLines(const char* /*comment*/)
01026 { GVX_TRACE("GLCanvas::beginLines"); glBegin(GL_LINES); }
01027 
01028 void GLCanvas::beginLineStrip(const char* /*comment*/)
01029 { GVX_TRACE("GLCanvas::beginLineStrip"); glBegin(GL_LINE_STRIP); }
01030 
01031 void GLCanvas::beginLineLoop(const char* /*comment*/)
01032 { GVX_TRACE("GLCanvas::beginLineLoop"); glBegin(GL_LINE_LOOP); }
01033 
01034 void GLCanvas::beginTriangles(const char* /*comment*/)
01035 { GVX_TRACE("GLCanvas::beginTriangles"); glBegin(GL_TRIANGLES); }
01036 
01037 void GLCanvas::beginTriangleStrip(const char* /*comment*/)
01038 { GVX_TRACE("GLCanvas::beginTriangleStrip"); glBegin(GL_TRIANGLE_STRIP); }
01039 
01040 void GLCanvas::beginTriangleFan(const char* /*comment*/)
01041 { GVX_TRACE("GLCanvas::beginTriangleFan"); glBegin(GL_TRIANGLE_FAN); }
01042 
01043 void GLCanvas::beginQuads(const char* /*comment*/)
01044 { GVX_TRACE("GLCanvas::beginQuads"); glBegin(GL_QUADS); }
01045 
01046 void GLCanvas::beginQuadStrip(const char* /*comment*/)
01047 { GVX_TRACE("GLCanvas::beginQuadStrip"); glBegin(GL_QUAD_STRIP); }
01048 
01049 void GLCanvas::beginPolygon(const char* /*comment*/)
01050 { GVX_TRACE("GLCanvas::beginPolygon"); glBegin(GL_POLYGON); }
01051 
01052 void GLCanvas::vertex2(const vec2d& v)
01053 {
01054 GVX_TRACE("GLCanvas::vertex2");
01055   glVertex2d(v.x(), v.y());
01056 }
01057 
01058 void GLCanvas::vertex3(const vec3d& v)
01059 {
01060 GVX_TRACE("GLCanvas::vertex3");
01061   glVertex3d(v.x(), v.y(), v.z());
01062 }
01063 
01064 void GLCanvas::end()
01065 {
01066 GVX_TRACE("GLCanvas::end");
01067   glEnd();
01068 }
01069 
01070 void GLCanvas::drawRasterText(const rutz::fstring& text,
01071                               const GxRasterFont& font)
01072 {
01073 GVX_TRACE("GLCanvas::drawRasterText");
01074 
01075   glListBase( font.listBase() );
01076 
01077   const char* p = text.c_str();
01078 
01079   int line = 0;
01080 
01081   while (1)
01082     {
01083       int len = 0;
01084       while (p[len] != '\0' && p[len] != '\n')
01085         ++len;
01086 
01087       dbg_eval(3, len); dbg_eval_nl(3, p);
01088 
01089       rasterPos( vec3d::zeros() );
01090       if (line > 0)
01091         {
01092           // this is a workaround to shift the raster position by a given
01093           // number of pixels
01094           glBitmap(0, 0, 0.0f, 0.0f,
01095                    0,                               // x shift
01096                    -1 * font.rasterHeight() * line, // y shift
01097                    static_cast<const GLubyte*>(0));
01098         }
01099 
01100       glCallLists( len, GL_BYTE, p );
01101 
01102       p += len;
01103 
01104       if (*p == '\0')
01105         break;
01106 
01107       // else...
01108       GVX_ASSERT(*p == '\n');
01109       ++p;
01110       ++line;
01111     }
01112 }
01113 
01114 void GLCanvas::drawVectorText(const rutz::fstring& text,
01115                               const GxVectorFont& font)
01116 {
01117 GVX_TRACE("GLCanvas::drawVectorText");
01118 
01119   glListBase( font.listBase() );
01120 
01121   const char* p = text.c_str();
01122 
01123   glMatrixMode(GL_MODELVIEW);
01124 
01125   int line = 0;
01126 
01127   while (1)
01128     {
01129       int len = 0;
01130       while (p[len] != '\0' && p[len] != '\n')
01131         ++len;
01132 
01133       dbg_eval(3, len); dbg_eval_nl(3, p);
01134 
01135       glPushMatrix();
01136 
01137       if (line > 0)
01138         glTranslated( 0.0,
01139                       -1.0 * font.vectorHeight() * line,
01140                       0.0 );
01141 
01142       glCallLists( len, GL_BYTE, p );
01143       glPopMatrix();
01144 
01145       p += len;
01146 
01147       if (*p == '\0')
01148         break;
01149 
01150       // else...
01151       GVX_ASSERT(*p == '\n');
01152       ++p;
01153       ++line;
01154     }
01155 }
01156 
01157 void GLCanvas::flushOutput()
01158 {
01159 GVX_TRACE("GLCanvas::flushOutput");
01160 
01161   if (rep->opts->doubleFlag)
01162     rep->glx->swapBuffers();
01163   else
01164     glFlush();
01165 }
01166 
01167 void GLCanvas::finishDrawing()
01168 {
01169 GVX_TRACE("GLCanvas::finishDrawing");
01170   glFinish();
01171 }
01172 
01173 int GLCanvas::genLists(int num)
01174 {
01175 GVX_TRACE("GLCanvas::genLists");
01176   const int i = glGenLists(num);
01177 
01178   if (i == 0)
01179     throw rutz::error("Couldn't allocate GL display list", SRC_POS);
01180 
01181   return i;
01182 }
01183 
01184 void GLCanvas::deleteLists(int start, int num)
01185 {
01186 GVX_TRACE("GLCanvas::deleteLists");
01187   glDeleteLists(start, num);
01188 }
01189 
01190 void GLCanvas::newList(int i, bool do_execute)
01191 {
01192 GVX_TRACE("GLCanvas::newList");
01193 
01194  glNewList(i,
01195            do_execute
01196            ? GL_COMPILE_AND_EXECUTE
01197            : GL_COMPILE);
01198 }
01199 
01200 void GLCanvas::endList()
01201 {
01202 GVX_TRACE("GLCanvas::endList");
01203   glEndList();
01204 }
01205 
01206 bool GLCanvas::isList(int i)
01207 {
01208 GVX_TRACE("GLCanvas::isList");
01209   return i != 0 && glIsList(i) == GL_TRUE;
01210 }
01211 
01212 void GLCanvas::callList(int i)
01213 {
01214 GVX_TRACE("GLCanvas::callList");
01215   glCallList(i);
01216 }
01217 
01218 void GLCanvas::light(int lightnum,
01219                      const Gfx::RgbaColor* spec,
01220                      const Gfx::RgbaColor* diff,
01221                      const Gfx::RgbaColor* ambi,
01222                      const vec3d* posi,
01223                      const vec3d* sdir,
01224                      double attenuation,
01225                      double spotExponent,
01226                      double spotCutoff)
01227 {
01228 GVX_TRACE("GLCanvas::light");
01229 
01230   glEnable(GL_LIGHTING);
01231   glEnable(GL_LIGHT0+lightnum);
01232   glEnable(GL_DEPTH_TEST);
01233 
01234   if (spec != 0)
01235     {
01236       const GLfloat fspecular[] = { spec->r(), spec->g(), spec->b(), spec->a() };
01237       glLightfv(GL_LIGHT0+lightnum, GL_SPECULAR, fspecular);
01238     }
01239 
01240   if (diff != 0)
01241     {
01242       const GLfloat fdiffuse[] = { diff->r(), diff->g(), diff->b(), diff->a() };
01243       glLightfv(GL_LIGHT0+lightnum, GL_DIFFUSE, fdiffuse);
01244     }
01245 
01246   if (ambi != 0)
01247     {
01248       const GLfloat fambient[] =  { ambi->r(), ambi->g(), ambi->b(), ambi->a() };
01249       glLightfv(GL_LIGHT0+lightnum, GL_AMBIENT, fambient);
01250     }
01251 
01252   if (posi != 0)
01253     {
01254       const GLfloat w = (attenuation == 0.0) ? 0.0 : 1.0;
01255       const GLfloat m = (attenuation == 0.0) ? 1.0 : (1.0/attenuation);
01256 
01257       const GLfloat fposition[] = { m*posi->x(), m*posi->y(), m*posi->z(), w };
01258       glLightfv(GL_LIGHT0+lightnum, GL_POSITION, fposition);
01259     }
01260 
01261   if (sdir != 0)
01262     {
01263       const GLfloat fdirection[] = { sdir->x(), sdir->y(), sdir->z(), 0.0 };
01264 
01265       glLightfv(GL_LIGHT0+lightnum, GL_SPOT_DIRECTION, fdirection);
01266       glLightf(GL_LIGHT0+lightnum, GL_SPOT_EXPONENT, spotExponent);
01267       glLightf(GL_LIGHT0+lightnum, GL_SPOT_CUTOFF, spotCutoff);
01268     }
01269 }
01270 
01271 void GLCanvas::material(const Gfx::RgbaColor* spec,
01272                         const Gfx::RgbaColor* diff,
01273                         const Gfx::RgbaColor* ambi,
01274                         const double* shininess)
01275 {
01276   glEnable(GL_DEPTH_TEST);
01277 
01278   if (spec != 0)
01279     {
01280       const GLfloat specular[] = { spec->r(), spec->g(), spec->b(), spec->a() };
01281       glMaterialfv(GL_FRONT, GL_SPECULAR, specular);
01282     }
01283 
01284   if (diff != 0)
01285     {
01286       const GLfloat diffuse[] =  { diff->r(), diff->g(), diff->b(), diff->a() };
01287       glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuse);
01288     }
01289 
01290   if (ambi != 0)
01291     {
01292       const GLfloat ambient[] =  { ambi->r(), ambi->g(), ambi->b(), ambi->a() };
01293       glMaterialfv(GL_FRONT, GL_AMBIENT, ambient);
01294     }
01295 
01296   if (shininess != 0)
01297     glMaterialf(GL_FRONT, GL_SHININESS, *shininess);
01298 }
01299 
01300 static const char __attribute__((used)) vcid_groovx_gfx_glcanvas_cc_utc20050626084024[] = "$Id: glcanvas.cc 10065 2007-04-12 05:54:56Z rjpeters $ $HeadURL: file:
01301 #endif // !GROOVX_GFX_GLCANVAS_CC_UTC20050626084024_DEFINED

The software described here is Copyright (c) 1998-2005, Rob Peters.
This page was generated Wed Dec 3 06:49:38 2008 by Doxygen version 1.5.5.