render.C

00001 #include <GL/gl.h>
00002 #include <GL/glu.h>
00003 
00004 #include <cmath>
00005 #include <string>
00006 #include <iostream>
00007 #include <algorithm>
00008 
00009 #include <archon/math/vector.H>
00010 
00011 #include <archon/util/options.H>
00012 #include <archon/util/text.H>
00013 #include <archon/util/thread.H>
00014 
00015 #include <archon/display/implementation.H>
00016 #include <archon/display/connection.H>
00017 #include <archon/display/screen.H>
00018 #include <archon/display/visual.H>
00019 #include <archon/display/window.H>
00020 #include <archon/display/context.H>
00021 #include <archon/display/keysyms.H>
00022 
00023 namespace Archon
00024 {
00025   using namespace Math;
00026 
00027   namespace Display
00028   {
00029     namespace Test
00030     {
00031       bool    opt_help                     = false;
00032       double  opt_frameRate                = 30;
00033       Options::IntByInt opt_windowSize     = { 500, 500 };
00034       Options::IntByInt opt_windowPosition = { 0, 0 };
00035       Options::IntByInt opt_screenDpcm     = { 0, 0 };
00036       double  opt_eyeToScreenDist          = 0.4; // 40 cm
00037       double  opt_depthOfField             = 100;
00038       double  opt_interestBallRadius       = 4;
00039       double  opt_detailLevel              = 1;
00040       bool    opt_directRendering          = true;
00041 
00042       unsigned adjust(unsigned val, unsigned min, double f)
00043       {
00044         return std::max(static_cast<unsigned>(f*val), min);
00045       }
00046 
00047       unsigned adjustDetail(unsigned val, unsigned min)
00048       {
00049         return adjust(val, min, opt_detailLevel);
00050       }
00051 
00285       struct PerspectiveProjection
00286       {
00291         double horizontalDotPitch;
00292 
00297         double verticalDotPitch;
00298 
00310         double viewDistance;
00311 
00315         double aspectRatio;
00316 
00351         double neutralFieldOfView;
00352 
00356         double farToNearClipRatio;
00357 
00398         double zoomFactor;
00399 
00403         double cameraDistance;
00404 
00405 
00406 
00434         PerspectiveProjection(double horizontalDotPitch = 0.0254/96, double verticalDotPitch = 0.0254/96,
00435                               double viewDistance = 0.4, double aspectRatio = 1, double neutralFieldOfView = 0.828427124747,
00436                               double farToNearClipRatio = 100, double zoomFactor = 1, double cameraDistance = 10):
00437           horizontalDotPitch(horizontalDotPitch), verticalDotPitch(verticalDotPitch),
00438           viewDistance(viewDistance), aspectRatio(aspectRatio), neutralFieldOfView(neutralFieldOfView),
00439           farToNearClipRatio(farToNearClipRatio), zoomFactor(zoomFactor), cameraDistance(cameraDistance) {}
00440 
00441 
00442 
00449         double getHorizontalResolutionDpcm()
00450         {
00451           return 0.01 / horizontalDotPitch;
00452         }
00453 
00460         double getVerticalResolutionDpcm()
00461         {
00462           return 0.01 / verticalDotPitch;
00463         }
00464 
00471         void setResolutionDpcm(double h, double v)
00472         {
00473           horizontalDotPitch = 0.01 / h;
00474           verticalDotPitch   = 0.01 / v;
00475         }
00476 
00477 
00478 
00485         double getHorizontalResolutionDpi()
00486         {
00487           return 0.0254 / horizontalDotPitch;
00488         }
00489 
00496         double getVerticalResolutionDpi()
00497         {
00498           return 0.0254 / verticalDotPitch;
00499         }
00500 
00507         void setResolutionDpi(double h, double v)
00508         {
00509           horizontalDotPitch = 0.0254 / h;
00510           verticalDotPitch   = 0.0254 / v;
00511         }
00512 
00513 
00514 
00515         double getViewportWidthMeters()
00516         {
00517           return  viewDistance * zoomFactor * getActualHorizontalFieldOfView();
00518         }
00519 
00520         int getViewportWidthPixels()
00521         {
00522           return lround(getViewportWidthMeters() / horizontalDotPitch);
00523         }
00524 
00525         double getViewportHeightMeters()
00526         {
00527           return  viewDistance * zoomFactor * getActualVerticalFieldOfView();
00528         }
00529 
00530         int getViewportHeightPixels()
00531         {
00532           return lround(getViewportHeightMeters() / verticalDotPitch);
00533         }
00534 
00535         void setViewportSizeMeters(double width, double height)
00536         {
00537           aspectRatio = width / height;
00538           neutralFieldOfView = sqrt(width * height) / viewDistance;
00539         }
00540 
00541         void setViewportSizePixels(int width, int height)
00542         {
00543           setViewportSizeMeters(width * horizontalDotPitch, height * verticalDotPitch);
00544         }
00545 
00546 
00547 
00569         double getNearClipDist()
00570         {
00571           return cameraDistance / sqrt(farToNearClipRatio);
00572         }
00573 
00585         double getFarClipDist()
00586         {
00587           return getNearClipDist() * farToNearClipRatio;
00588         }
00589 
00603         double getNearClipWidth()
00604         {
00605           return getNearClipDist() * getActualHorizontalFieldOfView();
00606         }
00607 
00621         double getNearClipHeight()
00622         {
00623           return getNearClipDist() * getActualVerticalFieldOfView();
00624         }
00625 
00626 
00627 
00667         void autoDistance(double interest, double f = 1)
00668         {
00669           cameraDistance = interest * sqrt(0.25 + sq(zoomFactor / neutralFieldOfView / f));
00670         }
00671 
00710         void autoZoom(double interest, double f = 1)
00711         {
00712           zoomFactor = neutralFieldOfView * f * sqrt(sq(cameraDistance/interest) - 0.25);
00713         }
00714 
00715 
00716 
00723         double getActualHorizontalFieldOfView()
00724         {
00725           return neutralFieldOfView / zoomFactor * getHorizontalFieldFactor();
00726         }
00727 
00734         double getActualVerticalFieldOfView()
00735         {
00736           return neutralFieldOfView / zoomFactor * getVerticalFieldFactor();
00737         }
00738 
00739 
00740 
00751         double getNeutralSolidAngleOfView()
00752         {
00753           return 4 * atan(1 / sqrt(4/sq(neutralFieldOfView) + aspectRatio + 1/aspectRatio));
00754         }
00755 
00771         void setNeutralSolidAngleOfView(double v)
00772         {
00773           neutralFieldOfView = sqrt(4/(sq(1/tan(v/4)) - aspectRatio - 1/aspectRatio));
00774         }
00775 
00776 
00788         double getActualSolidAngleOfView()
00789         {
00790           return 4 * atan(1 / sqrt(4/sq(neutralFieldOfView/zoomFactor) + aspectRatio + 1/aspectRatio));
00791         }
00792 
00805         void setActualSolidAngleOfView(double v)
00806         {
00807           neutralFieldOfView = zoomFactor * sqrt(4/(sq(1/tan(v/4)) - aspectRatio - 1/aspectRatio));
00808         }
00809 
00810 
00811 
00830         double getNeutralAngleOfView(double f = 1)
00831         {
00832           return 2 * atan(f*neutralFieldOfView/2);
00833         }
00834 
00859         void setNeutralAngleOfView(double v, double f = 1)
00860         {
00861           neutralFieldOfView = 2 * sqrt(tan(v/2)) / f;
00862         }
00863 
00864 
00884         double getActualAngleOfView(double f = 1)
00885         {
00886           return 2 * atan(f*neutralFieldOfView/zoomFactor/2);
00887         }
00888 
00909         void setActualAngleOfView(double v, double f = 1)
00910         {
00911           neutralFieldOfView = 2 * zoomFactor * sqrt(tan(v/2)) / f;
00912         }
00913 
00914 
00915 
00928         double getMeanFieldFactor()
00929         {
00930           return 1;
00931         }
00932 
00944         double getHorizontalFieldFactor()
00945         {
00946           return sqrt(aspectRatio);
00947         }
00948 
00960         double getVerticalFieldFactor()
00961         {
00962           return 1/sqrt(aspectRatio);
00963         }
00964 
00975         double getDiagonalFieldFactor()
00976         {
00977           return sqrt(aspectRatio + 1/aspectRatio);
00978         }
00979 
00990         double getMinimumFieldFactor()
00991         {
00992           return sqrt(min(aspectRatio, 1/aspectRatio));
00993         }
00994 
01005         double getMaximumFieldFactor()
01006         {
01007           return sqrt(max(aspectRatio, 1/aspectRatio));
01008         }
01009       };
01010 
01011 
01012       PerspectiveProjection projection;
01013 
01014       void resize(int w, int h)
01015       {
01016         glViewport(0, 0, w, h);
01017         projection.setViewportSizePixels(w, h);
01018         projection.autoDistance(2*opt_interestBallRadius, projection.getMinimumFieldFactor());
01019 
01020         double viewPlaneDist   = projection.getNearClipDist();
01021         double viewPlaneRight  = projection.getNearClipWidth() / 2;
01022         double viewPlaneTop    = projection.getNearClipHeight() / 2;
01023         double farClippingDist = projection.getFarClipDist();
01024 
01025         //cerr << "Camera distance     = " << projection.cameraDistance << " obj" << endl;
01026         //cerr << "Near clip distance  = " << viewPlaneDist << " obj" << endl;
01027         //cerr << "Far clip distance   = " << farClippingDist << " obj" << endl;
01028 
01029         glMatrixMode(GL_PROJECTION);
01030         glLoadIdentity();
01031         glFrustum(-viewPlaneRight, viewPlaneRight, -viewPlaneTop, viewPlaneTop, viewPlaneDist, farClippingDist);
01032       }
01033 
01034       void init(int w, int h)
01035       {
01036         resize(w, h);
01037 
01038         GLfloat v[4];
01039         v[3] = 1;
01040 
01041         glEnable(GL_LIGHTING);
01042         glEnable(GL_LIGHT0);
01043         glEnable(GL_DEPTH_TEST);
01044         glEnable(GL_COLOR_MATERIAL);
01045 
01046         v[0] = v[1] = v[2] = 0.2;
01047         glLightfv(GL_LIGHT0, GL_AMBIENT, v);
01048 
01049         v[0] = v[1] = v[2] = 0.9;
01050         glLightfv(GL_LIGHT0, GL_DIFFUSE, v);
01051 
01052         v[0] = v[1] = v[2] = 0.8;
01053         glLightfv(GL_LIGHT0, GL_SPECULAR, v);
01054 
01055         glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR);
01056         glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, 1);
01057         glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 1);
01058 
01059         v[0] = 0.9; v[1] = 0.9; v[2] = 0.9;
01060         glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, v);
01061 
01062         glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 32);
01063 
01064         glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);
01065       }
01066 
01067       void render(double w)
01068       {
01069         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
01070 
01071         glMatrixMode(GL_MODELVIEW);
01072         glLoadIdentity();
01073         Vector3 camera(-1,3,-19);
01074         camera.normalize();
01075         camera *= projection.cameraDistance;
01076         gluLookAt(camera[0], camera[1], camera[2], 0, 0, 0, 0, 1, 0);
01077 
01078         GLfloat v[4];
01079         v[3] = 1;
01080 
01081         v[0] = 25; v[1] = 100; v[2] = -25;
01082         glLightfv(GL_LIGHT0, GL_POSITION, v);
01083 
01084         glRotated(w/M_PI*180, 0, 1, 0);
01085 
01086         GLUquadric *quadric = gluNewQuadric();
01087 
01088         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
01089         gluSphere(quadric, opt_interestBallRadius, 50, 50);
01090         glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
01091 
01092         glPushMatrix();
01093         glColor3f(0.1, 0.9, 0.9);
01094         glTranslated(0, 0, -16);
01095         gluCylinder(quadric, 0.2, 0.2, 1.6,
01096                     adjustDetail(50, 3), adjustDetail(25, 1));
01097         glPopMatrix();
01098 
01099         glPushMatrix();
01100         glColor3f(0.2, 0.2, 0.8);
01101         glTranslated(0, 0, -12.5);
01102         gluCylinder(quadric, 0.2, 0.2, 1.6,
01103                     adjustDetail(50, 3), adjustDetail(25, 1));
01104         glPopMatrix();
01105 
01106         glPushMatrix();
01107         glColor3f(0.9, 0.1, 0.9);
01108         glTranslated(0, 0, -9);
01109         gluCylinder(quadric, 0.2, 0.2, 1.6,
01110                     adjustDetail(50, 3), adjustDetail(25, 1));
01111         glPopMatrix();
01112 
01113         glPushMatrix();
01114         glColor3f(0.2, 0.2, 0.8);
01115         glTranslated(0, 0, -5.5);
01116         gluCylinder(quadric, 0.2, 0.2, 1.6,
01117                     adjustDetail(50, 3), adjustDetail(25, 1));
01118         glPopMatrix();
01119 
01120         glPushMatrix();
01121         glColor3f(0.9, 0.9, 0.1);
01122         glTranslated(0, 0, -2);
01123         gluCylinder(quadric, 0.2, 0.2, 1.6,
01124                     adjustDetail(50, 3), adjustDetail(25, 1));
01125         glPopMatrix();
01126 
01127         glPushMatrix();
01128         glColor3f(0.2, 0.2, 0.8);
01129         glTranslated(0, 0, 1.5);
01130         gluCylinder(quadric, 0.2, 0.2, 1.6,
01131                     adjustDetail(50, 3), adjustDetail(25, 1));
01132         glPopMatrix();
01133 
01134         glPushMatrix();
01135         glColor3f(0.8, 0.3, 0.3);
01136         glTranslated(-0.07, 0, -60.5);
01137         gluCylinder(quadric, 0.02, 0.02, 64,
01138                     adjustDetail(25, 3), adjustDetail(200, 1));
01139         glPopMatrix();
01140 
01141         glPushMatrix();
01142         glColor3f(0.8, 0.3, 0.3);
01143         glTranslated(+0.07, 0, -60.5);
01144         gluCylinder(quadric, 0.02, 0.02, 64,
01145                     adjustDetail(25, 3), adjustDetail(200, 1));
01146         glPopMatrix();
01147 
01148         gluDeleteQuadric(quadric);
01149       }
01150 
01151       void measureFrameRate()
01152       {
01153         static Time next = Time::now() + Time(10.0);
01154         static double frames = 0;
01155 
01156         ++frames;
01157 
01158         while(Time::now() >= next)
01159         {
01160           cerr << "Frame rate (f/s): " << frames/10 << "\n";
01161           frames = 0;
01162           next += Time(10.0);
01163         }
01164       }
01165 
01166       struct EventHandler: Display::EventHandler
01167       {
01168         void resize(int w, int h) { Test::resize(w, h); }
01169         void close()              { run   = false; }
01170         void mouseIn()            { animate = true;  }
01171         void mouseOut()           { animate = false; }
01172         void keyDown(KeySym s)    { if(s == KeySym_q || s == KeySym_Escape) run = false; }
01173 
01174         bool run;
01175         bool animate;
01176 
01177         EventHandler(): run(true), animate(false) {}
01178       };
01179 
01180       int main(int argc, const char *argv[])
01181       {
01182         Options o;
01183 
01184         o.addSwitch("h", "help", opt_help, true,
01185                     "Describe the parameters");
01186         o.addConfig("f", "frame-rate", opt_frameRate, opt_frameRate,
01187                     "Upper limit on number of frames per second.",
01188                     Options::wantArg_always);
01189         o.addConfig("s", "window-size", opt_windowSize, opt_windowSize,
01190                     "Initial size in pixels of the windows contents area.",
01191                     Options::wantArg_always);
01192         o.addConfig("p", "window-position", opt_windowPosition, opt_windowPosition,
01193                     "Initial position in pixels of the upper left corner of the outside window frame.",
01194                     Options::wantArg_always);
01195         o.addConfig("r", "screen-dpcm", opt_screenDpcm, opt_screenDpcm,
01196                     "Horizontal by vertical dots per centimeter on the target screen. "
01197                     "Specify 0x0 to use the figures reported by the system.\n"
01198                     "If you have it in dots per inch (dpi) devide it by  2.54 cm/in to get dots per centimeter (dpcm).\n"
01199                     "Specifying the wrong values here will produce the wrong field of view, "
01200                     "which in turn will produce the wrong aspect ratio between the Z axis and the X-Y plane, "
01201                     "which in turn leads to the depth effect appearing either stretched or squeezed. "
01202                     "It may also produce the wrong aspect ratio between the X and Y axes, "
01203                     "which will lead to circles in the X Y plane appearing egg-shaped.",
01204                     Options::wantArg_always);
01205         o.addConfig("e", "eye-to-screen-dist", opt_eyeToScreenDist, opt_eyeToScreenDist,
01206                     "Distance in meters between your eyes and the screen.\n"
01207                     "Specifying the wrong distance here will produce the wrong field of view, "
01208                     "which in turn will produce the wrong aspect ratio between the Z axis and the X-Y plane, "
01209                     "which in turn leads to the depth effect appearing either stretched or squeezed.",
01210                     Options::wantArg_always);
01211         o.addConfig("d", "depth-of-field", opt_depthOfField, opt_depthOfField,
01212                     "Ratio between near and far clipping planes. Must be greater than 1.\n"
01213                     "Smaller values produce more accurate depth tests but makes it more likely that your scene will be clipped.",
01214                     Options::wantArg_always);
01215         o.addConfig("i", "interest-ball-radius", opt_interestBallRadius, opt_interestBallRadius,
01216                     "Radius of the interest ball in object coordinates.",
01217                     Options::wantArg_always);
01218         o.addConfig("l", "detail-level", opt_detailLevel, opt_detailLevel,
01219                     "A detail level modifier or factor. 1 corresponds to the normal level of detail. "
01220                     "2 corresponds to twice the normal level of detail.",
01221                     Options::wantArg_always);
01222         o.addConfig("D", "direct-rendering", opt_directRendering, opt_directRendering,
01223                     "Attempt to establist direct rendering contexts to gain performance.",
01224                     Options::wantArg_always);
01225 
01226         if(o.processCommandLine(argc, argv))
01227         {
01228           cerr << "Try --help\n";
01229           return 1;
01230         }
01231   
01232         if(opt_help)
01233         {
01234           cout <<
01235             "Test Application for the Archon::Display library\n"
01236             "by Brian Kristiansen & Kristian Spangsege\n"
01237             "\n"
01238             "Synopsis: " << argv[0] << "\n"
01239             "\n"
01240             "Available options:\n";
01241           cout << o.list();
01242           return 0;
01243         }
01244 
01245         if(argc > 1)
01246         {
01247           cerr << "Too many aguments\n";
01248           cerr << "Try --help\n";
01249           return 1;
01250         }
01251 
01252         Ref<Display::Implementation> implementation = Display::getDefaultImplementation();
01253         Ref<Display::Connection> connection = implementation->newConnection();
01254         Ref<Display::Screen> screen = connection->getDefaultScreen();
01255 
01256         projection.horizontalDotPitch = opt_screenDpcm.x ? 0.01 / opt_screenDpcm.x : screen->getHorizontalDotPitch();
01257         projection.verticalDotPitch   = opt_screenDpcm.y ? 0.01 / opt_screenDpcm.y : screen->getVerticalDotPitch();
01258         projection.viewDistance       = opt_eyeToScreenDist;
01259         cerr << "Horizontal dot pitch = " << projection.horizontalDotPitch << " m/px" << endl;
01260         cerr << "Vertical dot pitch   = " << projection.verticalDotPitch << " m/px" << endl;
01261 
01262         Ref<Display::Visual> visual = screen->chooseVisual();
01263 
01264         Ref<Display::Window> window = visual->newWindow(opt_windowPosition.x, opt_windowPosition.y,
01265                                                         opt_windowSize.x, opt_windowSize.y, "Archon::Display::Test");
01266 
01267         Ref<Display::Context> context = visual->newContext(opt_directRendering);
01268 
01269         cerr << "Direct rendering context: " << context->isDirect() << endl;
01270 
01271         double w = 0;
01272 
01273         Time timePerFrame;
01274         timePerFrame.setNanoSeconds(long(1E9/opt_frameRate));
01275 
01276         Display::Bind bind(context, window);
01277 
01278         init(opt_windowSize.x, opt_windowSize.y);
01279 
01280         EventHandler eventHandler;
01281 
01282         Time t = Time::now();
01283         while(eventHandler.run)
01284         {
01285           measureFrameRate();
01286 
01287           render(w);
01288 
01289           window->processEvents(&eventHandler);
01290 
01291           window->swapBuffers();
01292 
01293           if(eventHandler.animate) w += 2*M_PI/opt_frameRate/10;
01294 
01295           t += timePerFrame;
01296           Thread::sleepUntil(t);
01297         }
01298 
01299         return 0;
01300       }
01301     }
01302   }
01303 }
01304 
01305 int main(int argc, const char *argv[]) throw()
01306 {
01307   using namespace Archon::Utilities;
01308 
01309   std::set_unexpected(Exception::terminal<Archon::Display::exceptionCatchInfo>);
01310   std::set_terminate (Exception::terminal<Archon::Display::exceptionCatchInfo>);
01311 
01312   return Archon::Display::Test::main(argc, argv);
01313 }

Generated on Sun Jul 30 22:55:43 2006 for Archon by  doxygen 1.4.4