mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-11-04 07:56:40 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			1296 lines
		
	
	
	
		
			52 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1296 lines
		
	
	
	
		
			52 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/misc/stringops.hpp>
 | 
						|
 | 
						|
#include "skeleton.hpp"
 | 
						|
#include "material.hpp"
 | 
						|
#include "mesh.hpp"
 | 
						|
#include "controller.hpp"
 | 
						|
 | 
						|
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::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(NIFMaterialLoader::findTextureName(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::FloatKeyList 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::Vector3KeyList 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:
 | 
						|
        Nif::QuaternionKeyList mRotations;
 | 
						|
        Nif::Vector3KeyList mTranslations;
 | 
						|
        Nif::FloatKeyList mScales;
 | 
						|
 | 
						|
        using ValueInterpolator::interpKey;
 | 
						|
 | 
						|
        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>, public ValueInterpolator
 | 
						|
    {
 | 
						|
    private:
 | 
						|
        Ogre::MovableObject* mMovable;
 | 
						|
        Nif::FloatKeyList mUTrans;
 | 
						|
        Nif::FloatKeyList mVTrans;
 | 
						|
        Nif::FloatKeyList mUScale;
 | 
						|
        Nif::FloatKeyList 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;
 | 
						|
        node->getProperties(texprop, matprop, alphaprop, vertprop, zprop, specprop, wireprop);
 | 
						|
 | 
						|
        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);
 | 
						|
        emitter->setEmissionRate(partctrl->emitRate);
 | 
						|
        emitter->setTimeToLive(partctrl->lifetime,
 | 
						|
                               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->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::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;
 | 
						|
        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->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->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 as user data on the particle system
 | 
						|
                    // so the emitters/affectors can access it easily.
 | 
						|
                    partsys->getUserObjectBindings().setUserAny(Ogre::Any(trgtbone));
 | 
						|
                    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));
 | 
						|
 | 
						|
                if (partflags&Nif::NiNode::ParticleFlag_AutoPlay)
 | 
						|
                    partsys->fastForward(1, 0.1);
 | 
						|
            }
 | 
						|
            ctrl = ctrl->next;
 | 
						|
        }
 | 
						|
 | 
						|
        partsys->setVisible(!(flags&Nif::NiNode::Flag_Hidden));
 | 
						|
        scene->mParticles.push_back(partsys);
 | 
						|
 | 
						|
        createMaterialControllers(partnode, partsys, animflags, scene);
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
    static void createNodeControllers(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, 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 std::string &name, const std::string &group,
 | 
						|
                              Ogre::SceneNode *sceneNode, const Nif::Node *node,
 | 
						|
                              ObjectScenePtr scene, 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;
 | 
						|
 | 
						|
        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());
 | 
						|
 | 
						|
                if (scene->mSkelBase)
 | 
						|
                {
 | 
						|
                    int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, node->recIndex);
 | 
						|
                    extractTextKeys(tk, scene->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())
 | 
						|
            createNodeControllers(name, node->controller, scene, animflags);
 | 
						|
 | 
						|
        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(name, group, sceneNode, children[i].getPtr(), scene, flags, animflags, partflags);
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    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::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, sceneNode->getCreator(), node, scene);
 | 
						|
        }
 | 
						|
        createObjects(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::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;
 | 
						|
            }
 | 
						|
 | 
						|
            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, 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
 |