diff --git a/Make/makedirdefs b/Make/makedirdefs index d7470745e..1103d0c34 100644 --- a/Make/makedirdefs +++ b/Make/makedirdefs @@ -183,6 +183,7 @@ EXAMPLE_DIRS = \ osgcopy\ osgcubemap\ osgdepthshadow\ + osgdepthpartition\ osgdistortion\ osgfbo\ osgforest\ diff --git a/VisualStudio/VisualStudio.dsw b/VisualStudio/VisualStudio.dsw index a1a9e256b..b3c7436f1 100644 --- a/VisualStudio/VisualStudio.dsw +++ b/VisualStudio/VisualStudio.dsw @@ -627,6 +627,33 @@ Package=<4> ############################################################################### +Project: "Example osgdepthpartition"=.\examples\osgdepthpartition\osgdepthpartition.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name Core osg + End Project Dependency + Begin Project Dependency + Project_Dep_Name Core osgDB + End Project Dependency + Begin Project Dependency + Project_Dep_Name Core osgGA + End Project Dependency + Begin Project Dependency + Project_Dep_Name Core osgProducer + End Project Dependency + Begin Project Dependency + Project_Dep_Name Core osgUtil + End Project Dependency +}}} + +############################################################################### + Project: "Example osgdepthshadow"=.\examples\osgdepthshadow\osgdepthshadow.dsp - Package Owner=<4> Package=<5> diff --git a/VisualStudio/examples/osgdepthpartition/osgdepthpartition.dsp b/VisualStudio/examples/osgdepthpartition/osgdepthpartition.dsp new file mode 100644 index 000000000..df6ed50e4 --- /dev/null +++ b/VisualStudio/examples/osgdepthpartition/osgdepthpartition.dsp @@ -0,0 +1,125 @@ +# Microsoft Developer Studio Project File - Name="Example osgdepthpartition" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=Example osgdepthpartition - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "osgdepthpartition.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "osgdepthpartition.mak" CFG="Example osgdepthpartition - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "Example osgdepthpartition - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "Example osgdepthpartition - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "Example osgdepthpartition - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +MTL=midl.exe +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GR /GX /O2 /I "../../../include" /I "../../../../OpenThreads/include" /I "../../../../Producer/include" /I "../../../../3rdParty/include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /Zm200 /c +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x809 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 OpenThreadsWin32.lib opengl32.lib /nologo /subsystem:console /pdb:none /machine:I386 /out:"../../../bin/osgdepthpartition.exe" /libpath:"../../../lib" /libpath:"../../../../OpenThreads/lib/win32" /libpath:"../../../../Producer/lib" /libpath:"../../../../3rdParty/lib" + +!ELSEIF "$(CFG)" == "Example osgdepthpartition - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +MTL=midl.exe +# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MDd /W3 /Gm /GR /GX /Zi /Od /I "../../../include" /I "../../../../OpenThreads/include" /I "../../../../Producer/include" /I "../../../../3rdParty/include" /D "_CONSOLE" /D "_MBCS" /D "FL_DLL" /D "WIN32" /D "_DEBUG" /YX /FD /Zm200 /c +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x809 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 OpenThreadsWin32d.lib opengl32.lib /nologo /subsystem:console /debug /machine:I386 /nodefaultlib:"libcmt" /out:"../../../bin/osgdepthpartitiond.exe" /pdbtype:sept /libpath:"../../../lib" /libpath:"../../../../OpenThreads/lib/win32" /libpath:"../../../../Producer/lib" /libpath:"../../../../3rdParty/lib" +# SUBTRACT LINK32 /incremental:no + +!ENDIF + +# Begin Target + +# Name "Example osgdepthpartition - Win32 Release" +# Name "Example osgdepthpartition - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\..\..\examples\osgdepthpartition\DepthPartitionNode.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\examples\osgdepthpartition\DistanceAccumulator.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\examples\osgdepthpartition\osgdepthpartition.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter ";h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\..\..\examples\osgdepthpartition\DepthPartitionNode.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\examples\osgdepthpartition\DistanceAccumulator.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\icons\osg_icon.rc +# End Source File +# End Group +# End Target +# End Project diff --git a/examples/osgdepthpartition/DepthPartitionNode.cpp b/examples/osgdepthpartition/DepthPartitionNode.cpp new file mode 100644 index 000000000..460d06d08 --- /dev/null +++ b/examples/osgdepthpartition/DepthPartitionNode.cpp @@ -0,0 +1,263 @@ +#include "DepthPartitionNode.h" +#include +#include + +#define CURRENT_CLASS DepthPartitionNode +CURRENT_CLASS::CURRENT_CLASS() +{ + _distAccumulator = new DistanceAccumulator; + init(); +} + +CURRENT_CLASS::CURRENT_CLASS(const CURRENT_CLASS& dpn, const osg::CopyOp& copyop) + : _active(dpn._active), + _renderOrder(dpn._renderOrder), + _clearColorBuffer(dpn._clearColorBuffer) +{ + _distAccumulator = new DistanceAccumulator; + _numCameras = 0; +} + +CURRENT_CLASS::~CURRENT_CLASS() {} + +void CURRENT_CLASS::init() +{ + _active = true; + _numCameras = 0; + setCullingActive(false); + _renderOrder = osg::CameraNode::POST_RENDER; + _clearColorBuffer = true; +} + +void CURRENT_CLASS::setActive(bool active) +{ + if(_active == active) return; + _active = active; +} + +void CURRENT_CLASS::setClearColorBuffer(bool clear) +{ + _clearColorBuffer = clear; + + // Update the render order for the first CameraNode if it exists + if(!_cameraList.empty()) + { + if(clear) + _cameraList[0]->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + else + _cameraList[0]->setClearMask(GL_DEPTH_BUFFER_BIT); + } +} + +void CURRENT_CLASS::setRenderOrder(osg::CameraNode::RenderOrder order) +{ + _renderOrder = order; + + // Update the render order for existing CameraNodes + unsigned int numCameras = _cameraList.size(); + for(unsigned int i = 0; i < numCameras; i++) + { + _cameraList[i]->setRenderOrder(_renderOrder); + } +} + +void CURRENT_CLASS::traverse(osg::NodeVisitor &nv) +{ + // If the scene hasn't been defined then don't do anything + unsigned int numChildren = _children.size(); + if(numChildren == 0) return; + + // If the node is not active then don't analyze it + if(!_active) + { + // Traverse the graph as usual + Group::traverse(nv); + return; + } + + // If the visitor is not a cull visitor, pass it directly onto the scene. + osgUtil::CullVisitor* cv = dynamic_cast(&nv); + if(!cv) + { + Group::traverse(nv); + return; + } + + // We are in the cull traversal, so first collect information on the + // current modelview and projection matrices and viewport. + osg::RefMatrix& modelview = cv->getModelViewMatrix(); + osg::RefMatrix& projection = cv->getProjectionMatrix(); + osg::Viewport* viewport = cv->getViewport(); + + // Prepare for scene traversal. + _distAccumulator->setMatrices(modelview, projection); + _distAccumulator->setNearFarRatio(cv->getNearFarRatio()); + _distAccumulator->reset(); + + // Step 1: Traverse the children, collecting the near/far distances. + unsigned int i; + for(i = 0; i < numChildren; i++) + { + _children[i]->accept(*(_distAccumulator.get())); + } + + // Step 2: Compute the near and far distances for every CameraNode that + // should be used to render the scene. + _distAccumulator->computeCameraPairs(); + + // Step 3: Create the CameraNodes, and add them as children. + DistanceAccumulator::PairList& camPairs = _distAccumulator->getCameraPairs(); + _numCameras = camPairs.size(); // Get the number of cameras + + // Create the CameraNodes, and add them as children. + if(_numCameras > 0) + { + osg::CameraNode *currCam; + DistanceAccumulator::DistancePair currPair; + + for(unsigned int j = 0; j < _numCameras; j++) + { + // Create the camera, and clamp it's projection matrix + currPair = camPairs[j]; // (near,far) pair for current camera + currCam = createOrReuseCamera(projection, currPair.first, + currPair.second, j); + + // Set the modelview matrix and viewport of the camera + currCam->setViewMatrix(modelview); + currCam->setViewport(viewport); + +/* + // Set our children as the camera's children + currCam->removeChild(0, currCam->getNumChildren()); + for(i = 0; i < numChildren; i++) + { + currCam->addChild(_children[i].get()); + } +*/ + + // Redirect the CullVisitor to the current camera + currCam->accept(nv); + } + + // Set the clear color for the first camera + _cameraList[0]->setClearColor(cv->getRenderStage()->getClearColor()); + } +} + +bool CURRENT_CLASS::addChild(osg::Node *child) +{ + return insertChild(_children.size(), child); +} + +bool CURRENT_CLASS::insertChild(unsigned int index, osg::Node *child) +{ + if(!Group::insertChild(index, child)) return false; // Insert child + + // Insert child into each CameraNode + unsigned int totalCameras = _cameraList.size(); + for(unsigned int i = 0; i < totalCameras; i++) + { + _cameraList[i]->insertChild(index, child); + } + return true; +} + +bool CURRENT_CLASS::removeChild(osg::Node *child) +{ + return Group::removeChild(child); +} + +bool CURRENT_CLASS::removeChild(unsigned int pos, unsigned int numRemove) +{ + if(!Group::removeChild(pos, numRemove)) return false; // Remove child + + // Remove child from each CameraNode + unsigned int totalCameras = _cameraList.size(); + for(unsigned int i = 0; i < totalCameras; i++) + { + _cameraList[i]->removeChild(pos, numRemove); + } + return true; +} + +bool CURRENT_CLASS::setChild(unsigned int i, osg::Node *node) +{ + if(!Group::setChild(i, node)) return false; // Set child + + // Set child for each CameraNode + unsigned int totalCameras = _cameraList.size(); + for(unsigned int i = 0; i < totalCameras; i++) + { + _cameraList[i]->setChild(i, node); + } + return true; +} + +osg::CameraNode* CURRENT_CLASS::createOrReuseCamera(const osg::Matrix& proj, + double near, double far, const unsigned int &camNum) +{ + if(_cameraList.size() <= camNum) _cameraList.resize(camNum+1); + osg::CameraNode *camera = _cameraList[camNum].get(); + + if(!camera) // Create a new CameraNode + { + camera = new osg::CameraNode; + camera->setCullingActive(false); + camera->setRenderOrder(_renderOrder); + camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF); + + // We will compute the near/far planes ourselves + camera->setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR); + camera->setCullingMode(osg::CullSettings::ENABLE_ALL_CULLING); + + if(camNum == 0 && _clearColorBuffer) + camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + else + camera->setClearMask(GL_DEPTH_BUFFER_BIT); + + // Add our children to the new CameraNode's children + unsigned int numChildren = _children.size(); + for(unsigned int i = 0; i < numChildren; i++) + { + camera->addChild(_children[i].get()); + } + + _cameraList[camNum] = camera; + } + + osg::Matrixd &projection = camera->getProjectionMatrix(); + projection = proj; + + // Slightly inflate the near & far planes to avoid objects at the + // extremes being clipped out. + near *= 0.999; + far *= 1.001; + + // Clamp the projection matrix z values to the range (near, far) + double epsilon = 1.0e-6; + if(fabs(projection(0,3)) < epsilon && + fabs(projection(1,3)) < epsilon && + fabs(projection(2,3)) < epsilon ) // Projection is Orthographic + { + epsilon = -1.0/(far - near); // Used as a temp variable + projection(2,2) = 2.0*epsilon; + projection(3,2) = (far + near)*epsilon; + } + else // Projection is Perspective + { + double trans_near = (-near*projection(2,2) + projection(3,2)) / + (-near*projection(2,3) + projection(3,3)); + double trans_far = (-far*projection(2,2) + projection(3,2)) / + (-far*projection(2,3) + projection(3,3)); + double ratio = fabs(2.0/(trans_near - trans_far)); + double center = -0.5*(trans_near + trans_far); + + projection.postMult(osg::Matrixd(1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, ratio, 0.0, + 0.0, 0.0, center*ratio, 1.0)); + } + + return camera; +} +#undef CURRENT_CLASS diff --git a/examples/osgdepthpartition/DepthPartitionNode.h b/examples/osgdepthpartition/DepthPartitionNode.h new file mode 100644 index 000000000..ab0a78bc5 --- /dev/null +++ b/examples/osgdepthpartition/DepthPartitionNode.h @@ -0,0 +1,77 @@ +#ifndef _OF_DEPTHPARTITIONNODE_ +#define _OF_DEPTHPARTITIONNODE_ + +#include "DistanceAccumulator.h" +#include + +#define CURRENT_CLASS DepthPartitionNode +/********************************************************** + * Ravi Mathur + * OpenFrames API, class DepthPartitionNode + * A type of osg::Group that analyzes a scene, then partitions it into + * several segments that can be rendered separately. Each segment + * is small enough in the z-direction to avoid depth buffer problems + * for very large scenes. +**********************************************************/ +class CURRENT_CLASS : public osg::Group +{ + public: + CURRENT_CLASS(); + CURRENT_CLASS(const CURRENT_CLASS& dpn, + const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY); + + META_Node( OpenFrames, CURRENT_CLASS ); // Common Node functions + + /** Set the active state. If not active, this node will simply add the + specified scene as it's child, without analyzing it at all. */ + void setActive(bool active); + inline bool getActive() const { return _active; } + + /** Specify whether the color buffer should be cleared before the first + CameraNode draws it's scene. */ + void setClearColorBuffer(bool clear); + inline bool getClearColorBuffer() const { return _clearColorBuffer; } + + /** Specify the render order for each CameraNode */ + void setRenderOrder(osg::CameraNode::RenderOrder order); + inline osg::CameraNode::RenderOrder getRenderOrder() const + { return _renderOrder; } + + /** Override update and cull traversals */ + virtual void traverse(osg::NodeVisitor &nv); + + /** Catch child management functions so the CameraNodes can be informed + of new/removed children */ + virtual bool addChild(osg::Node *child); + virtual bool insertChild(unsigned int index, osg::Node *child); + virtual bool removeChild(osg::Node *child); + virtual bool removeChild(unsigned int pos, unsigned int numRemove = 1); + virtual bool setChild(unsigned int i, osg::Node *node); + + protected: + typedef std::vector< osg::ref_ptr > CameraList; + + ~CURRENT_CLASS(); + + void init(); + + // Creates a new CameraNode object with default settings + osg::CameraNode* createOrReuseCamera(const osg::Matrix& proj, double near, + double far, const unsigned int &camNum); + + bool _active; // Whether partitioning is active on the scene + + // The NodeVisitor that computes cameras for the scene + osg::ref_ptr _distAccumulator; + + osg::CameraNode::RenderOrder _renderOrder; + bool _clearColorBuffer; + + // Cameras that should be used to draw the scene. These cameras + // will be reused on every frame in order to save time and memory. + CameraList _cameraList; + unsigned int _numCameras; // Number of CameraNodes actually being used +}; +#undef CURRENT_CLASS + +#endif diff --git a/examples/osgdepthpartition/DistanceAccumulator.cpp b/examples/osgdepthpartition/DistanceAccumulator.cpp new file mode 100644 index 000000000..fdc00a3fd --- /dev/null +++ b/examples/osgdepthpartition/DistanceAccumulator.cpp @@ -0,0 +1,282 @@ +#include "DistanceAccumulator.h" +#include +#include +#include +#include +#include + +/** Function that sees whether one DistancePair should come before another in + an sorted list. Used to sort the vector of DistancePairs. */ +bool precedes(const DistanceAccumulator::DistancePair &a, + const DistanceAccumulator::DistancePair &b) +{ + // This results in sorting in order of descending far distances + if(a.second > b.second) return true; + else return false; +} + +/** Computes distance betwen a point and the viewpoint of a matrix */ +double distance(const osg::Vec3 &coord, const osg::Matrix& matrix) +{ + return -( coord[0]*matrix(0,2) + coord[1]*matrix(1,2) + + coord[2]*matrix(2,2) + matrix(3,2) ); +} + +#define CURRENT_CLASS DistanceAccumulator +CURRENT_CLASS::CURRENT_CLASS() + : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN), _nearFarRatio(0.0) +{ + setMatrices(osg::Matrix::identity(), osg::Matrix::identity()); + reset(); + setNearFarRatio(0.0005); +} + +CURRENT_CLASS::~CURRENT_CLASS() {} + +void CURRENT_CLASS::pushLocalFrustum() +{ + osg::Matrix& currMatrix = _viewMatrices.back(); + + // Compute the frustum in local space + osg::Polytope localFrustum; + localFrustum.setToUnitFrustum(false, false); + localFrustum.transformProvidingInverse(currMatrix*_projectionMatrices.back()); + _localFrusta.push_back(localFrustum); + + // Compute new bounding box corners + osg::Vec3d lookVector(-currMatrix(0,2), -currMatrix(1,2), -currMatrix(2,2)); + bbCornerPair corner; + corner.second = (lookVector.x()>=0?1:0) | + (lookVector.y()>=0?2:0) | + (lookVector.z()>=0?4:0); + corner.first = (~corner.second)&7; + _bbCorners.push_back(corner); +} + +void CURRENT_CLASS::apply(osg::CameraNode &camera) +{ + // We don't support scenes with CameraNodes in them + return; +} + +void CURRENT_CLASS::apply(osg::Projection &proj) +{ + // Push the new projection matrix view frustum + _projectionMatrices.push_back(proj.getMatrix()); + pushLocalFrustum(); + + traverse(proj); // Traverse the rest of the scene graph + + // Reload original matrix and frustum + _localFrusta.pop_back(); + _bbCorners.pop_back(); + _projectionMatrices.pop_back(); +} + +void CURRENT_CLASS::apply(osg::Transform &transform) +{ + // Compute transform for current node + osg::Matrix currMatrix = _viewMatrices.back(); + bool pushMatrix = transform.computeLocalToWorldMatrix(currMatrix, this); + + if(pushMatrix) + { + // Store the new modelview matrix and view frustum + _viewMatrices.push_back(currMatrix); + pushLocalFrustum(); + } + + traverse(transform); // Traverse the rest of the scene graph + + if(pushMatrix) + { + // Restore the old modelview matrix and view frustum + _localFrusta.pop_back(); + _bbCorners.pop_back(); + _viewMatrices.pop_back(); + } +} + +void CURRENT_CLASS::apply(osg::Geode &geode) +{ + osg::Drawable *drawable; + double zNear, zFar; + + // Handle each drawable in this geode + for(unsigned int i = 0; i < geode.getNumDrawables(); i++) + { + drawable = geode.getDrawable(i); + + const osg::BoundingBox &bb = drawable->getBound(); + if(bb.valid()) + { + // Make sure drawable will be visible in the scene + if(!_localFrusta.back().contains(bb)) continue; + + // Compute near/far distances for current drawable + zNear = distance(bb.corner(_bbCorners.back().first), + _viewMatrices.back()); + zFar = distance(bb.corner(_bbCorners.back().second), + _viewMatrices.back()); + if(zNear > zFar) std::swap(zNear, zFar); + + if(zFar > 0.0) // Make sure some of drawable is visible + { + // Make sure near plane is in front of viewpoint. + if(zNear <= 0.0) + { + zNear = zFar*_nearFarRatio; + if(zNear >= 1.0) zNear = 1.0; // 1.0 limit chosen arbitrarily! + } + + // Add distance pair for current drawable and current rendering mode + _distancePairs.push_back(DistancePair(zNear, zFar)); + + // Override the current nearest/farthest planes if necessary + if(zNear < _limits.first) _limits.first = zNear; + if(zFar > _limits.second) _limits.second = zFar; + } + } + } +} + +void CURRENT_CLASS::setMatrices(const osg::Matrix &modelview, + const osg::Matrix &projection) +{ + _modelview = modelview; + _projection = projection; +} + +void CURRENT_CLASS::reset() +{ + // Clear vectors & values + _distancePairs.clear(); + _cameraPairs.clear(); + _limits.first = DBL_MAX; + _limits.second = 0.0; + + // Initial transform matrix is the modelview matrix + _viewMatrices.clear(); + _viewMatrices.push_back(_modelview); + + // Set the initial projection matrix + _projectionMatrices.clear(); + _projectionMatrices.push_back(_projection); + + // Create a frustum without near/far planes, for cull computations + _localFrusta.clear(); + _bbCorners.clear(); + pushLocalFrustum(); +} + +void CURRENT_CLASS::computeCameraPairs() +{ + // Nothing in the scene, so no cameras needed + if(_distancePairs.empty()) return; + + // Entire scene can be handled by just one camera + if(_limits.first >= _limits.second*_nearFarRatio) + { + _cameraPairs.push_back(_limits); + return; + } + + PairList::iterator i,j; + + // Sort the list of distance pairs by descending far distance + std::sort(_distancePairs.begin(), _distancePairs.end(), precedes); + + // Combine overlapping distance pairs. The resulting set of distance + // pairs (called combined pairs) will not overlap. + PairList combinedPairs; + DistancePair currPair = _distancePairs.front(); + for(i = _distancePairs.begin(); i != _distancePairs.end(); i++) + { + // Current distance pair does not overlap current combined pair, so + // save the current combined pair and start a new one. + if(i->second < 0.99*currPair.first) + { + combinedPairs.push_back(currPair); + currPair = *i; + } + + // Current distance pair overlaps current combined pair, so expand + // current combined pair to encompass distance pair. + else + currPair.first = std::min(i->first, currPair.first); + } + combinedPairs.push_back(currPair); // Add last pair + + // Compute the (near,far) distance pairs for each camera. + // Each of these distance pairs is called a "view segment". + double currNearLimit, numSegs, new_ratio; + double ratio_invlog = 1.0/log(_nearFarRatio); + unsigned int temp; + for(i = combinedPairs.begin(); i != combinedPairs.end(); i++) + { + currPair = *i; // Save current view segment + + // Compute the fractional number of view segments needed to span + // the current combined distance pair. + currNearLimit = currPair.second*_nearFarRatio; + if(currPair.first >= currNearLimit) numSegs = 1.0; + else + { + numSegs = log(currPair.first/currPair.second)*ratio_invlog; + + // Compute the near plane of the last view segment + //currNearLimit *= pow(_nearFarRatio, -floor(-numSegs) - 1); + for(temp = (unsigned int)(-floor(-numSegs)); temp > 1; temp--) + { + currNearLimit *= _nearFarRatio; + } + } + + // See if the closest view segment can absorb other combined pairs + for(j = i+1; j != combinedPairs.end(); j++) + { + // No other distance pairs can be included + if(j->first < currNearLimit) break; + } + + // If we did absorb another combined distance pair, recompute the + // number of required view segments. + if(i != j-1) + { + i = j-1; + currPair.first = i->first; + if(currPair.first >= currPair.second*_nearFarRatio) numSegs = 1.0; + else numSegs = log(currPair.first/currPair.second)*ratio_invlog; + } + + /* Compute an integer number of segments by rounding the fractional + number of segments according to how many segments there are. + In general, the more segments there are, the more likely that the + integer number of segments will be rounded down. + The purpose of this is to try to minimize the number of view segments + that are used to render any section of the scene without violating + the specified _nearFarRatio by too much. */ + if(numSegs < 10.0) numSegs = floor(numSegs + 1.0 - 0.1*floor(numSegs)); + else numSegs = floor(numSegs); + + + // Compute the near/far ratio that will be used for each view segment + // in this section of the scene. + new_ratio = pow(currPair.first/currPair.second, 1.0/numSegs); + + // Add numSegs new view segments to the camera pairs list + for(temp = (unsigned int)numSegs; temp > 0; temp--) + { + currPair.first = currPair.second*new_ratio; + _cameraPairs.push_back(currPair); + currPair.second = currPair.first; + } + } +} + +void CURRENT_CLASS::setNearFarRatio(double ratio) +{ + if(_nearFarRatio == ratio || ratio <= 0.0 || ratio >= 1.0) return; + _nearFarRatio = ratio; +} +#undef CURRENT_CLASS diff --git a/examples/osgdepthpartition/DistanceAccumulator.h b/examples/osgdepthpartition/DistanceAccumulator.h new file mode 100644 index 000000000..b13d0ae7e --- /dev/null +++ b/examples/osgdepthpartition/DistanceAccumulator.h @@ -0,0 +1,87 @@ +#ifndef _OF_DISTANCEACCUMULATOR_ +#define _OF_DISTANCEACCUMULATOR_ + +#include +#include +#include +#include + +#define CURRENT_CLASS DistanceAccumulator +/********************************************************** + * Ravi Mathur + * OpenFrames API, class DistanceAccumulator + * Class that traverses the scene and computes the distance to each + * visible drawable, and splits up the scene if the drawables are + * too far away (in the z direction) from each other. +**********************************************************/ +class CURRENT_CLASS : public osg::NodeVisitor +{ + public: + typedef std::pair DistancePair; + typedef std::vector PairList; + + CURRENT_CLASS(); + + virtual void apply(osg::CameraNode &camera); + virtual void apply(osg::Projection &proj); + virtual void apply(osg::Transform &transform); + virtual void apply(osg::Geode &geode); + + // Specify the modelview & projection matrices + void setMatrices(const osg::Matrix &modelview, + const osg::Matrix &projection); + + // Reset visitor before a new traversal + virtual void reset(); + + // Create a (near,far) distance pair for each camera of the specified + // distance pair list and distance limits. + void computeCameraPairs(); + + // Get info on the cameras that should be used for scene rendering + PairList& getCameraPairs() { return _cameraPairs; } + + // Get info on the computed distance pairs + PairList& getDistancePairs() { return _distancePairs; } + + // Get info on the computed nearest/farthest distances + DistancePair& getLimits() { return _limits; } + + // Set/get the desired near/far ratio + void setNearFarRatio(double ratio); + inline double getNearFarRatio() const { return _nearFarRatio; } + + protected: + virtual ~CURRENT_CLASS(); + + void pushLocalFrustum(); + + // Stack of matrices accumulated during traversal + osg::fast_back_stack _viewMatrices; + osg::fast_back_stack _projectionMatrices; + + // Main modelview/projection matrices + osg::Matrix _modelview, _projection; + + // The view frusta in local coordinate space + osg::fast_back_stack _localFrusta; + + // Bounding box corners that should be used for cull computation + typedef std::pair bbCornerPair; + osg::fast_back_stack _bbCorners; + + // Nar/far planes that should be used for each camera + PairList _cameraPairs; + + // Accumulated pairs of min/max distances + PairList _distancePairs; + + // The closest & farthest distances found while traversing + DistancePair _limits; + + // Ratio of nearest/farthest clip plane for each section of the scene + double _nearFarRatio; +}; +#undef CURRENT_CLASS + +#endif diff --git a/examples/osgdepthpartition/GNUmakefile b/examples/osgdepthpartition/GNUmakefile new file mode 100644 index 000000000..411d90156 --- /dev/null +++ b/examples/osgdepthpartition/GNUmakefile @@ -0,0 +1,20 @@ +TOPDIR = ../.. +include $(TOPDIR)/Make/makedefs + +CXXFILES =\ + osgdepthpartition.cpp\ + DepthPartitionNode.cpp\ + DistanceAccumulator.cpp\ + +LIBS += -losgProducer -lProducer -losgText -losgGA -losgDB -losgUtil -losg $(GL_LIBS) $(X_LIBS) $(OTHER_LIBS) + +INSTFILES = \ + $(CXXFILES)\ + GNUmakefile.inst=GNUmakefile + +EXEC = osgdepthpartion + +INC += $(X_INC) + +include $(TOPDIR)/Make/makerules + diff --git a/examples/osgdepthpartition/GNUmakefile.inst b/examples/osgdepthpartition/GNUmakefile.inst new file mode 100644 index 000000000..4afdd3b2c --- /dev/null +++ b/examples/osgdepthpartition/GNUmakefile.inst @@ -0,0 +1,15 @@ +TOPDIR = ../.. +include $(TOPDIR)/Make/makedefs + +CXXFILES =\ + osgdepthpartition.cpp\ + DepthPartitionNode.cpp\ + DistanceAccumulator.cpp\ + +LIBS += -losgProducer -lProducer -losgDB -losgText -losgUtil -losg $(GL_LIBS) $(X_LIBS) $(OTHER_LIBS) + +EXEC = osgdepthpartion + +INC += $(X_INC) + +include $(TOPDIR)/Make/makerules diff --git a/examples/osgdepthpartition/osgdepthpartition.cpp b/examples/osgdepthpartition/osgdepthpartition.cpp new file mode 100644 index 000000000..37b5b82c3 --- /dev/null +++ b/examples/osgdepthpartition/osgdepthpartition.cpp @@ -0,0 +1,217 @@ +//C++ source file - Open Producer - Copyright (C) 2002 Don Burns +//Distributed under the terms of the GNU LIBRARY GENERAL PUBLIC LICENSE (LGPL) +//as published by the Free Software Foundation. + +// Simple example of use of Producer::RenderSurface +// The myGraphics class is a simple sample of how one would implement +// graphics drawing with Producer::RenderSurface + +#include + +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include +#include + +#include +#include + +#include "DepthPartitionNode.h" + +class MyKeyboardMouseCallback : public Producer::KeyboardMouseCallback +{ + public: + + MyKeyboardMouseCallback() : + Producer::KeyboardMouseCallback(), + _mx(0.0f),_my(0.0f),_mbutton(0), + _done(false) + {} + + virtual void specialKeyPress( Producer::KeyCharacter key ) + { + if (key==Producer::KeyChar_Escape) + shutdown(); + } + + virtual void shutdown() + { + _done = true; + } + + virtual void keyPress( Producer::KeyCharacter ) + { + } + + virtual void mouseMotion( float mx, float my ) + { + _mx = mx; + _my = my; + } + virtual void buttonPress( float mx, float my, unsigned int mbutton ) + { + _mx = mx; + _my = my; + _mbutton |= (1<<(mbutton-1)); + } + virtual void buttonRelease( float mx, float my, unsigned int mbutton ) + { + _mx = mx; + _my = my; + _mbutton &= ~(1<<(mbutton-1)); + } + + bool done() { return _done; } + float mx() { return _mx; } + float my() { return _my; } + unsigned int mbutton() { return _mbutton; } + + private: + + float _mx, _my; + unsigned int _mbutton; + bool _done; +}; + +const double r_earth = 6378.137; +const double r_sun = 695990.0; +const double AU = 149697900.0; + +osg::Node* createScene() +{ + // Create the Earth, in blue + osg::ShapeDrawable *earth_sd = new osg::ShapeDrawable; + osg::Sphere* earth_sphere = new osg::Sphere; + earth_sphere->setRadius(r_earth); + earth_sd->setShape(earth_sphere); + earth_sd->setColor(osg::Vec4(0, 0, 1.0, 1.0)); + + osg::Geode* earth = new osg::Geode; + earth->setName("earth"); + earth->addDrawable(earth_sd); + + // Create the Sun, in yellow + osg::ShapeDrawable *sun_sd = new osg::ShapeDrawable; + osg::Sphere* sun_sphere = new osg::Sphere; + sun_sphere->setRadius(r_sun); + sun_sd->setShape(sun_sphere); + sun_sd->setColor(osg::Vec4(1.0, 0.0, 0.0, 1.0)); + + osg::Geode* sun = new osg::Geode; + sun->setName("sun"); + sun->addDrawable(sun_sd); + + // Move the sun behind the earth + osg::PositionAttitudeTransform *pat = new osg::PositionAttitudeTransform; + pat->setPosition(osg::Vec3d(0.0, AU, 0.0)); + + osg::Group* scene = new osg::Group; + scene->addChild(earth); + scene->addChild(pat); + pat->addChild(sun); + + return scene; +} + +int main( int argc, char **argv ) +{ + + // use an ArgumentParser object to manage the program arguments. + osg::ArgumentParser arguments(&argc,argv); + + // set up the usage document, in case we need to print out how to use this program. + arguments.getApplicationUsage()->setApplicationName(arguments.getApplicationName()); + arguments.getApplicationUsage()->setDescription(arguments.getApplicationName()+" is the standard example of using osgProducer::CameraGroup."); + arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName()+" [options] ..."); + arguments.getApplicationUsage()->addCommandLineOption("-h or --help","Display this information"); + + // create the camera group. + osgProducer::OsgCameraGroup cg(arguments); + + // if user request help write it out to cout. + if (arguments.read("-h") || arguments.read("--help")) + { + arguments.getApplicationUsage()->write(std::cout); + return 1; + } + + // any option left unread are converted into errors to write out later. + arguments.reportRemainingOptionsAsUnrecognized(); + + // report any errors if they have occured when parsing the program aguments. + if (arguments.errors()) + { + arguments.writeErrorMessages(std::cout); + return 1; + } + + // set up the keyboard and mouse handling. + Producer::InputArea *ia = cg.getCameraConfig()->getInputArea(); + Producer::KeyboardMouse *kbm = ia ? + (new Producer::KeyboardMouse(ia)) : + (new Producer::KeyboardMouse(cg.getCamera(0)->getRenderSurface())); + + + osg::ref_ptr kbmcb = new MyKeyboardMouseCallback; + + // register the callback with the keyboard mouse manger. + kbm->setCallback( kbmcb.get() ); + kbm->startThread(); + + // Create a DepthPartitionNode to manage partitioning of the scene + DepthPartitionNode *dpn = new DepthPartitionNode; + osg::Node* scene = createScene(); + dpn->addChild(scene); + dpn->setActive(true); // Control whether the node analyzes the scene + + // set the scene to render + cg.setSceneData(dpn); + + // Set the view to be looking at the earth + osg::BoundingSphere bs; + bs._radius = r_earth; + + osg::ref_ptr tb = new Producer::Trackball; + tb->setOrientation( Producer::Trackball::Z_UP ); + tb->setDistance(bs.radius()*10.0f); + tb->translate(-bs.center().x(),-bs.center().y(),-bs.center().z()); + + // create the windows and run the threads. + cg.realize(); + + osgUtil::UpdateVisitor update; + + while( !kbmcb->done() ) + { + // syncronize to the when cull and draw threads have completed. + cg.sync(); + + // update the scene by traversing it with the the update visitor which will + // call all node update callbacks and animations. + cg.getSceneData()->accept(update); + + tb->input( kbmcb->mx(), kbmcb->my(), kbmcb->mbutton() ); + + // update the main producer camera + cg.setViewByMatrix(tb->getMatrix()); + + // fire off the cull and draw traversals of the scene. + cg.frame(); + } + + // syncronize to the when cull and draw threads have completed. + cg.sync(); + + return 0; +} +