mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-11-04 13:26:44 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			998 lines
		
	
	
	
		
			38 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			998 lines
		
	
	
	
		
			38 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 <OgreRoot.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 <components/nif/node.hpp>
 | 
						|
#include <components/misc/stringops.hpp>
 | 
						|
 | 
						|
#include "skeleton.hpp"
 | 
						|
#include "material.hpp"
 | 
						|
#include "mesh.hpp"
 | 
						|
 | 
						|
namespace NifOgre
 | 
						|
{
 | 
						|
 | 
						|
// FIXME: Should not be here.
 | 
						|
class DefaultFunction : public Ogre::ControllerFunction<Ogre::Real>
 | 
						|
{
 | 
						|
private:
 | 
						|
    float mFrequency;
 | 
						|
    float mPhase;
 | 
						|
    float mStartTime;
 | 
						|
    float mStopTime;
 | 
						|
 | 
						|
public:
 | 
						|
    DefaultFunction(const Nif::Controller *ctrl, bool deltaInput)
 | 
						|
        : Ogre::ControllerFunction<Ogre::Real>(deltaInput)
 | 
						|
        , mFrequency(ctrl->frequency)
 | 
						|
        , mPhase(ctrl->phase)
 | 
						|
        , mStartTime(ctrl->timeStart)
 | 
						|
        , mStopTime(ctrl->timeStop)
 | 
						|
    {
 | 
						|
        if(mDeltaInput)
 | 
						|
            mDeltaCount = mPhase;
 | 
						|
    }
 | 
						|
 | 
						|
    virtual Ogre::Real calculate(Ogre::Real value)
 | 
						|
    {
 | 
						|
        if(mDeltaInput)
 | 
						|
        {
 | 
						|
            mDeltaCount += value*mFrequency;
 | 
						|
            if(mDeltaCount < mStartTime)
 | 
						|
                mDeltaCount = mStopTime - std::fmod(mStartTime - mDeltaCount,
 | 
						|
                                                    mStopTime - mStartTime);
 | 
						|
            mDeltaCount = std::fmod(mDeltaCount - mStartTime,
 | 
						|
                                    mStopTime - mStartTime) + mStartTime;
 | 
						|
            return mDeltaCount;
 | 
						|
        }
 | 
						|
 | 
						|
        value = std::min(mStopTime, std::max(mStartTime, value+mPhase));
 | 
						|
        return value;
 | 
						|
    }
 | 
						|
};
 | 
						|
 | 
						|
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;
 | 
						|
        }
 | 
						|
 | 
						|
        // FIXME: We are not getting all objects here. Skinned meshes get
 | 
						|
        // attached to the object's root node, and won't be connected via a
 | 
						|
        // TagPoint.
 | 
						|
        static void setVisible(Ogre::Node *node, int vis)
 | 
						|
        {
 | 
						|
            Ogre::Node::ChildNodeIterator iter = node->getChildIterator();
 | 
						|
            while(iter.hasMoreElements())
 | 
						|
            {
 | 
						|
                node = iter.getNext();
 | 
						|
                setVisible(node, vis);
 | 
						|
 | 
						|
                Ogre::TagPoint *tag = dynamic_cast<Ogre::TagPoint*>(node);
 | 
						|
                if(tag != NULL)
 | 
						|
                {
 | 
						|
                    Ogre::MovableObject *obj = tag->getChildObject();
 | 
						|
                    if(obj != NULL)
 | 
						|
                        obj->setVisible(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>
 | 
						|
    {
 | 
						|
    private:
 | 
						|
        Nif::QuaternionKeyList mRotations;
 | 
						|
        Nif::Vector3KeyList mTranslations;
 | 
						|
        Nif::FloatKeyList mScales;
 | 
						|
 | 
						|
        static float interpKey(const Nif::FloatKeyList::VecType &keys, float time)
 | 
						|
        {
 | 
						|
            if(time <= keys.front().mTime)
 | 
						|
                return keys.front().mValue;
 | 
						|
 | 
						|
            Nif::FloatKeyList::VecType::const_iterator iter(keys.begin()+1);
 | 
						|
            for(;iter != keys.end();iter++)
 | 
						|
            {
 | 
						|
                if(iter->mTime < time)
 | 
						|
                    continue;
 | 
						|
 | 
						|
                Nif::FloatKeyList::VecType::const_iterator last(iter-1);
 | 
						|
                float a = (time-last->mTime) / (iter->mTime-last->mTime);
 | 
						|
                return last->mValue + ((iter->mValue - last->mValue)*a);
 | 
						|
            }
 | 
						|
            return keys.back().mValue;
 | 
						|
        }
 | 
						|
 | 
						|
        static Ogre::Vector3 interpKey(const Nif::Vector3KeyList::VecType &keys, float time)
 | 
						|
        {
 | 
						|
            if(time <= keys.front().mTime)
 | 
						|
                return keys.front().mValue;
 | 
						|
 | 
						|
            Nif::Vector3KeyList::VecType::const_iterator iter(keys.begin()+1);
 | 
						|
            for(;iter != keys.end();iter++)
 | 
						|
            {
 | 
						|
                if(iter->mTime < time)
 | 
						|
                    continue;
 | 
						|
 | 
						|
                Nif::Vector3KeyList::VecType::const_iterator last(iter-1);
 | 
						|
                float a = (time-last->mTime) / (iter->mTime-last->mTime);
 | 
						|
                return last->mValue + ((iter->mValue - last->mValue)*a);
 | 
						|
            }
 | 
						|
            return keys.back().mValue;
 | 
						|
        }
 | 
						|
 | 
						|
        static Ogre::Quaternion interpKey(const Nif::QuaternionKeyList::VecType &keys, float time)
 | 
						|
        {
 | 
						|
            if(time <= keys.front().mTime)
 | 
						|
                return keys.front().mValue;
 | 
						|
 | 
						|
            Nif::QuaternionKeyList::VecType::const_iterator iter(keys.begin()+1);
 | 
						|
            for(;iter != keys.end();iter++)
 | 
						|
            {
 | 
						|
                if(iter->mTime < time)
 | 
						|
                    continue;
 | 
						|
 | 
						|
                Nif::QuaternionKeyList::VecType::const_iterator last(iter-1);
 | 
						|
                float a = (time-last->mTime) / (iter->mTime-last->mTime);
 | 
						|
                return Ogre::Quaternion::nlerp(a, last->mValue, iter->mValue);
 | 
						|
            }
 | 
						|
            return keys.back().mValue;
 | 
						|
        }
 | 
						|
 | 
						|
    public:
 | 
						|
        Value(Ogre::Node *target, const Nif::NiKeyframeData *data)
 | 
						|
          : NodeTargetValue<Ogre::Real>(target)
 | 
						|
          , mRotations(data->mRotations)
 | 
						|
          , mTranslations(data->mTranslations)
 | 
						|
          , mScales(data->mScales)
 | 
						|
        { }
 | 
						|
 | 
						|
        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>
 | 
						|
    {
 | 
						|
    private:
 | 
						|
        Ogre::MaterialPtr mMaterial;
 | 
						|
        Nif::FloatKeyList mUTrans;
 | 
						|
        Nif::FloatKeyList mVTrans;
 | 
						|
        Nif::FloatKeyList mUScale;
 | 
						|
        Nif::FloatKeyList mVScale;
 | 
						|
 | 
						|
        static float lookupValue(const Nif::FloatKeyList &keys, float time, float def)
 | 
						|
        {
 | 
						|
            if(keys.mKeys.size() == 0)
 | 
						|
                return def;
 | 
						|
 | 
						|
            if(time <= keys.mKeys.front().mTime)
 | 
						|
                return keys.mKeys.front().mValue;
 | 
						|
 | 
						|
            Nif::FloatKeyList::VecType::const_iterator iter(keys.mKeys.begin()+1);
 | 
						|
            for(;iter != keys.mKeys.end();iter++)
 | 
						|
            {
 | 
						|
                if(iter->mTime < time)
 | 
						|
                    continue;
 | 
						|
 | 
						|
                Nif::FloatKeyList::VecType::const_iterator last(iter-1);
 | 
						|
                float a = (time-last->mTime) / (iter->mTime-last->mTime);
 | 
						|
                return last->mValue + ((iter->mValue - last->mValue)*a);
 | 
						|
            }
 | 
						|
            return keys.mKeys.back().mValue;
 | 
						|
        }
 | 
						|
 | 
						|
    public:
 | 
						|
        Value(const Ogre::MaterialPtr &material, const Nif::NiUVData *data)
 | 
						|
          : mMaterial(material)
 | 
						|
          , mUTrans(data->mKeyList[0])
 | 
						|
          , mVTrans(data->mKeyList[1])
 | 
						|
          , mUScale(data->mKeyList[2])
 | 
						|
          , mVScale(data->mKeyList[3])
 | 
						|
        { }
 | 
						|
 | 
						|
        virtual Ogre::Real getValue() const
 | 
						|
        {
 | 
						|
            // Should not be called
 | 
						|
            return 1.0f;
 | 
						|
        }
 | 
						|
 | 
						|
        virtual void setValue(Ogre::Real value)
 | 
						|
        {
 | 
						|
            float uTrans = lookupValue(mUTrans, value, 0.0f);
 | 
						|
            float vTrans = lookupValue(mVTrans, value, 0.0f);
 | 
						|
            float uScale = lookupValue(mUScale, value, 1.0f);
 | 
						|
            float vScale = lookupValue(mVScale, value, 1.0f);
 | 
						|
 | 
						|
            Ogre::Material::TechniqueIterator techs = mMaterial->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>
 | 
						|
    {
 | 
						|
    private:
 | 
						|
        Ogre::SubEntity *mSubEntity;
 | 
						|
        std::vector<Nif::NiMorphData::MorphData> mMorphs;
 | 
						|
 | 
						|
    public:
 | 
						|
        Value(Ogre::SubEntity *subent, const Nif::NiMorphData *data)
 | 
						|
          : mSubEntity(subent)
 | 
						|
          , mMorphs(data->mMorphs)
 | 
						|
        { }
 | 
						|
 | 
						|
        virtual Ogre::Real getValue() const
 | 
						|
        {
 | 
						|
            // Should not be called
 | 
						|
            return 0.0f;
 | 
						|
        }
 | 
						|
 | 
						|
        virtual void setValue(Ogre::Real value)
 | 
						|
        {
 | 
						|
            // TODO: Implement
 | 
						|
        }
 | 
						|
    };
 | 
						|
 | 
						|
    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 fail(const std::string &msg)
 | 
						|
    {
 | 
						|
        std::cerr << "NIFObjectLoader: Fail: "<< msg << std::endl;
 | 
						|
        abort();
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
    static void createEntity(const std::string &name, const std::string &group,
 | 
						|
                             Ogre::SceneManager *sceneMgr, ObjectList &objectlist,
 | 
						|
                             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);
 | 
						|
        entity->setVisible(!(flags&Nif::NiNode::Flag_Hidden));
 | 
						|
 | 
						|
        objectlist.mEntities.push_back(entity);
 | 
						|
        if(objectlist.mSkelBase)
 | 
						|
        {
 | 
						|
            if(entity->hasSkeleton())
 | 
						|
                entity->shareSkeletonInstanceWith(objectlist.mSkelBase);
 | 
						|
            else
 | 
						|
            {
 | 
						|
                int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, shape->recIndex);
 | 
						|
                Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid);
 | 
						|
                objectlist.mSkelBase->attachObjectToBone(trgtbone->getName(), entity);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        Nif::ControllerPtr ctrl = node->controller;
 | 
						|
        while(!ctrl.empty())
 | 
						|
        {
 | 
						|
            if(ctrl->recType == Nif::RC_NiUVController)
 | 
						|
            {
 | 
						|
                const Nif::NiUVController *uv = static_cast<const Nif::NiUVController*>(ctrl.getPtr());
 | 
						|
 | 
						|
                const Ogre::MaterialPtr &material = entity->getSubEntity(0)->getMaterial();
 | 
						|
                Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ?
 | 
						|
                                                    Ogre::ControllerManager::getSingleton().getFrameTimeSource() :
 | 
						|
                                                    Ogre::ControllerValueRealPtr());
 | 
						|
                Ogre::ControllerValueRealPtr dstval(OGRE_NEW UVController::Value(material, uv->data.getPtr()));
 | 
						|
                Ogre::ControllerFunctionRealPtr func(OGRE_NEW UVController::Function(uv, (animflags&Nif::NiNode::AnimFlag_AutoPlay)));
 | 
						|
 | 
						|
                objectlist.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::SubEntity *subent = entity->getSubEntity(0);
 | 
						|
                Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ?
 | 
						|
                                                    Ogre::ControllerManager::getSingleton().getFrameTimeSource() :
 | 
						|
                                                    Ogre::ControllerValueRealPtr());
 | 
						|
                Ogre::ControllerValueRealPtr dstval(OGRE_NEW GeomMorpherController::Value(subent, geom->data.getPtr()));
 | 
						|
                Ogre::ControllerFunctionRealPtr func(OGRE_NEW GeomMorpherController::Function(geom, (animflags&Nif::NiNode::AnimFlag_AutoPlay)));
 | 
						|
 | 
						|
                objectlist.mControllers.push_back(Ogre::Controller<Ogre::Real>(srcval, dstval, func));
 | 
						|
            }
 | 
						|
            ctrl = ctrl->next;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
    static void createParticleEmitterAffectors(Ogre::ParticleSystem *partsys, const Nif::NiParticleSystemController *partctrl)
 | 
						|
    {
 | 
						|
        Ogre::ParticleEmitter *emitter = partsys->addEmitter("Nif");
 | 
						|
        emitter->setParticleVelocity(partctrl->velocity - partctrl->velocityRandom*0.5f,
 | 
						|
                                     partctrl->velocity + partctrl->velocityRandom*0.5f);
 | 
						|
        emitter->setEmissionRate(partctrl->emitRate);
 | 
						|
        emitter->setTimeToLive(partctrl->lifetime - partctrl->lifetimeRandom*0.5f,
 | 
						|
                               partctrl->lifetime + partctrl->lifetimeRandom*0.5f);
 | 
						|
        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));
 | 
						|
            }
 | 
						|
            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->mKeyList.mKeys.size());
 | 
						|
                for(size_t i = 0;i < num_colors;i++)
 | 
						|
                {
 | 
						|
                    Ogre::ColourValue color;
 | 
						|
                    color.r = clrdata->mKeyList.mKeys[i].mValue[0];
 | 
						|
                    color.g = clrdata->mKeyList.mKeys[i].mValue[1];
 | 
						|
                    color.b = clrdata->mKeyList.mKeys[i].mValue[2];
 | 
						|
                    color.a = clrdata->mKeyList.mKeys[i].mValue[3];
 | 
						|
                    affector->setParameter("colour"+Ogre::StringConverter::toString(i),
 | 
						|
                                           Ogre::StringConverter::toString(color));
 | 
						|
                    affector->setParameter("time"+Ogre::StringConverter::toString(i),
 | 
						|
                                           Ogre::StringConverter::toString(clrdata->mKeyList.mKeys[i].mTime));
 | 
						|
                }
 | 
						|
            }
 | 
						|
            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::SceneManager *sceneMgr, ObjectList &objectlist,
 | 
						|
                                     const Nif::Node *partnode, int flags, int partflags)
 | 
						|
    {
 | 
						|
        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 = sceneMgr->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;
 | 
						|
        bool needTangents = false;
 | 
						|
 | 
						|
        partnode->getProperties(texprop, matprop, alphaprop, vertprop, zprop, specprop, wireprop);
 | 
						|
        partsys->setMaterialName(NIFMaterialLoader::getMaterial(particledata, fullname, group,
 | 
						|
                                                                texprop, matprop, alphaprop,
 | 
						|
                                                                vertprop, zprop, specprop,
 | 
						|
                                                                wireprop, needTangents));
 | 
						|
 | 
						|
        partsys->setDefaultDimensions(particledata->particleRadius*2.0f,
 | 
						|
                                        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(true);
 | 
						|
 | 
						|
        Nif::ControllerPtr ctrl = partnode->controller;
 | 
						|
        while(!ctrl.empty())
 | 
						|
        {
 | 
						|
            if(ctrl->recType == Nif::RC_NiParticleSystemController)
 | 
						|
            {
 | 
						|
                const Nif::NiParticleSystemController *partctrl = static_cast<const Nif::NiParticleSystemController*>(ctrl.getPtr());
 | 
						|
 | 
						|
                createParticleEmitterAffectors(partsys, partctrl);
 | 
						|
                if(!partctrl->emitter.empty() && !partsys->isAttached())
 | 
						|
                {
 | 
						|
                    int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, partctrl->emitter->recIndex);
 | 
						|
                    Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid);
 | 
						|
                    objectlist.mSkelBase->attachObjectToBone(trgtbone->getName(), partsys);
 | 
						|
                }
 | 
						|
 | 
						|
                Ogre::ControllerValueRealPtr srcval((partflags&Nif::NiNode::ParticleFlag_AutoPlay) ?
 | 
						|
                                                    Ogre::ControllerManager::getSingleton().getFrameTimeSource() :
 | 
						|
                                                    Ogre::ControllerValueRealPtr());
 | 
						|
                Ogre::ControllerValueRealPtr dstval(OGRE_NEW ParticleSystemController::Value(partsys, partctrl));
 | 
						|
                Ogre::ControllerFunctionRealPtr func(OGRE_NEW ParticleSystemController::Function(partctrl, (partflags&Nif::NiNode::ParticleFlag_AutoPlay)));
 | 
						|
 | 
						|
                objectlist.mControllers.push_back(Ogre::Controller<Ogre::Real>(srcval, dstval, func));
 | 
						|
            }
 | 
						|
            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);
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
    static void createNodeControllers(const std::string &name, Nif::ControllerPtr ctrl, ObjectList &objectlist, int animflags)
 | 
						|
    {
 | 
						|
        do {
 | 
						|
            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 = objectlist.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()));
 | 
						|
                Ogre::ControllerFunctionRealPtr func(OGRE_NEW VisController::Function(vis, (animflags&Nif::NiNode::AnimFlag_AutoPlay)));
 | 
						|
 | 
						|
                objectlist.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 = objectlist.mSkelBase->getSkeleton()->getBone(trgtid);
 | 
						|
                    Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ?
 | 
						|
                                                        Ogre::ControllerManager::getSingleton().getFrameTimeSource() :
 | 
						|
                                                        Ogre::ControllerValueRealPtr());
 | 
						|
                    Ogre::ControllerValueRealPtr dstval(OGRE_NEW KeyframeController::Value(trgtbone, key->data.getPtr()));
 | 
						|
                    Ogre::ControllerFunctionRealPtr func(OGRE_NEW KeyframeController::Function(key, (animflags&Nif::NiNode::AnimFlag_AutoPlay)));
 | 
						|
 | 
						|
                    objectlist.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 std::string &name, const std::string &group,
 | 
						|
                              Ogre::SceneManager *sceneMgr, const Nif::Node *node,
 | 
						|
                              ObjectList &objectlist, int flags, int animflags, int partflags)
 | 
						|
    {
 | 
						|
        // Do not create objects for the collision shape (includes all children)
 | 
						|
        if(node->recType == Nif::RC_RootCollisionNode)
 | 
						|
            return;
 | 
						|
 | 
						|
        // 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;
 | 
						|
 | 
						|
        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());
 | 
						|
 | 
						|
                int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, node->recIndex);
 | 
						|
                extractTextKeys(tk, objectlist.mTextKeys[trgtid]);
 | 
						|
            }
 | 
						|
            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() && (node->parent || node->recType != Nif::RC_NiNode))
 | 
						|
            createNodeControllers(name, node->controller, objectlist, animflags);
 | 
						|
 | 
						|
        if(node->recType == Nif::RC_NiCamera)
 | 
						|
        {
 | 
						|
            /* Ignored */
 | 
						|
        }
 | 
						|
 | 
						|
        if(node->recType == Nif::RC_NiTriShape && !(flags&0x80000000))
 | 
						|
        {
 | 
						|
            createEntity(name, group, sceneMgr, 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);
 | 
						|
        }
 | 
						|
 | 
						|
        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(name, group, sceneMgr, children[i].getPtr(), objectlist, flags, animflags, partflags);
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    static void createSkelBase(const std::string &name, const std::string &group,
 | 
						|
                               Ogre::SceneManager *sceneMgr, const Nif::Node *node,
 | 
						|
                               ObjectList &objectlist)
 | 
						|
    {
 | 
						|
        /* 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);
 | 
						|
 | 
						|
        objectlist.mSkelBase = sceneMgr->createEntity(name);
 | 
						|
        objectlist.mEntities.push_back(objectlist.mSkelBase);
 | 
						|
    }
 | 
						|
 | 
						|
public:
 | 
						|
    static void load(Ogre::SceneManager *sceneMgr, 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)
 | 
						|
        {
 | 
						|
            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, sceneMgr, node, objectlist);
 | 
						|
        }
 | 
						|
        createObjects(name, group, sceneMgr, node, objectlist, flags, 0, 0);
 | 
						|
    }
 | 
						|
 | 
						|
    static void loadKf(Ogre::Skeleton *skel, const std::string &name,
 | 
						|
                       TextKeyMap &textKeys, std::vector<Ogre::Controller<Ogre::Real> > &ctrls)
 | 
						|
    {
 | 
						|
        Nif::NIFFile::ptr nif = Nif::NIFFile::create(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;
 | 
						|
            }
 | 
						|
 | 
						|
            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, key->data.getPtr()));
 | 
						|
            Ogre::ControllerFunctionRealPtr func(OGRE_NEW KeyframeController::Function(key, false));
 | 
						|
 | 
						|
            ctrls.push_back(Ogre::Controller<Ogre::Real>(srcval, dstval, func));
 | 
						|
        }
 | 
						|
    }
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
ObjectList Loader::createObjects(Ogre::SceneNode *parentNode, std::string name, const std::string &group)
 | 
						|
{
 | 
						|
    ObjectList objectlist;
 | 
						|
 | 
						|
    Misc::StringUtils::toLower(name);
 | 
						|
    NIFObjectLoader::load(parentNode->getCreator(), objectlist, name, group);
 | 
						|
 | 
						|
    for(size_t i = 0;i < objectlist.mEntities.size();i++)
 | 
						|
    {
 | 
						|
        Ogre::Entity *entity = objectlist.mEntities[i];
 | 
						|
        if(!entity->isAttached())
 | 
						|
            parentNode->attachObject(entity);
 | 
						|
    }
 | 
						|
 | 
						|
    return objectlist;
 | 
						|
}
 | 
						|
 | 
						|
ObjectList Loader::createObjects(Ogre::Entity *parent, const std::string &bonename,
 | 
						|
                                 Ogre::SceneNode *parentNode,
 | 
						|
                                 std::string name, const std::string &group)
 | 
						|
{
 | 
						|
    ObjectList objectlist;
 | 
						|
 | 
						|
    Misc::StringUtils::toLower(name);
 | 
						|
    NIFObjectLoader::load(parentNode->getCreator(), objectlist, name, group);
 | 
						|
 | 
						|
    bool isskinned = false;
 | 
						|
    for(size_t i = 0;i < objectlist.mEntities.size();i++)
 | 
						|
    {
 | 
						|
        Ogre::Entity *ent = objectlist.mEntities[i];
 | 
						|
        if(objectlist.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)
 | 
						|
    {
 | 
						|
        std::string filter = "@shape=tri "+bonename;
 | 
						|
        Misc::StringUtils::toLower(filter);
 | 
						|
        for(size_t i = 0;i < objectlist.mEntities.size();i++)
 | 
						|
        {
 | 
						|
            Ogre::Entity *entity = objectlist.mEntities[i];
 | 
						|
            if(entity->hasSkeleton())
 | 
						|
            {
 | 
						|
                if(entity == objectlist.mSkelBase ||
 | 
						|
                   entity->getMesh()->getName().find(filter) != std::string::npos)
 | 
						|
                    parentNode->attachObject(entity);
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                if(entity->getMesh()->getName().find(filter) == std::string::npos)
 | 
						|
                    entity->detachFromParent();
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        for(size_t i = 0;i < objectlist.mEntities.size();i++)
 | 
						|
        {
 | 
						|
            Ogre::Entity *entity = objectlist.mEntities[i];
 | 
						|
            if(!entity->isAttached())
 | 
						|
            {
 | 
						|
                Ogre::TagPoint *tag = parent->attachObjectToBone(bonename, entity);
 | 
						|
                tag->setScale(scale);
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    return objectlist;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
ObjectList Loader::createObjectBase(Ogre::SceneNode *parentNode, std::string name, const std::string &group)
 | 
						|
{
 | 
						|
    ObjectList objectlist;
 | 
						|
 | 
						|
    Misc::StringUtils::toLower(name);
 | 
						|
    NIFObjectLoader::load(parentNode->getCreator(), objectlist, name, group, 0xC0000000);
 | 
						|
 | 
						|
    if(objectlist.mSkelBase)
 | 
						|
        parentNode->attachObject(objectlist.mSkelBase);
 | 
						|
 | 
						|
    return objectlist;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
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
 |