You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
openmw-tes3mp/components/nifogre/ogrenifloader.cpp

1373 lines
55 KiB
C++

/*
OpenMW - The completely unofficial reimplementation of Morrowind
Copyright (C) 2008-2010 Nicolay Korslund
Email: < korslund@gmail.com >
WWW: http://openmw.sourceforge.net/
This file (ogre_nif_loader.cpp) is part of the OpenMW package.
OpenMW is distributed as free software: you can redistribute it
and/or modify it under the terms of the GNU General Public License
version 3, as published by the Free Software Foundation.
This program 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 GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
version 3 along with this program. If not, see
http://www.gnu.org/licenses/ .
*/
#include "ogrenifloader.hpp"
#include <algorithm>
#include <OgreTechnique.h>
#include <OgreEntity.h>
#include <OgreSubEntity.h>
#include <OgreTagPoint.h>
#include <OgreParticleSystem.h>
#include <OgreParticleEmitter.h>
#include <OgreParticleAffector.h>
#include <OgreMeshManager.h>
#include <OgreSkeletonManager.h>
#include <OgreControllerManager.h>
#include <OgreMaterialManager.h>
#include <OgreCamera.h>
#include <OgreSceneManager.h>
#include <OgreSkeletonInstance.h>
#include <OgreSceneNode.h>
#include <OgreMesh.h>
#include <extern/shiny/Main/Factory.hpp>
#include <components/nif/node.hpp>
#include <components/nifcache/nifcache.hpp>
#include <components/misc/stringops.hpp>
#include <components/misc/resourcehelpers.hpp>
#include "skeleton.hpp"
#include "material.hpp"
#include "mesh.hpp"
#include "controller.hpp"
#include "particles.hpp"
namespace
{
void getAllNiNodes(const Nif::Node* node, std::vector<const Nif::NiNode*>& out)
{
const Nif::NiNode* ninode = dynamic_cast<const Nif::NiNode*>(node);
if (ninode)
{
out.push_back(ninode);
for (unsigned int i=0; i<ninode->children.length(); ++i)
if (!ninode->children[i].empty())
getAllNiNodes(ninode->children[i].getPtr(), out);
}
}
}
namespace NifOgre
{
Ogre::MaterialPtr MaterialControllerManager::getWritableMaterial(Ogre::MovableObject *movable)
{
if (mClonedMaterials.find(movable) != mClonedMaterials.end())
return mClonedMaterials[movable];
else
{
Ogre::MaterialPtr mat;
if (Ogre::Entity* ent = dynamic_cast<Ogre::Entity*>(movable))
mat = ent->getSubEntity(0)->getMaterial();
else if (Ogre::ParticleSystem* partSys = dynamic_cast<Ogre::ParticleSystem*>(movable))
mat = Ogre::MaterialManager::getSingleton().getByName(partSys->getMaterialName());
static int count=0;
Ogre::String newName = mat->getName() + Ogre::StringConverter::toString(count++);
sh::Factory::getInstance().createMaterialInstance(newName, mat->getName());
// Make sure techniques are created
sh::Factory::getInstance()._ensureMaterial(newName, "Default");
mat = Ogre::MaterialManager::getSingleton().getByName(newName);
mClonedMaterials[movable] = mat;
if (Ogre::Entity* ent = dynamic_cast<Ogre::Entity*>(movable))
ent->getSubEntity(0)->setMaterial(mat);
else if (Ogre::ParticleSystem* partSys = dynamic_cast<Ogre::ParticleSystem*>(movable))
partSys->setMaterialName(mat->getName());
return mat;
}
}
MaterialControllerManager::~MaterialControllerManager()
{
for (std::map<Ogre::MovableObject*, Ogre::MaterialPtr>::iterator it = mClonedMaterials.begin(); it != mClonedMaterials.end(); ++it)
{
sh::Factory::getInstance().destroyMaterialInstance(it->second->getName());
}
}
ObjectScene::~ObjectScene()
{
for(size_t i = 0;i < mLights.size();i++)
{
Ogre::Light *light = mLights[i];
// If parent is a scene node, it was created specifically for this light. Destroy it now.
if(light->isAttached() && !light->isParentTagPoint())
mSceneMgr->destroySceneNode(light->getParentSceneNode());
mSceneMgr->destroyLight(light);
}
for(size_t i = 0;i < mParticles.size();i++)
mSceneMgr->destroyParticleSystem(mParticles[i]);
for(size_t i = 0;i < mEntities.size();i++)
mSceneMgr->destroyEntity(mEntities[i]);
mControllers.clear();
mLights.clear();
mParticles.clear();
mEntities.clear();
mSkelBase = NULL;
}
void ObjectScene::setVisibilityFlags (unsigned int flags)
{
for (std::vector<Ogre::Entity*>::iterator iter (mEntities.begin()); iter!=mEntities.end();
++iter)
(*iter)->setVisibilityFlags (flags);
for (std::vector<Ogre::ParticleSystem*>::iterator iter (mParticles.begin());
iter!=mParticles.end(); ++iter)
(*iter)->setVisibilityFlags (flags);
for (std::vector<Ogre::Light*>::iterator iter (mLights.begin()); iter!=mLights.end();
++iter)
(*iter)->setVisibilityFlags (flags);
}
void ObjectScene::rotateBillboardNodes(Ogre::Camera *camera)
{
for (std::vector<Ogre::Node*>::iterator it = mBillboardNodes.begin(); it != mBillboardNodes.end(); ++it)
{
assert(mSkelBase);
Ogre::Node* node = *it;
node->_setDerivedOrientation(mSkelBase->getParentNode()->_getDerivedOrientation().Inverse() *
camera->getRealOrientation());
}
}
// Animates a texture
class FlipController
{
public:
class Value : public Ogre::ControllerValue<Ogre::Real>
{
private:
Ogre::MovableObject* mMovable;
int mTexSlot;
float mDelta;
std::vector<std::string> mTextures;
MaterialControllerManager* mMaterialControllerMgr;
public:
Value(Ogre::MovableObject *movable, const Nif::NiFlipController *ctrl, MaterialControllerManager* materialControllerMgr)
: mMovable(movable)
, mMaterialControllerMgr(materialControllerMgr)
{
mTexSlot = ctrl->mTexSlot;
mDelta = ctrl->mDelta;
for (unsigned int i=0; i<ctrl->mSources.length(); ++i)
{
const Nif::NiSourceTexture* tex = ctrl->mSources[i].getPtr();
if (!tex->external)
std::cerr << "Warning: Found internal texture, ignoring." << std::endl;
mTextures.push_back(Misc::ResourceHelpers::correctTexturePath(tex->filename));
}
}
virtual Ogre::Real getValue() const
{
// Should not be called
return 0.0f;
}
virtual void setValue(Ogre::Real time)
{
if (mDelta == 0)
return;
int curTexture = int(time / mDelta) % mTextures.size();
Ogre::MaterialPtr mat = mMaterialControllerMgr->getWritableMaterial(mMovable);
Ogre::Material::TechniqueIterator techs = mat->getTechniqueIterator();
while(techs.hasMoreElements())
{
Ogre::Technique *tech = techs.getNext();
Ogre::Technique::PassIterator passes = tech->getPassIterator();
while(passes.hasMoreElements())
{
Ogre::Pass *pass = passes.getNext();
Ogre::Pass::TextureUnitStateIterator textures = pass->getTextureUnitStateIterator();
while (textures.hasMoreElements())
{
Ogre::TextureUnitState *texture = textures.getNext();
if ((texture->getName() == "diffuseMap" && mTexSlot == Nif::NiTexturingProperty::BaseTexture)
|| (texture->getName() == "normalMap" && mTexSlot == Nif::NiTexturingProperty::BumpTexture)
|| (texture->getName() == "detailMap" && mTexSlot == Nif::NiTexturingProperty::DetailTexture)
|| (texture->getName() == "darkMap" && mTexSlot == Nif::NiTexturingProperty::DarkTexture)
|| (texture->getName() == "emissiveMap" && mTexSlot == Nif::NiTexturingProperty::GlowTexture))
texture->setTextureName(mTextures[curTexture]);
}
}
}
}
};
typedef DefaultFunction Function;
};
class AlphaController
{
public:
class Value : public Ogre::ControllerValue<Ogre::Real>, public ValueInterpolator
{
private:
Ogre::MovableObject* mMovable;
Nif::FloatKeyMap mData;
MaterialControllerManager* mMaterialControllerMgr;
public:
Value(Ogre::MovableObject *movable, const Nif::NiFloatData *data, MaterialControllerManager* materialControllerMgr)
: mMovable(movable)
, mData(data->mKeyList)
, mMaterialControllerMgr(materialControllerMgr)
{
}
virtual Ogre::Real getValue() const
{
// Should not be called
return 0.0f;
}
virtual void setValue(Ogre::Real time)
{
float value = interpKey(mData.mKeys, time);
Ogre::MaterialPtr mat = mMaterialControllerMgr->getWritableMaterial(mMovable);
Ogre::Material::TechniqueIterator techs = mat->getTechniqueIterator();
while(techs.hasMoreElements())
{
Ogre::Technique *tech = techs.getNext();
Ogre::Technique::PassIterator passes = tech->getPassIterator();
while(passes.hasMoreElements())
{
Ogre::Pass *pass = passes.getNext();
Ogre::ColourValue diffuse = pass->getDiffuse();
diffuse.a = value;
pass->setDiffuse(diffuse);
}
}
}
};
typedef DefaultFunction Function;
};
class MaterialColorController
{
public:
class Value : public Ogre::ControllerValue<Ogre::Real>, public ValueInterpolator
{
private:
Ogre::MovableObject* mMovable;
Nif::Vector3KeyMap mData;
MaterialControllerManager* mMaterialControllerMgr;
public:
Value(Ogre::MovableObject *movable, const Nif::NiPosData *data, MaterialControllerManager* materialControllerMgr)
: mMovable(movable)
, mData(data->mKeyList)
, mMaterialControllerMgr(materialControllerMgr)
{
}
virtual Ogre::Real getValue() const
{
// Should not be called
return 0.0f;
}
virtual void setValue(Ogre::Real time)
{
Ogre::Vector3 value = interpKey(mData.mKeys, time);
Ogre::MaterialPtr mat = mMaterialControllerMgr->getWritableMaterial(mMovable);
Ogre::Material::TechniqueIterator techs = mat->getTechniqueIterator();
while(techs.hasMoreElements())
{
Ogre::Technique *tech = techs.getNext();
Ogre::Technique::PassIterator passes = tech->getPassIterator();
while(passes.hasMoreElements())
{
Ogre::Pass *pass = passes.getNext();
Ogre::ColourValue diffuse = pass->getDiffuse();
diffuse.r = value.x;
diffuse.g = value.y;
diffuse.b = value.z;
pass->setDiffuse(diffuse);
}
}
}
};
typedef DefaultFunction Function;
};
class VisController
{
public:
class Value : public NodeTargetValue<Ogre::Real>
{
private:
std::vector<Nif::NiVisData::VisData> mData;
bool calculate(Ogre::Real time) const
{
if(mData.size() == 0)
return true;
for(size_t i = 1;i < mData.size();i++)
{
if(mData[i].time > time)
return mData[i-1].isSet;
}
return mData.back().isSet;
}
static void setVisible(Ogre::Node *node, int vis)
{
// Skinned meshes are attached to the scene node, not the bone.
// We use the Node's user data to connect it with the mesh.
Ogre::Any customData = node->getUserObjectBindings().getUserAny();
if (!customData.isEmpty())
Ogre::any_cast<Ogre::MovableObject*>(customData)->setVisible(vis);
Ogre::TagPoint *tag = dynamic_cast<Ogre::TagPoint*>(node);
if(tag != NULL)
{
Ogre::MovableObject *obj = tag->getChildObject();
if(obj != NULL)
obj->setVisible(vis);
}
Ogre::Node::ChildNodeIterator iter = node->getChildIterator();
while(iter.hasMoreElements())
{
node = iter.getNext();
setVisible(node, vis);
}
}
public:
Value(Ogre::Node *target, const Nif::NiVisData *data)
: NodeTargetValue<Ogre::Real>(target)
, mData(data->mVis)
{ }
virtual Ogre::Quaternion getRotation(float time) const
{ return Ogre::Quaternion(); }
virtual Ogre::Vector3 getTranslation(float time) const
{ return Ogre::Vector3(0.0f); }
virtual Ogre::Vector3 getScale(float time) const
{ return Ogre::Vector3(1.0f); }
virtual Ogre::Real getValue() const
{
// Should not be called
return 0.0f;
}
virtual void setValue(Ogre::Real time)
{
bool vis = calculate(time);
setVisible(mNode, vis);
}
};
typedef DefaultFunction Function;
};
class KeyframeController
{
public:
class Value : public NodeTargetValue<Ogre::Real>, public ValueInterpolator
{
private:
const Nif::QuaternionKeyMap* mRotations;
const Nif::Vector3KeyMap* mTranslations;
const Nif::FloatKeyMap* mScales;
Nif::NIFFilePtr mNif; // Hold a SharedPtr to make sure key lists stay valid
using ValueInterpolator::interpKey;
static Ogre::Quaternion interpKey(const Nif::QuaternionKeyMap::MapType &keys, float time)
{
if(time <= keys.begin()->first)
return keys.begin()->second.mValue;
Nif::QuaternionKeyMap::MapType::const_iterator it = keys.lower_bound(time);
if (it != keys.end())
{
float aTime = it->first;
const Nif::QuaternionKey* aKey = &it->second;
assert (it != keys.begin()); // Shouldn't happen, was checked at beginning of this function
Nif::QuaternionKeyMap::MapType::const_iterator last = --it;
float aLastTime = last->first;
const Nif::QuaternionKey* aLastKey = &last->second;
float a = (time - aLastTime) / (aTime - aLastTime);
return Ogre::Quaternion::nlerp(a, aLastKey->mValue, aKey->mValue);
}
else
return keys.rbegin()->second.mValue;
}
public:
/// @note The NiKeyFrameData must be valid as long as this KeyframeController exists.
Value(Ogre::Node *target, const Nif::NIFFilePtr& nif, const Nif::NiKeyframeData *data)
: NodeTargetValue<Ogre::Real>(target)
, mRotations(&data->mRotations)
, mTranslations(&data->mTranslations)
, mScales(&data->mScales)
, mNif(nif)
{ }
virtual Ogre::Quaternion getRotation(float time) const
{
if(mRotations->mKeys.size() > 0)
return interpKey(mRotations->mKeys, time);
return mNode->getOrientation();
}
virtual Ogre::Vector3 getTranslation(float time) const
{
if(mTranslations->mKeys.size() > 0)
return interpKey(mTranslations->mKeys, time);
return mNode->getPosition();
}
virtual Ogre::Vector3 getScale(float time) const
{
if(mScales->mKeys.size() > 0)
return Ogre::Vector3(interpKey(mScales->mKeys, time));
return mNode->getScale();
}
virtual Ogre::Real getValue() const
{
// Should not be called
return 0.0f;
}
virtual void setValue(Ogre::Real time)
{
if(mRotations->mKeys.size() > 0)
mNode->setOrientation(interpKey(mRotations->mKeys, time));
if(mTranslations->mKeys.size() > 0)
mNode->setPosition(interpKey(mTranslations->mKeys, time));
if(mScales->mKeys.size() > 0)
mNode->setScale(Ogre::Vector3(interpKey(mScales->mKeys, time)));
}
};
typedef DefaultFunction Function;
};
class UVController
{
public:
class Value : public Ogre::ControllerValue<Ogre::Real>, public ValueInterpolator
{
private:
Ogre::MovableObject* mMovable;
Nif::FloatKeyMap mUTrans;
Nif::FloatKeyMap mVTrans;
Nif::FloatKeyMap mUScale;
Nif::FloatKeyMap mVScale;
MaterialControllerManager* mMaterialControllerMgr;
public:
Value(Ogre::MovableObject* movable, const Nif::NiUVData *data, MaterialControllerManager* materialControllerMgr)
: mMovable(movable)
, mUTrans(data->mKeyList[0])
, mVTrans(data->mKeyList[1])
, mUScale(data->mKeyList[2])
, mVScale(data->mKeyList[3])
, mMaterialControllerMgr(materialControllerMgr)
{ }
virtual Ogre::Real getValue() const
{
// Should not be called
return 1.0f;
}
virtual void setValue(Ogre::Real value)
{
float uTrans = interpKey(mUTrans.mKeys, value, 0.0f);
float vTrans = interpKey(mVTrans.mKeys, value, 0.0f);
float uScale = interpKey(mUScale.mKeys, value, 1.0f);
float vScale = interpKey(mVScale.mKeys, value, 1.0f);
Ogre::MaterialPtr material = mMaterialControllerMgr->getWritableMaterial(mMovable);
Ogre::Material::TechniqueIterator techs = material->getTechniqueIterator();
while(techs.hasMoreElements())
{
Ogre::Technique *tech = techs.getNext();
Ogre::Technique::PassIterator passes = tech->getPassIterator();
while(passes.hasMoreElements())
{
Ogre::Pass *pass = passes.getNext();
Ogre::TextureUnitState *tex = pass->getTextureUnitState(0);
tex->setTextureScroll(uTrans, vTrans);
tex->setTextureScale(uScale, vScale);
}
}
}
};
typedef DefaultFunction Function;
};
class ParticleSystemController
{
public:
class Value : public Ogre::ControllerValue<Ogre::Real>
{
private:
Ogre::ParticleSystem *mParticleSys;
float mEmitStart;
float mEmitStop;
public:
Value(Ogre::ParticleSystem *psys, const Nif::NiParticleSystemController *pctrl)
: mParticleSys(psys)
, mEmitStart(pctrl->startTime)
, mEmitStop(pctrl->stopTime)
{
}
Ogre::Real getValue() const
{ return 0.0f; }
void setValue(Ogre::Real value)
{
mParticleSys->setEmitting(value >= mEmitStart && value < mEmitStop);
}
};
typedef DefaultFunction Function;
};
class GeomMorpherController
{
public:
class Value : public Ogre::ControllerValue<Ogre::Real>, public ValueInterpolator
{
private:
Ogre::Entity *mEntity;
std::vector<Nif::NiMorphData::MorphData> mMorphs;
size_t mControllerIndex;
std::vector<Ogre::Vector3> mVertices;
public:
Value(Ogre::Entity *ent, const Nif::NiMorphData *data, size_t controllerIndex)
: mEntity(ent)
, mMorphs(data->mMorphs)
, mControllerIndex(controllerIndex)
{
}
virtual Ogre::Real getValue() const
{
// Should not be called
return 0.0f;
}
virtual void setValue(Ogre::Real time)
{
if (mMorphs.size() <= 1)
return;
int i = 1;
for (std::vector<Nif::NiMorphData::MorphData>::iterator it = mMorphs.begin()+1; it != mMorphs.end(); ++it,++i)
{
float val = 0;
if (!it->mData.mKeys.empty())
val = interpKey(it->mData.mKeys, time);
val = std::max(0.f, std::min(1.f, val));
Ogre::String animationID = Ogre::StringConverter::toString(mControllerIndex)
+ "_" + Ogre::StringConverter::toString(i);
Ogre::AnimationState* state = mEntity->getAnimationState(animationID);
state->setEnabled(val > 0);
state->setWeight(val);
}
}
};
typedef DefaultFunction Function;
};
/** Object creator for NIFs. This is the main class responsible for creating
* "live" Ogre objects (entities, particle systems, controllers, etc) from
* their NIF equivalents.
*/
class NIFObjectLoader
{
static void warn(const std::string &msg)
{
std::cerr << "NIFObjectLoader: Warn: " << msg << std::endl;
}
static void createEntity(const std::string &name, const std::string &group,
Ogre::SceneManager *sceneMgr, ObjectScenePtr scene,
const Nif::Node *node, int flags, int animflags)
{
const Nif::NiTriShape *shape = static_cast<const Nif::NiTriShape*>(node);
std::string fullname = name+"@index="+Ogre::StringConverter::toString(shape->recIndex);
if(shape->name.length() > 0)
fullname += "@shape="+shape->name;
Misc::StringUtils::toLower(fullname);
Ogre::MeshManager &meshMgr = Ogre::MeshManager::getSingleton();
if(meshMgr.getByName(fullname).isNull())
NIFMeshLoader::createMesh(name, fullname, group, shape->recIndex);
Ogre::Entity *entity = sceneMgr->createEntity(fullname);
#if OGRE_VERSION >= (1 << 16 | 10 << 8 | 0)
// Enable skeleton-based bounding boxes. With the static bounding box,
// the animation may cause parts to go outside the box and cause culling problems.
if (entity->hasSkeleton())
entity->setUpdateBoundingBoxFromSkeleton(true);
#endif
entity->setVisible(!(flags&Nif::NiNode::Flag_Hidden));
scene->mEntities.push_back(entity);
if(scene->mSkelBase)
{
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, shape->recIndex);
Ogre::Bone *trgtbone = scene->mSkelBase->getSkeleton()->getBone(trgtid);
trgtbone->getUserObjectBindings().setUserAny(Ogre::Any(static_cast<Ogre::MovableObject*>(entity)));
if(entity->hasSkeleton())
entity->shareSkeletonInstanceWith(scene->mSkelBase);
else
scene->mSkelBase->attachObjectToBone(trgtbone->getName(), entity);
}
Nif::ControllerPtr ctrl = node->controller;
while(!ctrl.empty())
{
if (ctrl->flags & Nif::NiNode::ControllerFlag_Active)
{
if(ctrl->recType == Nif::RC_NiUVController)
{
const Nif::NiUVController *uv = static_cast<const Nif::NiUVController*>(ctrl.getPtr());
Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ?
Ogre::ControllerManager::getSingleton().getFrameTimeSource() :
Ogre::ControllerValueRealPtr());
Ogre::ControllerValueRealPtr dstval(OGRE_NEW UVController::Value(entity, uv->data.getPtr(), &scene->mMaterialControllerMgr));
UVController::Function* function = OGRE_NEW UVController::Function(uv, (animflags&Nif::NiNode::AnimFlag_AutoPlay));
scene->mMaxControllerLength = std::max(function->mStopTime, scene->mMaxControllerLength);
Ogre::ControllerFunctionRealPtr func(function);
scene->mControllers.push_back(Ogre::Controller<Ogre::Real>(srcval, dstval, func));
}
else if(ctrl->recType == Nif::RC_NiGeomMorpherController)
{
const Nif::NiGeomMorpherController *geom = static_cast<const Nif::NiGeomMorpherController*>(ctrl.getPtr());
Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ?
Ogre::ControllerManager::getSingleton().getFrameTimeSource() :
Ogre::ControllerValueRealPtr());
Ogre::ControllerValueRealPtr dstval(OGRE_NEW GeomMorpherController::Value(
entity, geom->data.getPtr(), geom->recIndex));
GeomMorpherController::Function* function = OGRE_NEW GeomMorpherController::Function(geom, (animflags&Nif::NiNode::AnimFlag_AutoPlay));
scene->mMaxControllerLength = std::max(function->mStopTime, scene->mMaxControllerLength);
Ogre::ControllerFunctionRealPtr func(function);
scene->mControllers.push_back(Ogre::Controller<Ogre::Real>(srcval, dstval, func));
}
}
ctrl = ctrl->next;
}
createMaterialControllers(shape, entity, animflags, scene);
}
static void createMaterialControllers (const Nif::Node* node, Ogre::MovableObject* movable, int animflags, ObjectScenePtr scene)
{
const Nif::NiTexturingProperty *texprop = NULL;
const Nif::NiMaterialProperty *matprop = NULL;
const Nif::NiAlphaProperty *alphaprop = NULL;
const Nif::NiVertexColorProperty *vertprop = NULL;
const Nif::NiZBufferProperty *zprop = NULL;
const Nif::NiSpecularProperty *specprop = NULL;
const Nif::NiWireframeProperty *wireprop = NULL;
const Nif::NiStencilProperty *stencilprop = NULL;
node->getProperties(texprop, matprop, alphaprop, vertprop, zprop, specprop, wireprop, stencilprop);
Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ?
Ogre::ControllerManager::getSingleton().getFrameTimeSource() :
Ogre::ControllerValueRealPtr());
if(matprop)
{
Nif::ControllerPtr ctrls = matprop->controller;
while(!ctrls.empty())
{
if (ctrls->recType == Nif::RC_NiAlphaController)
{
const Nif::NiAlphaController *alphaCtrl = dynamic_cast<const Nif::NiAlphaController*>(ctrls.getPtr());
Ogre::ControllerValueRealPtr dstval(OGRE_NEW AlphaController::Value(movable, alphaCtrl->data.getPtr(), &scene->mMaterialControllerMgr));
AlphaController::Function* function = OGRE_NEW AlphaController::Function(alphaCtrl, (animflags&Nif::NiNode::AnimFlag_AutoPlay));
scene->mMaxControllerLength = std::max(function->mStopTime, scene->mMaxControllerLength);
Ogre::ControllerFunctionRealPtr func(function);
scene->mControllers.push_back(Ogre::Controller<Ogre::Real>(srcval, dstval, func));
}
else if (ctrls->recType == Nif::RC_NiMaterialColorController)
{
const Nif::NiMaterialColorController *matCtrl = dynamic_cast<const Nif::NiMaterialColorController*>(ctrls.getPtr());
Ogre::ControllerValueRealPtr dstval(OGRE_NEW MaterialColorController::Value(movable, matCtrl->data.getPtr(), &scene->mMaterialControllerMgr));
MaterialColorController::Function* function = OGRE_NEW MaterialColorController::Function(matCtrl, (animflags&Nif::NiNode::AnimFlag_AutoPlay));
scene->mMaxControllerLength = std::max(function->mStopTime, scene->mMaxControllerLength);
Ogre::ControllerFunctionRealPtr func(function);
scene->mControllers.push_back(Ogre::Controller<Ogre::Real>(srcval, dstval, func));
}
ctrls = ctrls->next;
}
}
if (texprop)
{
Nif::ControllerPtr ctrls = texprop->controller;
while(!ctrls.empty())
{
if (ctrls->recType == Nif::RC_NiFlipController)
{
const Nif::NiFlipController *flipCtrl = dynamic_cast<const Nif::NiFlipController*>(ctrls.getPtr());
Ogre::ControllerValueRealPtr dstval(OGRE_NEW FlipController::Value(
movable, flipCtrl, &scene->mMaterialControllerMgr));
FlipController::Function* function = OGRE_NEW FlipController::Function(flipCtrl, (animflags&Nif::NiNode::AnimFlag_AutoPlay));
scene->mMaxControllerLength = std::max(function->mStopTime, scene->mMaxControllerLength);
Ogre::ControllerFunctionRealPtr func(function);
scene->mControllers.push_back(Ogre::Controller<Ogre::Real>(srcval, dstval, func));
}
ctrls = ctrls->next;
}
}
}
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,
partctrl->velocity + partctrl->velocityRandom*0.5f);
if (partctrl->emitFlags & Nif::NiParticleSystemController::NoAutoAdjust)
emitter->setEmissionRate(partctrl->emitRate);
else
emitter->setEmissionRate(partctrl->numParticles / (partctrl->lifetime + partctrl->lifetimeRandom/2));
emitter->setTimeToLive(std::max(0.f, partctrl->lifetime),
std::max(0.f, partctrl->lifetime + partctrl->lifetimeRandom));
emitter->setParameter("width", Ogre::StringConverter::toString(partctrl->offsetRandom.x));
emitter->setParameter("height", Ogre::StringConverter::toString(partctrl->offsetRandom.y));
emitter->setParameter("depth", Ogre::StringConverter::toString(partctrl->offsetRandom.z));
emitter->setParameter("vertical_direction", Ogre::StringConverter::toString(Ogre::Radian(partctrl->verticalDir).valueDegrees()));
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()));
Nif::ExtraPtr e = partctrl->extra;
while(!e.empty())
{
if(e->recType == Nif::RC_NiParticleGrowFade)
{
const Nif::NiParticleGrowFade *gf = static_cast<const Nif::NiParticleGrowFade*>(e.getPtr());
Ogre::ParticleAffector *affector = partsys->addAffector("GrowFade");
affector->setParameter("grow_time", Ogre::StringConverter::toString(gf->growTime));
affector->setParameter("fade_time", Ogre::StringConverter::toString(gf->fadeTime));
}
else if(e->recType == Nif::RC_NiGravity)
{
const Nif::NiGravity *gr = static_cast<const Nif::NiGravity*>(e.getPtr());
Ogre::ParticleAffector *affector = partsys->addAffector("Gravity");
affector->setParameter("force", Ogre::StringConverter::toString(gr->mForce));
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)
{
const Nif::NiParticleColorModifier *cl = static_cast<const Nif::NiParticleColorModifier*>(e.getPtr());
const Nif::NiColorData *clrdata = cl->data.getPtr();
Ogre::ParticleAffector *affector = partsys->addAffector("ColourInterpolator");
size_t num_colors = std::min<size_t>(6, clrdata->mKeyMap.mKeys.size());
unsigned int i=0;
for (Nif::Vector4KeyMap::MapType::const_iterator it = clrdata->mKeyMap.mKeys.begin(); it != clrdata->mKeyMap.mKeys.end() && i < num_colors; ++it,++i)
{
Ogre::ColourValue color;
color.r = it->second.mValue[0];
color.g = it->second.mValue[1];
color.b = it->second.mValue[2];
color.a = it->second.mValue[3];
affector->setParameter("colour"+Ogre::StringConverter::toString(i),
Ogre::StringConverter::toString(color));
affector->setParameter("time"+Ogre::StringConverter::toString(i),
Ogre::StringConverter::toString(it->first));
}
}
else if(e->recType == Nif::RC_NiParticleRotation)
{
// TODO: Implement (Ogre::RotationAffector?)
}
else
warn("Unhandled particle modifier "+e->recName);
e = e->extra;
}
}
static void createParticleSystem(const std::string &name, const std::string &group,
Ogre::SceneNode *sceneNode, ObjectScenePtr scene,
const Nif::Node *partnode, int flags, int partflags, int animflags)
{
const Nif::NiAutoNormalParticlesData *particledata = NULL;
if(partnode->recType == Nif::RC_NiAutoNormalParticles)
particledata = static_cast<const Nif::NiAutoNormalParticles*>(partnode)->data.getPtr();
else if(partnode->recType == Nif::RC_NiRotatingParticles)
particledata = static_cast<const Nif::NiRotatingParticles*>(partnode)->data.getPtr();
std::string fullname = name+"@index="+Ogre::StringConverter::toString(partnode->recIndex);
if(partnode->name.length() > 0)
fullname += "@type="+partnode->name;
Misc::StringUtils::toLower(fullname);
Ogre::ParticleSystem *partsys = sceneNode->getCreator()->createParticleSystem();
const Nif::NiTexturingProperty *texprop = NULL;
const Nif::NiMaterialProperty *matprop = NULL;
const Nif::NiAlphaProperty *alphaprop = NULL;
const Nif::NiVertexColorProperty *vertprop = NULL;
const Nif::NiZBufferProperty *zprop = NULL;
const Nif::NiSpecularProperty *specprop = NULL;
const Nif::NiWireframeProperty *wireprop = NULL;
const Nif::NiStencilProperty *stencilprop = NULL;
bool needTangents = false;
partnode->getProperties(texprop, matprop, alphaprop, vertprop, zprop, specprop, wireprop, stencilprop);
partsys->setMaterialName(NIFMaterialLoader::getMaterial(particledata, fullname, group,
texprop, matprop, alphaprop,
vertprop, zprop, specprop,
wireprop, stencilprop, needTangents,
// MW doesn't light particles, but the MaterialProperty
// used still has lighting, so that must be ignored.
true));
partsys->setCullIndividually(false);
partsys->setParticleQuota(particledata->numParticles);
partsys->setKeepParticlesInLocalSpace(partflags & (Nif::NiNode::ParticleFlag_LocalSpace));
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, partnode->recIndex);
Ogre::Bone *trgtbone = scene->mSkelBase->getSkeleton()->getBone(trgtid);
scene->mSkelBase->attachObjectToBone(trgtbone->getName(), partsys);
Nif::ControllerPtr ctrl = partnode->controller;
while(!ctrl.empty())
{
if((ctrl->recType == Nif::RC_NiParticleSystemController || ctrl->recType == Nif::RC_NiBSPArrayController)
&& ctrl->flags & Nif::NiNode::ControllerFlag_Active)
{
const Nif::NiParticleSystemController *partctrl = static_cast<const Nif::NiParticleSystemController*>(ctrl.getPtr());
partsys->setDefaultDimensions(partctrl->size*2, partctrl->size*2);
if(!partctrl->emitter.empty())
{
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, partctrl->emitter->recIndex);
Ogre::Bone *trgtbone = scene->mSkelBase->getSkeleton()->getBone(trgtid);
// Set the emitter bone(s) as user data on the particle system
// so the emitters/affectors can access it easily.
std::vector<Ogre::Bone*> bones;
if (partctrl->recType == Nif::RC_NiBSPArrayController)
{
std::vector<const Nif::NiNode*> nodes;
getAllNiNodes(partctrl->emitter.getPtr(), nodes);
if (nodes.empty())
throw std::runtime_error("Emitter for NiBSPArrayController must be a NiNode");
for (unsigned int i=0; i<nodes.size(); ++i)
{
bones.push_back(scene->mSkelBase->getSkeleton()->getBone(
NIFSkeletonLoader::lookupOgreBoneHandle(name, nodes[i]->recIndex)));
}
}
else
{
bones.push_back(trgtbone);
}
NiNodeHolder holder;
holder.mBones = bones;
partsys->getUserObjectBindings().setUserAny(Ogre::Any(holder));
createParticleEmitterAffectors(partsys, partctrl, trgtbone, scene->mSkelBase->getName());
}
Ogre::ControllerValueRealPtr srcval((partflags&Nif::NiNode::ParticleFlag_AutoPlay) ?
Ogre::ControllerManager::getSingleton().getFrameTimeSource() :
Ogre::ControllerValueRealPtr());
Ogre::ControllerValueRealPtr dstval(OGRE_NEW ParticleSystemController::Value(partsys, partctrl));
ParticleSystemController::Function* function =
OGRE_NEW ParticleSystemController::Function(partctrl, (partflags&Nif::NiNode::ParticleFlag_AutoPlay));
scene->mMaxControllerLength = std::max(function->mStopTime, scene->mMaxControllerLength);
Ogre::ControllerFunctionRealPtr func(function);
scene->mControllers.push_back(Ogre::Controller<Ogre::Real>(srcval, dstval, func));
// Emitting state will be overwritten on frame update by the ParticleSystemController,
// but set up an initial value anyway so the user can fast-forward particle systems
// immediately after creation if desired.
partsys->setEmitting(partflags&Nif::NiNode::ParticleFlag_AutoPlay);
}
ctrl = ctrl->next;
}
partsys->setVisible(!(flags&Nif::NiNode::Flag_Hidden));
scene->mParticles.push_back(partsys);
createMaterialControllers(partnode, partsys, animflags, scene);
}
static void createNodeControllers(const Nif::NIFFilePtr& nif, const std::string &name, Nif::ControllerPtr ctrl, ObjectScenePtr scene, int animflags)
{
do {
if (ctrl->flags & Nif::NiNode::ControllerFlag_Active)
{
if(ctrl->recType == Nif::RC_NiVisController)
{
const Nif::NiVisController *vis = static_cast<const Nif::NiVisController*>(ctrl.getPtr());
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, ctrl->target->recIndex);
Ogre::Bone *trgtbone = scene->mSkelBase->getSkeleton()->getBone(trgtid);
Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ?
Ogre::ControllerManager::getSingleton().getFrameTimeSource() :
Ogre::ControllerValueRealPtr());
Ogre::ControllerValueRealPtr dstval(OGRE_NEW VisController::Value(trgtbone, vis->data.getPtr()));
VisController::Function* function = OGRE_NEW VisController::Function(vis, (animflags&Nif::NiNode::AnimFlag_AutoPlay));
scene->mMaxControllerLength = std::max(function->mStopTime, scene->mMaxControllerLength);
Ogre::ControllerFunctionRealPtr func(function);
scene->mControllers.push_back(Ogre::Controller<Ogre::Real>(srcval, dstval, func));
}
else if(ctrl->recType == Nif::RC_NiKeyframeController)
{
const Nif::NiKeyframeController *key = static_cast<const Nif::NiKeyframeController*>(ctrl.getPtr());
if(!key->data.empty())
{
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, ctrl->target->recIndex);
Ogre::Bone *trgtbone = scene->mSkelBase->getSkeleton()->getBone(trgtid);
// The keyframe controller will control this bone manually
trgtbone->setManuallyControlled(true);
Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ?
Ogre::ControllerManager::getSingleton().getFrameTimeSource() :
Ogre::ControllerValueRealPtr());
Ogre::ControllerValueRealPtr dstval(OGRE_NEW KeyframeController::Value(trgtbone, nif, key->data.getPtr()));
KeyframeController::Function* function = OGRE_NEW KeyframeController::Function(key, (animflags&Nif::NiNode::AnimFlag_AutoPlay));
scene->mMaxControllerLength = std::max(function->mStopTime, scene->mMaxControllerLength);
Ogre::ControllerFunctionRealPtr func(function);
scene->mControllers.push_back(Ogre::Controller<Ogre::Real>(srcval, dstval, func));
}
}
}
ctrl = ctrl->next;
} while(!ctrl.empty());
}
static void extractTextKeys(const Nif::NiTextKeyExtraData *tk, TextKeyMap &textkeys)
{
for(size_t i = 0;i < tk->list.size();i++)
{
const std::string &str = tk->list[i].text;
std::string::size_type pos = 0;
while(pos < str.length())
{
if(::isspace(str[pos]))
{
pos++;
continue;
}
std::string::size_type nextpos = std::min(str.find('\r', pos), str.find('\n', pos));
if(nextpos != std::string::npos)
{
do {
nextpos--;
} while(nextpos > pos && ::isspace(str[nextpos]));
nextpos++;
}
else if(::isspace(*str.rbegin()))
{
std::string::const_iterator last = str.end();
do {
--last;
} while(last != str.begin() && ::isspace(*last));
nextpos = std::distance(str.begin(), ++last);
}
std::string result = str.substr(pos, nextpos-pos);
textkeys.insert(std::make_pair(tk->list[i].time, Misc::StringUtils::toLower(result)));
pos = nextpos;
}
}
}
static void createObjects(const Nif::NIFFilePtr& nif, const std::string &name, const std::string &group,
Ogre::SceneNode *sceneNode, const Nif::Node *node,
ObjectScenePtr scene, int flags, int animflags, int partflags, bool isRootCollisionNode=false)
{
// Do not create objects for the collision shape (includes all children)
if(node->recType == Nif::RC_RootCollisionNode)
isRootCollisionNode = true;
// Marker objects: just skip the entire node branch
/// \todo don't do this in the editor
if (node->name.find("marker") != std::string::npos)
return;
if(node->recType == Nif::RC_NiBSAnimationNode)
animflags |= node->flags;
else if(node->recType == Nif::RC_NiBSParticleNode)
partflags |= node->flags;
else
flags |= node->flags;
if (node->recType == Nif::RC_NiBillboardNode)
{
// TODO: figure out what the flags mean.
// NifSkope has names for them, but doesn't implement them.
// Change mBillboardNodes to map <Bone, billboard type>
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, node->recIndex);
Ogre::Bone* bone = scene->mSkelBase->getSkeleton()->getBone(trgtid);
bone->setManuallyControlled(true);
scene->mBillboardNodes.push_back(bone);
}
Nif::ExtraPtr e = node->extra;
while(!e.empty())
{
if(e->recType == Nif::RC_NiTextKeyExtraData)
{
const Nif::NiTextKeyExtraData *tk = static_cast<const Nif::NiTextKeyExtraData*>(e.getPtr());
extractTextKeys(tk, scene->mTextKeys);
}
else if(e->recType == Nif::RC_NiStringExtraData)
{
const Nif::NiStringExtraData *sd = static_cast<const Nif::NiStringExtraData*>(e.getPtr());
// String markers may contain important information
// affecting the entire subtree of this obj
if(sd->string == "MRK")
{
// Marker objects. These meshes are only visible in the
// editor.
flags |= 0x80000000;
}
}
e = e->extra;
}
if(!node->controller.empty())
createNodeControllers(nif, name, node->controller, scene, animflags);
if (!isRootCollisionNode)
{
if(node->recType == Nif::RC_NiCamera)
{
/* Ignored */
}
if(node->recType == Nif::RC_NiTriShape && !(flags&0x80000000))
{
createEntity(name, group, sceneNode->getCreator(), scene, node, flags, animflags);
}
if((node->recType == Nif::RC_NiAutoNormalParticles ||
node->recType == Nif::RC_NiRotatingParticles) && !(flags&0x40000000))
{
createParticleSystem(name, group, sceneNode, scene, node, flags, partflags, animflags);
}
}
const Nif::NiNode *ninode = dynamic_cast<const Nif::NiNode*>(node);
if(ninode)
{
const Nif::NodeList &children = ninode->children;
for(size_t i = 0;i < children.length();i++)
{
if(!children[i].empty())
createObjects(nif, name, group, sceneNode, children[i].getPtr(), scene, flags, animflags, partflags, isRootCollisionNode);
}
}
}
static void createSkelBase(const std::string &name, const std::string &group,
Ogre::SceneManager *sceneMgr, const Nif::Node *node,
ObjectScenePtr scene)
{
/* This creates an empty mesh to which a skeleton gets attached. This
* is to ensure we have an entity with a skeleton instance, even if all
* other entities are attached to bones and not skinned. */
Ogre::MeshManager &meshMgr = Ogre::MeshManager::getSingleton();
if(meshMgr.getByName(name).isNull())
NIFMeshLoader::createMesh(name, name, group, ~(size_t)0);
scene->mSkelBase = sceneMgr->createEntity(name);
scene->mEntities.push_back(scene->mSkelBase);
}
public:
static void load(Ogre::SceneNode *sceneNode, ObjectScenePtr scene, const std::string &name, const std::string &group, int flags=0)
{
Nif::NIFFilePtr nif = Nif::Cache::getInstance().load(name);
if(nif->numRoots() < 1)
{
nif->warn("Found no root nodes in "+name+".");
return;
}
const Nif::Record *r = nif->getRoot(0);
assert(r != NULL);
const Nif::Node *node = dynamic_cast<const Nif::Node*>(r);
if(node == NULL)
{
nif->warn("First root in "+name+" was not a node, but a "+
r->recName+".");
return;
}
if(Ogre::SkeletonManager::getSingleton().resourceExists(name) ||
!NIFSkeletonLoader::createSkeleton(name, group, node).isNull())
{
// Create a base skeleton entity if this NIF needs one
createSkelBase(name, group, sceneNode->getCreator(), node, scene);
}
createObjects(nif, name, group, sceneNode, node, scene, flags, 0, 0);
}
static void loadKf(Ogre::Skeleton *skel, const std::string &name,
TextKeyMap &textKeys, std::vector<Ogre::Controller<Ogre::Real> > &ctrls)
{
Nif::NIFFilePtr nif = Nif::Cache::getInstance().load(name);
if(nif->numRoots() < 1)
{
nif->warn("Found no root nodes in "+name+".");
return;
}
const Nif::Record *r = nif->getRoot(0);
assert(r != NULL);
if(r->recType != Nif::RC_NiSequenceStreamHelper)
{
nif->warn("First root was not a NiSequenceStreamHelper, but a "+
r->recName+".");
return;
}
const Nif::NiSequenceStreamHelper *seq = static_cast<const Nif::NiSequenceStreamHelper*>(r);
Nif::ExtraPtr extra = seq->extra;
if(extra.empty() || extra->recType != Nif::RC_NiTextKeyExtraData)
{
nif->warn("First extra data was not a NiTextKeyExtraData, but a "+
(extra.empty() ? std::string("nil") : extra->recName)+".");
return;
}
extractTextKeys(static_cast<const Nif::NiTextKeyExtraData*>(extra.getPtr()), textKeys);
extra = extra->extra;
Nif::ControllerPtr ctrl = seq->controller;
for(;!extra.empty() && !ctrl.empty();(extra=extra->extra),(ctrl=ctrl->next))
{
if(extra->recType != Nif::RC_NiStringExtraData || ctrl->recType != Nif::RC_NiKeyframeController)
{
nif->warn("Unexpected extra data "+extra->recName+" with controller "+ctrl->recName);
continue;
}
if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active))
continue;
const Nif::NiStringExtraData *strdata = static_cast<const Nif::NiStringExtraData*>(extra.getPtr());
const Nif::NiKeyframeController *key = static_cast<const Nif::NiKeyframeController*>(ctrl.getPtr());
if(key->data.empty())
continue;
if(!skel->hasBone(strdata->string))
continue;
Ogre::Bone *trgtbone = skel->getBone(strdata->string);
Ogre::ControllerValueRealPtr srcval;
Ogre::ControllerValueRealPtr dstval(OGRE_NEW KeyframeController::Value(trgtbone, nif, key->data.getPtr()));
Ogre::ControllerFunctionRealPtr func(OGRE_NEW KeyframeController::Function(key, false));
ctrls.push_back(Ogre::Controller<Ogre::Real>(srcval, dstval, func));
}
}
};
ObjectScenePtr Loader::createObjects(Ogre::SceneNode *parentNode, std::string name, const std::string &group)
{
ObjectScenePtr scene = ObjectScenePtr (new ObjectScene(parentNode->getCreator()));
Misc::StringUtils::toLower(name);
NIFObjectLoader::load(parentNode, scene, name, group);
for(size_t i = 0;i < scene->mEntities.size();i++)
{
Ogre::Entity *entity = scene->mEntities[i];
if(!entity->isAttached())
parentNode->attachObject(entity);
}
return scene;
}
ObjectScenePtr Loader::createObjects(Ogre::Entity *parent, const std::string &bonename,
Ogre::SceneNode *parentNode,
std::string name, const std::string &group)
{
ObjectScenePtr scene = ObjectScenePtr (new ObjectScene(parentNode->getCreator()));
Misc::StringUtils::toLower(name);
NIFObjectLoader::load(parentNode, scene, name, group);
bool isskinned = false;
for(size_t i = 0;i < scene->mEntities.size();i++)
{
Ogre::Entity *ent = scene->mEntities[i];
if(scene->mSkelBase != ent && ent->hasSkeleton())
{
isskinned = true;
break;
}
}
Ogre::Vector3 scale(1.0f);
if(bonename.find("Left") != std::string::npos)
scale.x *= -1.0f;
if(isskinned)
{
// Apparently both are allowed. Sigh.
// This could also mean that filters are supposed to work on the actual node
// hierarchy, rather than just trishapes, and the 'tri ' should be omitted?
std::string filter = "@shape=tri "+bonename;
std::string filter2 = "@shape="+bonename;
Misc::StringUtils::toLower(filter);
Misc::StringUtils::toLower(filter2);
for(size_t i = 0;i < scene->mEntities.size();i++)
{
Ogre::Entity *entity = scene->mEntities[i];
if(entity->hasSkeleton())
{
if(entity == scene->mSkelBase ||
entity->getMesh()->getName().find(filter) != std::string::npos
|| entity->getMesh()->getName().find(filter2) != std::string::npos)
parentNode->attachObject(entity);
}
else
{
if(entity->getMesh()->getName().find(filter) == std::string::npos
|| entity->getMesh()->getName().find(filter2) == std::string::npos)
entity->detachFromParent();
}
}
}
else
{
for(size_t i = 0;i < scene->mEntities.size();i++)
{
Ogre::Entity *entity = scene->mEntities[i];
if(!entity->isAttached())
{
Ogre::TagPoint *tag = parent->attachObjectToBone(bonename, entity);
tag->setScale(scale);
}
}
}
return scene;
}
ObjectScenePtr Loader::createObjectBase(Ogre::SceneNode *parentNode, std::string name, const std::string &group)
{
ObjectScenePtr scene = ObjectScenePtr (new ObjectScene(parentNode->getCreator()));
Misc::StringUtils::toLower(name);
NIFObjectLoader::load(parentNode, scene, name, group, 0xC0000000);
if(scene->mSkelBase)
parentNode->attachObject(scene->mSkelBase);
return scene;
}
void Loader::createKfControllers(Ogre::Entity *skelBase,
const std::string &name,
TextKeyMap &textKeys,
std::vector<Ogre::Controller<Ogre::Real> > &ctrls)
{
NIFObjectLoader::loadKf(skelBase->getSkeleton(), name, textKeys, ctrls);
}
} // namespace NifOgre