// Copyright (C) 2008 Tim Moore // // Much of this file was copied directly from the osgdepthpartition // example in the OpenSceneGraph distribution, so I feel it is right // to preserve the license in that code: /* * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include #include // For DotOsgWrapperProxy #include #include #include #include #include "ViewPartitionNode.hxx" using namespace osg; const double ViewPartitionNode::nearCameraFar = 100.0; ViewPartitionNode::ViewPartitionNode(): cameras(2), visibility(40000.0) { const GLbitfield inheritanceMask = (CullSettings::ALL_VARIABLES & ~CullSettings::COMPUTE_NEAR_FAR_MODE & ~CullSettings::NEAR_FAR_RATIO & ~CullSettings::CULLING_MODE); int i = 1; for (CameraList::iterator iter = cameras.begin(); iter != cameras.end(); ++iter, ++i) { Camera* camera = new Camera; camera->setReferenceFrame(Transform::RELATIVE_RF); camera->setInheritanceMask(inheritanceMask); camera->setComputeNearFarMode(CullSettings::DO_NOT_COMPUTE_NEAR_FAR); camera->setCullingMode(CullSettings::VIEW_FRUSTUM_CULLING); camera->setRenderOrder(Camera::POST_RENDER, i); // Slave camera "above" us clears the viewport. Perhaps the // far camera doesn't need to clear the depth buffer; it // probably doesn't make much difference. camera->setClearMask(GL_DEPTH_BUFFER_BIT); *iter = camera; } } ViewPartitionNode::ViewPartitionNode(const ViewPartitionNode& rhs, const CopyOp& copyop) : cameras(2), visibility(rhs.visibility) { for (int i = 0; i < 2; i++) cameras[i] = static_cast(copyop(rhs.cameras[i].get())); } void ViewPartitionNode::traverse(NodeVisitor& nv) { osgUtil::CullVisitor* cv = dynamic_cast(&nv); if(!cv) { Group::traverse(nv); return; } RefMatrix& modelview = *(cv->getModelViewMatrix()); RefMatrix& projection = *(cv->getProjectionMatrix()); // Get the frustum of the enclosing camera. The ViewPartitionNode // will divide up the frustum between that camera's near and far // planes i.e., parentNear and ParentFar. double left, right, bottom, top, parentNear, parentFar; projection.getFrustum(left, right, bottom, top, parentNear, parentFar); double nearPlanes[2], farPlanes[2]; nearPlanes[0] = nearCameraFar; farPlanes[0] = parentFar; nearPlanes[1] = parentNear; farPlanes[1] = nearCameraFar; for (int i = 0; i < 2; ++i) { if (farPlanes[i] >0.0) { ref_ptr newProj = new RefMatrix(); makeNewProjMat(projection, nearPlanes[i], farPlanes[i], *newProj.get()); cv->pushProjectionMatrix(newProj.get()); if (i == NEAR_CAMERA) { // The near camera shouldn't draw the background, // which is hard to cull out. unsigned int savedTraversalMask = cv->getTraversalMask(); cv->setTraversalMask(savedTraversalMask & ~simgear::BACKGROUND_BIT); cameras[i]->accept(nv); cv->setTraversalMask(savedTraversalMask); } else { cameras[i]->accept(nv); } cv->popProjectionMatrix(); } } } bool ViewPartitionNode::addChild(osg::Node *child) { return insertChild(_children.size(), child); } bool ViewPartitionNode::insertChild(unsigned int index, osg::Node *child) { if(!Group::insertChild(index, child)) return false; // Insert child into each Camera for (CameraList::iterator iter = cameras.begin(); iter != cameras.end(); ++iter) { (*iter)->insertChild(index, child); } return true; } bool ViewPartitionNode::removeChildren(unsigned int pos, unsigned int numRemove) { if (!Group::removeChildren(pos, numRemove)) return false; // Remove child from each Camera for (CameraList::iterator iter = cameras.begin(); iter != cameras.end(); ++iter) { (*iter)->removeChildren(pos, numRemove); } return true; } bool ViewPartitionNode::setChild(unsigned int i, osg::Node *node) { if(!Group::setChild(i, node)) return false; // Set child // Set child for each Camera for (CameraList::iterator iter = cameras.begin(); iter != cameras.end(); ++iter) { (*iter)->setChild(i, node); } return true; } // Given a projection matrix, return a new one with the same frustum // sides and new near / far values. void ViewPartitionNode::makeNewProjMat(Matrixd& oldProj, double znear, double zfar, Matrixd& projection) { projection = oldProj; // 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)); } } namespace { bool ViewPartitionNode_readLocalData(osg::Object& obj, osgDB::Input& fr) { ViewPartitionNode& node = static_cast(obj); if (fr[0].matchWord("visibility")) { ++fr; double visibility; if (fr[0].getFloat(visibility)) ++fr; else return false; node.setVisibility(visibility); } return true; } bool ViewPartitionNode_writeLocalData(const Object& obj, osgDB::Output& fw) { const ViewPartitionNode& node = static_cast(obj); fw.indent() << "visibility " << node.getVisibility() << std::endl; return true; } osgDB::RegisterDotOsgWrapperProxy viewPartitionNodeProxy ( new ViewPartitionNode, "ViewPartitionNode", "Object Node ViewPartitionNode Group", &ViewPartitionNode_readLocalData, &ViewPartitionNode_writeLocalData ); }