From 93f3378b863fd77d24e01f95a744baeffe3d3430 Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Thu, 7 Nov 2002 14:13:51 +0000 Subject: [PATCH] Intergrated Martin Kada's impostor test program with the old test program. --- src/Demos/osgimpostor/Makefile | 1 + src/Demos/osgimpostor/README | 26 -- src/Demos/osgimpostor/TestManipulator.cpp | 385 ++++++++++++++++++++++ src/Demos/osgimpostor/TestManipulator.h | 84 +++++ src/Demos/osgimpostor/osgimpostor.cpp | 361 ++++++++++++++------ 5 files changed, 725 insertions(+), 132 deletions(-) delete mode 100644 src/Demos/osgimpostor/README create mode 100644 src/Demos/osgimpostor/TestManipulator.cpp create mode 100644 src/Demos/osgimpostor/TestManipulator.h diff --git a/src/Demos/osgimpostor/Makefile b/src/Demos/osgimpostor/Makefile index 9d33a16c8..587288c7c 100644 --- a/src/Demos/osgimpostor/Makefile +++ b/src/Demos/osgimpostor/Makefile @@ -2,6 +2,7 @@ TOPDIR = ../../.. include $(TOPDIR)/Make/makedefs CXXFILES =\ + TestManipulator.cpp\ osgimpostor.cpp\ LIBS += $(OSG_LIBS) $(GLUT_LIB) $(GL_LIBS) $(X_LIBS) $(OTHER_LIBS) diff --git a/src/Demos/osgimpostor/README b/src/Demos/osgimpostor/README deleted file mode 100644 index 244042dfb..000000000 --- a/src/Demos/osgimpostor/README +++ /dev/null @@ -1,26 +0,0 @@ -Note: Using sgv with Peformer (for IRIX and Linux users only) -============================================================= - -If you find problems with loading .pfb files its likely that its due to undefined -symbols. This isn't a problem with the OSG implementation, but alas the only -current solution is to directly link you app with the Performer libraries. The -Makefile contains two library list. In Makefile you'll see something like : - - #note, use this library list when using the Performer osgPlugin. - #LIBS = ${PFLIBS} -losgGLUT -losgDB -losg -lGLU -lGL -lm -lXmu -lX11 -lXi - - #note, standard library list. - LIBS = -losgGLUT -losgDB -losg -lGLU -lGL -lm -lXmu -lX11 -lXi - -Simple comment in the LIBS line with PFLIBS and comment out the standard LIBS, -then : - - make clean - make - -Hopefully the Performer distribution will eventually work as a dynamic plugin -but until that day we're stuck with this 'hack'... - - -Robert Osfield, -March 2001. diff --git a/src/Demos/osgimpostor/TestManipulator.cpp b/src/Demos/osgimpostor/TestManipulator.cpp new file mode 100644 index 000000000..618735dab --- /dev/null +++ b/src/Demos/osgimpostor/TestManipulator.cpp @@ -0,0 +1,385 @@ +#include "TestManipulator.h" +#include +#include + +using namespace osg; +using namespace osgGA; + +TestManipulator::TestManipulator() +{ + _modelScale = 0.01f; + _minimumZoomScale = 0.05f; + _thrown = false; + + _distance = 1.0f; +} + + +TestManipulator::~TestManipulator() +{ +} + + +void TestManipulator::setNode(osg::Node* node) +{ + _node = node; + if (_node.get()) + { + const osg::BoundingSphere& boundingSphere=_node->getBound(); + _modelScale = boundingSphere._radius; + } +} + + +const osg::Node* TestManipulator::getNode() const +{ + return _node.get(); +} + + +osg::Node* TestManipulator::getNode() +{ + return _node.get(); +} + + + /*ea*/ +void TestManipulator::home(const GUIEventAdapter& ,GUIActionAdapter& us) +{ + if(_node.get() && _camera.get()) + { + + const osg::BoundingSphere& boundingSphere=_node->getBound(); + + _camera->setView( + osg::Vec3(0.0f, 0.0f, 20.0f), + osg::Vec3(0.0f, 1.0f, 20.0f), + osg::Vec3(0.0f, 0.0f, 1.0f)); + + computeLocalDataFromCamera(); + + us.requestRedraw(); + } +} + + +void TestManipulator::init(const GUIEventAdapter& ,GUIActionAdapter& ) +{ + flushMouseEventStack(); + + computeLocalDataFromCamera(); +} + +bool TestManipulator::handle(const GUIEventAdapter& ea,GUIActionAdapter& us) +{ + if(!_camera.get()) return false; + + switch(ea.getEventType()) + { + case(GUIEventAdapter::PUSH): + { + flushMouseEventStack(); + addMouseEvent(ea); + if (calcMovement()) us.requestRedraw(); + us.requestContinuousUpdate(false); + _thrown = false; + return true; + } + + case(GUIEventAdapter::RELEASE): + { + if (ea.getButtonMask()==0) + { + + if (isMouseMoving()) + { + if (calcMovement()) + { + us.requestRedraw(); + us.requestContinuousUpdate(true); + _thrown = true; + } + } + else + { + flushMouseEventStack(); + addMouseEvent(ea); + if (calcMovement()) us.requestRedraw(); + us.requestContinuousUpdate(false); + _thrown = false; + } + + } + else + { + flushMouseEventStack(); + addMouseEvent(ea); + if (calcMovement()) us.requestRedraw(); + us.requestContinuousUpdate(false); + _thrown = false; + } + return true; + } + + case(GUIEventAdapter::DRAG): + { + addMouseEvent(ea); + if (calcMovement()) us.requestRedraw(); + us.requestContinuousUpdate(false); + _thrown = false; + return true; + } + + case(GUIEventAdapter::MOVE): + { + return false; + } + + case(GUIEventAdapter::KEYBOARD): + if (ea.getKey()==' ') + { + flushMouseEventStack(); + _thrown = false; + home(ea,us); + us.requestRedraw(); + us.requestContinuousUpdate(false); + return true; + } else if (ea.getKey()=='+') + { + _camera->setFusionDistanceRatio(_camera->getFusionDistanceRatio()*1.25f); + return true; + } + else if (ea.getKey()=='-') + { + _camera->setFusionDistanceRatio(_camera->getFusionDistanceRatio()/1.25f); + return true; + } +// this is quick hack to test out othographic projection. +// else if (ea.getKey()=='O') +// { +// float dist = _camera->getLookDistance(); +// _camera->setOrtho(-dist,dist,-dist,dist,-dist,dist); +// return true; +// } + return false; + case(GUIEventAdapter::FRAME): + _camera->setFusionDistanceMode(osg::Camera::PROPORTIONAL_TO_LOOK_DISTANCE); + if (_thrown) + { + if (calcMovement()) us.requestRedraw(); + return true; + } + return false; + default: + return false; + } +} + + +bool TestManipulator::isMouseMoving() +{ + if (_ga_t0.get()==NULL || _ga_t1.get()==NULL) return false; + + static const float velocity = 100.0f; + + float dx = _ga_t0->getX()-_ga_t1->getX(); + float dy = _ga_t0->getY()-_ga_t1->getY(); + float len = sqrtf(dx*dx+dy*dy); + float dt = _ga_t0->time()-_ga_t1->time(); + + return (len>dt*velocity); +} + + +void TestManipulator::flushMouseEventStack() +{ + _ga_t1 = NULL; + _ga_t0 = NULL; +} + + +void TestManipulator::addMouseEvent(const GUIEventAdapter& ea) +{ + _ga_t1 = _ga_t0; + _ga_t0 = &ea; +} + + + +void TestManipulator::computeLocalDataFromCamera() +{ + // maths from gluLookAt/osg::Matrix::makeLookAt + osg::Vec3 f(_camera->getCenterPoint()-_camera->getEyePoint()); + f.normalize(); + osg::Vec3 s(f^_camera->getUpVector()); + s.normalize(); + osg::Vec3 u(s^f); + u.normalize(); + + osg::Matrix rotation_matrix(s[0], u[0], -f[0], 0.0f, + s[1], u[1], -f[1], 0.0f, + s[2], u[2], -f[2], 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f); + + _center = _camera->getCenterPoint(); + _distance = _camera->getLookDistance(); + _rotation.set(rotation_matrix); + _rotation = _rotation.inverse(); + +} + +void TestManipulator::computeCameraFromLocalData() +{ + osg::Matrix new_rotation; + new_rotation.makeRotate(_rotation); + + osg::Vec3 up = osg::Vec3(0.0f,1.0f,0.0) * new_rotation; + osg::Vec3 eye = (osg::Vec3(0.0f,0.0f,_distance) * new_rotation) + _center; + + _camera->setLookAt(eye,_center,up); +} + + +bool TestManipulator::calcMovement() +{ + + // return if less then two events have been added. + if (_ga_t0.get()==NULL || _ga_t1.get()==NULL) return false; + + float dx = _ga_t0->getX()-_ga_t1->getX(); + float dy = _ga_t0->getY()-_ga_t1->getY(); + + + // return if there is no movement. + if (dx==0 && dy==0) return false; + + float focalLength = (_camera->getCenterPoint()-_camera->getEyePoint()).length(); + unsigned int buttonMask = _ga_t1->getButtonMask(); + if (buttonMask==GUIEventAdapter::LEFT_MOUSE_BUTTON) + { + + // rotate camera. + + float rx0 = (_ga_t0->getXmax()-_ga_t0->getXmin())/2.0f; + + osg::Quat new_rotate; + float xRot = dx / rx0; + new_rotate.makeRotate(xRot / 5.0f, osg::Vec3(0.0f, 0.0f, 1.0f)); + + _rotation = _rotation*new_rotate; + + computeCameraFromLocalData(); + + return true; + + } + else if (buttonMask==GUIEventAdapter::MIDDLE_MOUSE_BUTTON) + { + + // pan model. + + osg::Vec3 dv = osg::Vec3(0.0f, 0.0f, 1.0f) * dy; + + _center += dv; + + computeCameraFromLocalData(); + + return true; + + } + else if (buttonMask==GUIEventAdapter::RIGHT_MOUSE_BUTTON) + { + + osg::Vec3 uv = _camera->getUpVector(); + osg::Vec3 sv = _camera->getSideVector(); + osg::Vec3 fv = uv ^ sv; + osg::Vec3 dv = fv*dy-sv*dx; + + _center += dv; + computeCameraFromLocalData(); + + return true; + } + + return false; +} + + +/* + * This size should really be based on the distance from the center of + * rotation to the point on the object underneath the mouse. That + * point would then track the mouse as closely as possible. This is a + * simple example, though, so that is left as an Exercise for the + * Programmer. + */ +const float TRACKBALLSIZE = 0.8f; + +/* + * Ok, simulate a track-ball. Project the points onto the virtual + * trackball, then figure out the axis of rotation, which is the cross + * product of P1 P2 and O P1 (O is the center of the ball, 0,0,0) + * Note: This is a deformed trackball-- is a trackball in the center, + * but is deformed into a hyperbolic sheet of rotation away from the + * center. This particular function was chosen after trying out + * several variations. + * + * It is assumed that the arguments to this routine are in the range + * (-1.0 ... 1.0) + */ +void TestManipulator::trackball(osg::Vec3& axis,float& angle, float p1x, float p1y, float p2x, float p2y) +{ + /* + * First, figure out z-coordinates for projection of P1 and P2 to + * deformed sphere + */ + + osg::Vec3 uv = _camera->getUpVector(); + osg::Vec3 sv = _camera->getSideVector(); + osg::Vec3 lv = _camera->getLookVector(); + + osg::Vec3 p1 = sv*p1x+uv*p1y-lv*tb_project_to_sphere(TRACKBALLSIZE,p1x,p1y); + osg::Vec3 p2 = sv*p2x+uv*p2y-lv*tb_project_to_sphere(TRACKBALLSIZE,p2x,p2y); + + /* + * Now, we want the cross product of P1 and P2 + */ + + axis = p2^p1; + axis.normalize(); + + /* + * Figure out how much to rotate around that axis. + */ + float t = (p2-p1).length() / (2.0*TRACKBALLSIZE); + + /* + * Avoid problems with out-of-control values... + */ + if (t > 1.0) t = 1.0; + if (t < -1.0) t = -1.0; + angle = inRadians(asin(t)); + +} + + +/* + * Project an x,y pair onto a sphere of radius r OR a hyperbolic sheet + * if we are away from the center of the sphere. + */ +float TestManipulator::tb_project_to_sphere(float r, float x, float y) +{ + float d, t, z; + + d = sqrt(x*x + y*y); + /* Inside sphere */ + if (d < r * 0.70710678118654752440) + { + z = sqrt(r*r - d*d); + } /* On hyperbola */ + else + { + t = r / 1.41421356237309504880; + z = t*t / d; + } + return z; +} diff --git a/src/Demos/osgimpostor/TestManipulator.h b/src/Demos/osgimpostor/TestManipulator.h new file mode 100644 index 000000000..4d19ca09d --- /dev/null +++ b/src/Demos/osgimpostor/TestManipulator.h @@ -0,0 +1,84 @@ +//C++ header - Open Scene Graph - Copyright (C) 1998-2002 Robert Osfield +//Distributed under the terms of the GNU Library General Public License (LGPL) +//as published by the Free Software Foundation. + +#ifndef OSGGA_TESTMANIPULATOR +#define OSGGA_TESTMANIPULATOR 1 + +#include + +namespace osgGA{ + +class TestManipulator : public CameraManipulator +{ + public: + + TestManipulator(); + virtual ~TestManipulator(); + + /** Attach a node to the manipulator. + Automatically detaches previously attached node. + setNode(NULL) detaches previously nodes. + Is ignored by manipulators which do not require a reference model.*/ + virtual void setNode(osg::Node*); + + /** Return node if attached.*/ + virtual const osg::Node* getNode() const; + + /** Return node if attached.*/ + virtual osg::Node* getNode(); + + /** Move the camera to the default position. + May be ignored by manipulators if home functionality is not appropriate.*/ + virtual void home(const GUIEventAdapter& ea,GUIActionAdapter& us); + + /** Start/restart the manipulator.*/ + virtual void init(const GUIEventAdapter& ea,GUIActionAdapter& us); + + + /** handle events, return true if handled, false otherwise.*/ + virtual bool handle(const GUIEventAdapter& ea,GUIActionAdapter& us); + + private: + + /** Reset the internal GUIEvent stack.*/ + void flushMouseEventStack(); + /** Add the current mouse GUIEvent to internal stack.*/ + void addMouseEvent(const GUIEventAdapter& ea); + + void computeLocalDataFromCamera(); + + void computeCameraFromLocalData(); + + /** For the give mouse movement calculate the movement of the camera. + Return true is camera has moved and a redraw is required.*/ + bool calcMovement(); + + void trackball(osg::Vec3& axis,float& angle, float p1x, float p1y, float p2x, float p2y); + float tb_project_to_sphere(float r, float x, float y); + + + /** Check the speed at which the mouse is moving. + If speed is below a threshold then return false, otherwise return true.*/ + bool isMouseMoving(); + + // Internal event stack comprising last three mouse events. + osg::ref_ptr _ga_t1; + osg::ref_ptr _ga_t0; + + osg::ref_ptr _node; + + float _modelScale; + float _minimumZoomScale; + + bool _thrown; + + osg::Vec3 _center; + osg::Quat _rotation; + float _distance; + +}; + +} + +#endif diff --git a/src/Demos/osgimpostor/osgimpostor.cpp b/src/Demos/osgimpostor/osgimpostor.cpp index 711f5b39a..3f36b71d1 100644 --- a/src/Demos/osgimpostor/osgimpostor.cpp +++ b/src/Demos/osgimpostor/osgimpostor.cpp @@ -1,146 +1,295 @@ +#include #include -#include +#include +#include +#include + +#include -#include #include #include #include #include -#include #include #include -#include +#include "TestManipulator.h" -void write_usage(std::ostream& out,const std::string& name) + +#include +#include + +// container storing all house nodes +typedef osg::ref_ptr NodePtr; +typedef std::list NodeContainer; +typedef NodeContainer::iterator NodeIterator; + +NodeContainer nodes; + +// +osg::Group * Root = 0; + +const int HOUSES_SIZE = 25000; // total number of houses +double XDim = 5000.0f; // area dimension +/- XDim +double ZDim = 5000.0f; // area dimension +/- YDim + +int GridX = 20; // number of grids in x direction +int GridY = 20; // number of grids in y direction + +bool UseImpostor = true; // use impostor (or do not use) + +float Threshold = 3000.0f; // distance where impostor are shown + +// create houses and store nodes in container +void CreateHouses() { - out << std::endl; - out <<"usage:"<< std::endl; - out <<" "< (rand()) / + static_cast (RAND_MAX)) + * 2.0 * XDim) - XDim; + + float yPos = ((static_cast (rand()) / + static_cast (RAND_MAX)) + * 2 * ZDim) - ZDim; + + float scale = 10.0f; + + osg::Vec3 offset(xPos,yPos,0.0f); + + // coords + osg::Vec3Array* coords = new osg::Vec3Array(10); + (*coords)[0] = osg::Vec3( 0.5f, -0.7f, 0.0f); + (*coords)[1] = osg::Vec3( 0.5f, 0.7f, 0.0f); + (*coords)[2] = osg::Vec3(-0.5f, 0.7f, 0.0f); + (*coords)[3] = osg::Vec3(-0.5f, -0.7f, 0.0f); + (*coords)[4] = osg::Vec3( 0.5f, -0.7f, 1.0f); + (*coords)[5] = osg::Vec3( 0.5f, 0.7f, 1.0f); + (*coords)[6] = osg::Vec3(-0.5f, 0.7f, 1.0f); + (*coords)[7] = osg::Vec3(-0.5f, -0.7f, 1.0f); + (*coords)[8] = osg::Vec3( 0.0f, -0.5f, 1.5f); + (*coords)[9] = osg::Vec3( 0.0f, 0.5f, 1.5f); + + for (i = 0; i < 10; i++) + { + (*coords)[i] = (*coords)[i] * scale + offset; + } + + + // create geometry + osg::Geometry * geometry = new osg::Geometry(); + + geometry->addPrimitiveSet(primitives); + + geometry->setVertexArray(coords); + geometry->setVertexIndices(coordIndices); + + geometry->setColorArray(colors); + geometry->setColorBinding(osg::Geometry::BIND_OVERALL); + + geometry->setNormalArray(normals); + geometry->setNormalBinding(osg::Geometry::BIND_PER_PRIMITIVE); + + osg::Geode * geode = new osg::Geode(); + geode->addDrawable(geometry); + + nodes.push_back(geode); + } } +void LayoutAsGrid() +{ + // calculate bounding box + osg::BoundingBox bbox; + for (NodeIterator node = nodes.begin(); node != nodes.end(); ++node) + bbox.expandBy((*node)->getBound()); + + // setup grid information + osg::Group ** groups = new osg::Group*[GridX * GridY]; + for (int i = 0; i < GridX * GridY; i++) + groups[i] = new osg::Group(); + + float xGridStart = bbox.xMin(); + float xGridSize = (bbox.xMax() - bbox.xMin()) / GridX; + + float yGridStart = bbox.yMin(); + float yGridSize = (bbox.yMax() - bbox.yMin()) / GridY; + + // arrange buildings into right grid + for (NodeIterator nodeIter = nodes.begin(); nodeIter != nodes.end(); ++nodeIter) + { + osg::Node * node = nodeIter->get(); + osg::Vec3 center = node->getBound().center(); + + int x = (int)floor((center.x() - xGridStart) / xGridSize); + int z = (int)floor((center.y() - yGridStart) / yGridSize); + + groups[z * GridX + x]->addChild(node); + } + + // add nodes to building root + for (int i = 0; i < GridX * GridY; i++) + { + osg::StateSet * stateset = new osg::StateSet(); + + osg::Material * material = new osg::Material(); + osg::Vec4 color = osg::Vec4( + 0.5f + (static_cast (rand()) / (2.0*static_cast (RAND_MAX))), + 0.5f + (static_cast (rand()) / (2.0*static_cast (RAND_MAX))), + 0.5f + (static_cast (rand()) / ( 2.0*static_cast(RAND_MAX))), + 1.0f); + + material->setAmbient(osg::Material::FRONT_AND_BACK, color); + material->setDiffuse(osg::Material::FRONT_AND_BACK, color); + stateset->setAttributeAndModes(material, osg::StateAttribute::ON); + + groups[i]->setStateSet(stateset); + + if (UseImpostor) + { + osg::Impostor * impostor = new osg::Impostor(); + impostor->setImpostorThreshold(static_cast (Threshold)); + impostor->addChild(groups[i]); + impostor->setRange(0, 0.0f, 1e7f); + impostor->setCenter(groups[i]->getBound().center()); + Root->addChild(impostor); + } + else + { + Root->addChild(groups[i]); + } + } + + delete[] groups; +} + + int main( int argc, char **argv ) { - - // initialize the GLUT glutInit( &argc, argv ); - - if (argc<2) - { - write_usage(osg::notify(osg::NOTICE),argv[0]); - return 0; - } + osgGLUT::Viewer viewer; // create the commandline args. std::vector commandLine; for(int i=1;i(model)==0) - { - const osg::BoundingSphere& bs = model->getBound(); - if (bs.valid()) + // the osgUtil::InsertImpostorsVisitor used lower down to insert impostors + // only operators on subclass of Group's, if the model top node is not + // a group then it won't be able to insert an impostor. We therefore + // manually insert an impostor above the model. + if (dynamic_cast(model)==0) { + const osg::BoundingSphere& bs = model->getBound(); + if (bs.valid()) + { - osg::Impostor* impostor = new osg::Impostor; + osg::Impostor* impostor = new osg::Impostor; - // standard LOD settings - impostor->addChild(model); - impostor->setRange(0,0.0f,1e7f); - impostor->setCenter(bs.center()); + // standard LOD settings + impostor->addChild(model); + impostor->setRange(0,0.0f,1e7f); + impostor->setCenter(bs.center()); - // impostor specfic settings. - impostor->setImpostorThresholdToBound(5.0f); - - model = impostor; + // impostor specfic settings. + impostor->setImpostorThresholdToBound(5.0f); + model = impostor; + + } } - } - - // we insert an impostor node above the model, so we keep a handle - // on the rootnode of the model, the is required since the - // InsertImpostorsVisitor can add a new root in automatically and - // we would know about it, other than by following the parent path - // up from model. This is really what should be done, but I'll pass - // on it right now as it requires a getRoots() method to be added to - // osg::Node, and we're about to make a release so no new features! - osg::Group* rootnode = new osg::Group; - rootnode->addChild(model); - - - // now insert impostors in the model using the InsertImpostorsVisitor. - osgUtil::InsertImpostorsVisitor ov; - - // traverse the model and collect all osg::Group's and osg::LOD's. - // however, don't traverse the rootnode since we want to keep it as - // the start of traversal, otherwise the insertImpostor could insert - // and Impostor above the current root, making it nolonger a root! - model->accept(ov); - - // insert the Impostors above groups and LOD's - ov.insertImpostors(); - - osg::Timer_t after_load = timer.tick(); - std::cout << "Time for load = "<addChild(model); + + + // now insert impostors in the model using the InsertImpostorsVisitor. + osgUtil::InsertImpostorsVisitor ov; + + // traverse the model and collect all osg::Group's and osg::LOD's. + // however, don't traverse the rootnode since we want to keep it as + // the start of traversal, otherwise the insertImpostor could insert + // and Impostor above the current root, making it nolonger a root! + model->accept(ov); + + // insert the Impostors above groups and LOD's + ov.insertImpostors(); + } + else + { + // no user model so we'll create our own world. + model = Root = new osg::Group(); + CreateHouses(); + LayoutAsGrid(); + } // add model to viewer. - viewer.addViewport( rootnode ); + viewer.addViewport(model); // register trackball, flight and drive. + viewer.registerCameraManipulator(new osgGA::TestManipulator); viewer.registerCameraManipulator(new osgGA::TrackballManipulator); viewer.registerCameraManipulator(new osgGA::FlightManipulator); viewer.registerCameraManipulator(new osgGA::DriveManipulator);