source: nanovis/trunk/Camera.cpp @ 4895

Last change on this file since 4895 was 3936, checked in by ldelgass, 6 years ago

Fix raw DX (no <DX> header> path in flow data loader

  • Property svn:eol-style set to native
File size: 11.7 KB
Line 
1/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/*
3 * Copyright (c) 2004-2013  HUBzero Foundation, LLC
4 *
5 *  Authors:
6 *    Wei Qiao <qiaow@purdue.edu>
7 */
8#include <stdio.h>
9#include <math.h>
10#include <float.h>
11
12#include <GL/glew.h>
13#include <GL/glu.h>
14
15#include <vrmath/Quaternion.h>
16#include <vrmath/Rotation.h>
17#include <vrmath/Matrix4x4d.h>
18#include <vrmath/Vector3f.h>
19#include <vrmath/Vector4f.h>
20#include <vrmath/BBox.h>
21
22#include "nanovis.h"
23#include "Camera.h"
24#include "Trace.h"
25
26using namespace nv;
27using namespace vrmath;
28
29static inline double deg2rad(double deg)
30{
31    return ((deg * M_PI) / 180.);
32}
33
34static inline double rad2deg(double rad)
35{
36    return ((rad * 180.) / M_PI);
37}
38
39Camera::Camera(int x, int y, int width, int height) :
40    _updir(Y_POS),
41    _zoomRatio(1),
42    _position(0, 0, 2.5),
43    _focalPoint(0, 0, 0),
44    _mvDirty(true),
45    _fov(30.0),
46    _near(0.1),
47    _far(50.0)
48{
49    _viewport[0] = x;
50    _viewport[1] = y;
51    _viewport[2] = width;
52    _viewport[3] = height;
53    _pan[0] = 0;
54    _pan[1] = 0;
55}
56
57void
58Camera::pan(float x, float y, bool absolute)
59{
60    y = -y;
61    if (absolute) {
62        double panAbs[2];
63        panAbs[0] = x;
64        panAbs[1] = y;
65        x -= _pan[0];
66        y -= _pan[1];
67        _pan[0] = panAbs[0];
68        _pan[1] = panAbs[1];
69    } else {
70        _pan[0] += x;
71        _pan[1] += y;
72    }
73
74    if (x != 0.0 || y != 0.0) {
75        Vector3f worldFocalPoint;
76        worldToWindowCoords(_focalPoint.x, _focalPoint.y, _focalPoint.z, 
77                            worldFocalPoint);
78        float focalDepth = worldFocalPoint.z;
79
80        Vector3f newPickPoint, oldPickPoint, motionVector;
81        windowToWorldCoords((x * 2. + 1.) * (float)_viewport[2] / 2.0,
82                            (y * 2. + 1.) * (float)_viewport[3] / 2.0,
83                            focalDepth, 
84                            newPickPoint);
85
86        windowToWorldCoords((float)_viewport[2] / 2.0,
87                            (float)_viewport[3] / 2.0,
88                            focalDepth, 
89                            oldPickPoint);
90
91        // Camera motion is reversed
92        motionVector = oldPickPoint - newPickPoint;
93
94        _focalPoint += motionVector;
95        _position += motionVector;
96
97        _mvDirty = true;
98    }
99}
100
101void
102Camera::zoom(float z, bool absolute)
103{
104    if (absolute) {
105        // Compute relative zoom
106        float zAbs = z;
107        z *= 1.0/_zoomRatio;
108        _zoomRatio = zAbs;
109    } else {
110        _zoomRatio *= z;
111    }
112    float distance = getDistance() / z;
113    Vector3f dir = getViewPlaneNormal();
114    dir = dir.scale(distance);
115    _position = _focalPoint + dir;
116    _mvDirty = true;
117}
118
119const Matrix4x4d&
120Camera::getUpDirMatrix() const
121{
122    return _updirMatrix;
123}
124
125void
126Camera::setUpDirMatrix(AxisDirection dir)
127{
128    _updir = dir;
129
130    switch (_updir) {
131    case X_POS: {
132        _updirMatrix.makeRotation(0, 0, 1, deg2rad(90));
133        Matrix4x4d tmp;
134        tmp.makeRotation(1, 0, 0, deg2rad(90));
135        _updirMatrix.multiply(tmp);
136    }
137        break;
138    case Y_POS:
139        _updirMatrix.makeIdentity();
140        break;
141    case Z_POS: {
142        _updirMatrix.makeRotation(1, 0, 0, deg2rad(-90));
143        Matrix4x4d tmp;
144        tmp.makeRotation(0, 0, 1, deg2rad(-90));
145        _updirMatrix.multiply(tmp);
146    }
147        break;
148    case X_NEG:
149        _updirMatrix.makeRotation(0, 0, 1, deg2rad(-90));
150        break;
151    case Y_NEG: {
152        _updirMatrix.makeRotation(0, 0, 1, deg2rad(180));
153        Matrix4x4d tmp;
154        tmp.makeRotation(0, 1, 0, deg2rad(-90));
155        _updirMatrix.multiply(tmp);
156    }
157        break;
158    case Z_NEG:
159        _updirMatrix.makeRotation(1, 0, 0, deg2rad(90));
160        break;
161    }
162}
163
164/**
165 * \brief Reset zoom to include extents
166 */
167void
168Camera::reset(const Vector3f& bboxMin,
169              const Vector3f& bboxMax,
170              bool resetOrientation)
171{
172    TRACE("Enter");
173
174    if (resetOrientation) {
175        _rotationMatrix.makeIdentity();
176    }
177
178    BBox bbox(bboxMin, bboxMax);
179    Vector3f center = bbox.getCenter();
180    _focalPoint.set(center.x, center.y, center.z);
181
182    Matrix4x4d mat;
183    mat.makeTranslation(-_focalPoint);
184    mat.multiply(_updirMatrix, mat); // premult
185    mat.multiply(_rotationMatrix, mat); // premult
186
187    bbox.transform(bbox, mat);
188
189    Vector3f emin = bbox.min;
190    Vector3f emax = bbox.max;
191
192    TRACE("Eye bounds: (%g,%g,%g) - (%g,%g,%g)",
193          emin.x, emin.y, emin.z,
194          emax.x, emax.y, emax.z);
195
196    double bwidth = emax.x - emin.x;
197    double bheight = emax.y - emin.y;
198    double bdepth = emax.z - emin.z;
199
200    TRACE("bwidth: %g, bheight: %g, bdepth: %g", bwidth, bheight, bdepth);
201
202    double angle = deg2rad(_fov);
203    double distance;
204
205    // Deal with vertical aspect window
206    double winAspect = (double)_viewport[2]/(double)_viewport[3];
207    double sceneAspect = 1.0;;
208    if (bheight > 0.0)
209        sceneAspect = bwidth / bheight;
210
211    if (sceneAspect >= winAspect) {
212        angle = 2.0 * atan(tan(angle*0.5)*winAspect);
213        _near = bwidth / (2.0 * tan(angle*0.5));
214    } else {
215        _near = bheight / (2.0 * tan(angle*0.5));
216    }
217
218    distance = _near + bdepth * 0.5;
219    _far = _near + bdepth;
220
221    Vector3f viewPlaneNormal(0, 0, 1);
222    mat.transpose();
223    viewPlaneNormal = mat.transformVec(viewPlaneNormal);
224    _position = _focalPoint + viewPlaneNormal * distance;
225
226    TRACE("win aspect: %g scene aspect: %g", winAspect, sceneAspect);
227    TRACE("vpn: %g, %g, %g", viewPlaneNormal.x, viewPlaneNormal.y, viewPlaneNormal.z);
228    TRACE("c: %g,%g,%g, d: %g", center.x, center.y, center.z, distance);
229    TRACE("pos: %g, %g, %g", _position.x, _position.y, _position.z);
230    TRACE("near: %g, far: %g", _near, _far);
231
232    _zoomRatio = 1.0;
233    _pan[0] = 0;
234    _pan[1] = 0;
235    _mvDirty = true;
236
237    initialize();
238    resetClippingRange(bboxMin, bboxMax);
239}
240
241/**
242 * \brief Reset near and far planes based on given world space
243 * bounding box
244 *
245 * This method is based on VTK's renderer class implementation
246 * The idea is to plug the bounding box corners into the view
247 * plane equation to find the min and max distance of the scene
248 * to the view plane.
249 */
250void
251Camera::resetClippingRange(const Vector3f& bboxMin, const Vector3f& bboxMax)
252{
253    BBox bbox(bboxMin, bboxMax);
254
255    // Set up view plane equation at camera position
256    Vector3f vpn = getViewPlaneNormal();
257    double a, b, c, d, dist;
258    a = -vpn.x;
259    b = -vpn.y;
260    c = -vpn.z;
261    d = -(a*_position.x + b*_position.y + c*_position.z);
262
263    // Now compute distance of bounding box corners to view plane
264
265    // Set starting limits
266    _near = a * bbox.min.x + b * bbox.min.y + c * bbox.min.z + d;
267    _far = 1e-18;
268
269    // Iterate over the 8 bbox corners
270    for (int k = 0; k < 2; k++) {
271        for (int j = 0; j < 2; j++) {
272            for (int i = 0; i < 2; i++) {
273                dist =
274                    (a * ((i == 0) ? bbox.min.x : bbox.max.x)) +
275                    (b * ((j == 0) ? bbox.min.y : bbox.max.y)) +
276                    (c * ((k == 0) ? bbox.min.z : bbox.max.z)) + d;
277                _near = (dist < _near) ? dist : _near;
278                _far =  (dist > _far)  ? dist : _far;
279            }
280        }
281    }
282
283    // Check for near plane behind the camera
284    if (_near < 0) {
285        _near = 0;
286    }
287
288    TRACE("near: %g far: %g", _near, _far);
289
290    // Extend the bounds a bit
291    _near = 0.99 * _near - (_far - _near) * 0.5;
292    _far  = 1.01 * _far  + (_far - _near) * 0.5;
293
294    TRACE("after extend: near: %g far: %g", _near, _far);
295
296    // Ensure near is closer than far
297    _near = (_near >= _far) ? (0.01 * _far) : _near;
298
299    // Limit clip range to make best use of z buffer precision
300    if (_near < .001 * _far)
301        _near = .001 * _far;
302
303    TRACE("Resetting camera clipping range to: near: %g, far: %g", _near, _far);
304
305    glMatrixMode(GL_PROJECTION);
306    glLoadIdentity();
307    gluPerspective(_fov, 
308                   (GLdouble)_viewport[2]/(GLdouble)_viewport[3], 
309                   _near, _far);
310    glMatrixMode(GL_MODELVIEW);
311
312    print();
313}
314
315void 
316Camera::initialize()
317{
318    TRACE("Enter");
319    print();
320
321    glViewport(_viewport[0], _viewport[1], _viewport[2], _viewport[3]);
322    glMatrixMode(GL_PROJECTION);
323    glLoadIdentity();
324    gluPerspective(_fov, 
325                   (GLdouble)_viewport[2]/(GLdouble)_viewport[3], 
326                   _near, _far);
327
328    glMatrixMode(GL_MODELVIEW);
329
330    computeModelViewMatrix();
331    glLoadMatrixd((const GLdouble *)_modelViewMatrix.get());
332}
333
334void Camera::windowToWorldCoords(double x, double y, double z, Vector3f& objPos)
335{
336    Matrix4x4d proj;
337    getProjectionMatrix(proj);
338    GLdouble outX, outY, outZ;
339    gluUnProject(x, y, z,
340                 (GLdouble *)getModelViewMatrix().get(),
341                 (GLdouble *)proj.get(),
342                 (GLint *)_viewport,
343                 &outX, &outY, &outZ);
344    objPos.set(outX, outY, outZ);
345}
346
347void Camera::worldToWindowCoords(double x, double y, double z, Vector3f& winPos)
348{
349    Matrix4x4d proj;
350    getProjectionMatrix(proj);
351    GLdouble outX, outY, outZ;
352    gluProject(x, y, z,
353               (GLdouble *)getModelViewMatrix().get(),
354               (GLdouble *)proj.get(),
355               (GLint *)_viewport,
356               &outX, &outY, &outZ);
357    winPos.set(outX, outY, outZ);
358}
359
360void Camera::computeModelViewMatrix()
361{
362    if (_mvDirty) {
363        _modelViewMatrix.makeTranslation(0, 0, -getDistance());
364        _modelViewMatrix.multiply(_rotationMatrix);
365        _modelViewMatrix.multiply(_updirMatrix);
366        Matrix4x4d mat;
367        mat.makeTranslation(-_focalPoint);
368        _modelViewMatrix.multiply(mat);
369        _mvDirty = false;
370    }
371}
372
373void Camera::getProjectionMatrix(Matrix4x4d& mat)
374{
375    glMatrixMode(GL_PROJECTION);
376    glPushMatrix();
377    glLoadIdentity();
378    gluPerspective(_fov, 
379                   (GLdouble)_viewport[2]/(GLdouble)_viewport[3], 
380                   _near, _far);
381    glGetDoublev(GL_PROJECTION_MATRIX, (GLdouble *)mat.get());
382    glPopMatrix();
383    glMatrixMode(GL_MODELVIEW);
384}
385
386void Camera::orient(double *quat)
387{
388    Quaternion q(quat[0], quat[1], quat[2], quat[3]);
389    Rotation rot;
390    rot.set(q);
391    _rotationMatrix.makeRotation(rot);
392    _rotationMatrix.transpose();
393    _mvDirty = true;
394    computeModelViewMatrix();
395
396    Vector3f viewPlaneNormal(0, 0, 1);
397    Matrix4x4d mat = getModelViewMatrix();
398    mat.transpose();
399    viewPlaneNormal = mat.transformVec(viewPlaneNormal);
400    TRACE("vpn: %g %g %g", viewPlaneNormal.x, viewPlaneNormal.y, viewPlaneNormal.z);
401    _position = _focalPoint + viewPlaneNormal * getDistance();
402
403    TRACE("Set rotation to quat: %g %g %g %g",
404          quat[0], quat[1], quat[2], quat[3]);
405}
406
407void Camera::orient(float angleX, float angleY, float angleZ)
408{
409    angleX = -angleX;
410    angleY = angleY - 180.;
411
412    _rotationMatrix.makeRotation(1, 0, 0, deg2rad(angleX));
413    Matrix4x4d mat;
414    mat.makeRotation(0, 1, 0, deg2rad(angleY));
415    _rotationMatrix.multiply(mat);
416    mat.makeRotation(0, 0, 1, deg2rad(angleZ));
417    _rotationMatrix.multiply(mat);
418    _mvDirty = true;
419    computeModelViewMatrix();
420
421    Vector3f viewPlaneNormal(0, 0, 1);
422    mat = getModelViewMatrix();
423    mat.transpose();
424    viewPlaneNormal = mat.transformVec(viewPlaneNormal);
425    TRACE("vpn: %g %g %g", viewPlaneNormal.x, viewPlaneNormal.y, viewPlaneNormal.z);
426    _position = _focalPoint + viewPlaneNormal * getDistance();
427
428    TRACE("Set rotation to angles: %g %g %g",
429          angleX, angleY, angleZ);
430}
431
432void Camera::print() const
433{
434    TRACE("x: %d y: %d w: %d h: %d",
435          _viewport[0], _viewport[1], _viewport[2], _viewport[3]);
436    TRACE("fov: %g near: %g far: %g", _fov, _near, _far);
437    TRACE("fp: %g, %g, %g",
438          _focalPoint.x, _focalPoint.y, _focalPoint.z);
439    TRACE("pos: %g, %g, %g",
440          _position.x, _position.y, _position.z);
441    TRACE("Rotation matrix: ");
442    _rotationMatrix.print();
443    TRACE("Modelview matrix: ");
444    _modelViewMatrix.print();
445}
Note: See TracBrowser for help on using the repository browser.