OpenSceneGraph/examples/osgdepthpartition/DepthPartitionNode.cpp
2005-11-10 19:16:01 +00:00

256 lines
7.2 KiB
C++

#include "DepthPartitionNode.h"
#include <osgUtil/CullVisitor>
#define CURRENT_CLASS DepthPartitionNode
CURRENT_CLASS::CURRENT_CLASS()
{
_distAccumulator = new DistanceAccumulator;
init();
}
CURRENT_CLASS::CURRENT_CLASS(const CURRENT_CLASS& dpn, const osg::CopyOp& copyop)
: Group(dpn, 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<osgUtil::CullVisitor*>(&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(i = 0; i < _numCameras; i++)
{
// Create the camera, and clamp it's projection matrix
currPair = camPairs[i]; // (near,far) pair for current camera
currCam = createOrReuseCamera(projection, currPair.first,
currPair.second, i);
// Set the modelview matrix and viewport of the camera
currCam->setViewMatrix(modelview);
currCam->setViewport(viewport);
// 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 j = 0; j < totalCameras; j++)
{
_cameraList[j]->setChild(i, node);
}
return true;
}
osg::CameraNode* CURRENT_CLASS::createOrReuseCamera(const osg::Matrix& proj,
double znear, double zfar,
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.
znear *= 0.999;
zfar *= 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/(zfar - znear); // Used as a temp variable
projection(2,2) = 2.0*epsilon;
projection(3,2) = (zfar + znear)*epsilon;
}
else // Projection is Perspective
{
double trans_near = (-znear*projection(2,2) + projection(3,2)) /
(-znear*projection(2,3) + projection(3,3));
double trans_far = (-zfar*projection(2,2) + projection(3,2)) /
(-zfar*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