From PawelKsiezopolski, "This submission contains a new example for OSG : a geometry instancing rendering

algorithm consisting of two consequent phases :

- first phase is a GLSL shader performing object culling and LOD picking ( a culling shader ).
  Every culled object is represented as GL_POINT in the input osg::Geometry.
  The output of the culling shader is a set of object LODs that need to be rendered.
  The output is stored in texture buffer objects. No pixel is drawn to the screen
  because GL_RASTERIZER_DISCARD mode is used.

- second phase draws osg::Geometry containing merged LODs using glDrawArraysIndirect()
  function. Information about quantity of instances to render, its positions and other
  parameters is sourced from texture buffer objects filled in the first phase.

The example uses various OpenGL 4.2 features such as texture buffer objects,
atomic counters, image units and functions defined in GL_ARB_shader_image_load_store
extension to achieve its goal and thus will not work on graphic cards with older OpenGL
versions.

The example was tested on Linux and Windows with NVidia 570 and 580 cards.
The tests on AMD cards were not conducted ( due to lack of it ).
The tests were performed using OSG revision 14088.

The main advantages of this rendering method :
- instanced rendering capable of drawing thousands of different objects with
  almost no CPU intervention  ( cull and draw times are close to 0 ms ).
- input objects may be sourced from any OSG graph ( for example - information about
  object points may be stored in a PagedLOD graph. This way we may cover the whole
  countries with trees, buildings and other objects ).
  Furthermore if we create osgDB plugins that generate data on the fly, we may
  generate information for every grass blade for that country.
- every object may have its own parameters and thus may be distinct from other objects
  of the same type.
- relatively low memory footprint ( single object information is stored in a few
  vertex attributes ).
- no GPU->CPU roundtrip typical for such methods ( method uses atomic counters
  and glDrawArraysIndirect() function instead of OpenGL queries. This way
  information about quantity of rendered objects never goes back to CPU.
  The typical GPU->CPU roundtrip cost is about 2 ms ).
- this example also shows how to render dynamic objects ( objects that may change
  its position ) with moving parts ( like car wheels or airplane propellers ) .
  The obvious extension to that dynamic method would be the animated crowd rendering.
- rendered objects may be easily replaced ( there is no need to process the whole
  OSG graphs, because these graphs store only positional information ).

The main disadvantages of a method :
- the maximum quantity of objects to render must be known beforehand
  ( because texture buffer objects holding data between phases have constant size ).
- OSG statistics are flawed ( they don't know anymore how many objects are drawn ).
- osgUtil::Intersection does not work

Example application may be used to make some performance tests, so below you
will find some extended parameter description :
--skip-dynamic       - skip rendering of dynamic objects if you only want to
                       observe static object statistics
--skip-static        - the same for static objects
--dynamic-area-size  - size of the area for dynamic rendering. Default = 1000 meters
                       ( square 1000m x 1000m ). Along with density defines
                       how many dynamic objects is there in the example.
--static-area-size   - the same for static objects. Default = 2000 meters
                       ( square 2000m x 2000m ).

Example application defines some parameters (density, LOD ranges, object's triangle count).
You may manipulate its values using below described modifiers:
--density-modifier   - density modifier in percent. Default = 100%.
                       Density ( along with LOD ranges ) defines maximum
                       quantity of rendered objects. registerType() function
                       accepts maximum density ( in objects per square kilometer )
                       as its parameter.
--lod-modifier       - defines the LOD ranges. Default = 100%.
--triangle-modifier  - defines the number of triangles in finally rendered objects.
                       Default = 100 %.
--instances-per-cell - for static rendering the application builds OSG graph using
                       InstanceCell class ( this class is a modified version of Cell class
                       from osgforest example - it builds simple quadtree from a list
                       of static instances ). This parameter defines maximum number
                       of instances in a single osg::Group in quadtree.
                       If, for example, you modify it to value=100, you will see
                       really big cull time in OSG statistics ( because resulting
                       tree generated by InstanceCell will be very deep ).
                       Default value = 4096 .
--export-objects     - write object geometries and quadtree of instances to osgt files
                       for later analysis.
--use-multi-draw     - use glMultiDrawArraysIndirect() instead of glDrawArraysIndirect() in a
                       draw shader. Thanks to this we may render all ( different ) objects
                       using only one draw call. Requires OpenGL version 4.3 and some more
                       work from me, because now it does not work ( probably I implemented
                       it wrong, or Windows NVidia driver has errors, because it hangs
                       the apllication at the moment ).

This application is inspired by Daniel Rákos work : "GPU based dynamic geometry LOD" that
may be found under this address : http://rastergrid.com/blog/2010/10/gpu-based-dynamic-geometry-lod/
There are however some differences :
- Daniel Rákos uses GL queries to count objects to render, while this example
  uses atomic counters ( no GPU->CPU roundtrip )
- this example does not use transform feedback buffers to store intermediate data
  ( it uses texture buffer objects instead ).
- I use only the vertex shader to cull objects, whereas Daniel Rákos uses vertex shader
  and geometry shader ( because only geometry shader can send more than one primitive
  to transform feedback buffers ).
- objects in the example are drawn using glDrawArraysIndirect() function,
  instead of glDrawElementsInstanced().

Finally there are some things to consider/discuss  :
- the whole algorithm exploits nice OpenGL feature that any GL buffer
  may be bound as any type of buffer ( in our example a buffer is once bound
  as a texture buffer object, and later is bound as GL_DRAW_INDIRECT_BUFFER ).
  osg::TextureBuffer class has one handy method to do that trick ( bindBufferAs() ),
  and new primitive sets use osg::TextureBuffer as input.
  For now I added new primitive sets to example ( DrawArraysIndirect and
  MultiDrawArraysIndirect defined in examples/osggpucull/DrawIndirectPrimitiveSet.h ),
  but if Robert will accept its current implementations ( I mean - primitive
  sets that have osg::TextureBuffer in constructor ), I may add it to
  osg/include/PrimitiveSet header.
- I used BufferTemplate class writen and published by Aurelien in submission forum
  some time ago. For some reason this class never got into osg/include, but is
  really needed during creation of UBOs, TBOs, and possibly SSBOs in the future.
  I added std::vector specialization to that template class.
- I needed to create similar osg::Geometries with variable number of vertices
  ( to create different LODs in my example ). For this reason I've written
  some code allowing me to create osg::Geometries from osg::Shape descendants.
  This code may be found in ShapeToGeometry.* files. Examples of use are in
  osggpucull.cpp . The question is : should this code stay in example, or should
  it be moved to osgUtil ?
- this remark is important for NVidia cards on Linux and Windows : if
  you have "Sync to VBlank" turned ON in nvidia-settings and you want to see
  real GPU times in OSG statistics window, you must set the power management
  settings to "Prefer maximum performance", because when "Adaptive mode" is used,
  the graphic card's clock may be slowed down by the driver during program execution
  ( On Linux when OpenGL application starts in adaptive mode, clock should work
  as fast as possible, but after one minute of program execution, the clock slows down ).
  This happens when GPU time in OSG statistics window is shorter than 3 ms.
"


git-svn-id: http://svn.openscenegraph.org/osg/OpenSceneGraph/trunk@14531 16af8721-9629-0410-8352-f15c8da7e697
This commit is contained in:
Robert Osfield 2014-11-25 10:58:23 +00:00
parent f06ea2bc6e
commit 4c5a1885d2
12 changed files with 4146 additions and 2 deletions

View File

@ -64,6 +64,7 @@ IF(DYNAMIC_OPENSCENEGRAPH)
ADD_SUBDIRECTORY(osglightpoint)
ADD_SUBDIRECTORY(osglogicop)
ADD_SUBDIRECTORY(osglogo)
ADD_SUBDIRECTORY(osggpucull)
ADD_SUBDIRECTORY(osggpx)
ADD_SUBDIRECTORY(osggraphicscost)
ADD_SUBDIRECTORY(osgmanipulator)
@ -174,7 +175,6 @@ IF(DYNAMIC_OPENSCENEGRAPH)
IF(NOT OSG_GLES1_AVAILABLE AND NOT OSG_GLES2_AVAILABLE AND NOT OSG_GL3_AVAILABLE)
ADD_SUBDIRECTORY(osgscreencapture)
ADD_SUBDIRECTORY(osgframerenderer)
ADD_SUBDIRECTORY(osgmotionblur)
ADD_SUBDIRECTORY(osgteapot)
ENDIF()

View File

@ -0,0 +1,84 @@
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2014 Robert Osfield
* Copyright (C) 2014 Pawel Ksiezopolski
*
* This library is open source and may be redistributed and/or modified under
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
* (at your option) any later version. The full license is in LICENSE file
* included with this distribution, and on the openscenegraph.org website.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* OpenSceneGraph Public License for more details.
*
*/
#include "AggregateGeometryVisitor.h"
AggregateGeometryVisitor::AggregateGeometryVisitor( ConvertTrianglesOperator* ctOperator )
: osg::NodeVisitor( osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
{
_ctOperator.setConverter(ctOperator);
init();
}
void AggregateGeometryVisitor::init()
{
_aggregatedGeometry = new osg::Geometry;
_ctOperator.initGeometry( _aggregatedGeometry.get() );
_matrixStack.clear();
}
AggregateGeometryVisitor::AddObjectResult AggregateGeometryVisitor::addObject( osg::Node* object, unsigned int typeID, unsigned int lodNumber )
{
unsigned int currentVertexFirst = _aggregatedGeometry->getVertexArray()->getNumElements();
_currentTypeID = typeID;
_currentLodNumber = lodNumber;
object->accept(*this);
unsigned int currentVertexCount = _aggregatedGeometry->getVertexArray()->getNumElements() - currentVertexFirst;
_aggregatedGeometry->addPrimitiveSet( new osg::DrawArrays( osg::PrimitiveSet::TRIANGLES, currentVertexFirst, currentVertexCount ) );
_matrixStack.clear();
return AddObjectResult( currentVertexFirst, currentVertexCount, _aggregatedGeometry->getNumPrimitiveSets()-1 );
}
void AggregateGeometryVisitor::apply( osg::Node& node )
{
bool pushed = _ctOperator.pushNode(&node);
traverse(node);
if( pushed ) _ctOperator.popNode();
}
void AggregateGeometryVisitor::apply( osg::Transform& transform )
{
bool pushed = _ctOperator.pushNode(&transform);
osg::Matrix matrix;
if( !_matrixStack.empty() )
matrix = _matrixStack.back();
transform.computeLocalToWorldMatrix( matrix, this );
_matrixStack.push_back( matrix );
traverse(transform);
_matrixStack.pop_back();
if( pushed ) _ctOperator.popNode();
}
void AggregateGeometryVisitor::apply( osg::Geode& geode )
{
bool pushed = _ctOperator.pushNode(&geode);
osg::Matrix matrix;
if(! _matrixStack.empty() )
matrix = _matrixStack.back();
for( unsigned int i=0; i<geode.getNumDrawables(); ++i)
{
osg::Geometry* geom = geode.getDrawable(i)->asGeometry();
if ( geom != NULL )
{
_ctOperator.setGeometryData( matrix, geom, _aggregatedGeometry.get(), (float) _currentTypeID, (float) _currentLodNumber );
geom->accept( _ctOperator );
}
}
traverse(geode);
if( pushed ) _ctOperator.popNode();
}

View File

@ -0,0 +1,290 @@
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2014 Robert Osfield
* Copyright (C) 2014 Pawel Ksiezopolski
*
* This library is open source and may be redistributed and/or modified under
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
* (at your option) any later version. The full license is in LICENSE file
* included with this distribution, and on the openscenegraph.org website.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* OpenSceneGraph Public License for more details.
*
*/
#ifndef AGREGATE_GEOMETRY_VISITOR_H
#define AGREGATE_GEOMETRY_VISITOR_H 1
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/Transform>
#include <osg/NodeVisitor>
#include <osg/TriangleIndexFunctor>
// AggregateGeometryVisitor uses ConvertTrianglesOperator to create and fill osg::Geometry
// with data matching users needs
struct ConvertTrianglesOperator : public osg::Referenced
{
ConvertTrianglesOperator()
: osg::Referenced()
{
}
virtual void initGeometry( osg::Geometry* outputGeometry ) = 0;
virtual bool pushNode( osg::Node* node )
{
return false;
}
virtual void popNode() = 0;
virtual void setGeometryData( const osg::Matrix &matrix, osg::Geometry *inputGeometry, osg::Geometry* outputGeometry, float typeID, float lodNumber ) = 0;
virtual void operator() ( unsigned int i1, unsigned int i2, unsigned int i3 ) = 0;
};
class GetVec2FromArrayVisitor : public osg::ValueVisitor
{
public:
GetVec2FromArrayVisitor()
{
}
void apply( GLfloat& value )
{
out = osg::Vec2( value, 0.0 );
}
void apply( osg::Vec2& value )
{
out = osg::Vec2( value.x(), value.y() );
}
virtual void apply( osg::Vec2d& value )
{
out = osg::Vec2( value.x(), value.y() );
}
void apply( osg::Vec3& value )
{
out = osg::Vec2( value.x(), value.y() );
}
void apply( osg::Vec4& value )
{
out = osg::Vec2( value.x(), value.y() );
}
void apply( osg::Vec3d& value )
{
out = osg::Vec2( value.x(), value.y() );
}
void apply( osg::Vec4d& value )
{
out = osg::Vec2( value.x(), value.y() );
}
osg::Vec2 out;
};
// ConvertTrianglesOperatorClassic is a ConvertTrianglesOperator that creates
// aggregated geometry with standard set of vertex attributes : vertices, normals, color and texCoord0.
// texCoord1 holds additional information about vertex ( typeID, lodNumber, boneIndex )
struct ConvertTrianglesOperatorClassic : public ConvertTrianglesOperator
{
ConvertTrianglesOperatorClassic()
: ConvertTrianglesOperator(), _typeID(0.0f), _lodNumber(0.0f)
{
_boneIndices.push_back(0.0);
}
virtual void initGeometry( osg::Geometry* outputGeometry )
{
osg::Vec3Array* vertices = new osg::Vec3Array; outputGeometry->setVertexArray( vertices );
osg::Vec4Array* colors = new osg::Vec4Array; outputGeometry->setColorArray( colors, osg::Array::BIND_PER_VERTEX );
osg::Vec3Array* normals = new osg::Vec3Array; outputGeometry->setNormalArray( normals, osg::Array::BIND_PER_VERTEX );
osg::Vec2Array* texCoords0 = new osg::Vec2Array; outputGeometry->setTexCoordArray( 0, texCoords0 );
osg::Vec3Array* texCoords1 = new osg::Vec3Array; outputGeometry->setTexCoordArray( 1, texCoords1 );
outputGeometry->setStateSet(NULL);
}
virtual bool pushNode( osg::Node* node )
{
std::map<std::string,float>::iterator it = _boneNames.find( node->getName() );
if(it==_boneNames.end())
return false;
_boneIndices.push_back( it->second );
return true;
}
virtual void popNode()
{
_boneIndices.pop_back();
}
virtual void setGeometryData( const osg::Matrix &matrix, osg::Geometry *inputGeometry, osg::Geometry* outputGeometry, float typeID, float lodNumber )
{
_matrix = matrix;
_inputVertices = dynamic_cast<osg::Vec3Array *>( inputGeometry->getVertexArray() );
_inputColors = dynamic_cast<osg::Vec4Array *>( inputGeometry->getColorArray() );
_inputNormals = dynamic_cast<osg::Vec3Array *>( inputGeometry->getNormalArray() );
_inputTexCoord0 = inputGeometry->getTexCoordArray(0);
_outputVertices = dynamic_cast<osg::Vec3Array *>( outputGeometry->getVertexArray() );
_outputColors = dynamic_cast<osg::Vec4Array *>( outputGeometry->getColorArray() );
_outputNormals = dynamic_cast<osg::Vec3Array *>( outputGeometry->getNormalArray() );
_outputTexCoord0 = dynamic_cast<osg::Vec2Array *>( outputGeometry->getTexCoordArray(0) );
_outputTexCoord1 = dynamic_cast<osg::Vec3Array *>( outputGeometry->getTexCoordArray(1) );
_typeID = typeID;
_lodNumber = lodNumber;
}
virtual void operator() ( unsigned int i1, unsigned int i2, unsigned int i3 )
{
unsigned int ic1=i1, ic2=i2, ic3=i3, in1=i1, in2=i2, in3=i3, it01=i1, it02=i2, it03=i3;
if ( _inputColors!=NULL && _inputColors->size() == 1 )
{
ic1=0; ic2=0; ic3=0;
}
if ( _inputNormals!=NULL && _inputNormals->size() == 1 )
{
in1=0; in2=0; in3=0;
}
if ( _inputTexCoord0!=NULL && _inputTexCoord0->getNumElements()==1 )
{
it01=0; it02=0; it03=0;
}
_outputVertices->push_back( _inputVertices->at( i1 ) * _matrix );
_outputVertices->push_back( _inputVertices->at( i2 ) * _matrix );
_outputVertices->push_back( _inputVertices->at( i3 ) * _matrix );
if( _inputColors != NULL )
{
_outputColors->push_back( _inputColors->at( ic1 ) );
_outputColors->push_back( _inputColors->at( ic2 ) );
_outputColors->push_back( _inputColors->at( ic3 ) );
}
else
{
for(unsigned int i=0; i<3; ++i)
_outputColors->push_back( osg::Vec4(1.0,1.0,1.0,1.0) );
}
if( _inputNormals != NULL )
{
_outputNormals->push_back( osg::Matrix::transform3x3( _inputNormals->at( in1 ), _matrix ) );
_outputNormals->push_back( osg::Matrix::transform3x3( _inputNormals->at( in2 ), _matrix ) );
_outputNormals->push_back( osg::Matrix::transform3x3( _inputNormals->at( in3 ), _matrix ) );
}
else
{
for(unsigned int i=0; i<3; ++i)
_outputNormals->push_back( osg::Vec3( 0.0,0.0,1.0 ) );
}
if( _inputTexCoord0 != NULL )
{
_inputTexCoord0->accept( it01, _inputTexCoord0Visitor );
_outputTexCoord0->push_back( _inputTexCoord0Visitor.out );
_inputTexCoord0->accept( it02, _inputTexCoord0Visitor );
_outputTexCoord0->push_back( _inputTexCoord0Visitor.out );
_inputTexCoord0->accept( it03, _inputTexCoord0Visitor );
_outputTexCoord0->push_back( _inputTexCoord0Visitor.out );
}
else
{
for(unsigned int i=0; i<3; ++i)
_outputTexCoord0->push_back( osg::Vec2(0.0,0.0) );
}
for(unsigned int i=0; i<3; ++i)
_outputTexCoord1->push_back( osg::Vec3( _typeID, _lodNumber, _boneIndices.back() ) );
}
void registerBoneByName( const std::string& boneName, int boneIndex )
{
_boneNames[boneName] = float(boneIndex);
}
osg::Matrix _matrix;
osg::Vec3Array* _inputVertices;
osg::Vec4Array* _inputColors;
osg::Vec3Array* _inputNormals;
osg::Array* _inputTexCoord0;
osg::Vec3Array* _outputVertices;
osg::Vec4Array* _outputColors;
osg::Vec3Array* _outputNormals;
osg::Vec2Array* _outputTexCoord0;
osg::Vec3Array* _outputTexCoord1;
float _typeID;
float _lodNumber;
std::vector<float> _boneIndices;
std::map<std::string,float> _boneNames;
GetVec2FromArrayVisitor _inputTexCoord0Visitor;
};
class AggregateGeometryVisitor : public osg::NodeVisitor
{
public:
AggregateGeometryVisitor( ConvertTrianglesOperator* ctOperator );
void init();
// osg::TriangleIndexFunctor uses its template parameter as a base class, so we must use an adapter pattern to hack it
struct ConvertTrianglesBridge
{
inline void setConverter( ConvertTrianglesOperator* cto )
{
_converter = cto;
}
inline void initGeometry( osg::Geometry* outputGeometry )
{
_converter->initGeometry(outputGeometry);
}
inline bool pushNode( osg::Node* node )
{
return _converter->pushNode( node );
}
inline void popNode()
{
_converter->popNode();
}
inline void setGeometryData( const osg::Matrix &matrix, osg::Geometry *inputGeometry, osg::Geometry* outputGeometry, float typeID, float lodNumber )
{
_converter->setGeometryData(matrix,inputGeometry,outputGeometry,typeID, lodNumber);
}
inline void operator() ( unsigned int i1, unsigned int i2, unsigned int i3 )
{
_converter->operator()(i1,i2,i3);
}
osg::ref_ptr<ConvertTrianglesOperator> _converter;
};
// struct returning information about added object ( first vertex, vertex count, primitiveset index )
// used later to create indirect command texture buffers
struct AddObjectResult
{
AddObjectResult( unsigned int f, unsigned int c, unsigned int i )
: first(f), count(c), index(i)
{
}
unsigned int first;
unsigned int count;
unsigned int index;
};
AddObjectResult addObject( osg::Node* object, unsigned int typeID, unsigned int lodNumber );
void apply( osg::Node& node );
void apply( osg::Transform& transform );
void apply( osg::Geode& geode );
inline osg::Geometry* getAggregatedGeometry()
{
return _aggregatedGeometry.get();
}
protected:
osg::ref_ptr<osg::Geometry> _aggregatedGeometry;
osg::TriangleIndexFunctor<ConvertTrianglesBridge> _ctOperator;
std::vector<osg::Matrix> _matrixStack;
unsigned int _currentTypeID;
unsigned int _currentLodNumber;
};
#endif

View File

@ -0,0 +1,16 @@
SET(TARGET_SRC
ShapeToGeometry.cpp
DrawIndirectPrimitiveSet.cpp
AggregateGeometryVisitor.cpp
osggpucull.cpp
)
SET(TARGET_H
ShapeToGeometry.h
DrawIndirectPrimitiveSet.h
AggregateGeometryVisitor.h
GpuCullShaders.h
)
#### end var setup ###
SETUP_EXAMPLE(osggpucull)

View File

@ -0,0 +1,139 @@
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2014 Robert Osfield
* Copyright (C) 2014 Pawel Ksiezopolski
*
* This library is open source and may be redistributed and/or modified under
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
* (at your option) any later version. The full license is in LICENSE file
* included with this distribution, and on the openscenegraph.org website.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* OpenSceneGraph Public License for more details.
*
*/
#include "DrawIndirectPrimitiveSet.h"
#include <osg/State>
#include <osg/buffered_value>
#include <osg/ref_ptr>
#include <osg/GLExtensions>
#include <osg/Drawable>
void DrawArraysIndirect::draw(osg::State& state, bool useVertexBufferObjects) const
{
if( !_buffer.valid() )
return;
_buffer->bindBufferAs( state.getContextID(), GL_DRAW_INDIRECT_BUFFER );
// if you want to see how many primitives were rendered - uncomment code below, but
// be warned : it is a serious performance killer ( because of GPU->CPU roundtrip )
// osg::Drawable::Extensions *dext = osg::Drawable::getExtensions( state.getContextID(),true );
// int* tab = (int*)dext->glMapBuffer(GL_DRAW_INDIRECT_BUFFER,GL_READ_ONLY);
// int val = _indirect/sizeof(int);
// OSG_WARN<<"DrawArraysIndirect ("<<val<<"): "<< tab[val] << " " << tab[val+1] << " " << tab[val+2] << " " << tab[val+3] << std::endl;
// dext->glUnmapBuffer(GL_DRAW_INDIRECT_BUFFER);
DrawIndirectGLExtensions *ext = DrawIndirectGLExtensions::getExtensions( state.getContextID(),true );
ext->glDrawArraysIndirect( _mode, reinterpret_cast<const void*>(_indirect) );
_buffer->unbindBufferAs( state.getContextID(), GL_DRAW_INDIRECT_BUFFER );
}
void MultiDrawArraysIndirect::draw(osg::State& state, bool useVertexBufferObjects) const
{
if( !_buffer.valid() )
return;
_buffer->bindBufferAs( state.getContextID(), GL_DRAW_INDIRECT_BUFFER );
DrawIndirectGLExtensions *ext = DrawIndirectGLExtensions::getExtensions( state.getContextID(),true );
ext->glMultiDrawArraysIndirect( _mode, reinterpret_cast<const void*>(_indirect), _drawcount, _stride );
_buffer->unbindBufferAs( state.getContextID(), GL_DRAW_INDIRECT_BUFFER );
}
DrawIndirectGLExtensions::DrawIndirectGLExtensions( unsigned int contextID )
{
setupGLExtensions( contextID );
}
DrawIndirectGLExtensions::DrawIndirectGLExtensions( const DrawIndirectGLExtensions &rhs )
: Referenced()
{
_glDrawArraysIndirect = rhs._glDrawArraysIndirect;
_glMultiDrawArraysIndirect = rhs._glMultiDrawArraysIndirect;
_glMemoryBarrier = rhs._glMemoryBarrier;
}
void DrawIndirectGLExtensions::lowestCommonDenominator( const DrawIndirectGLExtensions &rhs )
{
if ( !rhs._glDrawArraysIndirect )
{
_glDrawArraysIndirect = rhs._glDrawArraysIndirect;
}
if ( !rhs._glMultiDrawArraysIndirect )
{
_glMultiDrawArraysIndirect = rhs._glMultiDrawArraysIndirect;
}
if ( !rhs._glMemoryBarrier )
{
_glMemoryBarrier = rhs._glMemoryBarrier;
}
}
void DrawIndirectGLExtensions::setupGLExtensions( unsigned int contextID )
{
_glDrawArraysIndirect = 0;
_glMultiDrawArraysIndirect = 0;
osg::setGLExtensionFuncPtr( _glDrawArraysIndirect, "glDrawArraysIndirect" );
osg::setGLExtensionFuncPtr( _glMultiDrawArraysIndirect, "glMultiDrawArraysIndirect" );
osg::setGLExtensionFuncPtr( _glMemoryBarrier, "glMemoryBarrier" );
}
void DrawIndirectGLExtensions::glDrawArraysIndirect(GLenum mode, const void * indirect) const
{
if ( _glDrawArraysIndirect )
{
_glDrawArraysIndirect( mode, indirect );
}
else
{
OSG_WARN<<"Error: glDrawArraysIndirect not supported by OpenGL driver"<<std::endl;
}
}
void DrawIndirectGLExtensions::glMultiDrawArraysIndirect(GLenum mode, const void *indirect, GLsizei drawcount, GLsizei stride)
{
if ( _glMultiDrawArraysIndirect )
{
_glMultiDrawArraysIndirect( mode, indirect, drawcount, stride );
}
else
{
OSG_WARN<<"Error: glMultiDrawArraysIndirect not supported by OpenGL driver"<<std::endl;
}
}
void DrawIndirectGLExtensions::glMemoryBarrier(GLbitfield barriers)
{
if ( _glMemoryBarrier )
{
_glMemoryBarrier( barriers );
}
else
{
OSG_WARN<<"Error: glMemoryBarrier not supported by OpenGL driver"<<std::endl;
}
}
typedef osg::buffered_value< osg::ref_ptr<DrawIndirectGLExtensions> > BufferedDrawIndirectGLExtensions;
static BufferedDrawIndirectGLExtensions bdiExtensions;
DrawIndirectGLExtensions* DrawIndirectGLExtensions::getExtensions( unsigned int contextID,bool createIfNotInitalized )
{
if ( !bdiExtensions[contextID] && createIfNotInitalized )
{
bdiExtensions[contextID] = new DrawIndirectGLExtensions( contextID );
}
return bdiExtensions[contextID].get();
}

View File

@ -0,0 +1,100 @@
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2014 Robert Osfield
* Copyright (C) 2014 Pawel Ksiezopolski
*
* This library is open source and may be redistributed and/or modified under
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
* (at your option) any later version. The full license is in LICENSE file
* included with this distribution, and on the openscenegraph.org website.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* OpenSceneGraph Public License for more details.
*
*/
#ifndef OSG_DRAWINDIRECTPRIMITIVESET
#define OSG_DRAWINDIRECTPRIMITIVESET 1
#include <osg/PrimitiveSet>
#include <osg/BufferObject>
#include <osg/TextureBuffer>
#ifndef GL_ARB_draw_indirect
#define GL_DRAW_INDIRECT_BUFFER 0x8F3F
#define GL_DRAW_INDIRECT_BUFFER_BINDING 0x8F43
#endif
#ifndef GL_SHADER_IMAGE_ACCESS_BARRIER_BIT
#define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT 0x00000020
#endif
class DrawArraysIndirect : public osg::DrawArrays
{
public:
DrawArraysIndirect(GLenum mode=0, osg::TextureBuffer* buffer=NULL, unsigned int indirect=0)
: osg::DrawArrays(mode), _buffer(buffer), _indirect(indirect)
{
}
virtual osg::Object* cloneType() const { return new DrawArraysIndirect(); }
virtual osg::Object* clone(const osg::CopyOp& copyop) const { return NULL; }
virtual bool isSameKindAs(const osg::Object* obj) const { return dynamic_cast<const DrawArraysIndirect*>(obj)!=NULL; }
virtual const char* libraryName() const { return "osg"; }
virtual const char* className() const { return "DrawArraysIndirect"; }
virtual void draw(osg::State& state, bool useVertexBufferObjects) const;
protected:
osg::ref_ptr<osg::TextureBuffer> _buffer;
unsigned int _indirect;
};
class MultiDrawArraysIndirect : public osg::DrawArrays
{
public:
MultiDrawArraysIndirect(GLenum mode=0, osg::TextureBuffer* buffer=NULL, unsigned int indirect=0, GLsizei drawcount=0, GLsizei stride=0)
: osg::DrawArrays(mode), _buffer(buffer), _indirect(indirect), _drawcount(drawcount), _stride(stride)
{
}
virtual osg::Object* cloneType() const { return new MultiDrawArraysIndirect(); }
virtual osg::Object* clone(const osg::CopyOp& copyop) const { return NULL; }
virtual bool isSameKindAs(const osg::Object* obj) const { return dynamic_cast<const MultiDrawArraysIndirect*>(obj)!=NULL; }
virtual const char* libraryName() const { return "osg"; }
virtual const char* className() const { return "MultiDrawArraysIndirect"; }
virtual void draw(osg::State& state, bool useVertexBufferObjects) const;
protected:
osg::ref_ptr<osg::TextureBuffer> _buffer;
unsigned int _indirect;
GLsizei _drawcount;
GLsizei _stride;
};
class DrawIndirectGLExtensions : public osg::Referenced
{
public:
DrawIndirectGLExtensions( unsigned int contextID );
DrawIndirectGLExtensions( const DrawIndirectGLExtensions &rhs );
void lowestCommonDenominator( const DrawIndirectGLExtensions &rhs );
void setupGLExtensions( unsigned int contextID );
void glDrawArraysIndirect(GLenum mode, const void * indirect) const;
void glMultiDrawArraysIndirect(GLenum mode, const void *indirect, GLsizei drawcount, GLsizei stride);
void glMemoryBarrier(GLbitfield barriers);
static DrawIndirectGLExtensions* getExtensions( unsigned int contextID,bool createIfNotInitalized );
protected:
typedef void ( GL_APIENTRY *DrawArraysIndirectProc ) (GLenum mode, const void * indirect);
typedef void ( GL_APIENTRY *MultiDrawArraysIndirectProc ) (GLenum mode, const void *indirect, GLsizei drawcount, GLsizei stride);
typedef void ( GL_APIENTRY *MemoryBarrierProc ) (GLbitfield barriers);
DrawArraysIndirectProc _glDrawArraysIndirect;
MultiDrawArraysIndirectProc _glMultiDrawArraysIndirect;
MemoryBarrierProc _glMemoryBarrier;
};
#endif

View File

@ -0,0 +1,592 @@
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2014 Robert Osfield
* Copyright (C) 2014 Pawel Ksiezopolski
*
* This library is open source and may be redistributed and/or modified under
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
* (at your option) any later version. The full license is in LICENSE file
* included with this distribution, and on the openscenegraph.org website.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* OpenSceneGraph Public License for more details.
*
*/
#ifndef GPU_CULL_SHADERS
#define GPU_CULL_SHADERS 1
char SHADER_STATIC_CULL_VERTEX[] =
"#version 420 compatibility\n"
"\n"
"uniform mat4 osg_ViewMatrixInverse;\n"
"\n"
"layout(R32I) coherent uniform iimageBuffer indirectCommand0;\n"
"layout(R32I) coherent uniform iimageBuffer indirectCommand1;\n"
"uniform int indirectCommandSize; // = sizeof(DrawArraysIndirectCommand) / sizeof(unsigned int) = 4\n"
"\n"
"layout(R32I) coherent uniform iimageBuffer getIndirectCommand( int index )\n"
"{\n"
" if(index==0) return indirectCommand0;\n"
" if(index==1) return indirectCommand1;\n"
" return indirectCommand0;\n"
"}\n"
"\n"
"layout(RGBA32F) coherent uniform imageBuffer indirectTarget0;\n"
"layout(RGBA32F) coherent uniform imageBuffer indirectTarget1;\n"
"\n"
"layout(RGBA32F) coherent uniform imageBuffer getIndirectTarget( int index )\n"
"{\n"
" if(index==0) return indirectTarget0;\n"
" if(index==1) return indirectTarget1;\n"
" return indirectTarget0;\n"
"}\n"
"\n"
"struct InstanceLOD\n"
"{\n"
" vec4 bbMin;\n"
" vec4 bbMax;\n"
" ivec4 indirectTargetParams; // x=targetID, y=indexInTarget, z=offsetInTarget, w=lodMaxQuantity\n"
" vec4 distances; // x=minDistance, y=minFadeDistance, z=maxFadeDistance, w=maxDistance\n"
"};\n"
"\n"
"const int OSGGPUCULL_MAXIMUM_LOD_NUMBER = 8;\n"
"\n"
"struct InstanceType\n"
"{\n"
" vec4 bbMin;\n"
" vec4 bbMax;\n"
" ivec4 params;\n"
" InstanceLOD lods[OSGGPUCULL_MAXIMUM_LOD_NUMBER];\n"
"};\n"
"\n"
"layout(std140) uniform instanceTypesData\n"
"{\n"
" InstanceType instanceTypes[32];\n"
"};\n"
"\n"
"// StaticInstance params are stored in vertex attributes\n"
"layout(location = 0) in vec3 VertexPosition;\n"
"layout(location = 10) in vec4 M0;\n"
"layout(location = 11) in vec4 M1;\n"
"layout(location = 12) in vec4 M2;\n"
"layout(location = 13) in vec4 M3;\n"
"layout(location = 14) in vec4 ExtraParams;\n"
"layout(location = 15) in vec4 IdParams;\n"
"\n"
"out vec4 fColor;\n"
"\n"
"bool boundingBoxInViewFrustum( in mat4 matrix, in vec3 bbMin, in vec3 bbMax )\n"
"{\n"
" vec4 BoundingBox[8];\n"
" BoundingBox[0] = matrix * vec4( bbMax.x, bbMax.y, bbMax.z, 1.0);\n"
" BoundingBox[1] = matrix * vec4( bbMin.x, bbMax.y, bbMax.z, 1.0);\n"
" BoundingBox[2] = matrix * vec4( bbMax.x, bbMin.y, bbMax.z, 1.0);\n"
" BoundingBox[3] = matrix * vec4( bbMin.x, bbMin.y, bbMax.z, 1.0);\n"
" BoundingBox[4] = matrix * vec4( bbMax.x, bbMax.y, bbMin.z, 1.0);\n"
" BoundingBox[5] = matrix * vec4( bbMin.x, bbMax.y, bbMin.z, 1.0);\n"
" BoundingBox[6] = matrix * vec4( bbMax.x, bbMin.y, bbMin.z, 1.0);\n"
" BoundingBox[7] = matrix * vec4( bbMin.x, bbMin.y, bbMin.z, 1.0);\n"
"\n"
" int outOfBound[6] = int[6]( 0, 0, 0, 0, 0, 0 );\n"
" for (int i=0; i<8; i++)\n"
" {\n"
" outOfBound[0] += int( BoundingBox[i].x > BoundingBox[i].w );\n"
" outOfBound[1] += int( BoundingBox[i].x < -BoundingBox[i].w );\n"
" outOfBound[2] += int( BoundingBox[i].y > BoundingBox[i].w );\n"
" outOfBound[3] += int( BoundingBox[i].y < -BoundingBox[i].w );\n"
" outOfBound[4] += int( BoundingBox[i].z > BoundingBox[i].w );\n"
" outOfBound[5] += int( BoundingBox[i].z < -BoundingBox[i].w );\n"
" }\n"
" return (outOfBound[0] < 8 ) && ( outOfBound[1] < 8 ) && ( outOfBound[2] < 8 ) && ( outOfBound[3] < 8 ) && ( outOfBound[4] < 8 ) && ( outOfBound[5] < 8 );\n"
"}\n"
"\n"
"void main(void) \n"
"{\n"
" mat4 instanceMatrix = mat4(M0,M1,M2,M3);\n"
" mat4 mvpoMatrix = gl_ModelViewProjectionMatrix * instanceMatrix;\n"
"\n"
" // gl_Position is created only for debugging purposes\n"
" gl_Position = mvpoMatrix * vec4(0.0,0.0,0.0,1.0);\n"
"\n"
" int instanceTypeIndex = int(IdParams.x);\n"
"\n"
" fColor = vec4(0.0,0.0,0.0,1.0);\n"
" if( boundingBoxInViewFrustum( mvpoMatrix, instanceTypes[instanceTypeIndex].bbMin.xyz, instanceTypes[instanceTypeIndex].bbMax.xyz ) )\n"
" {\n"
" fColor = vec4(1.0,0.0,0.0,1.0);\n"
" float distanceToObject = distance(osg_ViewMatrixInverse[3].xyz / osg_ViewMatrixInverse[3].w, instanceMatrix[3].xyz / instanceMatrix[3].w );\n"
" for(int i=0;i<instanceTypes[instanceTypeIndex].params.x;++i)\n"
" {\n"
" if( boundingBoxInViewFrustum( mvpoMatrix, instanceTypes[instanceTypeIndex].lods[i].bbMin.xyz, instanceTypes[instanceTypeIndex].lods[i].bbMax.xyz ) &&\n"
" ( distanceToObject >= instanceTypes[instanceTypeIndex].lods[i].distances.x ) && ( distanceToObject < instanceTypes[instanceTypeIndex].lods[i].distances.w ) )\n"
" {\n"
" fColor = vec4(1.0,1.0,0.0,1.0);\n"
" float fadeAlpha = 4.0 * ( clamp( (distanceToObject-instanceTypes[instanceTypeIndex].lods[i].distances.x)/( instanceTypes[instanceTypeIndex].lods[i].distances.y - instanceTypes[instanceTypeIndex].lods[i].distances.x), 0.0, 1.0 ) \n"
" + clamp( (distanceToObject- instanceTypes[instanceTypeIndex].lods[i].distances.z)/( instanceTypes[instanceTypeIndex].lods[i].distances.w - instanceTypes[instanceTypeIndex].lods[i].distances.z), 0.0, 1.0 ) );\n"
" int sampleMask = ( 0xF0 >> int(fadeAlpha) ) & 0xF;\n"
" int indirectCommandIndex = instanceTypes[instanceTypeIndex].lods[i].indirectTargetParams.x;\n"
" int indirectCommandAddress = instanceTypes[instanceTypeIndex].lods[i].indirectTargetParams.y;\n"
" int objectIndex = imageAtomicAdd( getIndirectCommand( indirectCommandIndex ), indirectCommandAddress*indirectCommandSize+1, 1 );\n"
" int indirectTargetAddress = 6*(instanceTypes[instanceTypeIndex].lods[i].indirectTargetParams.z + objectIndex);\n"
" imageStore( getIndirectTarget(indirectCommandIndex), indirectTargetAddress+0, M0 );\n"
" imageStore( getIndirectTarget(indirectCommandIndex), indirectTargetAddress+1, M1 );\n"
" imageStore( getIndirectTarget(indirectCommandIndex), indirectTargetAddress+2, M2 );\n"
" imageStore( getIndirectTarget(indirectCommandIndex), indirectTargetAddress+3, M3 );\n"
" imageStore( getIndirectTarget(indirectCommandIndex), indirectTargetAddress+4, ExtraParams );\n"
" imageStore( getIndirectTarget(indirectCommandIndex), indirectTargetAddress+5, vec4(IdParams.x,IdParams.y,float(sampleMask),0.0) );\n"
" }\n"
" }\n"
" }\n"
"}\n";
char SHADER_STATIC_CULL_FRAGMENT[] =
"#version 420 compatibility\n"
"\n"
"layout(location = 0, index = 0) out vec4 FragmentColor;\n"
"\n"
"in vec4 fColor;\n"
"\n"
"void main()\n"
"{\n"
" FragmentColor = fColor;\n"
"}\n";
char SHADER_STATIC_DRAW_0_VERTEX[] =
"#version 420 compatibility\n"
"\n"
"layout(location = 0) in vec4 VertexPosition;\n"
"layout(location = 2) in vec3 VertexNormal;\n"
"layout(location = 3) in vec4 VertexColor;\n"
"layout(location = 8) in vec2 VertexTexCoord0;\n"
"layout(location = 9) in vec3 VertexTexCoord1;\n"
"\n"
"struct InstanceLOD\n"
"{\n"
" vec4 bbMin;\n"
" vec4 bbMax;\n"
" ivec4 indirectTargetParams; // x=targetID, y=indexInTarget, z=offsetInTarget, w=lodMaxQuantity\n"
" vec4 distances; // x=minDistance, y=minFadeDistance, z=maxFadeDistance, w=maxDistance\n"
"};\n"
"\n"
"const int OSGGPUCULL_MAXIMUM_LOD_NUMBER = 8;\n"
"\n"
"struct InstanceType\n"
"{\n"
" vec4 bbMin;\n"
" vec4 bbMax;\n"
" ivec4 params;\n"
" InstanceLOD lods[OSGGPUCULL_MAXIMUM_LOD_NUMBER];\n"
"};\n"
"\n"
"layout(std140) uniform instanceTypesData\n"
"{\n"
" InstanceType instanceTypes[32];\n"
"};\n"
"\n"
"layout(RGBA32F) coherent uniform imageBuffer indirectTarget;\n"
"\n"
"out vec3 ecPosition3;\n"
"out vec3 ecNormal;\n"
"out vec2 TexCoord;\n"
"out vec4 color;\n"
"flat out int sMask;\n"
" \n"
"void main()\n"
"{\n"
" // every vertex has its type coded on VertexTexCoord1.x,\n"
" // and its lodNumber coded in VertexTexCoord1.y\n"
" int instanceTypeIndex = int(VertexTexCoord1.x);\n"
" int instanceLodNumber = int(VertexTexCoord1.y);\n"
" int indirectTargetAddress = 6*(instanceTypes[instanceTypeIndex].lods[instanceLodNumber].indirectTargetParams.z + gl_InstanceID);\n"
" mat4 instanceMatrix = mat4(\n"
" imageLoad(indirectTarget, indirectTargetAddress + 0),\n"
" imageLoad(indirectTarget, indirectTargetAddress + 1),\n"
" imageLoad(indirectTarget, indirectTargetAddress + 2),\n"
" imageLoad(indirectTarget, indirectTargetAddress + 3) );\n"
" vec4 extraParams = imageLoad(indirectTarget, indirectTargetAddress + 4);\n"
" vec4 idParams = imageLoad(indirectTarget, indirectTargetAddress + 5);\n"
"\n"
" sMask = int(idParams.z);\n"
"\n"
" mat4 mvMatrix = gl_ModelViewMatrix * instanceMatrix;\n"
" \n"
" // Do fixed functionality vertex transform\n"
" vec4 ecPos = mvMatrix * vec4(VertexPosition.xyz,1.0);\n"
" gl_Position = gl_ProjectionMatrix * ecPos;\n"
"\n"
" ecPosition3 = ecPos.xyz / ecPos.w;\n"
" ecNormal = normalize( mat3(mvMatrix) * VertexNormal.xyz );\n"
" \n"
" TexCoord = VertexTexCoord0.xy;\n"
" color.xyz = VertexColor.xyz * extraParams.x;\n"
" color.a = VertexColor.a;\n"
"}\n";
char SHADER_STATIC_DRAW_0_FRAGMENT[] =
"#version 420 compatibility\n"
"\n"
"in vec3 ecPosition3;\n"
"in vec3 ecNormal;\n"
"in vec2 TexCoord;\n"
"in vec4 color;\n"
"\n"
"flat in int sMask;\n"
"\n"
"layout(location = 0) out vec4 FragColor;\n"
"\n"
"void main()\n"
"{\n"
"// simple fragment shader that calculates ambient + diffuse lighting with osg::LightSource\n"
" vec3 lightDir = normalize(vec3(gl_LightSource[0].position));\n"
" vec3 normal = normalize(ecNormal);\n"
" float NdotL = max(dot(normal, lightDir), 0.0);\n"
" FragColor = NdotL * color * gl_LightSource[0].diffuse + color * gl_LightSource[0].ambient;\n"
" gl_SampleMask[0] = sMask;\n"
"}\n";
char SHADER_STATIC_DRAW_1_VERTEX[] =
"#version 420 compatibility\n"
"\n"
"layout(location = 0) in vec4 VertexPosition;\n"
"layout(location = 2) in vec3 VertexNormal;\n"
"layout(location = 3) in vec4 VertexColor;\n"
"layout(location = 8) in vec2 VertexTexCoord0;\n"
"layout(location = 9) in vec3 VertexTexCoord1;\n"
"\n"
"struct InstanceLOD\n"
"{\n"
" vec4 bbMin;\n"
" vec4 bbMax;\n"
" ivec4 indirectTargetParams; // x=targetID, y=indexInTarget, z=offsetInTarget, w=lodMaxQuantity\n"
" vec4 distances; // x=minDistance, y=minFadeDistance, z=maxFadeDistance, w=maxDistance\n"
"};\n"
"\n"
"const int OSGGPUCULL_MAXIMUM_LOD_NUMBER = 8;\n"
"\n"
"struct InstanceType\n"
"{\n"
" vec4 bbMin;\n"
" vec4 bbMax;\n"
" ivec4 params;\n"
" InstanceLOD lods[OSGGPUCULL_MAXIMUM_LOD_NUMBER];\n"
"};\n"
"\n"
"layout(std140) uniform instanceTypesData\n"
"{\n"
" InstanceType instanceTypes[32];\n"
"};\n"
"\n"
"layout(RGBA32F) coherent uniform imageBuffer indirectTarget;\n"
"\n"
"uniform vec2 windDirection;\n"
"uniform float osg_FrameTime;\n"
"\n"
"out vec3 ecPosition3;\n"
"out vec3 ecNormal;\n"
"out vec2 TexCoord;\n"
"out vec4 color;\n"
"flat out int sMask;\n"
" \n"
"void main()\n"
"{\n"
" // every vertex has its type coded on VertexTexCoord1.x,\n"
" // and its lodNumber coded in VertexTexCoord1.y\n"
" int instanceTypeIndex = int(VertexTexCoord1.x);\n"
" int instanceLodNumber = int(VertexTexCoord1.y);\n"
" int indirectTargetAddress = 6*(instanceTypes[instanceTypeIndex].lods[instanceLodNumber].indirectTargetParams.z + gl_InstanceID);\n"
" mat4 instanceMatrix = mat4(\n"
" imageLoad(indirectTarget, indirectTargetAddress + 0),\n"
" imageLoad(indirectTarget, indirectTargetAddress + 1),\n"
" imageLoad(indirectTarget, indirectTargetAddress + 2),\n"
" imageLoad(indirectTarget, indirectTargetAddress + 3) );\n"
" vec4 extraParams = imageLoad(indirectTarget, indirectTargetAddress + 4);\n"
" vec4 idParams = imageLoad(indirectTarget, indirectTargetAddress + 5);\n"
"\n"
" sMask = int(idParams.z);\n"
"\n"
" // simple tree waving in the wind. Amplitude, frequency and offset are coded in extraParams\n"
" vec4 mPos = instanceMatrix * vec4(VertexPosition.xyz,1.0);\n"
" float wavingAmplitute = VertexPosition.z * extraParams.y;\n"
" mPos.xy += windDirection * wavingAmplitute * sin( extraParams.z*osg_FrameTime + extraParams.w );\n"
"\n"
" mat4 mvMatrix = gl_ModelViewMatrix * instanceMatrix;\n"
" \n"
" // Do fixed functionality vertex transform\n"
" vec4 ecPos = osg_ModelViewMatrix * mPos;\n"
" gl_Position = gl_ProjectionMatrix * ecPos;\n"
"\n"
" ecPosition3 = ecPos.xyz / ecPos.w;\n"
" ecNormal = normalize( mat3(mvMatrix) * VertexNormal.xyz );\n"
" \n"
" TexCoord = VertexTexCoord0.xy;\n"
" color.xyz = VertexColor.xyz * extraParams.x;\n"
" color.a = VertexColor.a;\n"
"}\n";
char SHADER_STATIC_DRAW_1_FRAGMENT[] =
"#version 420 compatibility\n"
"\n"
"in vec3 ecPosition3;\n"
"in vec3 ecNormal;\n"
"in vec2 TexCoord;\n"
"in vec4 color;\n"
"\n"
"flat in int sMask;\n"
"\n"
"layout(location = 0) out vec4 FragColor;\n"
"\n"
"void main()\n"
"{\n"
"// simple fragment shader that calculates ambient + diffuse lighting with osg::LightSource\n"
" vec3 lightDir = normalize(vec3(gl_LightSource[0].position));\n"
" vec3 normal = normalize(ecNormal);\n"
" float NdotL = max(dot(normal, lightDir), 0.0);\n"
" FragColor = NdotL * color * gl_LightSource[0].diffuse + color * gl_LightSource[0].ambient;\n"
" gl_SampleMask[0] = sMask;\n"
"}\n";
char SHADER_DYNAMIC_CULL_VERTEX[] =
"#version 420 compatibility\n"
"\n"
"uniform mat4 osg_ViewMatrixInverse;\n"
"\n"
"uniform samplerBuffer dynamicInstancesData;\n"
"uniform int dynamicInstancesDataSize; // = sizeof(DynamicInstance) / sizeof(osg::Vec4f)\n"
"\n"
"layout(R32I) coherent uniform iimageBuffer indirectCommand0;\n"
"uniform int indirectCommandSize; // = sizeof(DrawArraysIndirectCommand) / sizeof(unsigned int) = 4\n"
"\n"
"layout(R32I) coherent uniform iimageBuffer getIndirectCommand( int index )\n"
"{\n"
" if(index==0) return indirectCommand0;\n"
"}\n"
"\n"
"layout(RGBA32I) coherent uniform iimageBuffer indirectTarget0;\n"
"\n"
"layout(RGBA32I) coherent uniform iimageBuffer getIndirectTarget( int index )\n"
"{\n"
" if(index==0) return indirectTarget0;\n"
"}\n"
"\n"
"struct InstanceLOD\n"
"{\n"
" vec4 bbMin;\n"
" vec4 bbMax;\n"
" ivec4 indirectTargetParams; // x=targetID, y=indexInTarget, z=offsetInTarget, w=lodMaxQuantity\n"
" vec4 distances; // x=minDistance, y=minFadeDistance, z=maxFadeDistance, w=maxDistance\n"
"};\n"
"\n"
"const int OSGGPUCULL_MAXIMUM_LOD_NUMBER = 8;\n"
"\n"
"struct InstanceType\n"
"{\n"
" vec4 bbMin;\n"
" vec4 bbMax;\n"
" ivec4 params;\n"
" InstanceLOD lods[OSGGPUCULL_MAXIMUM_LOD_NUMBER];\n"
"};\n"
"\n"
"layout(std140) uniform instanceTypesData\n"
"{\n"
" InstanceType instanceTypes[32];\n"
"};\n"
"\n"
"layout(location = 0) in vec3 VertexPosition; // xyz - used only for debugging purposes\n"
"layout(location = 10) in vec4 InstanceID; // typeid, id - points to the data in dynamicInstancesData buffer\n"
"\n"
"bool boundingBoxInViewFrustum( in mat4 matrix, in vec3 bbMin, in vec3 bbMax )\n"
"{\n"
" vec4 BoundingBox[8];\n"
" BoundingBox[0] = matrix * vec4( bbMax.x, bbMax.y, bbMax.z, 1.0);\n"
" BoundingBox[1] = matrix * vec4( bbMin.x, bbMax.y, bbMax.z, 1.0);\n"
" BoundingBox[2] = matrix * vec4( bbMax.x, bbMin.y, bbMax.z, 1.0);\n"
" BoundingBox[3] = matrix * vec4( bbMin.x, bbMin.y, bbMax.z, 1.0);\n"
" BoundingBox[4] = matrix * vec4( bbMax.x, bbMax.y, bbMin.z, 1.0);\n"
" BoundingBox[5] = matrix * vec4( bbMin.x, bbMax.y, bbMin.z, 1.0);\n"
" BoundingBox[6] = matrix * vec4( bbMax.x, bbMin.y, bbMin.z, 1.0);\n"
" BoundingBox[7] = matrix * vec4( bbMin.x, bbMin.y, bbMin.z, 1.0);\n"
"\n"
" int outOfBound[6] = int[6]( 0, 0, 0, 0, 0, 0 );\n"
" for (int i=0; i<8; i++)\n"
" {\n"
" outOfBound[0] += int( BoundingBox[i].x > BoundingBox[i].w );\n"
" outOfBound[1] += int( BoundingBox[i].x < -BoundingBox[i].w );\n"
" outOfBound[2] += int( BoundingBox[i].y > BoundingBox[i].w );\n"
" outOfBound[3] += int( BoundingBox[i].y < -BoundingBox[i].w );\n"
" outOfBound[4] += int( BoundingBox[i].z > BoundingBox[i].w );\n"
" outOfBound[5] += int( BoundingBox[i].z < -BoundingBox[i].w );\n"
" }\n"
" return (outOfBound[0] < 8 ) && ( outOfBound[1] < 8 ) && ( outOfBound[2] < 8 ) && ( outOfBound[3] < 8 ) && ( outOfBound[4] < 8 ) && ( outOfBound[5] < 8 );\n"
"}\n"
"\n"
"void main(void) \n"
"{\n"
" // get object matrices\n"
" int instanceIndex = int(InstanceID.y);\n"
" int instanceAddress = instanceIndex * dynamicInstancesDataSize;\n"
" mat4 instanceMatrix = mat4 (\n"
" texelFetch(dynamicInstancesData, instanceAddress),\n"
" texelFetch(dynamicInstancesData, instanceAddress + 1),\n"
" texelFetch(dynamicInstancesData, instanceAddress + 2),\n"
" texelFetch(dynamicInstancesData, instanceAddress + 3) );\n"
" mat4 mvpoMatrix = gl_ModelViewProjectionMatrix * instanceMatrix;\n"
"\n"
" // gl_Position is created only for debugging purposes\n"
" gl_Position = mvpoMatrix * vec4(0.0,0.0,0.0,1.0);\n"
"\n"
" int instanceTypeIndex = int(InstanceID.x);\n"
"\n"
" if( boundingBoxInViewFrustum( mvpoMatrix, instanceTypes[instanceTypeIndex].bbMin.xyz, instanceTypes[instanceTypeIndex].bbMax.xyz ) )\n"
" {\n"
" float distanceToObject = distance(osg_ViewMatrixInverse[3].xyz / osg_ViewMatrixInverse[3].w, instanceMatrix[3].xyz / instanceMatrix[3].w );\n"
" for(int i=0;i<instanceTypes[instanceTypeIndex].params.x;++i)\n"
" {\n"
" if( boundingBoxInViewFrustum( mvpoMatrix, instanceTypes[instanceTypeIndex].lods[i].bbMin.xyz, instanceTypes[instanceTypeIndex].lods[i].bbMax.xyz ) &&\n"
" ( distanceToObject >= instanceTypes[instanceTypeIndex].lods[i].distances.x ) && ( distanceToObject < instanceTypes[instanceTypeIndex].lods[i].distances.w ) )\n"
" {\n"
" float fadeAlpha = 4.0 * ( clamp( (distanceToObject-instanceTypes[instanceTypeIndex].lods[i].distances.x)/( instanceTypes[instanceTypeIndex].lods[i].distances.y - instanceTypes[instanceTypeIndex].lods[i].distances.x), 0.0, 1.0 ) \n"
" + clamp( (distanceToObject- instanceTypes[instanceTypeIndex].lods[i].distances.z)/( instanceTypes[instanceTypeIndex].lods[i].distances.w - instanceTypes[instanceTypeIndex].lods[i].distances.z), 0.0, 1.0 ) );\n"
" int sampleMask = ( 0xF0 >> int(fadeAlpha) ) & 0xF;\n"
" int indirectCommandIndex = instanceTypes[instanceTypeIndex].lods[i].indirectTargetParams.x;\n"
" int indirectCommandAddress = instanceTypes[instanceTypeIndex].lods[i].indirectTargetParams.y;\n"
" int objectIndex = imageAtomicAdd( getIndirectCommand( indirectCommandIndex ), indirectCommandAddress*indirectCommandSize+1, 1 );\n"
" int indirectTargetAddress = instanceTypes[instanceTypeIndex].lods[i].indirectTargetParams.z + objectIndex;\n"
" ivec4 indirectCommandData = ivec4( instanceTypeIndex, instanceIndex, sampleMask, 0 );\n"
" imageStore( getIndirectTarget(indirectCommandIndex), indirectTargetAddress, indirectCommandData );\n"
" }\n"
" }\n"
" }\n"
"}\n";
char SHADER_DYNAMIC_CULL_FRAGMENT[] =
"#version 420 compatibility\n"
"\n"
"layout(location = 0, index = 0) out vec4 FragmentColor;\n"
"\n"
"void main()\n"
"{\n"
" FragmentColor = vec4(1.0,1.0,0.1,1.0);\n"
"}\n";
char SHADER_DYNAMIC_DRAW_0_VERTEX[] =
"#version 420 compatibility\n"
"\n"
"uniform samplerBuffer dynamicInstancesData;\n"
"uniform int dynamicInstancesDataSize; // = sizeof(DynamicInstance) / sizeof(osg::Vec4f)\n"
"\n"
"layout(location = 0) in vec4 VertexPosition;\n"
"layout(location = 2) in vec3 VertexNormal;\n"
"layout(location = 3) in vec4 VertexColor;\n"
"layout(location = 8) in vec2 VertexTexCoord0;\n"
"layout(location = 9) in vec3 VertexTexCoord1;\n"
"\n"
"struct InstanceLOD\n"
"{\n"
" vec4 bbMin;\n"
" vec4 bbMax;\n"
" ivec4 indirectTargetParams; // x=targetID, y=indexInTarget, z=offsetInTarget, w=lodMaxQuantity\n"
" vec4 distances; // x=minDistance, y=minFadeDistance, z=maxFadeDistance, w=maxDistance\n"
"};\n"
"\n"
"const int OSGGPUCULL_MAXIMUM_LOD_NUMBER = 8;\n"
"\n"
"struct InstanceType\n"
"{\n"
" vec4 bbMin;\n"
" vec4 bbMax;\n"
" ivec4 params;\n"
" InstanceLOD lods[OSGGPUCULL_MAXIMUM_LOD_NUMBER];\n"
"};\n"
"\n"
"layout(std140) uniform instanceTypesData\n"
"{\n"
" InstanceType instanceTypes[32];\n"
"};\n"
"\n"
"layout(RGBA32I) coherent uniform iimageBuffer indirectTarget;\n"
"\n"
"out vec3 ecPosition3;\n"
"out vec3 ecNormal;\n"
"out vec2 TexCoord;\n"
"out vec4 color;\n"
"flat out int sMask;\n"
" \n"
"void main()\n"
"{\n"
" // every vertex has its type coded on VertexTexCoord1.x,\n"
" // its lodNumber coded in VertexTexCoord1.y\n"
" // and bone index coded in VertexTexCoord1.z\n"
" int instanceTypeIndex = int(VertexTexCoord1.x);\n"
" int instanceLodNumber = int(VertexTexCoord1.y);\n"
" int boneIndex = int(VertexTexCoord1.z);\n"
" int indirectTargetAddress = instanceTypes[instanceTypeIndex].lods[instanceLodNumber].indirectTargetParams.z + gl_InstanceID;\n"
"\n"
" // if we know instance type, lod number and gl_InstanceID, we can reconstruct all instance data\n"
" ivec4 indirectCommandData = imageLoad(indirectTarget, indirectTargetAddress);\n"
" // now its time to read instance matrix and other additional params\n"
" int instanceAddress = indirectCommandData.y * dynamicInstancesDataSize;\n"
" mat4 instanceMatrix = mat4 (\n"
" texelFetch(dynamicInstancesData, instanceAddress),\n"
" texelFetch(dynamicInstancesData, instanceAddress + 1),\n"
" texelFetch(dynamicInstancesData, instanceAddress + 2),\n"
" texelFetch(dynamicInstancesData, instanceAddress + 3) );\n"
"\n"
" vec4 instanceExtraParams = texelFetch(dynamicInstancesData, instanceAddress + 4);\n"
" // we also need boneMatrix to animate an object\n"
" int boneAddress = instanceAddress + 6 + 4*boneIndex;\n"
" mat4 boneMatrix = mat4 (\n"
" texelFetch(dynamicInstancesData, boneAddress),\n"
" texelFetch(dynamicInstancesData, boneAddress + 1),\n"
" texelFetch(dynamicInstancesData, boneAddress + 2),\n"
" texelFetch(dynamicInstancesData, boneAddress + 3) );\n"
"\n"
" sMask = indirectCommandData.z;\n"
"\n"
" mat4 mvMatrix = gl_ModelViewMatrix * instanceMatrix * boneMatrix;\n"
" \n"
" // Do fixed functionality vertex transform\n"
" vec4 ecPos = mvMatrix * vec4(VertexPosition.xyz,1.0);\n"
" gl_Position = gl_ProjectionMatrix * ecPos;\n"
"\n"
" ecPosition3 = ecPos.xyz / ecPos.w;\n"
" ecNormal = normalize( mat3(mvMatrix) * VertexNormal.xyz );\n"
" \n"
" TexCoord = VertexTexCoord0.xy;\n"
" color.xyz = VertexColor.xyz * instanceExtraParams.x;\n"
" color.a = VertexColor.a;\n"
"}\n";
char SHADER_DYNAMIC_DRAW_0_FRAGMENT[] =
"#version 420 compatibility\n"
"\n"
"in vec3 ecPosition3;\n"
"in vec3 ecNormal;\n"
"in vec2 TexCoord;\n"
"in vec4 color;\n"
"\n"
"flat in int sMask;\n"
"\n"
"layout(location = 0) out vec4 FragColor;\n"
"\n"
"void main()\n"
"{\n"
"// simple fragment shader that calculates ambient + diffuse lighting with osg::LightSource\n"
" vec3 lightDir = normalize(vec3(gl_LightSource[0].position));\n"
" vec3 normal = normalize(ecNormal);\n"
" float NdotL = max(dot(normal, lightDir), 0.0);\n"
" FragColor = NdotL * color * gl_LightSource[0].diffuse + color * gl_LightSource[0].ambient;\n"
" gl_SampleMask[0] = sMask;\n"
"}\n";
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,109 @@
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2014 Robert Osfield
* Copyright (C) 2014 Pawel Ksiezopolski
*
* This library is open source and may be redistributed and/or modified under
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
* (at your option) any later version. The full license is in LICENSE file
* included with this distribution, and on the openscenegraph.org website.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* OpenSceneGraph Public License for more details.
*
*/
#ifndef SHAPE_TO_GEOMETRY
#define SHAPE_TO_GEOMETRY 1
#include <osg/Geometry>
#include <osg/Geode>
#include <osg/ShapeDrawable>
#include <osg/NodeVisitor>
// arbitrary minima for rows & segments ( from shapedrawable.cpp )
const unsigned int MIN_NUM_ROWS = 3;
const unsigned int MIN_NUM_SEGMENTS = 5;
// osg::GLBeginEndAdapter descendant that stores data for osg::Geometry creation
class FakeGLBeginEndAdapter : public osg::GLBeginEndAdapter
{
public:
FakeGLBeginEndAdapter();
void PushMatrix();
void MultMatrixd(const GLdouble* m);
void Translated(GLdouble x, GLdouble y, GLdouble z);
void Scaled(GLdouble x, GLdouble y, GLdouble z);
void Rotated(GLdouble angle, GLdouble x, GLdouble y, GLdouble z);
void End();
osg::ref_ptr<osg::Geometry> geometry;
};
class ShapeToGeometryVisitor : public osg::ConstShapeVisitor
{
public:
ShapeToGeometryVisitor(const osg::TessellationHints* hints)
: osg::ConstShapeVisitor(), _hints(hints)
{
}
virtual void apply(const osg::Sphere&);
virtual void apply(const osg::Box&);
virtual void apply(const osg::Cone&);
virtual void apply(const osg::Cylinder&);
virtual void apply(const osg::Capsule&);
virtual void apply(const osg::InfinitePlane&);
virtual void apply(const osg::TriangleMesh&);
virtual void apply(const osg::ConvexHull&);
virtual void apply(const osg::HeightField&);
virtual void apply(const osg::CompositeShape&);
osg::Geometry* getGeometry() { return gl.geometry.get(); }
const osg::TessellationHints* _hints;
FakeGLBeginEndAdapter gl;
protected:
ShapeToGeometryVisitor& operator = (const ShapeToGeometryVisitor&) { return *this; }
enum SphereHalf { SphereTopHalf, SphereBottomHalf };
// helpers for apply( Cylinder | Sphere | Capsule )
void drawCylinderBody(unsigned int numSegments, float radius, float height);
void drawHalfSphere(unsigned int numSegments, unsigned int numRows, float radius, SphereHalf which, float zOffset = 0.0f);
};
osg::Geometry* convertShapeToGeometry(const osg::Shape& shape, const osg::TessellationHints* hints);
osg::Geometry* convertShapeToGeometry(const osg::Shape& shape, const osg::TessellationHints* hints, const osg::Vec4& color);
osg::Geode* convertShapeToGeode(const osg::Shape& shape, const osg::TessellationHints* hints);
osg::Geode* convertShapeToGeode(const osg::Shape& shape, const osg::TessellationHints* hints, const osg::Vec4& color);
// example : how to use convertShapeToGeometry()
// osg::ref_ptr<osg::Capsule> shape = new osg::Capsule( osg::Vec3( 0.0, 0.0, 0.0 ), radius, height );
// osg::ref_ptr<osg::TessellationHints> tessHints = new osg::TessellationHints;
// tessHints->setDetailRatio(0.5f);
// tessHints->setCreateTextureCoords(true);
// osg::ref_ptr<osg::Geometry> capsuleGeometry = convertShapeToGeometry(*shape.get(), tessHints.get());
// osg::ref_ptr<osg::Geometry> redCapsuleGeometry = convertShapeToGeometry(*shape.get(), tessHints.get(), osg::Vec4(1.0,0.0,0.0,1.0) );
class ShapesToGeometriesVisitor : public osg::NodeVisitor
{
public:
ShapesToGeometriesVisitor( osg::TessellationHints* hints )
: osg::NodeVisitor( osg::NodeVisitor::TRAVERSE_ALL_CHILDREN ), _hints(hints)
{
}
void apply( osg::Geode& geode);
protected:
osg::TessellationHints* _hints;
};
#endif

File diff suppressed because it is too large Load Diff

104
include/osg/BufferTemplate Normal file
View File

@ -0,0 +1,104 @@
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2014 Robert Osfield
* Copyright (C) 2012 David Callu
* std::vector specialization : Pawel Ksiezopolski
*
* This library is open source and may be redistributed and/or modified under
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
* (at your option) any later version. The full license is in LICENSE file
* included with this distribution, and on the openscenegraph.org website.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* OpenSceneGraph Public License for more details.
*/
#ifndef OSG_BUFFERTEMPLATE
#define OSG_BUFFERTEMPLATE 1
#include <osg/BufferObject>
#include <vector>
namespace osg
{
/** Template buffer class to be used with a struct as template parameter.
* This class is usefull to send C++ structures on the GPU (e.g. for uniform blocks) but be carefull to the alignments rules on the GPU side !
*/
template <typename T>
class BufferTemplate : public BufferData
{
public:
BufferTemplate():
BufferData(),
_data(T())
{}
/** Copy constructor using CopyOp to manage deep vs shallow copy.*/
BufferTemplate(const BufferTemplate<T>& bt,const CopyOp& copyop=CopyOp::SHALLOW_COPY):
osg::BufferData(bt,copyop),
_data(bt._data)
{}
virtual bool isSameKindAs(const Object* obj) const { return dynamic_cast<const BufferTemplate<T>*>(obj)!=NULL; }
virtual const char* libraryName() const { return "osg"; }
virtual const char* className() const { return "BufferTemplate<T>"; }
virtual Object* cloneType() const { return new BufferTemplate<T>(); }
virtual Object* clone(const CopyOp& copyop) const { return new BufferTemplate<T>(*this,copyop); }
virtual const GLvoid* getDataPointer() const { return &_data; }
virtual unsigned int getTotalDataSize() const { return sizeof(T); }
const T& getData() const { return _data; }
T& getData() { return _data; }
void setData(const T& data) { _data = data; dirty(); }
protected:
virtual ~BufferTemplate() {};
private:
T _data;
};
/** BufferTemplate specialization for std::vector
*/
template <typename T>
class BufferTemplate< std::vector<T> > : public BufferData
{
public:
BufferTemplate():
BufferData(),
_data()
{}
/** Copy constructor using CopyOp to manage deep vs shallow copy.*/
BufferTemplate(const BufferTemplate< std::vector<T> >& bt,const CopyOp& copyop=CopyOp::SHALLOW_COPY):
osg::BufferData(bt,copyop),
_data(bt._data)
{}
virtual bool isSameKindAs(const Object* obj) const { return dynamic_cast<const BufferTemplate< std::vector<T> >*>(obj)!=NULL; }
virtual const char* libraryName() const { return "osg"; }
virtual const char* className() const { return "BufferTemplate<std::vector<T> >"; }
virtual Object* cloneType() const { return new BufferTemplate< std::vector<T> >(); }
virtual Object* clone(const CopyOp& copyop) const { return new BufferTemplate< std::vector<T> >(*this,copyop); }
virtual const GLvoid* getDataPointer() const { return &_data[0]; }
virtual unsigned int getTotalDataSize() const { return _data.size() * sizeof(T); }
const std::vector<T>& getData() const { return _data; }
std::vector<T>& getData() { return _data; }
void setData(const std::vector<T>& data) { _data = data; dirty(); }
protected:
virtual ~BufferTemplate() {};
private:
std::vector<T> _data;
};
}
#endif

View File

@ -37,7 +37,7 @@ SET(TARGET_H
${HEADER_PATH}/buffered_value
${HEADER_PATH}/BufferIndexBinding
${HEADER_PATH}/BufferObject
${HEADER_PATH}/Callback
${HEADER_PATH}/BufferTemplate
${HEADER_PATH}/Camera
${HEADER_PATH}/CameraView
${HEADER_PATH}/ClampColor
@ -72,6 +72,7 @@ SET(TARGET_H
${HEADER_PATH}/FrontFace
${HEADER_PATH}/Geode
${HEADER_PATH}/Geometry
${HEADER_PATH}/GL
${HEADER_PATH}/GL2Extensions
${HEADER_PATH}/GLExtensions
${HEADER_PATH}/GLBeginEndAdapter