1 // Copyright (C) 2008 Tim Moore
3 // Much of this file was copied directly from the osgdepthpartition
4 // example in the OpenSceneGraph distribution, so I feel it is right
5 // to preserve the license in that code:
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 #include <osg/CullSettings>
25 #include <osg/StateSet>
26 #include <osgUtil/CullVisitor>
27 // For DotOsgWrapperProxy
28 #include <osgDB/Registry>
29 #include <osgDB/Input>
30 #include <osgDB/Output>
32 #include <simgear/scene/util/RenderConstants.hxx>
33 #include "ViewPartitionNode.hxx"
37 const double ViewPartitionNode::nearCameraFar = 100.0;
38 ViewPartitionNode::ViewPartitionNode():
39 cameras(2), visibility(40000.0)
41 const GLbitfield inheritanceMask = (CullSettings::ALL_VARIABLES
42 & ~CullSettings::COMPUTE_NEAR_FAR_MODE
43 & ~CullSettings::NEAR_FAR_RATIO
44 & ~CullSettings::CULLING_MODE);
46 for (CameraList::iterator iter = cameras.begin();
47 iter != cameras.end();
49 Camera* camera = new Camera;
50 camera->setReferenceFrame(Transform::RELATIVE_RF);
51 camera->setInheritanceMask(inheritanceMask);
52 camera->setComputeNearFarMode(CullSettings::DO_NOT_COMPUTE_NEAR_FAR);
53 camera->setCullingMode(CullSettings::VIEW_FRUSTUM_CULLING);
54 camera->setRenderOrder(Camera::POST_RENDER, i);
55 // Slave camera "above" us clears the viewport. Perhaps the
56 // far camera doesn't need to clear the depth buffer; it
57 // probably doesn't make much difference.
58 camera->setClearMask(GL_DEPTH_BUFFER_BIT);
63 ViewPartitionNode::ViewPartitionNode(const ViewPartitionNode& rhs,
64 const CopyOp& copyop) :
65 cameras(2), visibility(rhs.visibility)
67 for (int i = 0; i < 2; i++)
68 cameras[i] = static_cast<Camera*>(copyop(rhs.cameras[i].get()));
71 void ViewPartitionNode::traverse(NodeVisitor& nv)
73 osgUtil::CullVisitor* cv = dynamic_cast<osgUtil::CullVisitor*>(&nv);
78 RefMatrix& modelview = *(cv->getModelViewMatrix());
79 RefMatrix& projection = *(cv->getProjectionMatrix());
80 // Get the frustum of the enclosing camera. The ViewPartitionNode
81 // will divide up the frustum between that camera's near and far
82 // planes i.e., parentNear and ParentFar.
83 double left, right, bottom, top, parentNear, parentFar;
84 projection.getFrustum(left, right, bottom, top, parentNear, parentFar);
85 double nearPlanes[2], farPlanes[2];
86 nearPlanes[0] = nearCameraFar;
87 farPlanes[0] = parentFar;
88 nearPlanes[1] = parentNear;
89 farPlanes[1] = nearCameraFar;
90 for (int i = 0; i < 2; ++i) {
91 if (farPlanes[i] >0.0) {
92 ref_ptr<RefMatrix> newProj = new RefMatrix();
93 makeNewProjMat(projection, nearPlanes[i], farPlanes[i],
95 cv->pushProjectionMatrix(newProj.get());
96 if (i == NEAR_CAMERA) {
97 // The near camera shouldn't draw the background,
98 // which is hard to cull out.
99 unsigned int savedTraversalMask = cv->getTraversalMask();
100 cv->setTraversalMask(savedTraversalMask
101 & ~simgear::BACKGROUND_BIT);
102 cameras[i]->accept(nv);
103 cv->setTraversalMask(savedTraversalMask);
105 cameras[i]->accept(nv);
107 cv->popProjectionMatrix();
112 bool ViewPartitionNode::addChild(osg::Node *child)
114 return insertChild(_children.size(), child);
117 bool ViewPartitionNode::insertChild(unsigned int index, osg::Node *child)
119 if(!Group::insertChild(index, child))
121 // Insert child into each Camera
123 for (CameraList::iterator iter = cameras.begin();
124 iter != cameras.end();
126 (*iter)->insertChild(index, child);
131 bool ViewPartitionNode::removeChildren(unsigned int pos, unsigned int numRemove)
133 if (!Group::removeChildren(pos, numRemove))
136 // Remove child from each Camera
138 for (CameraList::iterator iter = cameras.begin();
139 iter != cameras.end();
141 (*iter)->removeChildren(pos, numRemove);
146 bool ViewPartitionNode::setChild(unsigned int i, osg::Node *node)
148 if(!Group::setChild(i, node)) return false; // Set child
150 // Set child for each Camera
151 for (CameraList::iterator iter = cameras.begin();
152 iter != cameras.end();
154 (*iter)->setChild(i, node);
159 // Given a projection matrix, return a new one with the same frustum
160 // sides and new near / far values.
162 void ViewPartitionNode::makeNewProjMat(Matrixd& oldProj, double znear,
163 double zfar, Matrixd& projection)
165 projection = oldProj;
166 // Slightly inflate the near & far planes to avoid objects at the
167 // extremes being clipped out.
171 // Clamp the projection matrix z values to the range (near, far)
172 double epsilon = 1.0e-6;
173 if (fabs(projection(0,3)) < epsilon &&
174 fabs(projection(1,3)) < epsilon &&
175 fabs(projection(2,3)) < epsilon) {
176 // Projection is Orthographic
177 epsilon = -1.0/(zfar - znear); // Used as a temp variable
178 projection(2,2) = 2.0*epsilon;
179 projection(3,2) = (zfar + znear)*epsilon;
181 // Projection is Perspective
182 double trans_near = (-znear*projection(2,2) + projection(3,2)) /
183 (-znear*projection(2,3) + projection(3,3));
184 double trans_far = (-zfar*projection(2,2) + projection(3,2)) /
185 (-zfar*projection(2,3) + projection(3,3));
186 double ratio = fabs(2.0/(trans_near - trans_far));
187 double center = -0.5*(trans_near + trans_far);
189 projection.postMult(osg::Matrixd(1.0, 0.0, 0.0, 0.0,
191 0.0, 0.0, ratio, 0.0,
192 0.0, 0.0, center*ratio, 1.0));
198 bool ViewPartitionNode_readLocalData(osg::Object& obj, osgDB::Input& fr)
200 ViewPartitionNode& node = static_cast<ViewPartitionNode&>(obj);
201 if (fr[0].matchWord("visibility")) {
204 if (fr[0].getFloat(visibility))
208 node.setVisibility(visibility);
213 bool ViewPartitionNode_writeLocalData(const Object& obj, osgDB::Output& fw)
215 const ViewPartitionNode& node = static_cast<const ViewPartitionNode&>(obj);
216 fw.indent() << "visibility " << node.getVisibility() << std::endl;
220 osgDB::RegisterDotOsgWrapperProxy viewPartitionNodeProxy
222 new ViewPartitionNode,
224 "Object Node ViewPartitionNode Group",
225 &ViewPartitionNode_readLocalData,
226 &ViewPartitionNode_writeLocalData