diff --git a/CMakeLists.txt b/CMakeLists.txt index d5b37a05bb..1059b1c3ac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -221,7 +221,7 @@ if (${OGRE_VERSION} VERSION_LESS "1.9") message(FATAL_ERROR "OpenMW requires Ogre 1.9 or later, please install the latest stable version from http://ogre3d.org") endif() -find_package(OpenSceneGraph 3.2.0 REQUIRED osgDB osgViewer osgGA osgAnimation) +find_package(OpenSceneGraph 3.2.0 REQUIRED osgDB osgViewer osgGA osgAnimation osgParticle) include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS}) find_package(MyGUI REQUIRED) diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 34c2f0ce00..feffe2cba1 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -18,6 +18,10 @@ #include #include +// particle +#include +#include + #include #include #include @@ -177,6 +181,17 @@ namespace osg::Node* mSkelRoot; }; + // HACK: Particle doesn't allow setting the initial age, but we need this for loading the particle system state + class ParticleAgeSetter : public osgParticle::Particle + { + public: + ParticleAgeSetter(float age) + : Particle() + { + _t0 = age; + } + }; + osg::ref_ptr handleMorphGeometry(const Nif::NiGeomMorpherController* morpher) { osg::ref_ptr morphGeom = new osgAnimation::MorphGeometry; @@ -220,7 +235,7 @@ namespace NifOsg } mRootNode = parentNode; - handleNode(nifNode, parentNode, false, std::map(), 0); + handleNode(nifNode, parentNode, false, std::map(), 0, 0); } void Loader::loadAsSkeleton(Nif::NIFFilePtr nif, osg::Group *parentNode) @@ -249,7 +264,7 @@ namespace NifOsg mSkeleton = skel; mRootNode->addChild(mSkeleton); - handleNode(nifNode, mSkeleton, true, std::map(), 0); + handleNode(nifNode, mSkeleton, true, std::map(), 0, 0); } void Loader::applyNodeProperties(const Nif::Node *nifNode, osg::Node *applyTo, std::map& boundTextures, int animflags) @@ -276,7 +291,7 @@ namespace NifOsg } void Loader::handleNode(const Nif::Node* nifNode, osg::Group* parentNode, bool createSkeleton, - std::map boundTextures, int animflags, bool collisionNode) + std::map boundTextures, int animflags, int particleflags, bool collisionNode) { osg::ref_ptr transformNode; if (createSkeleton) @@ -327,6 +342,9 @@ namespace NifOsg handleMeshControllers(nifNode, transformNode, boundTextures, animflags); } + if(nifNode->recType == Nif::RC_NiAutoNormalParticles || nifNode->recType == Nif::RC_NiRotatingParticles) + handleParticleSystem(nifNode, transformNode, particleflags, animflags); + if (!nifNode->controller.empty()) handleNodeControllers(nifNode, transformNode, animflags); @@ -337,7 +355,7 @@ namespace NifOsg for(size_t i = 0;i < children.length();++i) { if(!children[i].empty()) - handleNode(children[i].getPtr(), transformNode, createSkeleton, boundTextures, animflags, collisionNode); + handleNode(children[i].getPtr(), transformNode, createSkeleton, boundTextures, animflags, particleflags, collisionNode); } } } @@ -346,6 +364,8 @@ namespace NifOsg { for (Nif::ControllerPtr ctrl = nifNode->controller; !ctrl.empty(); ctrl = ctrl->next) { + if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active)) + continue; if (ctrl->recType == Nif::RC_NiUVController) { const Nif::NiUVController *uvctrl = static_cast(ctrl.getPtr()); @@ -364,6 +384,8 @@ namespace NifOsg bool seenKeyframeCtrl = false; for (Nif::ControllerPtr ctrl = nifNode->controller; !ctrl.empty(); ctrl = ctrl->next) { + if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active)) + continue; if (ctrl->recType == Nif::RC_NiKeyframeController) { const Nif::NiKeyframeController *key = static_cast(ctrl.getPtr()); @@ -394,6 +416,8 @@ namespace NifOsg { for (Nif::ControllerPtr ctrl = materialProperty->controller; !ctrl.empty(); ctrl = ctrl->next) { + if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active)) + continue; if (ctrl->recType == Nif::RC_NiAlphaController) { const Nif::NiAlphaController* alphactrl = static_cast(ctrl.getPtr()); @@ -415,6 +439,8 @@ namespace NifOsg { for (Nif::ControllerPtr ctrl = texProperty->controller; !ctrl.empty(); ctrl = ctrl->next) { + if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active)) + continue; if (ctrl->recType == Nif::RC_NiFlipController) { const Nif::NiFlipController* flipctrl = static_cast(ctrl.getPtr()); @@ -450,6 +476,71 @@ namespace NifOsg } } + void Loader::handleParticleSystem(const Nif::Node *nifNode, osg::Group *parentNode, int particleflags, int animflags) + { + osg::ref_ptr partsys (new osgParticle::ParticleSystem); + + const Nif::NiAutoNormalParticlesData *particledata = NULL; + if(nifNode->recType == Nif::RC_NiAutoNormalParticles) + particledata = static_cast(nifNode)->data.getPtr(); + else if(nifNode->recType == Nif::RC_NiRotatingParticles) + particledata = static_cast(nifNode)->data.getPtr(); + else + return; + + const Nif::NiParticleSystemController* partctrl = NULL; + for (Nif::ControllerPtr ctrl = nifNode->controller; !ctrl.empty(); ctrl = ctrl->next) + { + if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active)) + continue; + if(ctrl->recType == Nif::RC_NiParticleSystemController || ctrl->recType == Nif::RC_NiBSPArrayController) + partctrl = static_cast(ctrl.getPtr()); + } + if (!partctrl) + return; + + int i=0; + for (std::vector::const_iterator it = partctrl->particles.begin(); + iactiveCount && it != partctrl->particles.end(); ++it, ++i) + { + const Nif::NiParticleSystemController::Particle& particle = *it; + + ParticleAgeSetter particletemplate(std::max(0.f, particle.lifespan - particle.lifetime)); + + osgParticle::Particle* created = partsys->createParticle(&particletemplate); + created->setLifeTime(500);//std::max(0.f, particle.lifespan)); + created->setVelocity(particle.velocity); + created->setPosition(particledata->vertices.at(particle.vertex)); + + osg::Vec4f partcolor (1.f,1.f,1.f,1.f); + if (particle.vertex < int(particledata->colors.size())) + partcolor = particledata->colors.at(particle.vertex); + + float size = particledata->sizes.at(particle.vertex) * partctrl->size; + + created->setSizeRange(osgParticle::rangef(size, size)); + } + + osg::NodeVisitor visitor; + partsys->update(0.f, visitor); + partsys->setFrozen(true); + partsys->setSortMode(osgParticle::ParticleSystem::SORT_BACK_TO_FRONT); + + std::vector materialProps; + collectMaterialProperties(nifNode, materialProps); + applyMaterialProperties(partsys->getOrCreateStateSet(), materialProps, true, animflags); + + partsys->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); + partsys->getOrCreateStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); + osg::ref_ptr geode (new osg::Geode); + geode->addDrawable(partsys); + parentNode->addChild(geode); + + osgParticle::ParticleSystemUpdater* updater = new osgParticle::ParticleSystemUpdater; + updater->addParticleSystem(partsys); + parentNode->addChild(updater); + } + void Loader::triShapeToGeometry(const Nif::NiTriShape *triShape, osg::Geometry *geometry, const std::map& boundTextures, int animflags) { const Nif::NiTriShapeData* data = triShape->data.getPtr(); @@ -554,18 +645,18 @@ namespace NifOsg if (!geometry.get()) geometry = new osg::Geometry; - triShapeToGeometry(triShape, geometry.get(), boundTextures, animflags); + triShapeToGeometry(triShape, geometry, boundTextures, animflags); osg::ref_ptr geode (new osg::Geode); - geode->addDrawable(geometry.get()); + geode->addDrawable(geometry); - parentNode->addChild(geode.get()); + parentNode->addChild(geode); } void Loader::handleSkinnedTriShape(const Nif::NiTriShape *triShape, osg::Group *parentNode, const std::map& boundTextures, int animflags) { osg::ref_ptr geometry (new osg::Geometry); - triShapeToGeometry(triShape, geometry.get(), boundTextures, animflags); + triShapeToGeometry(triShape, geometry, boundTextures, animflags); osg::ref_ptr rig(new osgAnimation::RigGeometry); rig->setSourceGeometry(geometry); @@ -600,16 +691,16 @@ namespace NifOsg map->insert(std::make_pair(boneName, influence)); } - rig->setInfluenceMap(map.get()); + rig->setInfluenceMap(map); osg::ref_ptr trans(new osg::MatrixTransform); trans->setUpdateCallback(new InvertBoneMatrix(mSkeleton)); osg::ref_ptr geode (new osg::Geode); - geode->addDrawable(rig.get()); + geode->addDrawable(rig); - trans->addChild(geode.get()); - parentNode->addChild(trans.get()); + trans->addChild(geode); + parentNode->addChild(trans); } void Loader::handleProperty(const Nif::Property *property, const Nif::Node* nifNode, @@ -820,7 +911,8 @@ namespace NifOsg } } - void Loader::applyMaterialProperties(osg::StateSet* stateset, const std::vector& properties, bool hasVertexColors, int animflags) + void Loader::applyMaterialProperties(osg::StateSet* stateset, const std::vector& properties, + bool hasVertexColors, int animflags) { int specFlags = 0; // Specular is disabled by default, even if there's a specular color in the NiMaterialProperty osg::Material* mat = new osg::Material; diff --git a/components/nifosg/nifloader.hpp b/components/nifosg/nifloader.hpp index 7f4aa7ea42..0535aa838f 100644 --- a/components/nifosg/nifloader.hpp +++ b/components/nifosg/nifloader.hpp @@ -51,7 +51,7 @@ namespace NifOsg /// @param createSkeleton If true, use an osgAnimation::Bone for NIF nodes, otherwise an osg::MatrixTransform. void handleNode(const Nif::Node* nifNode, osg::Group* parentNode, bool createSkeleton, - std::map boundTextures, int animflags, bool collisionNode=false); + std::map boundTextures, int animflags, int particleflags, bool collisionNode=false); void handleMeshControllers(const Nif::Node* nifNode, osg::MatrixTransform* transformNode, const std::map& boundTextures, int animflags); @@ -64,6 +64,8 @@ namespace NifOsg void handleProperty (const Nif::Property* property, const Nif::Node* nifNode, osg::Node* node, std::map& boundTextures, int animflags); + void handleParticleSystem(const Nif::Node* nifNode, osg::Group* parentNode, int particleflags, int animflags); + // Creates an osg::Geometry object for the given TriShape, populates it, and attaches it to the given node. void handleTriShape(const Nif::NiTriShape* triShape, osg::Group* parentNode, const std::map& boundTextures, int animflags); @@ -76,7 +78,8 @@ namespace NifOsg // Applies the Properties of the given nifNode onto the StateSet of the given OSG node. void applyNodeProperties(const Nif::Node* nifNode, osg::Node* applyTo, std::map& boundTextures, int animflags); - void applyMaterialProperties(osg::StateSet* stateset, const std::vector& properties, bool hasVertexColors, int animflags); + void applyMaterialProperties(osg::StateSet* stateset, const std::vector& properties, + bool hasVertexColors, int animflags); void createController(const Nif::Controller* ctrl, boost::shared_ptr value, int animflags);