]> git.mxchange.org Git - flightgear.git/blob - src/Main/ViewPartitionNode.cxx
Merge branch 'jt/runway' into next
[flightgear.git] / src / Main / ViewPartitionNode.cxx
1 // Copyright (C) 2008 Tim Moore
2 //
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:
6 /*
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:
13 *
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
20 *  THE SOFTWARE.
21 */
22
23 #include <osg/CullSettings>
24 #include <osg/Depth>
25 #include <osg/StateSet>
26 #include <osgUtil/CullVisitor>
27 // For DotOsgWrapperProxy
28 #include <osgDB/Registry>
29 #include <osgDB/Input>
30 #include <osgDB/Output>
31
32 #include <simgear/scene/util/RenderConstants.hxx>
33 #include "ViewPartitionNode.hxx"
34
35 using namespace osg;
36
37 const double ViewPartitionNode::nearCameraFar = 100.0;
38 ViewPartitionNode::ViewPartitionNode():
39     cameras(2), visibility(40000.0)
40 {
41     const GLbitfield inheritanceMask = (CullSettings::ALL_VARIABLES
42                                         & ~CullSettings::COMPUTE_NEAR_FAR_MODE
43                                         & ~CullSettings::NEAR_FAR_RATIO
44                                         & ~CullSettings::CULLING_MODE);
45     int i = 1;
46     for (CameraList::iterator iter = cameras.begin();
47          iter != cameras.end();
48          ++iter, ++i) {
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);
59         *iter = camera;
60     }
61 }
62
63 ViewPartitionNode::ViewPartitionNode(const ViewPartitionNode& rhs,
64                                      const CopyOp& copyop) :
65     cameras(2), visibility(rhs.visibility)
66 {
67     for (int i = 0; i < 2; i++)
68         cameras[i] = static_cast<Camera*>(copyop(rhs.cameras[i].get()));
69 }
70
71 void ViewPartitionNode::traverse(NodeVisitor& nv)
72 {
73     osgUtil::CullVisitor* cv = dynamic_cast<osgUtil::CullVisitor*>(&nv);
74     if(!cv) { 
75         Group::traverse(nv);
76         return; 
77     }
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],
94                            *newProj.get());
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);
104             } else {
105                 cameras[i]->accept(nv);
106             }
107             cv->popProjectionMatrix();
108         }
109     }
110 }
111
112 bool ViewPartitionNode::addChild(osg::Node *child)
113 {
114     return insertChild(_children.size(), child);
115 }
116
117 bool ViewPartitionNode::insertChild(unsigned int index, osg::Node *child)
118 {
119     if(!Group::insertChild(index, child))
120         return false;
121     // Insert child into each Camera
122
123     for (CameraList::iterator iter = cameras.begin();
124          iter != cameras.end();
125          ++iter) {
126         (*iter)->insertChild(index, child);
127     }
128     return true;
129 }
130
131 bool ViewPartitionNode::removeChildren(unsigned int pos, unsigned int numRemove)
132 {
133     if (!Group::removeChildren(pos, numRemove))
134         return false;
135
136     // Remove child from each Camera
137
138     for (CameraList::iterator iter = cameras.begin();
139          iter != cameras.end();
140          ++iter) {
141         (*iter)->removeChildren(pos, numRemove);
142     }
143     return true;
144 }
145
146 bool ViewPartitionNode::setChild(unsigned int i, osg::Node *node)
147 {
148     if(!Group::setChild(i, node)) return false; // Set child
149
150     // Set child for each Camera
151     for (CameraList::iterator iter = cameras.begin();
152          iter != cameras.end();
153          ++iter) {
154         (*iter)->setChild(i, node);
155     }
156     return true;
157 }
158
159 // Given a projection matrix, return a new one with the same frustum
160 // sides and new near / far values.
161
162 void ViewPartitionNode::makeNewProjMat(Matrixd& oldProj, double znear,
163                                        double zfar, Matrixd& projection)
164 {
165     projection = oldProj;
166     // Slightly inflate the near & far planes to avoid objects at the
167     // extremes being clipped out.
168     znear *= 0.999;
169     zfar *= 1.001;
170
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;
180     } else {
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);
188
189         projection.postMult(osg::Matrixd(1.0, 0.0, 0.0, 0.0,
190                                          0.0, 1.0, 0.0, 0.0,
191                                          0.0, 0.0, ratio, 0.0,
192                                          0.0, 0.0, center*ratio, 1.0));
193     }
194 }
195
196 namespace
197 {
198 bool ViewPartitionNode_readLocalData(osg::Object& obj, osgDB::Input& fr)
199 {
200     ViewPartitionNode& node = static_cast<ViewPartitionNode&>(obj);
201     if (fr[0].matchWord("visibility")) {
202         ++fr;
203         double visibility;
204         if (fr[0].getFloat(visibility))
205             ++fr;
206         else
207             return false;
208         node.setVisibility(visibility);
209     }
210     return true;
211 }
212
213 bool ViewPartitionNode_writeLocalData(const Object& obj, osgDB::Output& fw)
214 {
215     const ViewPartitionNode& node = static_cast<const ViewPartitionNode&>(obj);
216     fw.indent() << "visibility " << node.getVisibility() << std::endl;
217     return true;
218 }
219
220 osgDB::RegisterDotOsgWrapperProxy viewPartitionNodeProxy
221 (
222     new ViewPartitionNode,
223     "ViewPartitionNode",
224     "Object Node ViewPartitionNode Group",
225     &ViewPartitionNode_readLocalData,
226     &ViewPartitionNode_writeLocalData
227     );
228 }