#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define USE_LOCAL_SHADERS static double distance(const osg::Vec3& coord, const osg::Matrix& matrix) { // copied from CullVisitor.cpp return -(coord[0]*matrix(0,2)+coord[1]*matrix(1,2)+coord[2]*matrix(2,2)+matrix(3,2)); } osgParticle::ParticleSystem::ParticleSystem() : osg::Drawable(), _def_bbox(osg::Vec3(-10, -10, -10), osg::Vec3(10, 10, 10)), _alignment(BILLBOARD), _align_X_axis(1, 0, 0), _align_Y_axis(0, 1, 0), _particleScaleReferenceFrame(WORLD_COORDINATES), _useVertexArray(false), _useShaders(false), _dirty_uniforms(false), _doublepass(false), _frozen(false), _bmin(0, 0, 0), _bmax(0, 0, 0), _reset_bounds_flag(false), _bounds_computed(false), _def_ptemp(Particle()), _last_frame(0), _dirty_dt(true), _freeze_on_cull(false), _t0(0.0), _dt(0.0), _detail(1), _sortMode(NO_SORT), _visibilityDistance(-1.0), _estimatedMaxNumOfParticles(0) { // we don't support display lists because particle systems // are dynamic, and they always changes between frames setSupportsDisplayList(false); } osgParticle::ParticleSystem::ParticleSystem(const ParticleSystem& copy, const osg::CopyOp& copyop) : osg::Drawable(copy, copyop), _def_bbox(copy._def_bbox), _alignment(copy._alignment), _align_X_axis(copy._align_X_axis), _align_Y_axis(copy._align_Y_axis), _particleScaleReferenceFrame(copy._particleScaleReferenceFrame), _useVertexArray(copy._useVertexArray), _useShaders(copy._useShaders), _dirty_uniforms(copy._dirty_uniforms), _doublepass(copy._doublepass), _frozen(copy._frozen), _bmin(copy._bmin), _bmax(copy._bmax), _reset_bounds_flag(copy._reset_bounds_flag), _bounds_computed(copy._bounds_computed), _def_ptemp(copy._def_ptemp), _last_frame(copy._last_frame), _dirty_dt(copy._dirty_dt), _freeze_on_cull(copy._freeze_on_cull), _t0(copy._t0), _dt(copy._dt), _detail(copy._detail), _sortMode(copy._sortMode), _visibilityDistance(copy._visibilityDistance), _estimatedMaxNumOfParticles(0) { } osgParticle::ParticleSystem::~ParticleSystem() { // OSG_NOTICE<<"ParticleSystem::~ParticleSystem() "<set((float)_visibilityDistance); _dirty_uniforms = false; } } for(unsigned int i=0; i<_particles.size(); ++i) { Particle& particle = _particles[i]; if (particle.isAlive()) { if (particle.update(dt, _useShaders)) { update_bounds(particle.getPosition(), particle.getCurrentSize()); } else { reuseParticle(i); } } } if (_sortMode != NO_SORT) { // sort particles osgUtil::CullVisitor* cv = nv.asCullVisitor(); if (cv) { osg::Matrixd modelview = *(cv->getModelViewMatrix()); double scale = (_sortMode==SORT_FRONT_TO_BACK ? -1.0 : 1.0); double deadDistance = DBL_MAX; for (unsigned int i=0; i<_particles.size(); ++i) { Particle& particle = _particles[i]; if (particle.isAlive()) particle.setDepth(distance(particle.getPosition(), modelview) * scale); else particle.setDepth(deadDistance); } std::sort(_particles.begin(), _particles.end()); // Repopulate the death stack as it will have been invalidated by the sort. unsigned int numDead = _deadparts.size(); if (numDead>0) { // clear the death stack _deadparts = Death_stack(); // copy the tail of the _particles vector as this will contain all the dead Particle thanks to the depth sort against DBL_MAX Particle* first_dead_ptr = &_particles[_particles.size()-numDead]; Particle* last_dead_ptr = &_particles[_particles.size()-1]; for(Particle* dead_ptr = first_dead_ptr; dead_ptr<=last_dead_ptr; ++dead_ptr) { _deadparts.push(dead_ptr); } } } } // force recomputing of bounding box on next frame dirtyBound(); } void osgParticle::ParticleSystem::drawImplementation(osg::RenderInfo& renderInfo) const { if (_particles.size() <= 0) return; ScopedReadLock lock(_readWriteMutex); osg::State& state = *renderInfo.getState(); // update the frame count, so other objects can detect when // this particle system is culled _last_frame = state.getFrameStamp()->getFrameNumber(); // update the dirty flag of delta time, so next time a new request for delta time // will automatically cause recomputing _dirty_dt = true; // get the current modelview matrix osg::Matrix modelview = state.getModelViewMatrix(); ArrayData& ad = _bufferedArrayData[state.getContextID()]; if (_useVertexArray) { // note from Robert Osfield, September 2016, this block implementated for backwards compatibility but is pretty way vertex array/shaders were hacked into osgParticle // set up arrays and primitives ready to fill in if (!ad.vertices.valid()) { ad.init3(); ad.reserve(_particles.capacity()); } ad.clear(); ad.dirty(); osg::Vec3Array& vertices = *ad.vertices; osg::Vec3Array& normals = *ad.normals; osg::Vec4Array& colors = *ad.colors; osg::Vec3Array& texcoords = *ad.texcoords3; ArrayData::Primitives& primitives = ad.primitives; for(unsigned int i=0; i<_particles.size(); i+=_detail) { const Particle* particle = &_particles[i]; const osg::Vec4& color = particle->getCurrentColor(); const osg::Vec3& pos = particle->getPosition(); const osg::Vec3& vel = particle->getVelocity(); colors.push_back( color ); texcoords.push_back( osg::Vec3(particle->_alive, particle->_current_size, particle->_current_alpha) ); normals.push_back(vel); vertices.push_back(pos); } primitives.push_back(ArrayData::ModeCount(GL_POINTS, vertices.size())); } else { // set up arrays and primitives ready to fill in if (!ad.vertices.valid()) { ad.init(); ad.reserve(_particles.capacity()*4); } ad.clear(); ad.dirty(); osg::Vec3Array& vertices = *ad.vertices; osg::Vec4Array& colors = *ad.colors; osg::Vec2Array& texcoords = *ad.texcoords2; ArrayData::Primitives& primitives = ad.primitives; float scale = sqrtf(static_cast(_detail)); osg::Vec3 xAxis = _align_X_axis; osg::Vec3 yAxis = _align_Y_axis; osg::Vec3 scaled_aligned_xAxis = _align_X_axis; osg::Vec3 scaled_aligned_yAxis = _align_Y_axis; float xScale = 1.0f; float yScale = 1.0f; if (_alignment==BILLBOARD) { xAxis = osg::Matrix::transform3x3(modelview,_align_X_axis); yAxis = osg::Matrix::transform3x3(modelview,_align_Y_axis); float lengthX2 = xAxis.length2(); float lengthY2 = yAxis.length2(); if (_particleScaleReferenceFrame==LOCAL_COORDINATES) { xScale = 1.0f/sqrtf(lengthX2); yScale = 1.0f/sqrtf(lengthY2); } else { xScale = 1.0f/lengthX2; yScale = 1.0f/lengthY2; } scaled_aligned_xAxis *= xScale; scaled_aligned_yAxis *= yScale; xAxis *= xScale; yAxis *= yScale; } for(unsigned int i=0; i<_particles.size(); i+=_detail) { const Particle* currentParticle = &_particles[i]; bool insideDistance = true; if (_sortMode != NO_SORT && _visibilityDistance>0.0) { insideDistance = (currentParticle->getDepth()>=0.0 && currentParticle->getDepth()<=_visibilityDistance); } if (currentParticle->isAlive() && insideDistance) { const osg::Vec3& angle = currentParticle->getAngle(); bool requiresRotation = (angle.x()!=0.0f || angle.y()!=0.0f || angle.z()!=0.0f); if (requiresRotation) { osg::Matrix R; R.makeRotate( angle.x(), osg::Vec3(1, 0, 0), angle.y(), osg::Vec3(0, 1, 0), angle.z(), osg::Vec3(0, 0, 1)); if (_alignment==BILLBOARD) { xAxis = osg::Matrix::transform3x3(R,scaled_aligned_xAxis); xAxis = osg::Matrix::transform3x3(modelview,xAxis); yAxis = osg::Matrix::transform3x3(R,scaled_aligned_yAxis); yAxis = osg::Matrix::transform3x3(modelview,yAxis); } else { xAxis = osg::Matrix::transform3x3(R, scaled_aligned_xAxis); yAxis = osg::Matrix::transform3x3(R, scaled_aligned_yAxis); } } osg::Vec4 color = currentParticle->getCurrentColor(); color.a() *= currentParticle->getCurrentAlpha(); float currentSize = currentParticle->getCurrentSize(); const osg::Vec3& xpos = currentParticle->getPosition(); const float s_coord = currentParticle->getSTexCoord(); const float t_coord = currentParticle->getTTexCoord(); const float s_tile = currentParticle->getSTexTile(); const float t_tile = currentParticle->getTTexTile(); osg::Vec3 p1(xAxis * currentSize * scale); osg::Vec3 p2(yAxis * currentSize * scale); switch (currentParticle->getShape()) { case osgParticle::Particle::POINT: { vertices.push_back(currentParticle->getPosition()); colors.push_back(color); texcoords.push_back(osg::Vec2(0.5f,0.5f)); if (!primitives.empty() && primitives.back().first==GL_POINTS) { primitives.back().second++; } else { primitives.push_back(ArrayData::ModeCount(GL_POINTS,1)); } break; } case osgParticle::Particle::USER: case osgParticle::Particle::QUAD_TRIANGLESTRIP: case osgParticle::Particle::HEXAGON: case osgParticle::Particle::QUAD: { vertices.push_back(xpos-p1-p2); colors.push_back(color); texcoords.push_back(osg::Vec2(s_coord, t_coord)); vertices.push_back(xpos+p1-p2); colors.push_back(color); texcoords.push_back(osg::Vec2(s_coord+s_tile, t_coord)); vertices.push_back(xpos+p1+p2); colors.push_back(color); texcoords.push_back(osg::Vec2(s_coord+s_tile, t_coord+t_tile)); vertices.push_back(xpos-p1+p2); colors.push_back(color); texcoords.push_back(osg::Vec2(s_coord, t_coord+t_tile)); if (!primitives.empty() && primitives.back().first==GL_QUADS) { primitives.back().second+=4; } else { primitives.push_back(ArrayData::ModeCount(GL_QUADS,4)); } break; } case osgParticle::Particle::LINE: { // Get the normalized direction of the particle, to be used in the // calculation of one of the linesegment endpoints. const osg::Vec3& velocity = currentParticle->getVelocity(); float vl = velocity.length(); if (vl != 0) { osg::Vec3 v = velocity * currentSize * scale / vl; vertices.push_back(currentParticle->getPosition()); colors.push_back(color); texcoords.push_back(osg::Vec2(0.0f,0.0f)); vertices.push_back(currentParticle->getPosition()+v); colors.push_back(color); texcoords.push_back(osg::Vec2(1.0f,1.0f)); if (!primitives.empty() && primitives.back().first==GL_LINES) { primitives.back().second+=2; } else { primitives.push_back(ArrayData::ModeCount(GL_LINES,2)); } } break; } default: OSG_WARN << "Invalid shape for particles\n"; } } } } // set up depth mask for first rendering pass #if !defined(OSG_GLES1_AVAILABLE) && !defined(OSG_GLES2_AVAILABLE) && !defined(OSG_GLES3_AVAILABLE) && !defined(OSG_GL3_AVAILABLE) glPushAttrib(GL_DEPTH_BUFFER_BIT); #endif glDepthMask(GL_FALSE); ad.dispatchArrays(state); ad.dispatchPrimitives(); #if !defined(OSG_GLES1_AVAILABLE) && !defined(OSG_GLES2_AVAILABLE) && !defined(OSG_GLES3_AVAILABLE) && !defined(OSG_GL3_AVAILABLE) // restore depth mask settings glPopAttrib(); #endif // render, second pass if (_doublepass) { // set up color mask for second rendering pass #if !defined(OSG_GLES1_AVAILABLE) && !defined(OSG_GLES2_AVAILABLE) && !defined(OSG_GLES3_AVAILABLE) && !defined(OSG_GL3_AVAILABLE) glPushAttrib(GL_COLOR_BUFFER_BIT); #endif glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); ad.dispatchPrimitives(); #if !defined(OSG_GLES1_AVAILABLE) && !defined(OSG_GLES2_AVAILABLE) && !defined(OSG_GLES3_AVAILABLE) && !defined(OSG_GL3_AVAILABLE) // restore color mask settings glPopAttrib(); #endif } } void osgParticle::ParticleSystem::setDefaultAttributes(const std::string& texturefile, bool emissive_particles, bool lighting, int texture_unit) { osg::StateSet *stateset = new osg::StateSet; stateset->setMode(GL_LIGHTING, lighting? osg::StateAttribute::ON: osg::StateAttribute::OFF); stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); osg::Material *material = new osg::Material; material->setSpecular(osg::Material::FRONT, osg::Vec4(0, 0, 0, 1)); material->setEmission(osg::Material::FRONT, osg::Vec4(0, 0, 0, 1)); material->setColorMode(lighting? osg::Material::AMBIENT_AND_DIFFUSE : osg::Material::OFF); stateset->setAttributeAndModes(material, osg::StateAttribute::ON); if (!texturefile.empty()) { osg::Texture2D *texture = new osg::Texture2D; texture->setImage(osgDB::readRefImageFile(texturefile)); texture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::LINEAR); texture->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::LINEAR); texture->setWrap(osg::Texture2D::WRAP_S, osg::Texture2D::MIRROR); texture->setWrap(osg::Texture2D::WRAP_T, osg::Texture2D::MIRROR); stateset->setTextureAttributeAndModes(texture_unit, texture, osg::StateAttribute::ON); osg::TexEnv *texenv = new osg::TexEnv; texenv->setMode(osg::TexEnv::MODULATE); stateset->setTextureAttribute(texture_unit, texenv); } osg::BlendFunc *blend = new osg::BlendFunc; if (emissive_particles) { blend->setFunction(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE); } else { blend->setFunction(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE_MINUS_SRC_ALPHA); } stateset->setAttributeAndModes(blend, osg::StateAttribute::ON); setStateSet(stateset); setUseVertexArray(false); setUseShaders(false); } void osgParticle::ParticleSystem::setDefaultAttributesUsingShaders(const std::string& texturefile, bool emissive_particles, int texture_unit) { osg::StateSet *stateset = new osg::StateSet; stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); osg::PointSprite *sprite = new osg::PointSprite; stateset->setTextureAttributeAndModes(texture_unit, sprite, osg::StateAttribute::ON); #if !defined(OSG_GLES1_AVAILABLE) && !defined(OSG_GLES2_AVAILABLE) stateset->setMode(GL_VERTEX_PROGRAM_POINT_SIZE, osg::StateAttribute::ON); #else OSG_NOTICE<<"Warning: ParticleSystem::setDefaultAttributesUsingShaders(..) not fully implemented."<setImage(osgDB::readRefImageFile(texturefile)); texture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::LINEAR); texture->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::LINEAR); texture->setWrap(osg::Texture2D::WRAP_S, osg::Texture2D::MIRROR); texture->setWrap(osg::Texture2D::WRAP_T, osg::Texture2D::MIRROR); stateset->setTextureAttributeAndModes(texture_unit, texture, osg::StateAttribute::ON); } osg::BlendFunc *blend = new osg::BlendFunc; if (emissive_particles) { blend->setFunction(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE); } else { blend->setFunction(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE_MINUS_SRC_ALPHA); } stateset->setAttributeAndModes(blend, osg::StateAttribute::ON); osg::Program *program = new osg::Program; #ifdef USE_LOCAL_SHADERS char vertexShaderSource[] = "uniform float visibilityDistance;\n" "varying vec3 basic_prop;\n" "\n" "void main(void)\n" "{\n" " basic_prop = gl_MultiTexCoord0.xyz;\n" " \n" " vec4 ecPos = gl_ModelViewMatrix * gl_Vertex;\n" " float ecDepth = -ecPos.z;\n" " \n" " if (visibilityDistance > 0.0)\n" " {\n" " if (ecDepth <= 0.0 || ecDepth >= visibilityDistance)\n" " basic_prop.x = -1.0;\n" " }\n" " \n" " gl_Position = ftransform();\n" " gl_ClipVertex = ecPos;\n" " \n" " vec4 color = gl_Color;\n" " color.a *= basic_prop.z;\n" " gl_FrontColor = color;\n" " gl_BackColor = gl_FrontColor;\n" "}\n"; char fragmentShaderSource[] = "uniform sampler2D baseTexture;\n" "varying vec3 basic_prop;\n" "\n" "void main(void)\n" "{\n" " if (basic_prop.x < 0.0) discard;\n" " gl_FragColor = gl_Color * texture2D(baseTexture, gl_TexCoord[0].xy);\n" "}\n"; program->addShader(new osg::Shader(osg::Shader::VERTEX, vertexShaderSource)); program->addShader(new osg::Shader(osg::Shader::FRAGMENT, fragmentShaderSource)); #else program->addShader(osg::Shader::readShaderFile(osg::Shader::VERTEX, osgDB::findDataFile("shaders/particle.vert"))); program->addShader(osg::Shader::readShaderFile(osg::Shader::FRAGMENT, osgDB::findDataFile("shaders/particle.frag"))); #endif stateset->setAttributeAndModes(program, osg::StateAttribute::ON); stateset->addUniform(new osg::Uniform("visibilityDistance", (float)_visibilityDistance)); stateset->addUniform(new osg::Uniform("baseTexture", texture_unit)); setStateSet(stateset); setUseVertexArray(true); setUseShaders(true); } osg::BoundingBox osgParticle::ParticleSystem::computeBoundingBox() const { if (!_bounds_computed) { return _def_bbox; } else { return osg::BoundingBox(_bmin,_bmax); } } void osgParticle::ParticleSystem::resizeGLObjectBuffers(unsigned int maxSize) { _bufferedArrayData.resize(maxSize); for(unsigned int i=0; i<_bufferedArrayData.size(); ++i) { _bufferedArrayData[i].resizeGLObjectBuffers(maxSize); } } void osgParticle::ParticleSystem::releaseGLObjects(osg::State* state) const { if (state) { _bufferedArrayData[state->getContextID()].releaseGLObjects(state); } else { for(unsigned int i=0; i<_bufferedArrayData.size(); ++i) { _bufferedArrayData[i].releaseGLObjects(0); } } } osg::VertexArrayState* osgParticle::ParticleSystem::createVertexArrayStateImplemenation(osg::RenderInfo& renderInfo) const { osg::State& state = *renderInfo.getState(); osg::VertexArrayState* vas = new osg::VertexArrayState(&state); vas->assignVertexArrayDispatcher(); vas->assignNormalArrayDispatcher(); vas->assignColorArrayDispatcher(); vas->assignTexCoordArrayDispatcher(1); if (state.useVertexArrayObject(_useVertexArrayObject)) { vas->generateVertexArrayObject(); } return vas; } ///////////////////////////////////////////////////////////////////////////////////// // // ArrayData // osgParticle::ParticleSystem::ArrayData::ArrayData() { } void osgParticle::ParticleSystem::ArrayData::init() { vertexBufferObject = new osg::VertexBufferObject; vertexBufferObject->setUsage(GL_DYNAMIC_DRAW); vertices = new osg::Vec3Array(osg::Array::BIND_PER_VERTEX); vertices->setBufferObject(vertexBufferObject.get()); vertices->setDataVariance(osg::Object::DYNAMIC); colors = new osg::Vec4Array(osg::Array::BIND_PER_VERTEX); colors->setBufferObject(vertexBufferObject.get()); colors->setDataVariance(osg::Object::DYNAMIC); texcoords2 = new osg::Vec2Array(osg::Array::BIND_PER_VERTEX); texcoords2->setBufferObject(vertexBufferObject.get()); texcoords2->setDataVariance(osg::Object::DYNAMIC); } void osgParticle::ParticleSystem::ArrayData::init3() { vertexBufferObject = new osg::VertexBufferObject; vertexBufferObject->setUsage(GL_DYNAMIC_DRAW); vertices = new osg::Vec3Array(osg::Array::BIND_PER_VERTEX); vertices->setBufferObject(vertexBufferObject.get()); vertices->setDataVariance(osg::Object::DYNAMIC); normals = new osg::Vec3Array(osg::Array::BIND_PER_VERTEX); normals->setBufferObject(vertexBufferObject.get()); normals->setDataVariance(osg::Object::DYNAMIC); colors = new osg::Vec4Array(osg::Array::BIND_PER_VERTEX); colors->setBufferObject(vertexBufferObject.get()); colors->setDataVariance(osg::Object::DYNAMIC); texcoords3 = new osg::Vec3Array(osg::Array::BIND_PER_VERTEX); texcoords3->setBufferObject(vertexBufferObject.get()); texcoords3->setDataVariance(osg::Object::DYNAMIC); } void osgParticle::ParticleSystem::ArrayData::reserve(unsigned int numVertices) { unsigned int vertex_size = 0; if (vertices.valid()) { vertices->reserve(numVertices); vertex_size += 12; } if (normals.valid()) { normals->reserve(numVertices); vertex_size += 12; } if (colors.valid()) { colors->reserve(numVertices); vertex_size += 16; } if (texcoords2.valid()) { texcoords2->reserve(numVertices); vertex_size += 8; } if (texcoords3.valid()) { texcoords3->reserve(numVertices); vertex_size += 12; } vertexBufferObject->getProfile()._size = numVertices * vertex_size; } void osgParticle::ParticleSystem::ArrayData::resize(unsigned int numVertices) { if (vertices.valid()) vertices->resize(numVertices); if (normals.valid()) normals->resize(numVertices); if (colors.valid()) colors->resize(numVertices); if (texcoords2.valid()) texcoords2->resize(numVertices); if (texcoords3.valid()) texcoords3->resize(numVertices); } void osgParticle::ParticleSystem::ArrayData::resizeGLObjectBuffers(unsigned int maxSize) { // OSG_NOTICE<<"osgParticle::ParticleSystem::resizeGLObjectBuffers("<resizeGLObjectBuffers(maxSize); if (vertices.valid()) vertices->resizeGLObjectBuffers(maxSize); if (normals.valid()) normals->resizeGLObjectBuffers(maxSize); if (colors.valid()) colors->resizeGLObjectBuffers(maxSize); if (texcoords2.valid()) texcoords2->resizeGLObjectBuffers(maxSize); if (texcoords3.valid()) texcoords3->resizeGLObjectBuffers(maxSize); } void osgParticle::ParticleSystem::ArrayData::releaseGLObjects(osg::State* state) { // OSG_NOTICE<<"osgParticle::ParticleSystem::releaseGLObjects("<releaseGLObjects(state); if (vertices.valid()) vertices->releaseGLObjects(state); if (normals.valid()) normals->releaseGLObjects(state); if (colors.valid()) colors->releaseGLObjects(state); if (texcoords2.valid()) texcoords2->releaseGLObjects(state); if (texcoords3.valid()) texcoords3->releaseGLObjects(state); } void osgParticle::ParticleSystem::ArrayData::clear() { if (vertices.valid()) vertices->clear(); if (normals.valid()) normals->clear(); if (colors.valid()) colors->clear(); if (texcoords2.valid()) texcoords2->clear(); if (texcoords3.valid()) texcoords3->clear(); primitives.clear(); } void osgParticle::ParticleSystem::ArrayData::dirty() { if (vertices.valid()) vertices->dirty(); if (normals.valid()) normals->dirty(); if (colors.valid()) colors->dirty(); if (texcoords2.valid()) texcoords2->dirty(); if (texcoords3.valid()) texcoords3->dirty(); } void osgParticle::ParticleSystem::ArrayData::dispatchArrays(osg::State& state) { osg::VertexArrayState* vas = state.getCurrentVertexArrayState(); vas->lazyDisablingOfVertexAttributes(); if (vertices.valid()) vas->setVertexArray(state, vertices.get()); if (normals.valid()) vas->setNormalArray(state, normals.get()); if (colors.valid()) vas->setColorArray(state, colors.get()); if (texcoords2.valid()) vas->setTexCoordArray(state, 0, texcoords2.get()); if (texcoords3.valid()) vas->setTexCoordArray(state, 0, texcoords3.get()); vas->applyDisablingOfVertexAttributes(state); } void osgParticle::ParticleSystem::ArrayData::dispatchPrimitives() { unsigned int base = 0; for(ArrayData::Primitives::iterator itr = primitives.begin(); itr != primitives.end(); ++itr) { ArrayData::ModeCount& mc = *itr; glDrawArrays(mc.first, base, mc.second); base += mc.second; } }