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;
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
01026
01027
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 }