source: trunk/packages/vizservers/nanovis/Camera.cpp @ 3884

Last change on this file since 3884 was 3630, checked in by ldelgass, 11 years ago

Nanovis refactoring to fix problems with scaling and multiple results.
Do rendering in world space to properly place and scale multiple data sets.
Also fix flows to reduce resets of animations. More work toward removing
Cg dependency. Fix panning to convert viewport coords to world coords.

  • Property svn:eol-style set to native
File size: 11.6 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    // Extend the bounds a bit
289    _near = 0.99 * _near - (_far - _near) * 0.5;
290    _far  = 1.01 * _far  + (_far - _near) * 0.5;
291
292    // Ensure near is closer than far
293    _near = (_near >= _far) ? (0.01 * _far) : _near;
294
295    // Limit clip range to make best use of z buffer precision
296    if (_near < .001 * _far)
297        _near = .001 * _far;
298
299    TRACE("Resetting camera clipping range to: near: %g, far: %g", _near, _far);
300
301    glMatrixMode(GL_PROJECTION);
302    glLoadIdentity();
303    gluPerspective(_fov,
304                   (GLdouble)_viewport[2]/(GLdouble)_viewport[3],
305                   _near, _far);
306    glMatrixMode(GL_MODELVIEW);
307
308    print();
309}
310
311void
312Camera::initialize()
313{
314    TRACE("Enter");
315    print();
316
317    glViewport(_viewport[0], _viewport[1], _viewport[2], _viewport[3]);
318    glMatrixMode(GL_PROJECTION);
319    glLoadIdentity();
320    gluPerspective(_fov,
321                   (GLdouble)_viewport[2]/(GLdouble)_viewport[3],
322                   _near, _far);
323
324    glMatrixMode(GL_MODELVIEW);
325
326    computeModelViewMatrix();
327    glLoadMatrixd((const GLdouble *)_modelViewMatrix.get());
328}
329
330void Camera::windowToWorldCoords(double x, double y, double z, Vector3f& objPos)
331{
332    Matrix4x4d proj;
333    getProjectionMatrix(proj);
334    GLdouble outX, outY, outZ;
335    gluUnProject(x, y, z,
336                 (GLdouble *)getModelViewMatrix().get(),
337                 (GLdouble *)proj.get(),
338                 (GLint *)_viewport,
339                 &outX, &outY, &outZ);
340    objPos.set(outX, outY, outZ);
341}
342
343void Camera::worldToWindowCoords(double x, double y, double z, Vector3f& winPos)
344{
345    Matrix4x4d proj;
346    getProjectionMatrix(proj);
347    GLdouble outX, outY, outZ;
348    gluProject(x, y, z,
349               (GLdouble *)getModelViewMatrix().get(),
350               (GLdouble *)proj.get(),
351               (GLint *)_viewport,
352               &outX, &outY, &outZ);
353    winPos.set(outX, outY, outZ);
354}
355
356void Camera::computeModelViewMatrix()
357{
358    if (_mvDirty) {
359        _modelViewMatrix.makeTranslation(0, 0, -getDistance());
360        _modelViewMatrix.multiply(_rotationMatrix);
361        _modelViewMatrix.multiply(_updirMatrix);
362        Matrix4x4d mat;
363        mat.makeTranslation(-_focalPoint);
364        _modelViewMatrix.multiply(mat);
365        _mvDirty = false;
366    }
367}
368
369void Camera::getProjectionMatrix(Matrix4x4d& mat)
370{
371    glMatrixMode(GL_PROJECTION);
372    glPushMatrix();
373    glLoadIdentity();
374    gluPerspective(_fov,
375                   (GLdouble)_viewport[2]/(GLdouble)_viewport[3],
376                   _near, _far);
377    glGetDoublev(GL_PROJECTION_MATRIX, (GLdouble *)mat.get());
378    glPopMatrix();
379    glMatrixMode(GL_MODELVIEW);
380}
381
382void Camera::orient(double *quat)
383{
384    Quaternion q(quat[0], quat[1], quat[2], quat[3]);
385    Rotation rot;
386    rot.set(q);
387    _rotationMatrix.makeRotation(rot);
388    _rotationMatrix.transpose();
389    _mvDirty = true;
390    computeModelViewMatrix();
391
392    Vector3f viewPlaneNormal(0, 0, 1);
393    Matrix4x4d mat = getModelViewMatrix();
394    mat.transpose();
395    viewPlaneNormal = mat.transformVec(viewPlaneNormal);
396    TRACE("vpn: %g %g %g", viewPlaneNormal.x, viewPlaneNormal.y, viewPlaneNormal.z);
397    _position = _focalPoint + viewPlaneNormal * getDistance();
398
399    TRACE("Set rotation to quat: %g %g %g %g",
400          quat[0], quat[1], quat[2], quat[3]);
401}
402
403void Camera::orient(float angleX, float angleY, float angleZ)
404{
405    angleX = -angleX;
406    angleY = angleY - 180.;
407
408    _rotationMatrix.makeRotation(1, 0, 0, deg2rad(angleX));
409    Matrix4x4d mat;
410    mat.makeRotation(0, 1, 0, deg2rad(angleY));
411    _rotationMatrix.multiply(mat);
412    mat.makeRotation(0, 0, 1, deg2rad(angleZ));
413    _rotationMatrix.multiply(mat);
414    _mvDirty = true;
415    computeModelViewMatrix();
416
417    Vector3f viewPlaneNormal(0, 0, 1);
418    mat = getModelViewMatrix();
419    mat.transpose();
420    viewPlaneNormal = mat.transformVec(viewPlaneNormal);
421    TRACE("vpn: %g %g %g", viewPlaneNormal.x, viewPlaneNormal.y, viewPlaneNormal.z);
422    _position = _focalPoint + viewPlaneNormal * getDistance();
423
424    TRACE("Set rotation to angles: %g %g %g",
425          angleX, angleY, angleZ);
426}
427
428void Camera::print() const
429{
430    TRACE("x: %d y: %d w: %d h: %d",
431          _viewport[0], _viewport[1], _viewport[2], _viewport[3]);
432    TRACE("fov: %g near: %g far: %g", _fov, _near, _far);
433    TRACE("fp: %g, %g, %g",
434          _focalPoint.x, _focalPoint.y, _focalPoint.z);
435    TRACE("pos: %g, %g, %g",
436          _position.x, _position.y, _position.z);
437    TRACE("Rotation matrix: ");
438    _rotationMatrix.print();
439    TRACE("Modelview matrix: ");
440    _modelViewMatrix.print();
441}
Note: See TracBrowser for help on using the repository browser.