Merge remote-tracking branch 'kcat/nifogre'

actorid
Marc Zinnschlag 12 years ago
commit 31289237ce

@ -1,6 +1,9 @@
#ifndef _GAME_RENDER_ANIMATION_H
#define _GAME_RENDER_ANIMATION_H
#include <OgreController.h>
#include <OgreVector3.h>
#include <components/nifogre/ogrenifloader.hpp>
#include "../mwworld/ptr.hpp"

@ -19,7 +19,7 @@ add_component_dir (nif
)
add_component_dir (nifogre
ogrenifloader
ogrenifloader skeleton material mesh
)
add_component_dir (nifbullet

@ -384,6 +384,44 @@ void NiSkinInstance::post(NIFFile *nif)
}
}
void Node::getProperties(const Nif::NiTexturingProperty *&texprop,
const Nif::NiMaterialProperty *&matprop,
const Nif::NiAlphaProperty *&alphaprop,
const Nif::NiVertexColorProperty *&vertprop,
const Nif::NiZBufferProperty *&zprop,
const Nif::NiSpecularProperty *&specprop,
const Nif::NiWireframeProperty *&wireprop) const
{
if(parent)
parent->getProperties(texprop, matprop, alphaprop, vertprop, zprop, specprop, wireprop);
for(size_t i = 0;i < props.length();i++)
{
// Entries may be empty
if(props[i].empty())
continue;
const Nif::Property *pr = props[i].getPtr();
if(pr->recType == Nif::RC_NiTexturingProperty)
texprop = static_cast<const Nif::NiTexturingProperty*>(pr);
else if(pr->recType == Nif::RC_NiMaterialProperty)
matprop = static_cast<const Nif::NiMaterialProperty*>(pr);
else if(pr->recType == Nif::RC_NiAlphaProperty)
alphaprop = static_cast<const Nif::NiAlphaProperty*>(pr);
else if(pr->recType == Nif::RC_NiVertexColorProperty)
vertprop = static_cast<const Nif::NiVertexColorProperty*>(pr);
else if(pr->recType == Nif::RC_NiZBufferProperty)
zprop = static_cast<const Nif::NiZBufferProperty*>(pr);
else if(pr->recType == Nif::RC_NiSpecularProperty)
specprop = static_cast<const Nif::NiSpecularProperty*>(pr);
else if(pr->recType == Nif::RC_NiWireframeProperty)
wireprop = static_cast<const Nif::NiWireframeProperty*>(pr);
else
std::cerr<< "Unhandled property type: "<<pr->recName <<std::endl;
}
}
Ogre::Matrix4 Node::getLocalTransform() const
{
Ogre::Matrix4 mat4(Ogre::Matrix4::IDENTITY);

@ -111,6 +111,14 @@ public:
boneIndex = ind;
}
void getProperties(const Nif::NiTexturingProperty *&texprop,
const Nif::NiMaterialProperty *&matprop,
const Nif::NiAlphaProperty *&alphaprop,
const Nif::NiVertexColorProperty *&vertprop,
const Nif::NiZBufferProperty *&zprop,
const Nif::NiSpecularProperty *&specprop,
const Nif::NiWireframeProperty *&wireprop) const;
Ogre::Matrix4 getLocalTransform() const;
Ogre::Matrix4 getWorldTransform() const;
};

@ -156,11 +156,11 @@ public:
};
// These contain no other data than the 'flags' field in Property
typedef Property NiShadeProperty;
typedef Property NiDitherProperty;
typedef Property NiZBufferProperty;
typedef Property NiSpecularProperty;
typedef Property NiWireframeProperty;
class NiShadeProperty : public Property { };
class NiDitherProperty : public Property { };
class NiZBufferProperty : public Property { };
class NiSpecularProperty : public Property { };
class NiWireframeProperty : public Property { };
// The rest are all struct-based
template <typename T>
@ -324,10 +324,10 @@ struct S_StencilProperty
}
};
typedef StructPropT<S_AlphaProperty> NiAlphaProperty;
typedef StructPropT<S_MaterialProperty> NiMaterialProperty;
typedef StructPropT<S_VertexColorProperty> NiVertexColorProperty;
typedef StructPropT<S_StencilProperty> NiStencilProperty;
class NiAlphaProperty : public StructPropT<S_AlphaProperty> { };
class NiMaterialProperty : public StructPropT<S_MaterialProperty> { };
class NiVertexColorProperty : public StructPropT<S_VertexColorProperty> { };
class NiStencilProperty : public StructPropT<S_StencilProperty> { };
} // Namespace
#endif

@ -0,0 +1,399 @@
#include "material.hpp"
#include <components/nif/node.hpp>
#include <components/misc/stringops.hpp>
#include <components/settings/settings.hpp>
#include <components/nifoverrides/nifoverrides.hpp>
#include <extern/shiny/Main/Factory.hpp>
#include <OgreMaterialManager.h>
#include <OgreMaterial.h>
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/functional/hash.hpp>
namespace NifOgre
{
// Conversion of blend / test mode from NIF
static const char *getBlendFactor(int mode)
{
switch(mode)
{
case 0: return "one";
case 1: return "zero";
case 2: return "src_colour";
case 3: return "one_minus_src_colour";
case 4: return "dest_colour";
case 5: return "one_minus_dest_colour";
case 6: return "src_alpha";
case 7: return "one_minus_src_alpha";
case 8: return "dest_alpha";
case 9: return "one_minus_dest_alpha";
case 10: return "src_alpha_saturate";
}
std::cerr<< "Unexpected blend mode: "<<mode <<std::endl;
return "src_alpha";
}
static const char *getTestMode(int mode)
{
switch(mode)
{
case 0: return "always_pass";
case 1: return "less";
case 2: return "equal";
case 3: return "less_equal";
case 4: return "greater";
case 5: return "not_equal";
case 6: return "greater_equal";
case 7: return "always_fail";
}
std::cerr<< "Unexpected test mode: "<<mode <<std::endl;
return "less_equal";
}
std::string NIFMaterialLoader::findTextureName(const std::string &filename)
{
/* Bethesda at some point converted all their BSA
* textures from tga to dds for increased load speed, but all
* texture file name references were kept as .tga.
*/
static const char path[] = "textures\\";
static const char path2[] = "textures/";
std::string texname = filename;
Misc::StringUtils::toLower(texname);
if(texname.compare(0, sizeof(path)-1, path) != 0 &&
texname.compare(0, sizeof(path2)-1, path2) != 0)
texname = path + texname;
Ogre::String::size_type pos = texname.rfind('.');
if(pos != Ogre::String::npos && texname.compare(pos, texname.length() - pos, ".dds") != 0)
{
// since we know all (GOTY edition or less) textures end
// in .dds, we change the extension
texname.replace(pos, texname.length(), ".dds");
// if it turns out that the above wasn't true in all cases (not for vanilla, but maybe mods)
// verify, and revert if false (this call succeeds quickly, but fails slowly)
if(!Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(texname))
{
texname = filename;
Misc::StringUtils::toLower(texname);
if(texname.compare(0, sizeof(path)-1, path) != 0 &&
texname.compare(0, sizeof(path2)-1, path2) != 0)
texname = path + texname;
}
}
return texname;
}
Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata,
const Ogre::String &name, const Ogre::String &group,
const Nif::NiTexturingProperty *texprop,
const Nif::NiMaterialProperty *matprop,
const Nif::NiAlphaProperty *alphaprop,
const Nif::NiVertexColorProperty *vertprop,
const Nif::NiZBufferProperty *zprop,
const Nif::NiSpecularProperty *specprop,
const Nif::NiWireframeProperty *wireprop,
bool &needTangents)
{
Ogre::MaterialManager &matMgr = Ogre::MaterialManager::getSingleton();
Ogre::MaterialPtr material = matMgr.getByName(name);
if(!material.isNull())
return name;
Ogre::Vector3 ambient(1.0f);
Ogre::Vector3 diffuse(1.0f);
Ogre::Vector3 specular(0.0f);
Ogre::Vector3 emissive(0.0f);
float glossiness = 0.0f;
float alpha = 1.0f;
int alphaFlags = 0;
int alphaTest = 0;
int vertMode = 2;
//int lightMode = 1;
int depthFlags = 3;
// Default should be 1, but Bloodmoon's models are broken
int specFlags = 0;
int wireFlags = 0;
Ogre::String texName[7];
bool vertexColour = (shapedata->colors.size() != 0);
// Texture
if(texprop)
{
for(int i = 0;i < 7;i++)
{
if(!texprop->textures[i].inUse)
continue;
if(texprop->textures[i].texture.empty())
{
warn("Texture layer "+Ogre::StringConverter::toString(i)+" is in use but empty in "+name);
continue;
}
const Nif::NiSourceTexture *st = texprop->textures[i].texture.getPtr();
if(st->external)
texName[i] = findTextureName(st->filename);
else
warn("Found internal texture, ignoring.");
}
Nif::ControllerPtr ctrls = texprop->controller;
while(!ctrls.empty())
{
warn("Unhandled texture controller "+ctrls->recName+" in "+name);
ctrls = ctrls->next;
}
}
needTangents = !texName[Nif::NiTexturingProperty::BumpTexture].empty();
// Alpha modifiers
if(alphaprop)
{
alphaFlags = alphaprop->flags;
alphaTest = alphaprop->data.threshold;
Nif::ControllerPtr ctrls = alphaprop->controller;
while(!ctrls.empty())
{
warn("Unhandled alpha controller "+ctrls->recName+" in "+name);
ctrls = ctrls->next;
}
}
// Vertex color handling
if(vertprop)
{
vertMode = vertprop->data.vertmode;
// FIXME: Handle lightmode?
//lightMode = vertprop->data.lightmode;
Nif::ControllerPtr ctrls = vertprop->controller;
while(!ctrls.empty())
{
warn("Unhandled vertex color controller "+ctrls->recName+" in "+name);
ctrls = ctrls->next;
}
}
if(zprop)
{
depthFlags = zprop->flags;
// Depth function???
Nif::ControllerPtr ctrls = zprop->controller;
while(!ctrls.empty())
{
warn("Unhandled depth controller "+ctrls->recName+" in "+name);
ctrls = ctrls->next;
}
}
if(specprop)
{
specFlags = specprop->flags;
Nif::ControllerPtr ctrls = specprop->controller;
while(!ctrls.empty())
{
warn("Unhandled specular controller "+ctrls->recName+" in "+name);
ctrls = ctrls->next;
}
}
if(wireprop)
{
wireFlags = wireprop->flags;
Nif::ControllerPtr ctrls = wireprop->controller;
while(!ctrls.empty())
{
warn("Unhandled wireframe controller "+ctrls->recName+" in "+name);
ctrls = ctrls->next;
}
}
// Material
if(matprop)
{
ambient = matprop->data.ambient;
diffuse = matprop->data.diffuse;
specular = matprop->data.specular;
emissive = matprop->data.emissive;
glossiness = matprop->data.glossiness;
alpha = matprop->data.alpha;
Nif::ControllerPtr ctrls = matprop->controller;
while(!ctrls.empty())
{
warn("Unhandled material controller "+ctrls->recName+" in "+name);
ctrls = ctrls->next;
}
}
{
// Generate a hash out of all properties that can affect the material.
size_t h = 0;
boost::hash_combine(h, ambient.x);
boost::hash_combine(h, ambient.y);
boost::hash_combine(h, ambient.z);
boost::hash_combine(h, diffuse.x);
boost::hash_combine(h, diffuse.y);
boost::hash_combine(h, diffuse.z);
boost::hash_combine(h, alpha);
boost::hash_combine(h, specular.x);
boost::hash_combine(h, specular.y);
boost::hash_combine(h, specular.z);
boost::hash_combine(h, glossiness);
boost::hash_combine(h, emissive.x);
boost::hash_combine(h, emissive.y);
boost::hash_combine(h, emissive.z);
for(int i = 0;i < 7;i++)
{
if(!texName[i].empty())
boost::hash_combine(h, texName[i]);
}
boost::hash_combine(h, vertexColour);
boost::hash_combine(h, alphaFlags);
boost::hash_combine(h, alphaTest);
boost::hash_combine(h, vertMode);
boost::hash_combine(h, depthFlags);
boost::hash_combine(h, specFlags);
boost::hash_combine(h, wireFlags);
std::map<size_t,std::string>::iterator itr = sMaterialMap.find(h);
if (itr != sMaterialMap.end())
{
// a suitable material exists already - use it
return itr->second;
}
// not found, create a new one
sMaterialMap.insert(std::make_pair(h, name));
}
// No existing material like this. Create a new one.
sh::MaterialInstance *instance = sh::Factory::getInstance().createMaterialInstance(name, "openmw_objects_base");
if(vertMode == 0 || !vertexColour)
{
instance->setProperty("ambient", sh::makeProperty(new sh::Vector4(ambient.x, ambient.y, ambient.z, 1)));
instance->setProperty("diffuse", sh::makeProperty(new sh::Vector4(diffuse.x, diffuse.y, diffuse.z, alpha)));
instance->setProperty("emissive", sh::makeProperty(new sh::Vector4(emissive.x, emissive.y, emissive.z, 1)));
instance->setProperty("vertmode", sh::makeProperty(new sh::StringValue("0")));
}
else if(vertMode == 1)
{
instance->setProperty("ambient", sh::makeProperty(new sh::Vector4(ambient.x, ambient.y, ambient.z, 1)));
instance->setProperty("diffuse", sh::makeProperty(new sh::Vector4(diffuse.x, diffuse.y, diffuse.z, alpha)));
instance->setProperty("emissive", sh::makeProperty(new sh::StringValue("vertexcolour")));
instance->setProperty("vertmode", sh::makeProperty(new sh::StringValue("1")));
}
else if(vertMode == 2)
{
instance->setProperty("ambient", sh::makeProperty(new sh::StringValue("vertexcolour")));
instance->setProperty("diffuse", sh::makeProperty(new sh::StringValue("vertexcolour")));
instance->setProperty("emissive", sh::makeProperty(new sh::Vector4(emissive.x, emissive.y, emissive.z, 1)));
instance->setProperty("vertmode", sh::makeProperty(new sh::StringValue("2")));
}
else
std::cerr<< "Unhandled vertex mode: "<<vertMode <<std::endl;
if(specFlags)
{
instance->setProperty("specular", sh::makeProperty(
new sh::Vector4(specular.x, specular.y, specular.z, glossiness)));
}
if(wireFlags)
{
instance->setProperty("polygon_mode", sh::makeProperty(new sh::StringValue("wireframe")));
}
instance->setProperty("diffuseMap", sh::makeProperty(texName[Nif::NiTexturingProperty::BaseTexture]));
instance->setProperty("normalMap", sh::makeProperty(texName[Nif::NiTexturingProperty::BumpTexture]));
instance->setProperty("detailMap", sh::makeProperty(texName[Nif::NiTexturingProperty::DetailTexture]));
instance->setProperty("emissiveMap", sh::makeProperty(texName[Nif::NiTexturingProperty::GlowTexture]));
if (!texName[Nif::NiTexturingProperty::GlowTexture].empty())
{
instance->setProperty("use_emissive_map", sh::makeProperty(new sh::BooleanValue(true)));
instance->setProperty("emissiveMapUVSet", sh::makeProperty(new sh::IntValue(texprop->textures[Nif::NiTexturingProperty::GlowTexture].uvSet)));
}
if (!texName[Nif::NiTexturingProperty::DetailTexture].empty())
{
instance->setProperty("use_detail_map", sh::makeProperty(new sh::BooleanValue(true)));
instance->setProperty("detailMapUVSet", sh::makeProperty(new sh::IntValue(texprop->textures[Nif::NiTexturingProperty::DetailTexture].uvSet)));
}
if (!texName[Nif::NiTexturingProperty::BumpTexture].empty())
{
// force automips on normal maps for now
instance->setProperty("normalMap", sh::makeProperty(texName[Nif::NiTexturingProperty::BumpTexture] + " 4"));
}
for(int i = 0;i < 7;i++)
{
if(i == Nif::NiTexturingProperty::BaseTexture ||
i == Nif::NiTexturingProperty::DetailTexture ||
i == Nif::NiTexturingProperty::BumpTexture ||
i == Nif::NiTexturingProperty::GlowTexture)
continue;
if(!texName[i].empty())
warn("Ignored texture "+texName[i]+" on layer "+Ogre::StringConverter::toString(i));
}
if (vertexColour)
instance->setProperty("has_vertex_colour", sh::makeProperty(new sh::BooleanValue(true)));
// Add transparency if NiAlphaProperty was present
NifOverrides::TransparencyResult result = NifOverrides::Overrides::getTransparencyOverride(texName[0]);
if (result.first)
{
alphaFlags = (1<<9) | (6<<10); /* alpha_rejection enabled, greater_equal */
alphaTest = result.second;
depthFlags = (1<<0) | (1<<1); // depth_write on, depth_check on
}
if((alphaFlags&1))
{
std::string blend_mode;
blend_mode += getBlendFactor((alphaFlags>>1)&0xf);
blend_mode += " ";
blend_mode += getBlendFactor((alphaFlags>>5)&0xf);
instance->setProperty("scene_blend", sh::makeProperty(new sh::StringValue(blend_mode)));
}
if((alphaFlags>>9)&1)
{
std::string reject;
reject += getTestMode((alphaFlags>>10)&0x7);
reject += " ";
reject += Ogre::StringConverter::toString(alphaTest);
instance->setProperty("alpha_rejection", sh::makeProperty(new sh::StringValue(reject)));
}
else
instance->getMaterial()->setShadowCasterMaterial("openmw_shadowcaster_noalpha");
// Ogre usually only sorts if depth write is disabled, so we want "force" instead of "on"
instance->setProperty("transparent_sorting", sh::makeProperty(new sh::StringValue(
((alphaFlags&1) && !((alphaFlags>>13)&1)) ? "force" : "off")));
instance->setProperty("depth_check", sh::makeProperty(new sh::StringValue((depthFlags&1) ? "on" : "off")));
instance->setProperty("depth_write", sh::makeProperty(new sh::StringValue(((depthFlags>>1)&1) ? "on" : "off")));
// depth_func???
sh::Factory::getInstance()._ensureMaterial(name, "Default");
return name;
}
std::map<size_t,std::string> NIFMaterialLoader::sMaterialMap;
}

@ -0,0 +1,57 @@
#ifndef COMPONENTS_NIFOGRE_MATERIAL_HPP
#define COMPONENTS_NIFOGRE_MATERIAL_HPP
#include <iostream>
#include <string>
#include <map>
#include <cassert>
#include <OgreString.h>
namespace Nif
{
class ShapeData;
class NiTexturingProperty;
class NiMaterialProperty;
class NiAlphaProperty;
class NiVertexColorProperty;
class NiZBufferProperty;
class NiSpecularProperty;
class NiWireframeProperty;
}
namespace NifOgre
{
class NIFMaterialLoader {
static void warn(const std::string &msg)
{
std::cerr << "NIFMaterialLoader: Warn: " << msg << std::endl;
}
static void fail(const std::string &msg)
{
std::cerr << "NIFMaterialLoader: Fail: "<< msg << std::endl;
abort();
}
static std::map<size_t,std::string> sMaterialMap;
static std::string findTextureName(const std::string &filename);
public:
static Ogre::String getMaterial(const Nif::ShapeData *shapedata,
const Ogre::String &name, const Ogre::String &group,
const Nif::NiTexturingProperty *texprop,
const Nif::NiMaterialProperty *matprop,
const Nif::NiAlphaProperty *alphaprop,
const Nif::NiVertexColorProperty *vertprop,
const Nif::NiZBufferProperty *zprop,
const Nif::NiSpecularProperty *specprop,
const Nif::NiWireframeProperty *wireprop,
bool &needTangents);
};
}
#endif

@ -0,0 +1,366 @@
#include "mesh.hpp"
#include <limits>
#include <OgreMeshManager.h>
#include <OgreMesh.h>
#include <OgreSubMesh.h>
#include <OgreBone.h>
#include <OgreHardwareBufferManager.h>
#include <OgreMaterialManager.h>
#include <OgreSkeletonManager.h>
#include <OgreRenderSystem.h>
#include <OgreRoot.h>
#include <components/nif/node.hpp>
#include <components/misc/stringops.hpp>
#include "material.hpp"
namespace NifOgre
{
// Helper class that computes the bounding box and of a mesh
class BoundsFinder
{
struct MaxMinFinder
{
float max, min;
MaxMinFinder()
{
min = std::numeric_limits<float>::infinity();
max = -min;
}
void add(float f)
{
if (f > max) max = f;
if (f < min) min = f;
}
// Return Max(max**2, min**2)
float getMaxSquared()
{
float m1 = max*max;
float m2 = min*min;
if (m1 >= m2) return m1;
return m2;
}
};
MaxMinFinder X, Y, Z;
public:
// Add 'verts' vertices to the calculation. The 'data' pointer is
// expected to point to 3*verts floats representing x,y,z for each
// point.
void add(float *data, int verts)
{
for (int i=0;i<verts;i++)
{
X.add(*(data++));
Y.add(*(data++));
Z.add(*(data++));
}
}
// True if this structure has valid values
bool isValid()
{
return
minX() <= maxX() &&
minY() <= maxY() &&
minZ() <= maxZ();
}
// Compute radius
float getRadius()
{
assert(isValid());
// The radius is computed from the origin, not from the geometric
// center of the mesh.
return sqrt(X.getMaxSquared() + Y.getMaxSquared() + Z.getMaxSquared());
}
float minX() {
return X.min;
}
float maxX() {
return X.max;
}
float minY() {
return Y.min;
}
float maxY() {
return Y.max;
}
float minZ() {
return Z.min;
}
float maxZ() {
return Z.max;
}
};
NIFMeshLoader::LoaderMap NIFMeshLoader::sLoaders;
void NIFMeshLoader::createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape)
{
Ogre::SkeletonPtr skel;
const Nif::NiTriShapeData *data = shape->data.getPtr();
const Nif::NiSkinInstance *skin = (shape->skin.empty() ? NULL : shape->skin.getPtr());
std::vector<Ogre::Vector3> srcVerts = data->vertices;
std::vector<Ogre::Vector3> srcNorms = data->normals;
Ogre::HardwareBuffer::Usage vertUsage = Ogre::HardwareBuffer::HBU_STATIC;
bool vertShadowBuffer = false;
if(skin != NULL)
{
vertUsage = Ogre::HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY;
vertShadowBuffer = true;
// Only set a skeleton when skinning. Unskinned meshes with a skeleton will be
// explicitly attached later.
mesh->setSkeletonName(mName);
// Get the skeleton resource, so vertices can be transformed into the bones' initial state.
Ogre::SkeletonManager *skelMgr = Ogre::SkeletonManager::getSingletonPtr();
skel = skelMgr->getByName(mName);
// Convert vertices and normals to bone space from bind position. It would be
// better to transform the bones into bind position, but there doesn't seem to
// be a reliable way to do that.
std::vector<Ogre::Vector3> newVerts(srcVerts.size(), Ogre::Vector3(0.0f));
std::vector<Ogre::Vector3> newNorms(srcNorms.size(), Ogre::Vector3(0.0f));
const Nif::NiSkinData *data = skin->data.getPtr();
const Nif::NodeList &bones = skin->bones;
for(size_t b = 0;b < bones.length();b++)
{
Ogre::Bone *bone = skel->getBone(bones[b]->name);
Ogre::Matrix4 mat;
mat.makeTransform(data->bones[b].trafo.trans, Ogre::Vector3(data->bones[b].trafo.scale),
Ogre::Quaternion(data->bones[b].trafo.rotation));
mat = bone->_getFullTransform() * mat;
const std::vector<Nif::NiSkinData::VertWeight> &weights = data->bones[b].weights;
for(size_t i = 0;i < weights.size();i++)
{
size_t index = weights[i].vertex;
float weight = weights[i].weight;
newVerts.at(index) += (mat*srcVerts[index]) * weight;
if(newNorms.size() > index)
{
Ogre::Vector4 vec4(srcNorms[index][0], srcNorms[index][1], srcNorms[index][2], 0.0f);
vec4 = mat*vec4 * weight;
newNorms[index] += Ogre::Vector3(&vec4[0]);
}
}
}
srcVerts = newVerts;
srcNorms = newNorms;
}
else
{
Ogre::SkeletonManager *skelMgr = Ogre::SkeletonManager::getSingletonPtr();
if(skelMgr->getByName(mName).isNull())
{
// No skinning and no skeleton, so just transform the vertices and
// normals into position.
Ogre::Matrix4 mat4 = shape->getWorldTransform();
for(size_t i = 0;i < srcVerts.size();i++)
{
Ogre::Vector4 vec4(srcVerts[i].x, srcVerts[i].y, srcVerts[i].z, 1.0f);
vec4 = mat4*vec4;
srcVerts[i] = Ogre::Vector3(&vec4[0]);
}
for(size_t i = 0;i < srcNorms.size();i++)
{
Ogre::Vector4 vec4(srcNorms[i].x, srcNorms[i].y, srcNorms[i].z, 0.0f);
vec4 = mat4*vec4;
srcNorms[i] = Ogre::Vector3(&vec4[0]);
}
}
}
// Set the bounding box first
BoundsFinder bounds;
bounds.add(&srcVerts[0][0], srcVerts.size());
if(!bounds.isValid())
{
float v[3] = { 0.0f, 0.0f, 0.0f };
bounds.add(&v[0], 1);
}
mesh->_setBounds(Ogre::AxisAlignedBox(bounds.minX()-0.5f, bounds.minY()-0.5f, bounds.minZ()-0.5f,
bounds.maxX()+0.5f, bounds.maxY()+0.5f, bounds.maxZ()+0.5f));
mesh->_setBoundingSphereRadius(bounds.getRadius());
// This function is just one long stream of Ogre-barf, but it works
// great.
Ogre::HardwareBufferManager *hwBufMgr = Ogre::HardwareBufferManager::getSingletonPtr();
Ogre::HardwareVertexBufferSharedPtr vbuf;
Ogre::HardwareIndexBufferSharedPtr ibuf;
Ogre::VertexBufferBinding *bind;
Ogre::VertexDeclaration *decl;
int nextBuf = 0;
Ogre::SubMesh *sub = mesh->createSubMesh();
// Add vertices
sub->useSharedVertices = false;
sub->vertexData = new Ogre::VertexData();
sub->vertexData->vertexStart = 0;
sub->vertexData->vertexCount = srcVerts.size();
decl = sub->vertexData->vertexDeclaration;
bind = sub->vertexData->vertexBufferBinding;
if(srcVerts.size())
{
vbuf = hwBufMgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3),
srcVerts.size(), vertUsage, vertShadowBuffer);
vbuf->writeData(0, vbuf->getSizeInBytes(), &srcVerts[0][0], true);
decl->addElement(nextBuf, 0, Ogre::VET_FLOAT3, Ogre::VES_POSITION);
bind->setBinding(nextBuf++, vbuf);
}
// Vertex normals
if(srcNorms.size())
{
vbuf = hwBufMgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3),
srcNorms.size(), vertUsage, vertShadowBuffer);
vbuf->writeData(0, vbuf->getSizeInBytes(), &srcNorms[0][0], true);
decl->addElement(nextBuf, 0, Ogre::VET_FLOAT3, Ogre::VES_NORMAL);
bind->setBinding(nextBuf++, vbuf);
}
// Vertex colors
const std::vector<Ogre::Vector4> &colors = data->colors;
if(colors.size())
{
Ogre::RenderSystem *rs = Ogre::Root::getSingleton().getRenderSystem();
std::vector<Ogre::RGBA> colorsRGB(colors.size());
for(size_t i = 0;i < colorsRGB.size();i++)
{
Ogre::ColourValue clr(colors[i][0], colors[i][1], colors[i][2], colors[i][3]);
rs->convertColourValue(clr, &colorsRGB[i]);
}
vbuf = hwBufMgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_COLOUR),
colorsRGB.size(), Ogre::HardwareBuffer::HBU_STATIC);
vbuf->writeData(0, vbuf->getSizeInBytes(), &colorsRGB[0], true);
decl->addElement(nextBuf, 0, Ogre::VET_COLOUR, Ogre::VES_DIFFUSE);
bind->setBinding(nextBuf++, vbuf);
}
// Texture UV coordinates
size_t numUVs = data->uvlist.size();
for(size_t i = 0;i < numUVs;i++)
{
vbuf = hwBufMgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT2),
srcVerts.size(), Ogre::HardwareBuffer::HBU_STATIC);
vbuf->writeData(0, vbuf->getSizeInBytes(), &data->uvlist[i][0], true);
decl->addElement(nextBuf, 0, Ogre::VET_FLOAT2, Ogre::VES_TEXTURE_COORDINATES, i);
bind->setBinding(nextBuf++, vbuf);
}
// Triangle faces
const std::vector<short> &srcIdx = data->triangles;
if(srcIdx.size())
{
ibuf = hwBufMgr->createIndexBuffer(Ogre::HardwareIndexBuffer::IT_16BIT, srcIdx.size(),
Ogre::HardwareBuffer::HBU_STATIC);
ibuf->writeData(0, ibuf->getSizeInBytes(), &srcIdx[0], true);
sub->indexData->indexBuffer = ibuf;
sub->indexData->indexCount = srcIdx.size();
sub->indexData->indexStart = 0;
}
// Assign bone weights for this TriShape
if(skin != NULL)
{
const Nif::NiSkinData *data = skin->data.getPtr();
const Nif::NodeList &bones = skin->bones;
for(size_t i = 0;i < bones.length();i++)
{
Ogre::VertexBoneAssignment boneInf;
boneInf.boneIndex = skel->getBone(bones[i]->name)->getHandle();
const std::vector<Nif::NiSkinData::VertWeight> &weights = data->bones[i].weights;
for(size_t j = 0;j < weights.size();j++)
{
boneInf.vertexIndex = weights[j].vertex;
boneInf.weight = weights[j].weight;
sub->addBoneAssignment(boneInf);
}
}
}
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;
shape->getProperties(texprop, matprop, alphaprop, vertprop, zprop, specprop, wireprop);
std::string matname = NIFMaterialLoader::getMaterial(data, mesh->getName(), mGroup,
texprop, matprop, alphaprop,
vertprop, zprop, specprop,
wireprop, needTangents);
if(matname.length() > 0)
sub->setMaterialName(matname);
// build tangents if the material needs them
if (needTangents)
{
unsigned short src,dest;
if (!mesh->suggestTangentVectorBuildParams(Ogre::VES_TANGENT, src,dest))
mesh->buildTangentVectors(Ogre::VES_TANGENT, src,dest);
}
}
NIFMeshLoader::NIFMeshLoader(const std::string &name, const std::string &group, size_t idx)
: mName(name), mGroup(group), mShapeIndex(idx)
{
}
void NIFMeshLoader::loadResource(Ogre::Resource *resource)
{
Ogre::Mesh *mesh = dynamic_cast<Ogre::Mesh*>(resource);
OgreAssert(mesh, "Attempting to load a mesh into a non-mesh resource!");
Nif::NIFFile::ptr nif = Nif::NIFFile::create(mName);
if(mShapeIndex >= nif->numRecords())
{
Ogre::SkeletonManager *skelMgr = Ogre::SkeletonManager::getSingletonPtr();
if(!skelMgr->getByName(mName).isNull())
mesh->setSkeletonName(mName);
return;
}
const Nif::Record *record = nif->getRecord(mShapeIndex);
createSubMesh(mesh, dynamic_cast<const Nif::NiTriShape*>(record));
}
void NIFMeshLoader::createMesh(const std::string &name, const std::string &fullname, const std::string &group, size_t idx)
{
NIFMeshLoader::LoaderMap::iterator loader;
loader = sLoaders.insert(std::make_pair(fullname, NIFMeshLoader(name, group, idx))).first;
Ogre::MeshManager &meshMgr = Ogre::MeshManager::getSingleton();
Ogre::MeshPtr mesh = meshMgr.createManual(fullname, group, &loader->second);
mesh->setAutoBuildEdgeLists(false);
}
}

@ -0,0 +1,55 @@
#ifndef COMPONENTS_NIFOGRE_MESH_HPP
#define COMPONENTS_NIFOGRE_MESH_HPP
#include <iostream>
#include <string>
#include <map>
#include <cassert>
#include <OgreResource.h>
namespace Nif
{
class NiTriShape;
}
namespace NifOgre
{
/** Manual resource loader for NiTriShapes. This is the main class responsible
* for translating the internal NIF meshes into something Ogre can use.
*/
class NIFMeshLoader : Ogre::ManualResourceLoader
{
static void warn(const std::string &msg)
{
std::cerr << "NIFMeshLoader: Warn: " << msg << std::endl;
}
static void fail(const std::string &msg)
{
std::cerr << "NIFMeshLoader: Fail: "<< msg << std::endl;
abort();
}
std::string mName;
std::string mGroup;
size_t mShapeIndex;
// Convert NiTriShape to Ogre::SubMesh
void createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape);
typedef std::map<std::string,NIFMeshLoader> LoaderMap;
static LoaderMap sLoaders;
NIFMeshLoader(const std::string &name, const std::string &group, size_t idx);
virtual void loadResource(Ogre::Resource *resource);
public:
static void createMesh(const std::string &name, const std::string &fullname, const std::string &group, size_t idx);
};
}
#endif

File diff suppressed because it is too large Load Diff

@ -25,11 +25,11 @@
#define OPENMW_COMPONENTS_NIFOGRE_OGRENIFLOADER_HPP
#include <OgreResource.h>
#include <OgreMesh.h>
#include <OgreSkeleton.h>
#include <OgreController.h>
#include <vector>
#include <string>
#include <map>
// FIXME: This namespace really doesn't do anything Nif-specific. Any supportable

@ -0,0 +1,351 @@
#include "skeleton.hpp"
#include <OgreSkeletonManager.h>
#include <OgreResource.h>
#include <OgreSkeleton.h>
#include <OgreBone.h>
#include <components/nif/node.hpp>
#include <components/misc/stringops.hpp>
namespace NifOgre
{
void NIFSkeletonLoader::buildAnimation(Ogre::Skeleton *skel, const std::string &name, const std::vector<const Nif::NiKeyframeController*> &ctrls, const std::vector<std::string> &targets, float startTime, float stopTime)
{
Ogre::Animation *anim = skel->createAnimation(name, stopTime);
for(size_t i = 0;i < ctrls.size();i++)
{
const Nif::NiKeyframeController *kfc = ctrls[i];
if(kfc->data.empty())
continue;
const Nif::NiKeyframeData *kf = kfc->data.getPtr();
/* Get the keyframes and make sure they're sorted first to last */
const Nif::QuaternionKeyList &quatkeys = kf->mRotations;
const Nif::Vector3KeyList &trankeys = kf->mTranslations;
const Nif::FloatKeyList &scalekeys = kf->mScales;
Nif::QuaternionKeyList::VecType::const_iterator quatiter = quatkeys.mKeys.begin();
Nif::Vector3KeyList::VecType::const_iterator traniter = trankeys.mKeys.begin();
Nif::FloatKeyList::VecType::const_iterator scaleiter = scalekeys.mKeys.begin();
Ogre::Bone *bone = skel->getBone(targets[i]);
// NOTE: For some reason, Ogre doesn't like the node track ID being different from
// the bone ID
Ogre::NodeAnimationTrack *nodetrack = anim->hasNodeTrack(bone->getHandle()) ?
anim->getNodeTrack(bone->getHandle()) :
anim->createNodeTrack(bone->getHandle(), bone);
Ogre::Quaternion lastquat, curquat;
Ogre::Vector3 lasttrans(0.0f), curtrans(0.0f);
Ogre::Vector3 lastscale(1.0f), curscale(1.0f);
if(quatiter != quatkeys.mKeys.end())
lastquat = curquat = quatiter->mValue;
if(traniter != trankeys.mKeys.end())
lasttrans = curtrans = traniter->mValue;
if(scaleiter != scalekeys.mKeys.end())
lastscale = curscale = Ogre::Vector3(scaleiter->mValue);
bool didlast = false;
while(!didlast)
{
float curtime = std::numeric_limits<float>::max();
//Get latest time
if(quatiter != quatkeys.mKeys.end())
curtime = std::min(curtime, quatiter->mTime);
if(traniter != trankeys.mKeys.end())
curtime = std::min(curtime, traniter->mTime);
if(scaleiter != scalekeys.mKeys.end())
curtime = std::min(curtime, scaleiter->mTime);
curtime = std::max(curtime, startTime);
if(curtime >= stopTime)
{
didlast = true;
curtime = stopTime;
}
// Get the latest quaternions, translations, and scales for the
// current time
while(quatiter != quatkeys.mKeys.end() && curtime >= quatiter->mTime)
{
lastquat = curquat;
if(++quatiter != quatkeys.mKeys.end())
curquat = quatiter->mValue;
}
while(traniter != trankeys.mKeys.end() && curtime >= traniter->mTime)
{
lasttrans = curtrans;
if(++traniter != trankeys.mKeys.end())
curtrans = traniter->mValue;
}
while(scaleiter != scalekeys.mKeys.end() && curtime >= scaleiter->mTime)
{
lastscale = curscale;
if(++scaleiter != scalekeys.mKeys.end())
curscale = Ogre::Vector3(scaleiter->mValue);
}
Ogre::TransformKeyFrame *kframe;
kframe = nodetrack->createNodeKeyFrame(curtime);
if(quatiter == quatkeys.mKeys.end() || quatiter == quatkeys.mKeys.begin())
kframe->setRotation(curquat);
else
{
Nif::QuaternionKeyList::VecType::const_iterator last = quatiter-1;
float diff = (curtime-last->mTime) / (quatiter->mTime-last->mTime);
kframe->setRotation(Ogre::Quaternion::nlerp(diff, lastquat, curquat));
}
if(traniter == trankeys.mKeys.end() || traniter == trankeys.mKeys.begin())
kframe->setTranslate(curtrans);
else
{
Nif::Vector3KeyList::VecType::const_iterator last = traniter-1;
float diff = (curtime-last->mTime) / (traniter->mTime-last->mTime);
kframe->setTranslate(lasttrans + ((curtrans-lasttrans)*diff));
}
if(scaleiter == scalekeys.mKeys.end() || scaleiter == scalekeys.mKeys.begin())
kframe->setScale(curscale);
else
{
Nif::FloatKeyList::VecType::const_iterator last = scaleiter-1;
float diff = (curtime-last->mTime) / (scaleiter->mTime-last->mTime);
kframe->setScale(lastscale + ((curscale-lastscale)*diff));
}
}
}
anim->optimise();
}
TextKeyMap NIFSkeletonLoader::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));
std::string result = str.substr(pos, nextpos-pos);
textkeys.insert(std::make_pair(tk->list[i].time, Misc::StringUtils::toLower(result)));
pos = nextpos;
}
}
return textkeys;
}
void NIFSkeletonLoader::buildBones(Ogre::Skeleton *skel, const Nif::Node *node, Ogre::Bone *&animroot, TextKeyMap &textkeys, std::vector<Nif::NiKeyframeController const*> &ctrls, Ogre::Bone *parent)
{
Ogre::Bone *bone;
if(!skel->hasBone(node->name))
bone = skel->createBone(node->name);
else
bone = skel->createBone();
if(parent) parent->addChild(bone);
mNifToOgreHandleMap[node->recIndex] = bone->getHandle();
bone->setOrientation(node->trafo.rotation);
bone->setPosition(node->trafo.pos);
bone->setScale(Ogre::Vector3(node->trafo.scale));
bone->setBindingPose();
if(!(node->recType == Nif::RC_NiNode || /* Nothing special; children traversed below */
node->recType == Nif::RC_RootCollisionNode || /* handled in nifbullet (hopefully) */
node->recType == Nif::RC_NiTriShape || /* Handled in the mesh loader */
node->recType == Nif::RC_NiCamera ||
node->recType == Nif::RC_NiAutoNormalParticles ||
node->recType == Nif::RC_NiRotatingParticles
))
warn("Unhandled "+node->recName+" "+node->name+" in "+skel->getName());
Nif::ControllerPtr ctrl = node->controller;
while(!ctrl.empty())
{
if(ctrl->recType == Nif::RC_NiKeyframeController)
ctrls.push_back(static_cast<const Nif::NiKeyframeController*>(ctrl.getPtr()));
else if(!(ctrl->recType == Nif::RC_NiParticleSystemController ||
ctrl->recType == Nif::RC_NiVisController ||
ctrl->recType == Nif::RC_NiUVController
))
warn("Unhandled "+ctrl->recName+" from node "+node->name+" in "+skel->getName());
ctrl = ctrl->next;
}
Nif::ExtraPtr e = node->extra;
while(!e.empty())
{
if(e->recType == Nif::RC_NiTextKeyExtraData && !animroot)
{
const Nif::NiTextKeyExtraData *tk = static_cast<const Nif::NiTextKeyExtraData*>(e.getPtr());
textkeys = extractTextKeys(tk);
animroot = bone;
}
e = e->extra;
}
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())
buildBones(skel, children[i].getPtr(), animroot, textkeys, ctrls, bone);
}
}
}
void NIFSkeletonLoader::loadResource(Ogre::Resource *resource)
{
Ogre::Skeleton *skel = dynamic_cast<Ogre::Skeleton*>(resource);
OgreAssert(skel, "Attempting to load a skeleton into a non-skeleton resource!");
Nif::NIFFile::ptr nif(Nif::NIFFile::create(skel->getName()));
const Nif::Node *node = static_cast<const Nif::Node*>(nif->getRoot(0));
std::vector<const Nif::NiKeyframeController*> ctrls;
Ogre::Bone *animroot = NULL;
TextKeyMap textkeys;
try {
buildBones(skel, node, animroot, textkeys, ctrls);
}
catch(std::exception &e) {
std::cerr<< "Exception while loading "<<skel->getName() <<std::endl;
std::cerr<< e.what() <<std::endl;
return;
}
/* Animations without textkeys don't get Ogre::Animation objects. */
if(!animroot)
return;
std::vector<std::string> targets;
// TODO: If ctrls.size() == 0, check for a .kf file sharing the name of the .nif file
if(ctrls.size() == 0) // No animations? Then we're done.
return;
float maxtime = 0.0f;
for(size_t i = 0;i < ctrls.size();i++)
{
const Nif::NiKeyframeController *ctrl = ctrls[i];
maxtime = std::max(maxtime, ctrl->timeStop);
Nif::Named *target = dynamic_cast<Nif::Named*>(ctrl->target.getPtr());
if(target != NULL)
targets.push_back(target->name);
}
if(targets.size() != ctrls.size())
{
warn("Target size mismatch ("+Ogre::StringConverter::toString(targets.size())+" targets, "+
Ogre::StringConverter::toString(ctrls.size())+" controllers)");
return;
}
Ogre::UserObjectBindings &bindings = animroot->getUserObjectBindings();
bindings.setUserAny(sTextKeyExtraDataID, Ogre::Any(true));
std::string currentgroup;
TextKeyMap::const_iterator keyiter = textkeys.begin();
for(keyiter = textkeys.begin();keyiter != textkeys.end();keyiter++)
{
std::string::size_type sep = keyiter->second.find(':');
if((sep == currentgroup.length() && keyiter->second.compare(0, sep, currentgroup) == 0) ||
(sep == sizeof("soundgen")-1 && keyiter->second.compare(0, sep, "soundgen") == 0) ||
(sep == sizeof("sound")-1 && keyiter->second.compare(0, sep, "sound") == 0))
continue;
currentgroup = keyiter->second.substr(0, sep);
if(skel->hasAnimation(currentgroup))
continue;
TextKeyMap::const_iterator lastkeyiter = textkeys.end();
while((--lastkeyiter)->first > keyiter->first)
{
if(lastkeyiter->second.find(':') == currentgroup.length() &&
lastkeyiter->second.compare(0, currentgroup.length(), currentgroup) == 0)
break;
}
buildAnimation(skel, currentgroup, ctrls, targets, keyiter->first, lastkeyiter->first);
TextKeyMap::const_iterator insiter(keyiter);
TextKeyMap groupkeys;
do {
sep = insiter->second.find(':');
if(sep == currentgroup.length() && insiter->second.compare(0, sep, currentgroup) == 0)
groupkeys.insert(std::make_pair(insiter->first, insiter->second.substr(sep+2)));
else if((sep == sizeof("soundgen")-1 && insiter->second.compare(0, sep, "soundgen") == 0) ||
(sep == sizeof("sound")-1 && insiter->second.compare(0, sep, "sound") == 0))
groupkeys.insert(std::make_pair(insiter->first, insiter->second));
} while(insiter++ != lastkeyiter);
bindings.setUserAny(std::string(sTextKeyExtraDataID)+"@"+currentgroup, Ogre::Any(groupkeys));
}
}
Ogre::SkeletonPtr NIFSkeletonLoader::createSkeleton(const std::string &name, const std::string &group, const Nif::Node *node)
{
/* We need to be a little aggressive here, since some NIFs have a crap-ton
* of nodes and Ogre only supports 256 bones. We will skip a skeleton if:
* There are no bones used for skinning, there are no controllers on non-
* NiTriShape nodes, there are no nodes named "AttachLight", and the tree
* consists of NiNode, NiTriShape, and RootCollisionNode types only.
*/
if(!node->boneTrafo)
{
if(node->recType == Nif::RC_NiTriShape)
return Ogre::SkeletonPtr();
if(node->controller.empty() && node->name != "AttachLight")
{
if(node->recType == Nif::RC_NiNode || node->recType == Nif::RC_RootCollisionNode)
{
const Nif::NiNode *ninode = static_cast<const Nif::NiNode*>(node);
const Nif::NodeList &children = ninode->children;
for(size_t i = 0;i < children.length();i++)
{
if(!children[i].empty())
{
Ogre::SkeletonPtr skel = createSkeleton(name, group, children[i].getPtr());
if(!skel.isNull())
return skel;
}
}
return Ogre::SkeletonPtr();
}
}
}
Ogre::SkeletonManager &skelMgr = Ogre::SkeletonManager::getSingleton();
return skelMgr.create(name, group, true, &sLoaders[name]);
}
// Looks up an Ogre Bone handle ID from a NIF's record index. Should only be
// used when the bone name is insufficient as this is a relatively slow lookup
int NIFSkeletonLoader::lookupOgreBoneHandle(const std::string &nifname, int idx)
{
LoaderMap::const_iterator loader = sLoaders.find(nifname);
if(loader != sLoaders.end())
{
std::map<int,int>::const_iterator entry = loader->second.mNifToOgreHandleMap.find(idx);
if(entry != loader->second.mNifToOgreHandleMap.end())
return entry->second;
}
throw std::runtime_error("Invalid NIF record lookup ("+nifname+", index "+Ogre::StringConverter::toString(idx)+")");
}
NIFSkeletonLoader::LoaderMap NIFSkeletonLoader::sLoaders;
}

@ -0,0 +1,63 @@
#ifndef COMPONENTS_NIFOGRE_SKELETON_HPP
#define COMPONENTS_NIFOGRE_SKELETON_HPP
#include <iostream>
#include <string>
#include <cassert>
#include <OgreResource.h>
#include "ogrenifloader.hpp"
namespace Nif
{
class NiTextKeyExtraData;
class Node;
class NiKeyframeController;
}
namespace NifOgre
{
/** Manual resource loader for NIF skeletons. This is the main class
responsible for translating the internal NIF skeleton structure into
something Ogre can use (includes animations and node TextKeyData).
*/
class NIFSkeletonLoader : public Ogre::ManualResourceLoader
{
static void warn(const std::string &msg)
{
std::cerr << "NIFSkeletonLoader: Warn: " << msg << std::endl;
}
static void fail(const std::string &msg)
{
std::cerr << "NIFSkeletonLoader: Fail: "<< msg << std::endl;
abort();
}
static void buildAnimation(Ogre::Skeleton *skel, const std::string &name, const std::vector<const Nif::NiKeyframeController*> &ctrls, const std::vector<std::string> &targets, float startTime, float stopTime);
static TextKeyMap extractTextKeys(const Nif::NiTextKeyExtraData *tk);
void buildBones(Ogre::Skeleton *skel, const Nif::Node *node, Ogre::Bone *&animroot, TextKeyMap &textkeys, std::vector<Nif::NiKeyframeController const*> &ctrls, Ogre::Bone *parent=NULL);
// Lookup to retrieve an Ogre bone handle for a given Nif record index
std::map<int,int> mNifToOgreHandleMap;
typedef std::map<std::string,NIFSkeletonLoader> LoaderMap;
static LoaderMap sLoaders;
public:
void loadResource(Ogre::Resource *resource);
static Ogre::SkeletonPtr createSkeleton(const std::string &name, const std::string &group, const Nif::Node *node);
// Looks up an Ogre Bone handle ID from a NIF's record index. Should only
// be used when the bone name is insufficient as this is a relatively slow
// lookup
static int lookupOgreBoneHandle(const std::string &nifname, int idx);
};
}
#endif
Loading…
Cancel
Save