1 /* -*-c++-*- */
2 /* osgEarth - Geospatial SDK for OpenSceneGraph
3 * Copyright 2019 Pelican Mapping
4 * http://osgearth.org
5 *
6 * osgEarth is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
12 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
14 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
15 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
16 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
17 * IN THE SOFTWARE.
18 *
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>
21 */
22 #include <osgEarth/DrapingTechnique>
23 #include <osgEarth/Capabilities>
24 #include <osgEarth/Registry>
25 #include <osgEarth/Shaders>
26 #include <osgEarth/Lighting>
27
28 #include <osg/BlendFunc>
29 #include <osg/Texture2D>
30
31 #define LC "[DrapingTechnique] "
32
33 //#define OE_TEST if (_dumpRequested) OE_INFO << std::setprecision(9)
34 #define OE_TEST OE_NULL
35
36 using namespace osgEarth;
37
38 //---------------------------------------------------------------------------
39
40 #undef LC
41 #define LC "[DrapingCamera] "
42
43 namespace
44 {
45 struct RenderBinSortingVisitor
46 {
apply__anon411a13980111::RenderBinSortingVisitor47 void apply(osgUtil::RenderBin* bin)
48 {
49 bin->setSortMode(osgUtil::RenderBin::TRAVERSAL_ORDER);
50
51 for(osgUtil::RenderBin::RenderBinList::iterator i = bin->getRenderBinList().begin();
52 i != bin->getRenderBinList().end();
53 ++i)
54 {
55 osgUtil::RenderBin* child = i->second.get();
56 apply(child);
57 }
58 }
59 };
60
61 /**
62 * A camera that will traverse the per-thread DrapingCullSet instead of
63 * its own children.
64 */
65 class DrapingCamera : public osg::Camera
66 {
67 public:
DrapingCamera(DrapingManager & dm)68 DrapingCamera(DrapingManager& dm) : osg::Camera(), _dm(dm), _camera(0L)
69 {
70 setCullingActive( false );
71 osg::StateSet* ss = getOrCreateStateSet();
72 ss->setMode(GL_DEPTH_TEST, 0);
73 ss->setRenderBinDetails(1, "TraversalOrderBin", osg::StateSet::OVERRIDE_PROTECTED_RENDERBIN_DETAILS);
74 }
75
76 public: // osg::Node
77
accept(osg::NodeVisitor & nv,const osg::Camera * camera)78 void accept(osg::NodeVisitor& nv, const osg::Camera* camera)
79 {
80 _camera = camera;
81 osg::Camera::accept( nv );
82 }
83
traverse(osg::NodeVisitor & nv)84 void traverse(osg::NodeVisitor& nv)
85 {
86 DrapingCullSet& cullSet = _dm.get(_camera);
87 cullSet.accept( nv );
88
89 // manhandle the render bin sorting, since OSG ignores the override
90 // in the render bin details above
91 osgUtil::CullVisitor* cv = dynamic_cast<osgUtil::CullVisitor*>(&nv);
92 if (cv)
93 {
94 applyTraversalOrderSorting(cv->getCurrentRenderBin());
95 }
96 }
97
applyTraversalOrderSorting(osgUtil::RenderBin * bin)98 void applyTraversalOrderSorting(osgUtil::RenderBin* bin)
99 {
100 bin->setSortMode(osgUtil::RenderBin::TRAVERSAL_ORDER);
101
102 for (osgUtil::RenderBin::RenderBinList::iterator i = bin->getRenderBinList().begin();
103 i != bin->getRenderBinList().end();
104 ++i)
105 {
106 osgUtil::RenderBin* child = i->second.get();
107 applyTraversalOrderSorting(child);
108 }
109 }
110
111 protected:
~DrapingCamera()112 virtual ~DrapingCamera() { }
113 DrapingManager& _dm;
114 const osg::Camera* _camera;
115 };
116
117
118 // Additional per-view data stored by the draping technique.
119 struct LocalPerViewData : public osg::Object
120 {
121 META_Object(osgEarth, LocalPerViewData);
122
123 osg::ref_ptr<osg::Uniform> _texGenUniform;
124
resizeGLObjectBuffers__anon411a13980111::LocalPerViewData125 void resizeGLObjectBuffers(unsigned maxSize) {
126 if (_texGenUniform.valid())
127 _texGenUniform->resizeGLObjectBuffers(maxSize);
128 }
releaseGLObjects__anon411a13980111::LocalPerViewData129 void releaseGLObjects(osg::State* state) const {
130 if (_texGenUniform.valid())
131 _texGenUniform->releaseGLObjects(state);
132 }
133
LocalPerViewData__anon411a13980111::LocalPerViewData134 LocalPerViewData() { }
LocalPerViewData__anon411a13980111::LocalPerViewData135 LocalPerViewData(const LocalPerViewData& rhs, const osg::CopyOp& co) { }
136 };
137 }
138
139 //---------------------------------------------------------------------------
140
141 namespace
142 {
143 struct Line2d
144 {
intersectRaysXY__anon411a13980211::Line2d145 bool intersectRaysXY(
146 const osg::Vec3d& p0, const osg::Vec3d& d0,
147 const osg::Vec3d& p1, const osg::Vec3d& d1,
148 osg::Vec3d& out_p,
149 double& out_u,
150 double& out_v) const
151 {
152 static const double epsilon = 0.001;
153
154 double det = d0.y()*d1.x() - d0.x()*d1.y();
155 if ( osg::equivalent(det, 0.0, epsilon) )
156 return false; // parallel
157
158 out_u = (d1.x()*(p1.y()-p0.y())+d1.y()*(p0.x()-p1.x()))/det;
159 out_v = (d0.x()*(p1.y()-p0.y())+d0.y()*(p0.x()-p1.x()))/det;
160 out_p = p0 + d0*out_u;
161 return true;
162 }
163
164 osg::Vec3d _a, _b;
165
Line2d__anon411a13980211::Line2d166 Line2d(const osg::Vec3d& p0, const osg::Vec3d& p1) : _a(p0), _b(p1) { }
167
Line2d__anon411a13980211::Line2d168 Line2d(const osg::Vec4d& p0, const osg::Vec4d& p1)
169 : _a(p0.x()/p0.w(), p0.y()/p0.w(), p0.x()/p0.w()), _b(p1.x()/p1.w(), p1.y()/p1.w(), p1.z()/p1.w()) { }
170
intersect__anon411a13980211::Line2d171 bool intersect(const Line2d& rhs, osg::Vec4d& out) const {
172 double u, v;
173 osg::Vec3d temp;
174 bool ok = intersectRaysXY(_a, (_b-_a), rhs._a, (rhs._b-rhs._a), temp, u, v);
175 out.set( temp.x(), temp.y(), temp.z(), 1.0 );
176 return ok;
177 }
intersect__anon411a13980211::Line2d178 bool intersect(const Line2d& rhs, osg::Vec3d& out) const {
179 double u, v;
180 return intersectRaysXY(_a, (_b-_a), rhs._a, (rhs._b-rhs._a), out, u, v);
181 }
182 };
183
184 // Experimental.
optimizeProjectionMatrix(OverlayDecorator::TechRTTParams & params,double maxFarNearRatio)185 void optimizeProjectionMatrix(OverlayDecorator::TechRTTParams& params, double maxFarNearRatio)
186 {
187 // t0,t1,t2,t3 will form a polygon that tightly fits the
188 // main camera's frustum. Texture near the camera will get
189 // more resolution then texture far away.
190 //
191 // The line segment t0t1 represents the near clip plane and is
192 // along the y=-1 line of the clip volume. t2t3 represents the
193 // far plane and lies long y=+1. This code calculates the optimal
194 // width of those 2 line segments off-center. Since the view
195 // frustum is symmertical (as calculated in OverlayDecorator)
196 // we only need find the half-width of each.
197 //
198 // NOTE: this algorithm only works with the top-down RTT camera
199 // created by the OverlayDecorator, AND assumes a "level" view
200 // camera (no roll) with respect to the RTT camera.
201 osg::Vec4d t0, t1, t2, t3;
202 {
203 // cap the width of the far line w.r.t to y-axis of the clip
204 // space. (derived empirically)
205 const double maxApsectRatio = 1.0;
206
207 // this matrix xforms the verts from model to clip space.
208 osg::Matrix rttMVP = params._rttViewMatrix * params._rttProjMatrix;
209
210 // if the eyepoint lies within the RTT clip space, don't bother to
211 // optimize because the camera is looking downish and the existing
212 // rectangular volume is sufficient.
213 osg::Vec3d eyeClip = params._eyeWorld * rttMVP;
214 if ( eyeClip.y() >= -1.0 && eyeClip.y() <= 1.0 )
215 return;
216
217 // sanity check. 6 faces requires since we need near and far
218 if ( params._visibleFrustumPH._faces.size() < 6 )
219 return;
220
221 // discover the max near-plane width.
222 double halfWidthNear = 0.0;
223 osgShadow::ConvexPolyhedron::Faces::iterator f = params._visibleFrustumPH._faces.begin();
224 f++; f++; f++; f++; // the near plane Face
225 // f->vertices.size() should always be 4, I would think.. but it's not..
226 for(unsigned i=0; i<f->vertices.size(); ++i)
227 {
228 osg::Vec3d p = f->vertices[i] * rttMVP;
229 if ( fabs(p.x()) > halfWidthNear )
230 halfWidthNear = fabs(p.x());
231 }
232
233 double aspectRatio = DBL_MAX;
234 double farNearRatio = DBL_MAX;
235 double halfWidthFar = DBL_MAX;
236
237 // Next, find the point in the camera frustum that forms the largest angle
238 // with the center line (line of sight). This is simply the minimum dot
239 // product of LOS vector and the vector from (0,-1,0) to the point.
240 osg::Vec3d look(0,1,0);
241 double min_dp = 1.0;
242 osg::Vec3d rightmost_p;
243 f++; // the Far plane face
244 for(unsigned i=0; i<f->vertices.size(); ++i)
245 {
246 osg::Vec3d p = f->vertices[i] * rttMVP;
247 // only check points on the right (since it's symmetrical)
248 if ( p.x() > 0 )
249 {
250 osg::Vec3d pv(p.x(), p.y()+1.0, 0); pv.normalize();
251 double dp = look * pv;
252 if ( dp < min_dp )
253 {
254 min_dp = dp;
255 rightmost_p = p;
256 }
257 }
258 }
259
260 // Now calculate the far extent. This is an iterative process;
261 // If either the aspectRatio or far/near-ratio limits are exceeded
262 // by the value we calculate, reset the near width to accomodate
263 // and try again. Worst case this should be no more than 3 iterations.
264 double minHalfWidthNear = halfWidthNear;
265
266 Line2d farLine( osg::Vec3d(-1,1,0), osg::Vec3d(1,1,0) );
267
268 int iterations = 0;
269 while(
270 (aspectRatio > maxApsectRatio || farNearRatio > maxFarNearRatio) &&
271 (halfWidthFar > halfWidthNear) &&
272 (iterations++ < 10) )
273 {
274 // make sure all the far-clip verts are inside our polygon.
275 // stretch out the far line to accomodate them.
276 osg::Vec3d NR( halfWidthNear, -1, 0);
277
278 osg::Vec3d i;
279 Line2d( NR, rightmost_p ).intersect( farLine, i );
280 halfWidthFar = i.x();
281
282 aspectRatio = (halfWidthFar-halfWidthNear)/2.0;
283 if ( aspectRatio > maxApsectRatio )
284 {
285 halfWidthNear = halfWidthFar - 2.0*maxApsectRatio;
286 }
287
288 farNearRatio = halfWidthFar/halfWidthNear;
289 if ( farNearRatio > maxFarNearRatio )
290 {
291 halfWidthNear = halfWidthFar / maxFarNearRatio;
292 //break;
293 }
294
295 halfWidthNear = osg::maximum(halfWidthNear, minHalfWidthNear);
296 }
297
298 // if the far plane is narrower than the near plane, bail out and
299 // fall back on a simple rectangular clip camera.
300 if ( halfWidthFar <= halfWidthNear )
301 return;
302
303 //OE_NOTICE << "\n"
304 // << "HN = " << halfWidthNear << "\n"
305 // << "HF = " << halfWidthFar << "\n"
306 // << "AR = " << aspectRatio << "\n"
307 // << "FNR= " << farNearRatio << "\n"
308 // << std::endl;
309
310 // construct the polygon.
311 t0.set( halfWidthFar, 1.0, 0.0, 1.0 );
312 t1.set( -halfWidthFar, 1.0, 0.0, 1.0 );
313 t2.set( -halfWidthNear, -1.0, 0.0, 1.0 );
314 t3.set( halfWidthNear, -1.0, 0.0, 1.0 );
315 }
316
317 // OK now warp our polygon t0,t1,t2,t3 into a clip-space square
318 // through a series of matrix operations.
319 osg::Vec4d u, v;
320 osg::Matrix M;
321
322 // translate the center of the near plane to the origin
323 u = (t2 + t3) / 2.0;
324 osg::Matrix T1;
325 T1.makeTranslate(-u.x(), -u.y(), 0.0);
326 M = T1;
327
328 // find the intersection of the side lines t0,t3 and t1,t2
329 // and translate that point is at the origin:
330 osg::Vec4d i;
331 Line2d(t0, t3).intersect( Line2d(t1, t2), i );
332 u = i*M;
333 osg::Matrix T2;
334 T2.makeTranslate( -u.x(), -u.y(), 0.0 );
335 M = T2*M;
336
337 // scale the near corners to [-1,1] and [1,1] respectively:
338 u = t3*M; // ...not t2.
339 osg::Matrix S1;
340 S1.makeScale( 1/u.x(), 1/u.y(), 1.0 );
341 M = M*S1;
342
343 // project onto the Y plane and translate the whole thing
344 // back down to the origin at the same time.
345 osg::Matrix N(
346 1, 0, 0, 0,
347 0, 1, 0, 1,
348 0, 0, 1, 0,
349 0, -1, 0, 0);
350 M = M*N;
351
352 // scale it back to unit size:
353 u = t0*M;
354 v = t3*M;
355 osg::Matrix S3;
356 S3.makeScale( 1.0, 2.0/(u.y()/u.w() - v.y()/v.w()), 1.0 );
357 M = M*S3;
358
359 // finally, translate it to it lines up with the clip space boundaries.
360 osg::Matrix T4;
361 T4.makeTranslate( 0.0, -1.0, 0.0 );
362 M = M*T4;
363
364 // apply the result to the projection matrix.
365 params._rttProjMatrix.postMult( M );
366 }
367 }
368
369 //---------------------------------------------------------------------------
370
371 #undef LC
372 #define LC "[DrapingTechnique] "
373
DrapingTechnique()374 DrapingTechnique::DrapingTechnique() :
375 _textureUnit ( 1 ),
376 _textureSize ( 1024 ),
377 _mipmapping ( false ),
378 _rttBlending ( true ),
379 _attachStencil ( false ),
380 _maxFarNearRatio ( 5.0 )
381 {
382 _supported = Registry::capabilities().supportsGLSL();
383
384 // try newer version
385 const char* nfr2 = ::getenv("OSGEARTH_OVERLAY_RESOLUTION_RATIO");
386 if ( nfr2 )
387 _maxFarNearRatio = as<double>(nfr2, 0.0);
388 }
389
390
391 bool
hasData(OverlayDecorator::TechRTTParams & params) const392 DrapingTechnique::hasData(OverlayDecorator::TechRTTParams& params) const
393 {
394 return getBound(params).valid();
395 }
396
397 namespace
398 {
399 // Customized texture class will disable texture filtering when rendering under a pick camera.
400 class DrapingTexture : public osg::Texture2D
401 {
402 public:
apply(osg::State & state) const403 virtual void apply(osg::State& state) const
404 {
405 const osg::StateSet::DefineList& defines = state.getDefineMap().currentDefines;
406 if (defines.find("OE_IS_PICK_CAMERA") != defines.end())
407 {
408 FilterMode minFilter = _min_filter;
409 FilterMode magFilter = _mag_filter;
410 DrapingTexture* ncThis = const_cast<DrapingTexture*>(this);
411 ncThis->_min_filter = NEAREST;
412 ncThis->_mag_filter = NEAREST;
413 ncThis->dirtyTextureParameters();
414 osg::Texture2D::apply(state);
415 ncThis->_min_filter = minFilter;
416 ncThis->_mag_filter = magFilter;
417 ncThis->dirtyTextureParameters();
418 }
419 else
420 {
421 osg::Texture2D::apply(state);
422 }
423 }
424 };
425 }
426
427 void
setUpCamera(OverlayDecorator::TechRTTParams & params)428 DrapingTechnique::setUpCamera(OverlayDecorator::TechRTTParams& params)
429 {
430 OE_INFO << LC << "Using texture size = " << _textureSize.get() << std::endl;
431
432 // create the projected texture:
433 osg::Texture2D* projTexture = new DrapingTexture();
434
435 projTexture->setTextureSize( *_textureSize, *_textureSize );
436 projTexture->setInternalFormat( GL_RGBA8 ); //use GL_RGBA8 for compatibility with osg's glTexStorage extension
437 projTexture->setSourceFormat( GL_RGBA );
438 projTexture->setSourceType( GL_UNSIGNED_BYTE );
439 projTexture->setFilter( osg::Texture::MIN_FILTER, _mipmapping? osg::Texture::LINEAR_MIPMAP_LINEAR: osg::Texture::LINEAR );
440 projTexture->setFilter( osg::Texture::MAG_FILTER, osg::Texture::LINEAR );
441 projTexture->setWrap( osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_BORDER );
442 projTexture->setWrap( osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_BORDER );
443 //projTexture->setWrap( osg::Texture::WRAP_R, osg::Texture::CLAMP_TO_EDGE );
444 projTexture->setBorderColor( osg::Vec4(0,0,0,0) );
445
446 // set up the RTT camera:
447 params._rttCamera = new DrapingCamera(_drapingManager);
448 params._rttCamera->setClearColor( osg::Vec4f(0,0,0,0) );
449 // this ref frame causes the RTT to inherit its viewpoint from above (in order to properly
450 // process PagedLOD's etc. -- it doesn't affect the perspective of the RTT camera though)
451 params._rttCamera->setReferenceFrame( osg::Camera::ABSOLUTE_RF_INHERIT_VIEWPOINT );
452 params._rttCamera->setViewport( 0, 0, *_textureSize, *_textureSize );
453 params._rttCamera->setComputeNearFarMode( osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR );
454 params._rttCamera->setRenderOrder( osg::Camera::PRE_RENDER );
455 params._rttCamera->setRenderTargetImplementation( osg::Camera::FRAME_BUFFER_OBJECT );
456 params._rttCamera->setImplicitBufferAttachmentMask(0, 0);
457 params._rttCamera->attach( osg::Camera::COLOR_BUFFER0, projTexture, 0, 0, _mipmapping );
458
459 if ( _attachStencil )
460 {
461 OE_INFO << LC << "Attaching a stencil buffer to the RTT camera" << std::endl;
462
463 // try a depth-packed buffer. failing that, try a normal one.. if the FBO doesn't support
464 // that (which is doesn't on some GPUs like Intel), it will automatically fall back on
465 // a PBUFFER_RTT impl
466 if ( Registry::capabilities().supportsDepthPackedStencilBuffer() )
467 {
468 #if defined(OSG_GLES2_AVAILABLE) || defined(OSG_GLES3_AVAILABLE)
469 params._rttCamera->attach( osg::Camera::PACKED_DEPTH_STENCIL_BUFFER, GL_DEPTH24_STENCIL8_EXT );
470 #else
471 params._rttCamera->attach( osg::Camera::PACKED_DEPTH_STENCIL_BUFFER, GL_DEPTH_STENCIL_EXT );
472 #endif
473 }
474 else
475 {
476 params._rttCamera->attach( osg::Camera::STENCIL_BUFFER, GL_STENCIL_INDEX );
477 }
478
479 params._rttCamera->setClearStencil( 0 );
480 params._rttCamera->setClearMask( GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
481 }
482 else
483 {
484 params._rttCamera->setClearMask( GL_COLOR_BUFFER_BIT );
485 }
486
487
488 // set up a StateSet for the RTT camera.
489 osg::StateSet* rttStateSet = params._rttCamera->getOrCreateStateSet();
490
491 osg::StateAttribute::OverrideValue forceOff =
492 osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED | osg::StateAttribute::OVERRIDE;
493
494 rttStateSet->setDefine(OE_LIGHTING_DEFINE, forceOff);
495 //rttStateSet->addUniform( Registry::shaderFactory()->createUniformForGLMode(GL_LIGHTING, forceOff) );
496 rttStateSet->setMode( GL_LIGHTING, forceOff );
497
498 // activate blending within the RTT camera's FBO
499 if ( _rttBlending )
500 {
501 //Setup a separate blend function for the alpha components and the RGB components.
502 //Because the destination alpha is initialized to 0 instead of 1
503 osg::BlendFunc* blendFunc = 0;
504 if (Registry::capabilities().supportsGLSL(140u))
505 {
506 //Blend Func Separate is only available on OpenGL 1.4 and above
507 blendFunc = new osg::BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
508 }
509 else
510 {
511 blendFunc = new osg::BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
512 }
513
514 rttStateSet->setAttributeAndModes(blendFunc, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
515 }
516 else
517 {
518 rttStateSet->setMode(GL_BLEND, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE);
519 }
520
521 // attach the overlay group to the camera.
522 // TODO: we should probably lock this since other cull traversals might be accessing the group
523 // while we are changing its children.
524 params._rttCamera->addChild( params._group );
525
526 // add to the terrain stateset, i.e. the stateset that the OverlayDecorator will
527 // apply to the terrain before cull-traversing it. This will activate the projective
528 // texturing on the terrain.
529 params._terrainStateSet->setTextureAttributeAndModes( *_textureUnit, projTexture, osg::StateAttribute::ON );
530
531 // fire up the local per-view data:
532 LocalPerViewData* local = new LocalPerViewData();
533 params._techniqueData = local;
534
535 if ( _maxFarNearRatio > 1.0 )
536 {
537 // Custom clipper that accounts for the projection matrix warping.
538 // Without this, you will get geometry beyond the original ortho far plane.
539 // (i.e. geometry from beyond the horizon will show through the earth)
540 // When the projection matrix has been warped, verts with Z=1.0 are no longer
541 // always on the far plane (only when y=-1) because of the perspective divide (w).
542 // So we need to test the Z directly (without w) and clip. NOTE: this seems to work
543 // fine, although I think it would be proper to clip at the fragment level with
544 // alpha. We shall see.
545 const char* warpClip =
546 "#version " GLSL_VERSION_STR "\n"
547 GLSL_DEFAULT_PRECISION_FLOAT "\n"
548 "void oe_overlay_warpClip(inout vec4 vclip) { \n"
549 " if (vclip.z > 1.0) vclip.z = vclip.w+1.0; \n"
550 "} \n";
551 VirtualProgram* rtt_vp = VirtualProgram::getOrCreate(rttStateSet);
552 rtt_vp->setName("Draping RTT");
553 rtt_vp->setFunction( "oe_overlay_warpClip", warpClip, ShaderComp::LOCATION_VERTEX_CLIP );
554 }
555
556 // Assemble the terrain shaders that will apply projective texturing.
557 VirtualProgram* terrain_vp = VirtualProgram::getOrCreate(params._terrainStateSet);
558 terrain_vp->setName( "Draping apply");
559
560 // sampler for projected texture:
561 params._terrainStateSet->getOrCreateUniform(
562 "oe_overlay_tex", osg::Uniform::SAMPLER_2D )->set( *_textureUnit );
563
564 // the texture projection matrix uniform.
565 local->_texGenUniform = params._terrainStateSet->getOrCreateUniform(
566 "oe_overlay_texmatrix", osg::Uniform::FLOAT_MAT4 );
567
568 // shaders
569 Shaders pkg;
570 pkg.load( terrain_vp, pkg.DrapingVertex );
571 pkg.load( terrain_vp, pkg.DrapingFragment );
572 }
573
574
575 void
preCullTerrain(OverlayDecorator::TechRTTParams & params,osgUtil::CullVisitor * cv)576 DrapingTechnique::preCullTerrain(OverlayDecorator::TechRTTParams& params,
577 osgUtil::CullVisitor* cv )
578 {
579 // allocate a texture image unit the first time through.
580 if ( !_textureUnit.isSet() )
581 {
582 static Threading::Mutex m;
583 m.lock();
584 if ( !_textureUnit.isSet() )
585 {
586 // apply the user-request texture unit, if applicable:
587 if ( _explicitTextureUnit.isSet() )
588 {
589 if ( !_textureUnit.isSet() || *_textureUnit != *_explicitTextureUnit )
590 {
591 _textureUnit = *_explicitTextureUnit;
592 }
593 }
594
595 // otherwise, automatically allocate a texture unit if necessary:
596 else if ( !_textureUnit.isSet() )
597 {
598 int texUnit;
599 if ( params._terrainResources->reserveTextureImageUnit(texUnit, "Draping") )
600 {
601 _textureUnit = texUnit;
602 OE_INFO << LC << "Reserved texture image unit " << *_textureUnit << std::endl;
603 }
604 else
605 {
606 OE_WARN << LC << "No texture image units available." << std::endl;
607 }
608 }
609 }
610 m.unlock();
611 }
612
613 if ( !params._rttCamera.valid() && _textureUnit.isSet() )
614 {
615 setUpCamera( params );
616
617 // We do this so we can detect the RTT's camera's parent for
618 // things like auto-scaling, picking, and so on.
619 params._rttCamera->setView(cv->getCurrentCamera()->getView());
620 }
621 }
622
623 const osg::BoundingSphere&
getBound(OverlayDecorator::TechRTTParams & params) const624 DrapingTechnique::getBound(OverlayDecorator::TechRTTParams& params) const
625 {
626 return _drapingManager.get(params._mainCamera).getBound();
627 }
628
629 void
cullOverlayGroup(OverlayDecorator::TechRTTParams & params,osgUtil::CullVisitor * cv)630 DrapingTechnique::cullOverlayGroup(OverlayDecorator::TechRTTParams& params,
631 osgUtil::CullVisitor* cv )
632 {
633 if ( params._rttCamera.valid() )
634 {
635 LocalPerViewData& local = *static_cast<LocalPerViewData*>(params._techniqueData.get());
636
637 // this xforms from clip [-1..1] to texture [0..1] space
638 static osg::Matrix s_scaleBiasMat =
639 osg::Matrix::translate(1.0,1.0,1.0) *
640 osg::Matrix::scale(0.5,0.5,0.5);
641
642 // resolution weighting based on camera distance.
643 if ( _maxFarNearRatio > 1.0 )
644 {
645 optimizeProjectionMatrix( params, _maxFarNearRatio );
646 }
647
648 params._rttCamera->setViewMatrix ( params._rttViewMatrix );
649 params._rttCamera->setProjectionMatrix( params._rttProjMatrix );
650
651 osg::Matrix VPT = params._rttViewMatrix * params._rttProjMatrix * s_scaleBiasMat;
652
653 if ( local._texGenUniform.valid() )
654 {
655 // premultiply the inv view matrix so we don't have precision problems in the shader
656 // (and it's faster too)
657
658 // TODO:
659 // This only works properly if the terrain tiles have a DYNAMIC data variance.
660 // That is because we are setting a Uniform value during the CULL traversal, and
661 // it's possible that the stateset from the previous frame has not yet been
662 // dispatched to render. So we need to come up with a way to address this.
663 // In the meantime, I patched the MP engine to set a DYNAMIC data variance on
664 // terrain tiles to work around the problem.
665 //
666 // Note that we require the InverseViewMatrix, but it is OK to invert the ModelView matrix as the model matrix is identity here.
667 osg::Matrix vm;
668 vm.invert( *cv->getModelViewMatrix() );
669 local._texGenUniform->set( vm * VPT );
670 }
671
672 // traverse the overlay group (via the RTT camera).
673 static_cast<DrapingCamera*>(params._rttCamera.get())->accept( *cv, cv->getCurrentCamera() );
674 }
675 }
676
677
678 void
setTextureSize(int texSize)679 DrapingTechnique::setTextureSize( int texSize )
680 {
681 _textureSize = texSize;
682 }
683
684 void
setTextureUnit(int texUnit)685 DrapingTechnique::setTextureUnit( int texUnit )
686 {
687 if ( !_explicitTextureUnit.isSet() || texUnit != _explicitTextureUnit.value() )
688 {
689 _explicitTextureUnit = texUnit;
690 }
691 }
692
693 void
setMipMapping(bool value)694 DrapingTechnique::setMipMapping( bool value )
695 {
696 if ( value != _mipmapping )
697 {
698 _mipmapping = value;
699
700 if ( _mipmapping )
701 OE_INFO << LC << "Overlay mipmapping " << (value?"enabled":"disabled") << std::endl;
702 }
703 }
704
705 void
setOverlayBlending(bool value)706 DrapingTechnique::setOverlayBlending( bool value )
707 {
708 if ( value != _rttBlending )
709 {
710 _rttBlending = value;
711
712 if ( _rttBlending )
713 OE_INFO << LC << "Overlay blending " << (value?"enabled":"disabled")<< std::endl;
714 }
715 }
716
717 bool
getAttachStencil() const718 DrapingTechnique::getAttachStencil() const
719 {
720 return _attachStencil;
721 }
722
723 void
setAttachStencil(bool value)724 DrapingTechnique::setAttachStencil( bool value )
725 {
726 _attachStencil = value;
727 }
728
729 void
setResolutionRatio(float value)730 DrapingTechnique::setResolutionRatio(float value)
731 {
732 // not a typo. "near/far resolution" is equivalent to "far/near clip plane extent"
733 // with respect to the overlay projection frustum.
734 _maxFarNearRatio = (double)osg::clampAbove(value, 1.0f);
735 }
736
737 float
getResolutionRatio() const738 DrapingTechnique::getResolutionRatio() const
739 {
740 // not a typo. "near/far resolution" is equivalent to "far/near clip plane extent"
741 // with respect to the overlay projection frustum.
742 return (float)_maxFarNearRatio;
743 }
744
745 void
onInstall(TerrainEngineNode * engine)746 DrapingTechnique::onInstall( TerrainEngineNode* engine )
747 {
748 if ( !_textureSize.isSet() )
749 {
750 unsigned maxSize = Registry::capabilities().getMaxFastTextureSize();
751 _textureSize.init( osg::minimum( 2048u, maxSize ) );
752 }
753 }
754
755 void
onUninstall(TerrainEngineNode * engine)756 DrapingTechnique::onUninstall( TerrainEngineNode* engine )
757 {
758 if ( !_explicitTextureUnit.isSet() && _textureUnit.isSet() )
759 {
760 engine->getResources()->releaseTextureImageUnit( *_textureUnit );
761 _textureUnit.unset();
762 }
763 }
764