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 "MPGeometry"
23
24 #include <osg/Version>
25 #include <osgUtil/MeshOptimizers>
26 #include <iterator>
27 #include <osgEarth/Registry>
28 #include <osgEarth/Capabilities>
29
30 #include <osgUtil/IncrementalCompileOperation>
31 #include <osg/Version>
32
33 using namespace osg;
34 using namespace osgEarth::Drivers::MPTerrainEngine;
35 using namespace osgEarth;
36
37 #define LC "[MPGeometry] "
38
39
MPGeometry()40 MPGeometry::MPGeometry() :
41 osg::Geometry(),
42 _frame(0L),
43 _uidUniformNameID(0),
44 _birthTimeUniformNameID(0u),
45 _orderUniformNameID(0u),
46 _opacityUniformNameID(0u),
47 _texMatParentUniformNameID(0u),
48 _tileKeyUniformNameID(0u),
49 _minRangeUniformNameID(0u),
50 _maxRangeUniformNameID(0u),
51 _imageUnit(0),
52 _imageUnitParent(0),
53 _elevUnit(0),
54 _supportsGLSL(false)
55 {
56 }
57
MPGeometry(const MPGeometry & rhs,const osg::CopyOp & cop)58 MPGeometry::MPGeometry(const MPGeometry& rhs, const osg::CopyOp& cop) :
59 osg::Geometry(rhs, cop),
60 _frame(rhs._frame),
61 _uidUniformNameID(rhs._uidUniformNameID),
62 _birthTimeUniformNameID(rhs._birthTimeUniformNameID),
63 _orderUniformNameID(rhs._orderUniformNameID),
64 _opacityUniformNameID(rhs._opacityUniformNameID),
65 _texMatParentUniformNameID(rhs._texMatParentUniformNameID),
66 _tileKeyUniformNameID(rhs._tileKeyUniformNameID),
67 _minRangeUniformNameID(rhs._minRangeUniformNameID),
68 _maxRangeUniformNameID(rhs._maxRangeUniformNameID),
69 _imageUnit(rhs._imageUnit),
70 _imageUnitParent(rhs._imageUnitParent),
71 _elevUnit(rhs._elevUnit),
72 _supportsGLSL(rhs._supportsGLSL)
73 {
74 }
75
76
MPGeometry(const TileKey & key,const MapFrame & frame,int imageUnit)77 MPGeometry::MPGeometry(const TileKey& key, const MapFrame& frame, int imageUnit) :
78 osg::Geometry ( ),
79 _frame ( frame ),
80 _imageUnit ( imageUnit ),
81 _uidUniformNameID(0),
82 _birthTimeUniformNameID(0u),
83 _orderUniformNameID(0u),
84 _opacityUniformNameID(0u),
85 _texMatParentUniformNameID(0u),
86 _tileKeyUniformNameID(0u),
87 _minRangeUniformNameID(0u),
88 _maxRangeUniformNameID(0u),
89 _imageUnitParent(0),
90 _elevUnit(0),
91 _supportsGLSL(false)
92 {
93 _supportsGLSL = Registry::capabilities().supportsGLSL();
94
95 // Encode the tile key in a uniform. Note! The X and Y components are presented
96 // modulo 2^16 form so they don't overrun single-precision space.
97 unsigned tw, th;
98 key.getProfile()->getNumTiles(key.getLOD(), tw, th);
99
100 const double m = pow(2.0, 16.0);
101
102 double x = (double)key.getTileX();
103 double y = (double)(th - key.getTileY()-1);
104
105 _tileKeyValue.set(
106 (float)fmod(x, m),
107 (float)fmod(y, m),
108 (float)key.getLOD(),
109 -1.0f);
110
111 _imageUnitParent = _imageUnit + 1; // temp
112
113 _elevUnit = _imageUnit + 2; // temp
114
115 // establish uniform name IDs.
116 _tileKeyUniformNameID = osg::Uniform::getNameID( "oe_tile_key" );
117 _birthTimeUniformNameID = osg::Uniform::getNameID( "oe_tile_birthtime" );
118 _uidUniformNameID = osg::Uniform::getNameID( "oe_layer_uid" );
119 _orderUniformNameID = osg::Uniform::getNameID( "oe_layer_order" );
120 _opacityUniformNameID = osg::Uniform::getNameID( "oe_layer_opacity" );
121 _texMatParentUniformNameID = osg::Uniform::getNameID( "oe_layer_parent_texmat" );
122 _minRangeUniformNameID = osg::Uniform::getNameID( "oe_layer_minRange" );
123 _maxRangeUniformNameID = osg::Uniform::getNameID( "oe_layer_maxRange" );
124
125 // we will set these later (in TileModelCompiler)
126 this->setUseDisplayList(false);
127 this->setUseVertexBufferObjects(true);
128 }
129
130
131 void
renderPrimitiveSets(osg::State & state,bool renderColor,bool usingVBOs) const132 MPGeometry::renderPrimitiveSets(osg::State& state,
133 bool renderColor,
134 bool usingVBOs) const
135 {
136 // check the map frame to see if it's up to date
137 if ( _frame.needsSync() )
138 {
139 // this lock protects a MapFrame sync when we have multiple DRAW threads.
140 Threading::ScopedMutexLock exclusive( _frameSyncMutex );
141
142 if ( _frame.needsSync() && _frame.sync() ) // always double check
143 {
144 // This should only happen is the layer ordering changes;
145 // If layers are added or removed, the Tile gets rebuilt and
146 // the point is moot.
147 ImageLayerVector layers;
148 _frame.getLayers(layers);
149
150 std::vector<Layer> reordered;
151 reordered.reserve( layers.size() );
152
153 for( ImageLayerVector::const_iterator i = layers.begin(); i != layers.end(); ++i )
154 {
155 std::vector<Layer>::iterator j = std::find( _layers.begin(), _layers.end(), i->get()->getUID() );
156 if ( j != _layers.end() )
157 reordered.push_back( *j );
158 }
159 _layers.swap( reordered );
160 }
161 }
162
163 unsigned layersDrawn = 0;
164
165 // access the GL extensions interface for the current GC:
166 const osg::Program::PerContextProgram* pcp = 0L;
167
168 osg::ref_ptr<osg::GLExtensions> ext;
169 unsigned contextID;
170
171 if (_supportsGLSL)
172 {
173 contextID = state.getContextID();
174 ext = osg::GLExtensions::Get(contextID, true);
175 pcp = state.getLastAppliedProgramObject();
176 }
177
178 // cannot store these in the object since there could be multiple GCs (and multiple
179 // PerContextPrograms) at large
180 GLint tileKeyLocation = -1;
181 GLint birthTimeLocation = -1;
182 GLint opacityLocation = -1;
183 GLint uidLocation = -1;
184 GLint orderLocation = -1;
185 GLint texMatParentLocation = -1;
186 GLint minRangeLocation = -1;
187 GLint maxRangeLocation = -1;
188
189 // The PCP can change (especially in a VirtualProgram environment). So we do need to
190 // requery the uni locations each time unfortunately. TODO: explore optimizations.
191 if ( pcp )
192 {
193 tileKeyLocation = pcp->getUniformLocation( _tileKeyUniformNameID );
194 birthTimeLocation = pcp->getUniformLocation( _birthTimeUniformNameID );
195 opacityLocation = pcp->getUniformLocation( _opacityUniformNameID );
196 uidLocation = pcp->getUniformLocation( _uidUniformNameID );
197 orderLocation = pcp->getUniformLocation( _orderUniformNameID );
198 texMatParentLocation = pcp->getUniformLocation( _texMatParentUniformNameID );
199 minRangeLocation = pcp->getUniformLocation( _minRangeUniformNameID );
200 maxRangeLocation = pcp->getUniformLocation( _maxRangeUniformNameID );
201 }
202
203 // apply the tilekey uniform once.
204 if ( tileKeyLocation >= 0 )
205 {
206 ext->glUniform4fv( tileKeyLocation, 1, _tileKeyValue.ptr() );
207 }
208
209 // set the "birth time" - i.e. the time this tile last entered the scene in the current GC.
210 if ( birthTimeLocation >= 0 )
211 {
212 PerContextData& pcd = _pcd[contextID];
213 if ( pcd.birthTime < 0.0f )
214 {
215 const osg::FrameStamp* stamp = state.getFrameStamp();
216 if ( stamp )
217 {
218 pcd.birthTime = stamp->getReferenceTime();
219 }
220 }
221 ext->glUniform1f( birthTimeLocation, pcd.birthTime );
222 }
223
224 // activate the tile coordinate set - same for all layers
225 if ( renderColor && _layers.size() > 0 )
226 {
227 state.setTexCoordPointer( _imageUnit+1, _tileCoords.get() );
228 }
229
230 #if !( defined(OSG_GLES2_AVAILABLE) || defined(OSG_GLES3_AVAILABLE) || defined(OSG_GL3_AVAILABLE) )
231 if ( renderColor )
232 {
233 // emit a default terrain color since we're not binding a color array:
234 glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
235 }
236 #endif
237
238 // track the active image unit.
239 int activeImageUnit = -1;
240
241 // remember whether we applied a parent texture.
242 bool usedTexParent = false;
243
244 if ( _layers.size() > 0 )
245 {
246 float prev_opacity = -1.0f;
247
248 // first bind any shared layers. We still have to do this even if we are
249 // in !renderColor mode b/c these textures could be used by vertex shaders
250 // to alter the geometry.
251 int sharedLayers = 0;
252 if ( pcp )
253 {
254 for(unsigned i=0; i<_layers.size(); ++i)
255 {
256 const Layer& layer = _layers[i];
257
258 // a "shared" layer binds to a secondary texture unit so that other layers
259 // can see it and use it.
260 if ( layer._imageLayer->isShared() )
261 {
262 ++sharedLayers;
263 int sharedUnit = layer._imageLayer->shareImageUnit().get();
264 {
265 state.setActiveTextureUnit( sharedUnit );
266
267 state.setTexCoordPointer( sharedUnit, layer._texCoords.get() );
268 // bind the texture for this layer to the active share unit.
269 layer._tex->apply( state );
270
271 // Shared layers need a texture matrix since the terrain engine doesn't
272 // provide a "current texture coordinate set" uniform (i.e. oe_layer_texc)
273 GLint texMatLocation = 0;
274 texMatLocation = pcp->getUniformLocation( layer._texMatUniformID );
275 if ( texMatLocation >= 0 )
276 {
277 ext->glUniformMatrix4fv( texMatLocation, 1, GL_FALSE, layer._texMat.ptr() );
278 }
279 }
280 }
281 }
282 }
283 if (renderColor)
284 {
285 // find the first opaque layer, top-down, and start there:
286 unsigned first = 0;
287 for(first = _layers.size()-1; first > 0; --first)
288 {
289 const Layer& layer = _layers[first];
290 if (layer._opaque &&
291 //Color filters can modify the opacity
292 layer._imageLayer->getColorFilters().empty() &&
293 layer._imageLayer->getVisible() &&
294 layer._imageLayer->getOpacity() >= 1.0f)
295 {
296 break;
297 }
298 }
299
300 // interate over all the image layers
301 for(unsigned i=first; i<_layers.size(); ++i)
302 {
303 const Layer& layer = _layers[i];
304
305 if ( layer._imageLayer->getVisible() && layer._imageLayer->getOpacity() > 0.0f )
306 {
307 // activate the visible unit if necessary:
308 if ( activeImageUnit != _imageUnit )
309 {
310 state.setActiveTextureUnit( _imageUnit );
311 activeImageUnit = _imageUnit;
312 }
313
314 // bind the texture for this layer:
315 layer._tex->apply( state );
316
317 // in FFP mode, we need to enable the GL mode for texturing:
318 if ( !pcp ) //!_supportsGLSL)
319 {
320 state.applyMode(GL_TEXTURE_2D, true);
321 }
322
323 // if we're using a parent texture for blending, activate that now
324 if ( texMatParentLocation >= 0 && layer._texParent.valid() )
325 {
326 state.setActiveTextureUnit( _imageUnitParent );
327 activeImageUnit = _imageUnitParent;
328 layer._texParent->apply( state );
329 usedTexParent = true;
330 }
331
332 // bind the texture coordinates for this layer.
333 // TODO: can probably optimize this by sharing or using texture matrixes.
334 // State::setTexCoordPointer does some redundant work under the hood.
335 state.setTexCoordPointer( _imageUnit, layer._texCoords.get() );
336
337 // apply uniform values:
338 if ( pcp )
339 {
340 // apply opacity:
341 if ( opacityLocation >= 0 )
342 {
343 float opacity = layer._imageLayer->getOpacity();
344 if ( opacity != prev_opacity )
345 {
346 ext->glUniform1f( opacityLocation, (GLfloat)opacity );
347 prev_opacity = opacity;
348 }
349 }
350
351 // assign the layer UID:
352 if ( uidLocation >= 0 )
353 {
354 ext->glUniform1i( uidLocation, (GLint)layer._layerID );
355 }
356
357 // assign the layer order:
358 if ( orderLocation >= 0 )
359 {
360 ext->glUniform1i( orderLocation, (GLint)layersDrawn );
361 }
362
363 // assign the parent texture matrix
364 if ( texMatParentLocation >= 0 && layer._texParent.valid() )
365 {
366 ext->glUniformMatrix4fv( texMatParentLocation, 1, GL_FALSE, layer._texMatParent.ptr() );
367 }
368
369 // assign the min range
370 if ( minRangeLocation >= 0 )
371 {
372 ext->glUniform1f( minRangeLocation, layer._imageLayer->options().minVisibleRange().get() );
373 }
374
375 // assign the max range
376 if ( maxRangeLocation >= 0 )
377 {
378 ext->glUniform1f( maxRangeLocation, layer._imageLayer->options().maxVisibleRange().get() );
379 }
380 }
381
382 // draw the primitive sets.
383 for(unsigned int primitiveSetNum=0; primitiveSetNum!=_primitives.size(); ++primitiveSetNum)
384 {
385 const osg::PrimitiveSet* primitiveset = _primitives[primitiveSetNum].get();
386 if ( primitiveset )
387 {
388 primitiveset->draw(state, usingVBOs);
389 }
390 else
391 {
392 OE_WARN << LC << "Strange, MPGeometry had a 0L primset" << std::endl;
393 }
394 }
395
396 ++layersDrawn;
397 }
398 }
399 }
400 }
401
402 // if we didn't draw anything, draw the raw tiles anyway with no texture.
403 if ( layersDrawn == 0 )
404 {
405 if ( pcp )
406 {
407 if ( opacityLocation >= 0 )
408 ext->glUniform1f( opacityLocation, (GLfloat)1.0f );
409 if ( uidLocation >= 0 )
410 ext->glUniform1i( uidLocation, (GLint)-1 );
411 if ( orderLocation >= 0 )
412 ext->glUniform1i( orderLocation, (GLint)0 );
413 }
414
415 // draw the primitives themselves.
416 for(unsigned int primitiveSetNum=0; primitiveSetNum!=_primitives.size(); ++primitiveSetNum)
417 {
418 const osg::PrimitiveSet* primitiveset = _primitives[primitiveSetNum].get();
419 primitiveset->draw(state, usingVBOs);
420 }
421 }
422
423 else // at least one textured layer was drawn:
424 {
425 // prevent texture leakage
426 // TODO: find a way to remove this to speed things up
427 if ( renderColor )
428 {
429 glBindTexture( GL_TEXTURE_2D, 0 );
430
431 // if a parent texture was applied, need to disable both.
432 if ( usedTexParent )
433 {
434 state.setActiveTextureUnit(
435 activeImageUnit != _imageUnitParent ? _imageUnitParent :
436 _imageUnit );
437
438 glBindTexture( GL_TEXTURE_2D, 0);
439 }
440 }
441 }
442 }
443
444 #if OSG_VERSION_GREATER_OR_EQUAL(3,3,2)
445 # define COMPUTE_BOUND computeBoundingBox
446 #else
447 # define COMPUTE_BOUND computeBound
448 #endif
449
450 #if OSG_VERSION_GREATER_OR_EQUAL(3,1,8)
451 # define GET_ARRAY(a) (a)
452 #else
453 # define GET_ARRAY(a) (a).array
454 #endif
455
456 osg::BoundingBox
COMPUTE_BOUND() const457 MPGeometry:: COMPUTE_BOUND() const
458 {
459 osg::BoundingBox bbox = osg::Geometry:: COMPUTE_BOUND ();
460 {
461 // update the uniform.
462 Threading::ScopedMutexLock exclusive(_frameSyncMutex);
463 _tileKeyValue.w() = bbox.radius();
464
465 // create the patch triangles index if necessary.
466 if ( getNumPrimitiveSets() > 0 && getPrimitiveSet(0)->getMode() == GL_PATCHES )
467 {
468 _patchTriangles = osg::clone( getPrimitiveSet(0), osg::CopyOp::SHALLOW_COPY );
469 _patchTriangles->setMode( GL_TRIANGLES );
470 }
471 }
472 return bbox;
473 }
474
475 void
validate()476 MPGeometry::validate()
477 {
478 unsigned numVerts = getVertexArray()->getNumElements();
479
480 for(unsigned i=0; i < _primitives.size(); ++i)
481 {
482 osg::DrawElements* de = static_cast<osg::DrawElements*>(_primitives[i].get());
483 if ( de->getMode() != GL_TRIANGLES )
484 {
485 OE_WARN << LC << "Invalid primitive set - not GL_TRIANGLES" << std::endl;
486 _primitives.clear();
487 }
488
489 else if ( de->getNumIndices() % 3 != 0 )
490 {
491 OE_WARN << LC << "Invalid primitive set - wrong number of indices" << std::endl;
492 //_primitives.clear();
493 osg::DrawElementsUShort* deus = static_cast<osg::DrawElementsUShort*>(de);
494 int extra = de->getNumIndices() % 3;
495 deus->resize(de->getNumIndices() - extra);
496 OE_WARN << LC << " ..removed " << extra << " indices" << std::endl;
497 //return;
498 }
499 else
500 {
501 for( unsigned j=0; j<de->getNumIndices(); ++j )
502 {
503 unsigned index = de->index(j);
504 if ( index >= numVerts )
505 {
506 OE_WARN << LC << "Invalid primitive set - index out of bounds" << std::endl;
507 _primitives.clear();
508 return;
509 }
510 }
511 }
512 }
513 }
514
515
516 void
releaseGLObjects(osg::State * state) const517 MPGeometry::releaseGLObjects(osg::State* state) const
518 {
519 osg::Geometry::releaseGLObjects( state );
520
521 // Note: don't release the textures here; instead we release them in the
522 // TileModel where they were created. -gw
523 }
524
525
526 void
resizeGLObjectBuffers(unsigned maxSize)527 MPGeometry::resizeGLObjectBuffers(unsigned maxSize)
528 {
529 osg::Geometry::resizeGLObjectBuffers( maxSize );
530
531 for(unsigned i=0; i<_layers.size(); ++i)
532 {
533 const Layer& layer = _layers[i];
534 if ( layer._tex.valid() )
535 layer._tex->resizeGLObjectBuffers( maxSize );
536 }
537
538 if ( _pcd.size() < maxSize )
539 {
540 _pcd.resize(maxSize);
541 }
542 }
543
544
545 void
compileGLObjects(osg::RenderInfo & renderInfo) const546 MPGeometry::compileGLObjects( osg::RenderInfo& renderInfo ) const
547 {
548 State& state = *renderInfo.getState();
549
550 // compile the image textures:
551 for(unsigned i=0; i<_layers.size(); ++i)
552 {
553 const Layer& layer = _layers[i];
554 if ( layer._tex.valid() )
555 layer._tex->apply( state );
556 }
557
558 // compile the elevation texture:
559 if ( _elevTex.valid() )
560 {
561 _elevTex->apply( state );
562 }
563
564 osg::Geometry::compileGLObjects( renderInfo );
565 }
566
567 #if OSG_MIN_VERSION_REQUIRED(3,5,6)
568
569 osg::VertexArrayState*
570 #if OSG_MIN_VERSION_REQUIRED(3,5,9)
createVertexArrayStateImplementation(osg::RenderInfo & renderInfo) const571 MPGeometry::createVertexArrayStateImplementation(osg::RenderInfo& renderInfo) const
572 {
573 osg::VertexArrayState* vas = osg::Geometry::createVertexArrayStateImplementation(renderInfo);
574 #else
575 MPGeometry::createVertexArrayState(osg::RenderInfo& renderInfo) const
576 {
577 osg::VertexArrayState* vas = osg::Geometry::createVertexArrayState(renderInfo);
578 #endif
579 // make sure we have array dispatchers for the multipass coords
580 vas->assignTexCoordArrayDispatcher(_texCoordList.size() + 2);
581
582 return vas;
583 }
584 #endif
585
586
587 void
588 MPGeometry::drawImplementation(osg::RenderInfo& renderInfo) const
589 {
590 // See if this is a pre-render depth-only camera. If so we can skip all the layers
591 // and just render the primitive sets.
592 osg::Camera* camera = renderInfo.getCurrentCamera();
593 bool renderColor =
594 (camera->getRenderOrder() != osg::Camera::PRE_RENDER) ||
595 ((camera->getClearMask() & GL_COLOR_BUFFER_BIT) != 0L);
596
597 osg::State& state = *renderInfo.getState();
598
599 bool hasVertexAttributes = !_vertexAttribList.empty();
600
601 #if OSG_VERSION_LESS_THAN(3,5,6)
602 osg::ArrayDispatchers& dispatchers = state.getArrayDispatchers();
603 #else
604 osg::AttributeDispatchers& dispatchers = state.getAttributeDispatchers();
605 #endif
606
607 dispatchers.reset();
608 dispatchers.setUseVertexAttribAlias(state.getUseVertexAttributeAliasing());
609 dispatchers.activateNormalArray(_normalArray.get());
610
611 if (hasVertexAttributes)
612 {
613 for(unsigned int unit=0;unit<_vertexAttribList.size();++unit)
614 {
615 dispatchers.activateVertexAttribArray(unit, _vertexAttribList[unit].get());
616 }
617 }
618
619 // dispatch any attributes that are bound overall
620 #if OSG_VERSION_LESS_THAN(3,5,6)
621 dispatchers.dispatch(BIND_OVERALL,0);
622 #else
623 dispatchers.dispatch(0);
624 #endif
625 state.lazyDisablingOfVertexAttributes();
626
627
628 // set up arrays
629 if( _vertexArray.valid() )
630 state.setVertexPointer(_vertexArray.get());
631
632 if (_normalArray.valid() && _normalArray->getBinding()==osg::Array::BIND_PER_VERTEX)
633 state.setNormalPointer(_normalArray.get());
634
635 if( hasVertexAttributes )
636 {
637 for(unsigned int index = 0; index < _vertexAttribList.size(); ++index )
638 {
639 const Array* array = _vertexAttribList[index].get();
640 if (array && array->getBinding()==osg::Array::BIND_PER_VERTEX)
641 {
642 if (array->getPreserveDataType())
643 {
644 GLenum dataType = array->getDataType();
645 if (dataType==GL_FLOAT) state.setVertexAttribPointer( index, array );
646 else if (dataType==GL_DOUBLE) state.setVertexAttribLPointer( index, array );
647 else state.setVertexAttribIPointer( index, array );
648 }
649 else
650 {
651 state.setVertexAttribPointer( index, array );
652 }
653 }
654 }
655 }
656
657 state.applyDisablingOfVertexAttributes();
658
659 // draw the multipass geometry.
660 renderPrimitiveSets(state, renderColor, true);
661
662 // unbind the VBO's if any are used.
663 #if OSG_MIN_VERSION_REQUIRED(3,5,6)
664 if (!state.useVertexArrayObject(_useVertexArrayObject) || state.getCurrentVertexArrayState()->getRequiresSetArrays())
665 #endif
666 {
667 state.unbindVertexBufferObject();
668 state.unbindElementBufferObject();
669 }
670 }
671
672 void
673 MPGeometry::accept(osg::PrimitiveIndexFunctor& functor) const
674 {
675 osg::Geometry::accept(functor);
676
677 if ( getNumPrimitiveSets() > 0 && getPrimitiveSet(0)->getMode() == GL_PATCHES && _patchTriangles.valid() )
678 {
679 _patchTriangles->accept( functor );
680 }
681 }
682
683 void
684 MPGeometry::accept(osg::PrimitiveFunctor& functor) const
685 {
686 osg::Geometry::accept(functor);
687
688 if ( getNumPrimitiveSets() > 0 && getPrimitiveSet(0)->getMode() == GL_PATCHES && _patchTriangles.valid() )
689 {
690 _patchTriangles->accept( functor );
691 }
692 }
693