diff --git a/components/nif/node.hpp b/components/nif/node.hpp index 917bc8add..6816a79a2 100644 --- a/components/nif/node.hpp +++ b/components/nif/node.hpp @@ -137,7 +137,8 @@ struct NiNode : Node AnimFlag_AutoPlay = 0x0020 }; enum BSParticleFlags { - ParticleFlag_AutoPlay = 0x0020 + ParticleFlag_AutoPlay = 0x0020, + ParticleFlag_LocalSpace = 0x0080 }; void read(NIFStream *nif) diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index bb98501f4..a530d060d 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -497,7 +497,9 @@ class NIFObjectLoader } - static void createParticleEmitterAffectors(Ogre::ParticleSystem *partsys, const Nif::NiParticleSystemController *partctrl) + static void createParticleEmitterAffectors(Ogre::ParticleSystem *partsys, + const Nif::NiParticleSystemController *partctrl, Ogre::Bone* bone, + const std::string& skelBaseName) { Ogre::ParticleEmitter *emitter = partsys->addEmitter("Nif"); emitter->setParticleVelocity(partctrl->velocity - partctrl->velocityRandom*0.5f, @@ -512,6 +514,8 @@ class NIFObjectLoader emitter->setParameter("vertical_angle", Ogre::StringConverter::toString(Ogre::Radian(partctrl->verticalAngle).valueDegrees())); emitter->setParameter("horizontal_direction", Ogre::StringConverter::toString(Ogre::Radian(partctrl->horizontalDir).valueDegrees())); emitter->setParameter("horizontal_angle", Ogre::StringConverter::toString(Ogre::Radian(partctrl->horizontalAngle).valueDegrees())); + emitter->setParameter("skelbase", skelBaseName); + emitter->setParameter("bone", bone->getName()); Nif::ExtraPtr e = partctrl->extra; while(!e.empty()) @@ -533,6 +537,8 @@ class NIFObjectLoader affector->setParameter("force_type", (gr->mType==0) ? "wind" : "point"); affector->setParameter("direction", Ogre::StringConverter::toString(gr->mDirection)); affector->setParameter("position", Ogre::StringConverter::toString(gr->mPosition)); + affector->setParameter("skelbase", skelBaseName); + affector->setParameter("bone", bone->getName()); } else if(e->recType == Nif::RC_NiParticleColorModifier) { @@ -565,7 +571,7 @@ class NIFObjectLoader } static void createParticleSystem(const std::string &name, const std::string &group, - Ogre::SceneManager *sceneMgr, ObjectList &objectlist, + Ogre::SceneNode *sceneNode, ObjectList &objectlist, const Nif::Node *partnode, int flags, int partflags) { const Nif::NiAutoNormalParticlesData *particledata = NULL; @@ -579,7 +585,7 @@ class NIFObjectLoader fullname += "@type="+partnode->name; Misc::StringUtils::toLower(fullname); - Ogre::ParticleSystem *partsys = sceneMgr->createParticleSystem(); + Ogre::ParticleSystem *partsys = sceneNode->getCreator()->createParticleSystem(); const Nif::NiTexturingProperty *texprop = NULL; const Nif::NiMaterialProperty *matprop = NULL; @@ -600,9 +606,9 @@ class NIFObjectLoader particledata->particleRadius*2.0f); partsys->setCullIndividually(false); partsys->setParticleQuota(particledata->numParticles); - // TODO: There is probably a field or flag to specify this, as some - // particle effects have it and some don't. - partsys->setKeepParticlesInLocalSpace(false); + partsys->setKeepParticlesInLocalSpace(partflags & (Nif::NiNode::ParticleFlag_LocalSpace)); + + sceneNode->attachObject(partsys); Nif::ControllerPtr ctrl = partnode->controller; while(!ctrl.empty()) @@ -611,12 +617,11 @@ class NIFObjectLoader { const Nif::NiParticleSystemController *partctrl = static_cast(ctrl.getPtr()); - createParticleEmitterAffectors(partsys, partctrl); - if(!partctrl->emitter.empty() && !partsys->isAttached()) + if(!partctrl->emitter.empty()) { int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, partctrl->emitter->recIndex); Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid); - objectlist.mSkelBase->attachObjectToBone(trgtbone->getName(), partsys); + createParticleEmitterAffectors(partsys, partctrl, trgtbone, objectlist.mSkelBase->getName()); } Ogre::ControllerValueRealPtr srcval((partflags&Nif::NiNode::ParticleFlag_AutoPlay) ? @@ -634,13 +639,6 @@ class NIFObjectLoader ctrl = ctrl->next; } - if(!partsys->isAttached()) - { - int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, partnode->recIndex); - Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid); - objectlist.mSkelBase->attachObjectToBone(trgtbone->getName(), partsys); - } - partsys->setVisible(!(flags&Nif::NiNode::Flag_Hidden)); objectlist.mParticles.push_back(partsys); } @@ -729,7 +727,7 @@ class NIFObjectLoader static void createObjects(const std::string &name, const std::string &group, - Ogre::SceneManager *sceneMgr, const Nif::Node *node, + Ogre::SceneNode *sceneNode, const Nif::Node *node, ObjectList &objectlist, int flags, int animflags, int partflags) { // Do not create objects for the collision shape (includes all children) @@ -784,13 +782,13 @@ class NIFObjectLoader if(node->recType == Nif::RC_NiTriShape && !(flags&0x80000000)) { - createEntity(name, group, sceneMgr, objectlist, node, flags, animflags); + createEntity(name, group, sceneNode->getCreator(), objectlist, node, flags, animflags); } if((node->recType == Nif::RC_NiAutoNormalParticles || node->recType == Nif::RC_NiRotatingParticles) && !(flags&0x40000000)) { - createParticleSystem(name, group, sceneMgr, objectlist, node, flags, partflags); + createParticleSystem(name, group, sceneNode, objectlist, node, flags, partflags); } const Nif::NiNode *ninode = dynamic_cast(node); @@ -800,7 +798,7 @@ class NIFObjectLoader for(size_t i = 0;i < children.length();i++) { if(!children[i].empty()) - createObjects(name, group, sceneMgr, children[i].getPtr(), objectlist, flags, animflags, partflags); + createObjects(name, group, sceneNode, children[i].getPtr(), objectlist, flags, animflags, partflags); } } } @@ -821,7 +819,7 @@ class NIFObjectLoader } public: - static void load(Ogre::SceneManager *sceneMgr, ObjectList &objectlist, const std::string &name, const std::string &group, int flags=0) + static void load(Ogre::SceneNode *sceneNode, ObjectList &objectlist, const std::string &name, const std::string &group, int flags=0) { Nif::NIFFile::ptr nif = Nif::NIFFile::create(name); if(nif->numRoots() < 1) @@ -845,9 +843,9 @@ public: !NIFSkeletonLoader::createSkeleton(name, group, node).isNull()) { // Create a base skeleton entity if this NIF needs one - createSkelBase(name, group, sceneMgr, node, objectlist); + createSkelBase(name, group, sceneNode->getCreator(), node, objectlist); } - createObjects(name, group, sceneMgr, node, objectlist, flags, 0, 0); + createObjects(name, group, sceneNode, node, objectlist, flags, 0, 0); } static void loadKf(Ogre::Skeleton *skel, const std::string &name, @@ -915,7 +913,7 @@ ObjectList Loader::createObjects(Ogre::SceneNode *parentNode, std::string name, ObjectList objectlist; Misc::StringUtils::toLower(name); - NIFObjectLoader::load(parentNode->getCreator(), objectlist, name, group); + NIFObjectLoader::load(parentNode, objectlist, name, group); for(size_t i = 0;i < objectlist.mEntities.size();i++) { @@ -934,7 +932,7 @@ ObjectList Loader::createObjects(Ogre::Entity *parent, const std::string &bonena ObjectList objectlist; Misc::StringUtils::toLower(name); - NIFObjectLoader::load(parentNode->getCreator(), objectlist, name, group); + NIFObjectLoader::load(parentNode, objectlist, name, group); bool isskinned = false; for(size_t i = 0;i < objectlist.mEntities.size();i++) @@ -984,6 +982,17 @@ ObjectList Loader::createObjects(Ogre::Entity *parent, const std::string &bonena } } + for(size_t i = 0;i < objectlist.mParticles.size();i++) + { + Ogre::ParticleSystem *partsys = objectlist.mParticles[i]; + if(partsys->isAttached()) + partsys->detachFromParent(); + + Ogre::TagPoint *tag = objectlist.mSkelBase->attachObjectToBone( + objectlist.mSkelBase->getSkeleton()->getRootBone()->getName(), partsys); + tag->setScale(scale); + } + return objectlist; } @@ -993,7 +1002,7 @@ ObjectList Loader::createObjectBase(Ogre::SceneNode *parentNode, std::string nam ObjectList objectlist; Misc::StringUtils::toLower(name); - NIFObjectLoader::load(parentNode->getCreator(), objectlist, name, group, 0xC0000000); + NIFObjectLoader::load(parentNode, objectlist, name, group, 0xC0000000); if(objectlist.mSkelBase) parentNode->attachObject(objectlist.mSkelBase); diff --git a/components/nifogre/particles.cpp b/components/nifogre/particles.cpp index 707bd75e0..006a570dc 100644 --- a/components/nifogre/particles.cpp +++ b/components/nifogre/particles.cpp @@ -5,11 +5,55 @@ #include #include #include +#include +#include +#include +#include +#include +#include /* FIXME: "Nif" isn't really an appropriate emitter name. */ class NifEmitter : public Ogre::ParticleEmitter { public: + std::string mSkelBaseName; + Ogre::Bone* mBone; + + Ogre::ParticleSystem* getPartSys() { return mParent; } + + class CmdSkelBase : public Ogre::ParamCommand + { + public: + Ogre::String doGet(const void *target) const + { + assert(false && "Unimplemented"); + } + void doSet(void *target, const Ogre::String &val) + { + NifEmitter* emitter = static_cast(target); + emitter->mSkelBaseName = val; + } + }; + + class CmdBone : public Ogre::ParamCommand + { + public: + Ogre::String doGet(const void *target) const + { + assert(false && "Unimplemented"); + } + void doSet(void *target, const Ogre::String &val) + { + NifEmitter* emitter = static_cast(target); + assert(!emitter->mSkelBaseName.empty() && "Base entity needs to be set first"); + Ogre::ParticleSystem* partsys = emitter->getPartSys(); + Ogre::Entity* ent = partsys->getParentSceneNode()->getCreator()->getEntity(emitter->mSkelBaseName); + Ogre::Bone* bone = ent->getSkeleton()->getBone(val); + assert(bone); + emitter->mBone = bone; + } + }; + /** Command object for the emitter width (see Ogre::ParamCommand).*/ class CmdWidth : public Ogre::ParamCommand { @@ -119,6 +163,7 @@ public: NifEmitter(Ogre::ParticleSystem *psys) : Ogre::ParticleEmitter(psys) + , mBone(NULL) { initDefaults("Nif"); } @@ -133,6 +178,7 @@ public: /** See Ogre::ParticleEmitter. */ void _initParticle(Ogre::Particle *particle) { + assert (mBone && "No node set"); Ogre::Vector3 xOff, yOff, zOff; // Call superclass @@ -141,8 +187,8 @@ public: xOff = Ogre::Math::SymmetricRandom() * mXRange; yOff = Ogre::Math::SymmetricRandom() * mYRange; zOff = Ogre::Math::SymmetricRandom() * mZRange; - - particle->position = mPosition + xOff + yOff + zOff; + + particle->position = mBone->_getDerivedPosition() + xOff + yOff + zOff; // Generate complex data by reference genEmissionColour(particle->colour); @@ -150,7 +196,7 @@ public: // NOTE: We do not use mDirection/mAngle for the initial direction. Ogre::Radian hdir = mHorizontalDir + mHorizontalAngle*Ogre::Math::SymmetricRandom(); Ogre::Radian vdir = mVerticalDir + mVerticalAngle*Ogre::Math::SymmetricRandom(); - particle->direction = (Ogre::Quaternion(hdir, Ogre::Vector3::UNIT_Z) * + particle->direction = (mBone->_getDerivedOrientation() * Ogre::Quaternion(hdir, Ogre::Vector3::UNIT_Z) * Ogre::Quaternion(vdir, Ogre::Vector3::UNIT_X)) * Ogre::Vector3::UNIT_Z; @@ -313,6 +359,16 @@ protected: Ogre::PT_REAL), &msHorizontalAngleCmd); + dict->addParameter(Ogre::ParameterDef("bone", + "The bone where the particles should be spawned", + Ogre::PT_STRING), + &msBoneCmd); + + dict->addParameter(Ogre::ParameterDef("skelbase", + "The name of the entity containing the bone (see 'bone' parameter)", + Ogre::PT_STRING), + &msSkelBaseCmd); + return true; } return false; @@ -326,6 +382,8 @@ protected: static CmdVerticalAngle msVerticalAngleCmd; static CmdHorizontalDir msHorizontalDirCmd; static CmdHorizontalAngle msHorizontalAngleCmd; + static CmdBone msBoneCmd; + static CmdSkelBase msSkelBaseCmd; }; NifEmitter::CmdWidth NifEmitter::msWidthCmd; NifEmitter::CmdHeight NifEmitter::msHeightCmd; @@ -334,12 +392,14 @@ NifEmitter::CmdVerticalDir NifEmitter::msVerticalDirCmd; NifEmitter::CmdVerticalAngle NifEmitter::msVerticalAngleCmd; NifEmitter::CmdHorizontalDir NifEmitter::msHorizontalDirCmd; NifEmitter::CmdHorizontalAngle NifEmitter::msHorizontalAngleCmd; +NifEmitter::CmdBone NifEmitter::msBoneCmd; +NifEmitter::CmdSkelBase NifEmitter::msSkelBaseCmd; Ogre::ParticleEmitter* NifEmitterFactory::createEmitter(Ogre::ParticleSystem *psys) { - Ogre::ParticleEmitter *emit = OGRE_NEW NifEmitter(psys); - mEmitters.push_back(emit); - return emit; + Ogre::ParticleEmitter *emitter = OGRE_NEW NifEmitter(psys); + mEmitters.push_back(emitter); + return emitter; } @@ -492,6 +552,45 @@ class GravityAffector : public Ogre::ParticleAffector }; public: + std::string mSkelBaseName; + Ogre::Bone* mBone; + + Ogre::ParticleSystem* getPartSys() { return mParent; } + + class CmdSkelBase : public Ogre::ParamCommand + { + public: + Ogre::String doGet(const void *target) const + { + assert(false && "Unimplemented"); + } + void doSet(void *target, const Ogre::String &val) + { + GravityAffector* affector = static_cast(target); + affector->mSkelBaseName = val; + } + }; + + class CmdBone : public Ogre::ParamCommand + { + public: + Ogre::String doGet(const void *target) const + { + assert(false && "Unimplemented"); + } + void doSet(void *target, const Ogre::String &val) + { + GravityAffector* affector = static_cast(target); + assert(!affector->mSkelBaseName.empty() && "Base entity needs to be set first"); + Ogre::ParticleSystem* partsys = affector->getPartSys(); + Ogre::Entity* ent = partsys->getParentSceneNode()->getCreator()->getEntity(affector->mSkelBaseName); + Ogre::Bone* bone = ent->getSkeleton()->getBone(val); + assert(bone); + affector->mBone = bone; + } + }; + + /** Command object for force (see Ogre::ParamCommand).*/ class CmdForce : public Ogre::ParamCommand { @@ -585,6 +684,7 @@ public: , mForceType(Type_Wind) , mPosition(0.0f) , mDirection(0.0f) + , mBone(NULL) { mType = "Gravity"; @@ -606,6 +706,16 @@ public: dict->addParameter(Ogre::ParameterDef(force_type_title, force_type_descr, Ogre::PT_STRING), &msForceTypeCmd); dict->addParameter(Ogre::ParameterDef(direction_title, direction_descr, Ogre::PT_VECTOR3), &msDirectionCmd); dict->addParameter(Ogre::ParameterDef(position_title, position_descr, Ogre::PT_VECTOR3), &msPositionCmd); + + dict->addParameter(Ogre::ParameterDef("bone", + "The bone where the particles should be spawned", + Ogre::PT_STRING), + &msBoneCmd); + + dict->addParameter(Ogre::ParameterDef("skelbase", + "The name of the entity containing the bone (see 'bone' parameter)", + Ogre::PT_STRING), + &msSkelBaseCmd); } } @@ -647,6 +757,8 @@ public: static CmdForceType msForceTypeCmd; static CmdDirection msDirectionCmd; static CmdPosition msPositionCmd; + static CmdBone msBoneCmd; + static CmdSkelBase msSkelBaseCmd; protected: void applyWindForce(Ogre::ParticleSystem *psys, Ogre::Real timeElapsed) @@ -667,7 +779,8 @@ protected: while (!pi.end()) { Ogre::Particle *p = pi.getNext(); - const Ogre::Vector3 vec = (p->position - mPosition).normalisedCopy() * force; + const Ogre::Vector3 vec = ( + (mBone->_getDerivedOrientation() * mPosition + mBone->_getDerivedPosition()) - p->position).normalisedCopy() * force; p->direction += vec; } } @@ -684,6 +797,8 @@ GravityAffector::CmdForce GravityAffector::msForceCmd; GravityAffector::CmdForceType GravityAffector::msForceTypeCmd; GravityAffector::CmdDirection GravityAffector::msDirectionCmd; GravityAffector::CmdPosition GravityAffector::msPositionCmd; +GravityAffector::CmdBone GravityAffector::msBoneCmd; +GravityAffector::CmdSkelBase GravityAffector::msSkelBaseCmd; Ogre::ParticleAffector *GravityAffectorFactory::createAffector(Ogre::ParticleSystem *psys) {