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