mirror of https://github.com/OpenMW/openmw.git
Delete the old nifogre loader
parent
376f0f3ac1
commit
1affa497d5
@ -1,111 +0,0 @@
|
||||
#ifndef COMPONENTS_NIFOGRE_CONTROLLER_H
|
||||
#define COMPONENTS_NIFOGRE_CONTROLLER_H
|
||||
|
||||
#include <components/nif/niffile.hpp>
|
||||
#include <components/nif/nifkey.hpp>
|
||||
#include <OgreController.h>
|
||||
|
||||
namespace NifOgre
|
||||
{
|
||||
|
||||
class ValueInterpolator
|
||||
{
|
||||
protected:
|
||||
float interpKey(const Nif::FloatKeyMap::MapType &keys, float time, float def=0.f) const
|
||||
{
|
||||
if (keys.size() == 0)
|
||||
return def;
|
||||
|
||||
if(time <= keys.begin()->first)
|
||||
return keys.begin()->second.mValue;
|
||||
|
||||
Nif::FloatKeyMap::MapType::const_iterator it = keys.lower_bound(time);
|
||||
if (it != keys.end())
|
||||
{
|
||||
float aTime = it->first;
|
||||
const Nif::FloatKey* aKey = &it->second;
|
||||
|
||||
assert (it != keys.begin()); // Shouldn't happen, was checked at beginning of this function
|
||||
|
||||
Nif::FloatKeyMap::MapType::const_iterator last = --it;
|
||||
float aLastTime = last->first;
|
||||
const Nif::FloatKey* aLastKey = &last->second;
|
||||
|
||||
float a = (time - aLastTime) / (aTime - aLastTime);
|
||||
return aLastKey->mValue + ((aKey->mValue - aLastKey->mValue) * a);
|
||||
}
|
||||
else
|
||||
return keys.rbegin()->second.mValue;
|
||||
}
|
||||
|
||||
Ogre::Vector3 interpKey(const Nif::Vector3KeyMap::MapType &keys, float time) const
|
||||
{
|
||||
if(time <= keys.begin()->first)
|
||||
return keys.begin()->second.mValue;
|
||||
|
||||
Nif::Vector3KeyMap::MapType::const_iterator it = keys.lower_bound(time);
|
||||
if (it != keys.end())
|
||||
{
|
||||
float aTime = it->first;
|
||||
const Nif::Vector3Key* aKey = &it->second;
|
||||
|
||||
assert (it != keys.begin()); // Shouldn't happen, was checked at beginning of this function
|
||||
|
||||
Nif::Vector3KeyMap::MapType::const_iterator last = --it;
|
||||
float aLastTime = last->first;
|
||||
const Nif::Vector3Key* aLastKey = &last->second;
|
||||
|
||||
float a = (time - aLastTime) / (aTime - aLastTime);
|
||||
return aLastKey->mValue + ((aKey->mValue - aLastKey->mValue) * a);
|
||||
}
|
||||
else
|
||||
return keys.rbegin()->second.mValue;
|
||||
}
|
||||
};
|
||||
|
||||
// FIXME: Should not be here.
|
||||
class DefaultFunction : public Ogre::ControllerFunction<Ogre::Real>
|
||||
{
|
||||
private:
|
||||
float mFrequency;
|
||||
float mPhase;
|
||||
float mStartTime;
|
||||
public:
|
||||
float mStopTime;
|
||||
|
||||
public:
|
||||
DefaultFunction(const Nif::Controller *ctrl, bool deltaInput)
|
||||
: Ogre::ControllerFunction<Ogre::Real>(deltaInput)
|
||||
, mFrequency(ctrl->frequency)
|
||||
, mPhase(ctrl->phase)
|
||||
, mStartTime(ctrl->timeStart)
|
||||
, mStopTime(ctrl->timeStop)
|
||||
{
|
||||
if(mDeltaInput)
|
||||
mDeltaCount = mPhase;
|
||||
}
|
||||
|
||||
virtual Ogre::Real calculate(Ogre::Real value)
|
||||
{
|
||||
if(mDeltaInput)
|
||||
{
|
||||
if (mStopTime - mStartTime == 0.f)
|
||||
return 0.f;
|
||||
|
||||
mDeltaCount += value*mFrequency;
|
||||
if(mDeltaCount < mStartTime)
|
||||
mDeltaCount = mStopTime - std::fmod(mStartTime - mDeltaCount,
|
||||
mStopTime - mStartTime);
|
||||
mDeltaCount = std::fmod(mDeltaCount - mStartTime,
|
||||
mStopTime - mStartTime) + mStartTime;
|
||||
return mDeltaCount;
|
||||
}
|
||||
|
||||
value = std::min(mStopTime, std::max(mStartTime, value+mPhase));
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -1,442 +0,0 @@
|
||||
#include "material.hpp"
|
||||
|
||||
#include <components/nif/node.hpp>
|
||||
#include <components/misc/resourcehelpers.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/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";
|
||||
}
|
||||
|
||||
static void setTextureProperties(sh::MaterialInstance* material, const std::string& textureSlotName, const Nif::NiTexturingProperty::Texture& tex)
|
||||
{
|
||||
material->setProperty(textureSlotName + "UVSet", sh::makeProperty(new sh::IntValue(tex.uvSet)));
|
||||
const std::string clampMode = textureSlotName + "ClampMode";
|
||||
switch (tex.clamp)
|
||||
{
|
||||
case 0:
|
||||
material->setProperty(clampMode, sh::makeProperty(new sh::StringValue("clamp clamp")));
|
||||
break;
|
||||
case 1:
|
||||
material->setProperty(clampMode, sh::makeProperty(new sh::StringValue("clamp wrap")));
|
||||
break;
|
||||
case 2:
|
||||
material->setProperty(clampMode, sh::makeProperty(new sh::StringValue("wrap clamp")));
|
||||
break;
|
||||
case 3:
|
||||
default:
|
||||
material->setProperty(clampMode, sh::makeProperty(new sh::StringValue("wrap wrap")));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
const Nif::NiStencilProperty *stencilprop,
|
||||
bool &needTangents, bool particleMaterial)
|
||||
{
|
||||
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;
|
||||
int drawMode = 1;
|
||||
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] = Misc::ResourceHelpers::correctTexturePath(st->filename);
|
||||
else
|
||||
warn("Found internal texture, ignoring.");
|
||||
}
|
||||
|
||||
Nif::ControllerPtr ctrls = texprop->controller;
|
||||
while(!ctrls.empty())
|
||||
{
|
||||
if (ctrls->recType != Nif::RC_NiFlipController) // Handled in ogrenifloader
|
||||
warn("Unhandled texture controller "+ctrls->recName+" in "+name);
|
||||
ctrls = ctrls->next;
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
if(stencilprop)
|
||||
{
|
||||
drawMode = stencilprop->data.drawMode;
|
||||
if (stencilprop->data.enabled)
|
||||
warn("Unhandled stencil test in "+name);
|
||||
|
||||
Nif::ControllerPtr ctrls = stencilprop->controller;
|
||||
while(!ctrls.empty())
|
||||
{
|
||||
warn("Unhandled stencil 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())
|
||||
{
|
||||
if (ctrls->recType != Nif::RC_NiAlphaController && ctrls->recType != Nif::RC_NiMaterialColorController)
|
||||
warn("Unhandled material controller "+ctrls->recName+" in "+name);
|
||||
ctrls = ctrls->next;
|
||||
}
|
||||
}
|
||||
|
||||
if (particleMaterial)
|
||||
{
|
||||
alpha = 1.f; // Apparently ignored, might be overridden by particle vertex colors?
|
||||
}
|
||||
|
||||
{
|
||||
// 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, texprop->textures[i].clamp);
|
||||
boost::hash_combine(h, texprop->textures[i].uvSet);
|
||||
}
|
||||
}
|
||||
boost::hash_combine(h, drawMode);
|
||||
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
|
||||
sh::MaterialInstance* instance = sh::Factory::getInstance().getMaterialInstance(itr->second);
|
||||
needTangents = !sh::retrieveValue<sh::StringValue>(instance->getProperty("normalMap"), instance).get().empty();
|
||||
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")));
|
||||
}
|
||||
|
||||
if (drawMode == 1)
|
||||
instance->setProperty("cullmode", sh::makeProperty(new sh::StringValue("clockwise")));
|
||||
else if (drawMode == 2)
|
||||
instance->setProperty("cullmode", sh::makeProperty(new sh::StringValue("anticlockwise")));
|
||||
else if (drawMode == 3)
|
||||
instance->setProperty("cullmode", sh::makeProperty(new sh::StringValue("none")));
|
||||
|
||||
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]));
|
||||
instance->setProperty("darkMap", sh::makeProperty(texName[Nif::NiTexturingProperty::DarkTexture]));
|
||||
if (!texName[Nif::NiTexturingProperty::BaseTexture].empty())
|
||||
{
|
||||
instance->setProperty("use_diffuse_map", sh::makeProperty(new sh::BooleanValue(true)));
|
||||
setTextureProperties(instance, "diffuseMap", texprop->textures[Nif::NiTexturingProperty::BaseTexture]);
|
||||
}
|
||||
if (!texName[Nif::NiTexturingProperty::GlowTexture].empty())
|
||||
{
|
||||
instance->setProperty("use_emissive_map", sh::makeProperty(new sh::BooleanValue(true)));
|
||||
setTextureProperties(instance, "emissiveMap", texprop->textures[Nif::NiTexturingProperty::GlowTexture]);
|
||||
}
|
||||
if (!texName[Nif::NiTexturingProperty::DetailTexture].empty())
|
||||
{
|
||||
instance->setProperty("use_detail_map", sh::makeProperty(new sh::BooleanValue(true)));
|
||||
setTextureProperties(instance, "detailMap", texprop->textures[Nif::NiTexturingProperty::DetailTexture]);
|
||||
}
|
||||
if (!texName[Nif::NiTexturingProperty::DarkTexture].empty())
|
||||
{
|
||||
instance->setProperty("use_dark_map", sh::makeProperty(new sh::BooleanValue(true)));
|
||||
setTextureProperties(instance, "darkMap", texprop->textures[Nif::NiTexturingProperty::DarkTexture]);
|
||||
}
|
||||
|
||||
bool useParallax = !texName[Nif::NiTexturingProperty::BumpTexture].empty()
|
||||
&& texName[Nif::NiTexturingProperty::BumpTexture].find("_nh.") != std::string::npos;
|
||||
instance->setProperty("use_parallax", sh::makeProperty(new sh::BooleanValue(useParallax)));
|
||||
|
||||
for(int i = 0;i < 7;i++)
|
||||
{
|
||||
if(i == Nif::NiTexturingProperty::BaseTexture ||
|
||||
i == Nif::NiTexturingProperty::DetailTexture ||
|
||||
i == Nif::NiTexturingProperty::DarkTexture ||
|
||||
i == Nif::NiTexturingProperty::BumpTexture ||
|
||||
i == Nif::NiTexturingProperty::GlowTexture)
|
||||
continue;
|
||||
if(!texName[i].empty())
|
||||
warn("Ignored texture "+texName[i]+" on layer "+Ogre::StringConverter::toString(i) + " in " + name);
|
||||
}
|
||||
|
||||
if (vertexColour)
|
||||
instance->setProperty("has_vertex_colour", sh::makeProperty(new sh::BooleanValue(true)));
|
||||
|
||||
// Override alpha flags based on our override list (transparency-overrides.cfg)
|
||||
if ((alphaFlags&1) && !texName[0].empty())
|
||||
{
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// Add transparency if NiAlphaProperty was present
|
||||
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)
|
||||
{
|
||||
#ifndef ANDROID
|
||||
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
|
||||
// alpha test not supported in OpenGL ES 2, use manual implementation in shader
|
||||
instance->setProperty("alphaTestMode", sh::makeProperty(new sh::IntValue((alphaFlags>>10)&0x7)));
|
||||
instance->setProperty("alphaTestValue", sh::makeProperty(new sh::FloatValue(alphaTest/255.f)));
|
||||
#endif
|
||||
}
|
||||
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???
|
||||
|
||||
if (!texName[0].empty())
|
||||
NifOverrides::Overrides::getMaterialOverrides(texName[0], instance);
|
||||
|
||||
// Don't use texName, as it may be overridden
|
||||
needTangents = !sh::retrieveValue<sh::StringValue>(instance->getProperty("normalMap"), instance).get().empty();
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
std::map<size_t,std::string> NIFMaterialLoader::sMaterialMap;
|
||||
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
#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;
|
||||
class NiStencilProperty;
|
||||
}
|
||||
|
||||
namespace NifOgre
|
||||
{
|
||||
|
||||
class NIFMaterialLoader {
|
||||
static void warn(const std::string &msg)
|
||||
{
|
||||
std::cerr << "NIFMaterialLoader: Warn: " << msg << std::endl;
|
||||
}
|
||||
|
||||
static std::map<size_t,std::string> sMaterialMap;
|
||||
|
||||
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,
|
||||
const Nif::NiStencilProperty *stencilprop,
|
||||
bool &needTangents, bool particleMaterial=false);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -1,412 +0,0 @@
|
||||
#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 <OgreSkeleton.h>
|
||||
#include <OgreKeyFrame.h>
|
||||
|
||||
#include <components/nif/node.hpp>
|
||||
#include <components/nifcache/nifcache.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)
|
||||
{
|
||||
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);
|
||||
|
||||
// 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::Matrix4 mat;
|
||||
mat.makeTransform(data->bones[b].trafo.trans, Ogre::Vector3(data->bones[b].trafo.scale),
|
||||
Ogre::Quaternion(data->bones[b].trafo.rotation));
|
||||
mat = bones[b]->getWorldTransform() * 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();
|
||||
if (numUVs)
|
||||
{
|
||||
size_t elemSize = Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT2);
|
||||
|
||||
for(size_t i = 0; i < numUVs; i++)
|
||||
decl->addElement(nextBuf, elemSize*i, Ogre::VET_FLOAT2, Ogre::VES_TEXTURE_COORDINATES, i);
|
||||
|
||||
vbuf = hwBufMgr->createVertexBuffer(decl->getVertexSize(nextBuf), srcVerts.size(),
|
||||
Ogre::HardwareBuffer::HBU_STATIC);
|
||||
|
||||
std::vector<Ogre::Vector2> allUVs;
|
||||
allUVs.reserve(srcVerts.size()*numUVs);
|
||||
for (size_t vert = 0; vert<srcVerts.size(); ++vert)
|
||||
for(size_t i = 0; i < numUVs; i++)
|
||||
allUVs.push_back(data->uvlist[i][vert]);
|
||||
|
||||
vbuf->writeData(0, elemSize*srcVerts.size()*numUVs, &allUVs[0], true);
|
||||
|
||||
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)
|
||||
{
|
||||
Ogre::SkeletonPtr skel = Ogre::SkeletonManager::getSingleton().getByName(mName);
|
||||
|
||||
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;
|
||||
const Nif::NiStencilProperty *stencilprop = NULL;
|
||||
bool needTangents = false;
|
||||
|
||||
shape->getProperties(texprop, matprop, alphaprop, vertprop, zprop, specprop, wireprop, stencilprop);
|
||||
std::string matname = NIFMaterialLoader::getMaterial(data, mesh->getName(), mGroup,
|
||||
texprop, matprop, alphaprop,
|
||||
vertprop, zprop, specprop,
|
||||
wireprop, stencilprop, 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);
|
||||
}
|
||||
|
||||
|
||||
if(!shape->controller.empty())
|
||||
{
|
||||
Nif::ControllerPtr ctrl = shape->controller;
|
||||
do {
|
||||
// Load GeomMorpherController into an Ogre::Pose and Animation
|
||||
if(ctrl->recType == Nif::RC_NiGeomMorpherController && ctrl->flags & Nif::NiNode::ControllerFlag_Active)
|
||||
{
|
||||
const Nif::NiGeomMorpherController *geom =
|
||||
static_cast<const Nif::NiGeomMorpherController*>(ctrl.getPtr());
|
||||
|
||||
const std::vector<Nif::NiMorphData::MorphData>& morphs = geom->data.getPtr()->mMorphs;
|
||||
// Note we are not interested in morph 0, which just contains the original vertices
|
||||
for (unsigned int i = 1; i < morphs.size(); ++i)
|
||||
{
|
||||
Ogre::Pose* pose = mesh->createPose(i);
|
||||
const Nif::NiMorphData::MorphData& data = morphs[i];
|
||||
for (unsigned int v = 0; v < data.mVertices.size(); ++v)
|
||||
pose->addVertex(v, data.mVertices[v]);
|
||||
|
||||
Ogre::String animationID = Ogre::StringConverter::toString(ctrl->recIndex)
|
||||
+ "_" + Ogre::StringConverter::toString(i);
|
||||
Ogre::VertexAnimationTrack* track =
|
||||
mesh->createAnimation(animationID, 0)
|
||||
->createVertexTrack(1, Ogre::VAT_POSE);
|
||||
Ogre::VertexPoseKeyFrame* keyframe = track->createVertexPoseKeyFrame(0);
|
||||
keyframe->addPoseReference(i-1, 1);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
} while(!(ctrl=ctrl->next).empty());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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 = static_cast<Ogre::Mesh*>(resource);
|
||||
OgreAssert(mesh, "Attempting to load a mesh into a non-mesh resource!");
|
||||
|
||||
Nif::NIFFilePtr nif = Nif::Cache::getInstance().load(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, static_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);
|
||||
}
|
||||
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
#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
@ -1,151 +0,0 @@
|
||||
/*
|
||||
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.h) 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/ .
|
||||
|
||||
*/
|
||||
|
||||
#ifndef OPENMW_COMPONENTS_NIFOGRE_OGRENIFLOADER_HPP
|
||||
#define OPENMW_COMPONENTS_NIFOGRE_OGRENIFLOADER_HPP
|
||||
|
||||
#include <OgreResource.h>
|
||||
#include <OgreMaterial.h>
|
||||
#include <OgreController.h>
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
|
||||
// FIXME: This namespace really doesn't do anything Nif-specific. Any supportable
|
||||
// model format should go through this.
|
||||
namespace NifOgre
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Clones materials as necessary to not make controllers affect other objects (that share the original material).
|
||||
*/
|
||||
class MaterialControllerManager
|
||||
{
|
||||
public:
|
||||
~MaterialControllerManager();
|
||||
|
||||
/// @attention if \a movable is an Entity, it needs to have *one* SubEntity
|
||||
Ogre::MaterialPtr getWritableMaterial (Ogre::MovableObject* movable);
|
||||
|
||||
private:
|
||||
std::map<Ogre::MovableObject*, Ogre::MaterialPtr> mClonedMaterials;
|
||||
};
|
||||
|
||||
typedef std::multimap<float,std::string> TextKeyMap;
|
||||
static const char sTextKeyExtraDataID[] = "TextKeyExtraData";
|
||||
struct ObjectScene {
|
||||
Ogre::Entity *mSkelBase;
|
||||
std::vector<Ogre::Entity*> mEntities;
|
||||
std::vector<Ogre::ParticleSystem*> mParticles;
|
||||
std::vector<Ogre::Light*> mLights;
|
||||
|
||||
// Nodes that should always face the camera when rendering
|
||||
std::vector<Ogre::Node*> mBillboardNodes;
|
||||
|
||||
Ogre::SceneManager* mSceneMgr;
|
||||
|
||||
// The maximum length on any of the controllers. For animations with controllers, but no text keys, consider this the animation length.
|
||||
float mMaxControllerLength;
|
||||
|
||||
TextKeyMap mTextKeys;
|
||||
|
||||
MaterialControllerManager mMaterialControllerMgr;
|
||||
|
||||
std::vector<Ogre::Controller<Ogre::Real> > mControllers;
|
||||
|
||||
ObjectScene(Ogre::SceneManager* sceneMgr) : mSkelBase(0), mMaxControllerLength(0), mSceneMgr(sceneMgr)
|
||||
{ }
|
||||
|
||||
~ObjectScene();
|
||||
|
||||
// Rotate nodes in mBillboardNodes so they face the given camera
|
||||
void rotateBillboardNodes(Ogre::Camera* camera);
|
||||
|
||||
void setVisibilityFlags (unsigned int flags);
|
||||
|
||||
// This is called internally by the OgreNifLoader once all elements of the
|
||||
// scene have been attached to their respective nodes.
|
||||
void _notifyAttached();
|
||||
};
|
||||
|
||||
typedef Ogre::SharedPtr<ObjectScene> ObjectScenePtr;
|
||||
|
||||
|
||||
class Loader
|
||||
{
|
||||
public:
|
||||
static ObjectScenePtr createObjects(Ogre::Entity *parent, const std::string &bonename,
|
||||
const std::string& filter,
|
||||
Ogre::SceneNode *parentNode,
|
||||
std::string name,
|
||||
const std::string &group="General");
|
||||
|
||||
static ObjectScenePtr createObjects(Ogre::SceneNode *parentNode,
|
||||
std::string name,
|
||||
const std::string &group="General");
|
||||
|
||||
static ObjectScenePtr createObjectBase(Ogre::SceneNode *parentNode,
|
||||
std::string name,
|
||||
const std::string &group="General");
|
||||
|
||||
/// Set whether or not nodes marked as "MRK" should be shown.
|
||||
/// These should be hidden ingame, but visible in the editior.
|
||||
/// Default: false.
|
||||
static void setShowMarkers(bool show);
|
||||
|
||||
static void createKfControllers(Ogre::Entity *skelBase,
|
||||
const std::string &name,
|
||||
TextKeyMap &textKeys,
|
||||
std::vector<Ogre::Controller<Ogre::Real> > &ctrls);
|
||||
|
||||
private:
|
||||
static bool sShowMarkers;
|
||||
};
|
||||
|
||||
// FIXME: Should be with other general Ogre extensions.
|
||||
template<typename T>
|
||||
class NodeTargetValue : public Ogre::ControllerValue<T>
|
||||
{
|
||||
protected:
|
||||
Ogre::Node *mNode;
|
||||
|
||||
public:
|
||||
NodeTargetValue(Ogre::Node *target) : mNode(target)
|
||||
{ }
|
||||
|
||||
virtual Ogre::Quaternion getRotation(T value) const = 0;
|
||||
virtual Ogre::Vector3 getTranslation(T value) const = 0;
|
||||
virtual Ogre::Vector3 getScale(T value) const = 0;
|
||||
|
||||
void setNode(Ogre::Node *target)
|
||||
{ mNode = target; }
|
||||
Ogre::Node *getNode() const
|
||||
{ return mNode; }
|
||||
};
|
||||
typedef Ogre::SharedPtr<NodeTargetValue<Ogre::Real> > NodeTargetValueRealPtr;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -1,773 +0,0 @@
|
||||
#include "particles.hpp"
|
||||
|
||||
#include <OgreStringConverter.h>
|
||||
#include <OgreParticleSystem.h>
|
||||
#include <OgreParticleEmitter.h>
|
||||
#include <OgreParticleAffector.h>
|
||||
#include <OgreParticle.h>
|
||||
#include <OgreBone.h>
|
||||
#include <OgreTagPoint.h>
|
||||
#include <OgreEntity.h>
|
||||
#include <OgreSkeletonInstance.h>
|
||||
#include <OgreSceneNode.h>
|
||||
#include <OgreSceneManager.h>
|
||||
|
||||
/* FIXME: "Nif" isn't really an appropriate emitter name. */
|
||||
class NifEmitter : public Ogre::ParticleEmitter
|
||||
{
|
||||
public:
|
||||
std::vector<Ogre::Bone*> mEmitterBones;
|
||||
Ogre::Bone* mParticleBone;
|
||||
|
||||
Ogre::ParticleSystem* getPartSys() { return mParent; }
|
||||
|
||||
/** Command object for the emitter width (see Ogre::ParamCommand).*/
|
||||
class CmdWidth : public Ogre::ParamCommand
|
||||
{
|
||||
public:
|
||||
Ogre::String doGet(const void *target) const
|
||||
{
|
||||
return Ogre::StringConverter::toString(static_cast<const NifEmitter*>(target)->getWidth());
|
||||
}
|
||||
void doSet(void *target, const Ogre::String &val)
|
||||
{
|
||||
static_cast<NifEmitter*>(target)->setWidth(Ogre::StringConverter::parseReal(val));
|
||||
}
|
||||
};
|
||||
|
||||
/** Command object for the emitter height (see Ogre::ParamCommand).*/
|
||||
class CmdHeight : public Ogre::ParamCommand
|
||||
{
|
||||
public:
|
||||
Ogre::String doGet(const void *target) const
|
||||
{
|
||||
return Ogre::StringConverter::toString(static_cast<const NifEmitter*>(target)->getHeight());
|
||||
}
|
||||
void doSet(void *target, const Ogre::String &val)
|
||||
{
|
||||
static_cast<NifEmitter*>(target)->setHeight(Ogre::StringConverter::parseReal(val));
|
||||
}
|
||||
};
|
||||
|
||||
/** Command object for the emitter depth (see Ogre::ParamCommand).*/
|
||||
class CmdDepth : public Ogre::ParamCommand
|
||||
{
|
||||
public:
|
||||
Ogre::String doGet(const void *target) const
|
||||
{
|
||||
return Ogre::StringConverter::toString(static_cast<const NifEmitter*>(target)->getDepth());
|
||||
}
|
||||
void doSet(void *target, const Ogre::String &val)
|
||||
{
|
||||
static_cast<NifEmitter*>(target)->setDepth(Ogre::StringConverter::parseReal(val));
|
||||
}
|
||||
};
|
||||
|
||||
/** Command object for the emitter vertical_direction (see Ogre::ParamCommand).*/
|
||||
class CmdVerticalDir : public Ogre::ParamCommand
|
||||
{
|
||||
public:
|
||||
Ogre::String doGet(const void *target) const
|
||||
{
|
||||
const NifEmitter *self = static_cast<const NifEmitter*>(target);
|
||||
return Ogre::StringConverter::toString(self->getVerticalDirection().valueDegrees());
|
||||
}
|
||||
void doSet(void *target, const Ogre::String &val)
|
||||
{
|
||||
NifEmitter *self = static_cast<NifEmitter*>(target);
|
||||
self->setVerticalDirection(Ogre::Degree(Ogre::StringConverter::parseReal(val)));
|
||||
}
|
||||
};
|
||||
|
||||
/** Command object for the emitter vertical_angle (see Ogre::ParamCommand).*/
|
||||
class CmdVerticalAngle : public Ogre::ParamCommand
|
||||
{
|
||||
public:
|
||||
Ogre::String doGet(const void *target) const
|
||||
{
|
||||
const NifEmitter *self = static_cast<const NifEmitter*>(target);
|
||||
return Ogre::StringConverter::toString(self->getVerticalAngle().valueDegrees());
|
||||
}
|
||||
void doSet(void *target, const Ogre::String &val)
|
||||
{
|
||||
NifEmitter *self = static_cast<NifEmitter*>(target);
|
||||
self->setVerticalAngle(Ogre::Degree(Ogre::StringConverter::parseReal(val)));
|
||||
}
|
||||
};
|
||||
|
||||
/** Command object for the emitter horizontal_direction (see Ogre::ParamCommand).*/
|
||||
class CmdHorizontalDir : public Ogre::ParamCommand
|
||||
{
|
||||
public:
|
||||
Ogre::String doGet(const void *target) const
|
||||
{
|
||||
const NifEmitter *self = static_cast<const NifEmitter*>(target);
|
||||
return Ogre::StringConverter::toString(self->getHorizontalDirection().valueDegrees());
|
||||
}
|
||||
void doSet(void *target, const Ogre::String &val)
|
||||
{
|
||||
NifEmitter *self = static_cast<NifEmitter*>(target);
|
||||
self->setHorizontalDirection(Ogre::Degree(Ogre::StringConverter::parseReal(val)));
|
||||
}
|
||||
};
|
||||
|
||||
/** Command object for the emitter horizontal_angle (see Ogre::ParamCommand).*/
|
||||
class CmdHorizontalAngle : public Ogre::ParamCommand
|
||||
{
|
||||
public:
|
||||
Ogre::String doGet(const void *target) const
|
||||
{
|
||||
const NifEmitter *self = static_cast<const NifEmitter*>(target);
|
||||
return Ogre::StringConverter::toString(self->getHorizontalAngle().valueDegrees());
|
||||
}
|
||||
void doSet(void *target, const Ogre::String &val)
|
||||
{
|
||||
NifEmitter *self = static_cast<NifEmitter*>(target);
|
||||
self->setHorizontalAngle(Ogre::Degree(Ogre::StringConverter::parseReal(val)));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
NifEmitter(Ogre::ParticleSystem *psys)
|
||||
: Ogre::ParticleEmitter(psys)
|
||||
, mEmitterBones(Ogre::any_cast<NiNodeHolder>(psys->getUserObjectBindings().getUserAny()).mBones)
|
||||
{
|
||||
assert (!mEmitterBones.empty());
|
||||
Ogre::TagPoint* tag = static_cast<Ogre::TagPoint*>(mParent->getParentNode());
|
||||
mParticleBone = static_cast<Ogre::Bone*>(tag->getParent());
|
||||
initDefaults("Nif");
|
||||
}
|
||||
|
||||
/** See Ogre::ParticleEmitter. */
|
||||
unsigned short _getEmissionCount(Ogre::Real timeElapsed)
|
||||
{
|
||||
// Use basic constant emission
|
||||
return genConstantEmissionCount(timeElapsed);
|
||||
}
|
||||
|
||||
/** See Ogre::ParticleEmitter. */
|
||||
void _initParticle(Ogre::Particle *particle)
|
||||
{
|
||||
Ogre::Vector3 xOff, yOff, zOff;
|
||||
|
||||
// Call superclass
|
||||
ParticleEmitter::_initParticle(particle);
|
||||
|
||||
xOff = Ogre::Math::SymmetricRandom() * mXRange;
|
||||
yOff = Ogre::Math::SymmetricRandom() * mYRange;
|
||||
zOff = Ogre::Math::SymmetricRandom() * mZRange;
|
||||
|
||||
#if OGRE_VERSION >= (1 << 16 | 10 << 8 | 0)
|
||||
Ogre::Vector3& position = particle->mPosition;
|
||||
Ogre::Vector3& direction = particle->mDirection;
|
||||
Ogre::ColourValue& colour = particle->mColour;
|
||||
Ogre::Real& totalTimeToLive = particle->mTotalTimeToLive;
|
||||
Ogre::Real& timeToLive = particle->mTimeToLive;
|
||||
#else
|
||||
Ogre::Vector3& position = particle->position;
|
||||
Ogre::Vector3& direction = particle->direction;
|
||||
Ogre::ColourValue& colour = particle->colour;
|
||||
Ogre::Real& totalTimeToLive = particle->totalTimeToLive;
|
||||
Ogre::Real& timeToLive = particle->timeToLive;
|
||||
#endif
|
||||
|
||||
Ogre::Node* emitterBone = mEmitterBones.at((int)(::rand()/(RAND_MAX+1.0)*mEmitterBones.size()));
|
||||
|
||||
position = xOff + yOff + zOff +
|
||||
mParticleBone->_getDerivedOrientation().Inverse() * (emitterBone->_getDerivedPosition()
|
||||
- mParticleBone->_getDerivedPosition());
|
||||
|
||||
// Generate complex data by reference
|
||||
genEmissionColour(colour);
|
||||
|
||||
// NOTE: We do not use mDirection/mAngle for the initial direction.
|
||||
Ogre::Radian hdir = mHorizontalDir + mHorizontalAngle*Ogre::Math::SymmetricRandom();
|
||||
Ogre::Radian vdir = mVerticalDir + mVerticalAngle*Ogre::Math::SymmetricRandom();
|
||||
direction = (mParticleBone->_getDerivedOrientation().Inverse()
|
||||
* emitterBone->_getDerivedOrientation() *
|
||||
Ogre::Quaternion(hdir, Ogre::Vector3::UNIT_Z) *
|
||||
Ogre::Quaternion(vdir, Ogre::Vector3::UNIT_X)) *
|
||||
Ogre::Vector3::UNIT_Z;
|
||||
|
||||
genEmissionVelocity(direction);
|
||||
|
||||
// Generate simpler data
|
||||
timeToLive = totalTimeToLive = genEmissionTTL();
|
||||
}
|
||||
|
||||
/** Overloaded to update the trans. matrix */
|
||||
void setDirection(const Ogre::Vector3 &dir)
|
||||
{
|
||||
ParticleEmitter::setDirection(dir);
|
||||
genAreaAxes();
|
||||
}
|
||||
|
||||
/** Sets the size of the area from which particles are emitted.
|
||||
@param
|
||||
size Vector describing the size of the area. The area extends
|
||||
around the center point by half the x, y and z components of
|
||||
this vector. The box is aligned such that it's local Z axis points
|
||||
along it's direction (see setDirection)
|
||||
*/
|
||||
void setSize(const Ogre::Vector3 &size)
|
||||
{
|
||||
mSize = size;
|
||||
genAreaAxes();
|
||||
}
|
||||
|
||||
/** Sets the size of the area from which particles are emitted.
|
||||
@param x,y,z
|
||||
Individual axis lengths describing the size of the area. The area
|
||||
extends around the center point by half the x, y and z components
|
||||
of this vector. The box is aligned such that it's local Z axis
|
||||
points along it's direction (see setDirection)
|
||||
*/
|
||||
void setSize(Ogre::Real x, Ogre::Real y, Ogre::Real z)
|
||||
{
|
||||
mSize.x = x;
|
||||
mSize.y = y;
|
||||
mSize.z = z;
|
||||
genAreaAxes();
|
||||
}
|
||||
|
||||
/** Sets the width (local x size) of the emitter. */
|
||||
void setWidth(Ogre::Real width)
|
||||
{
|
||||
mSize.x = width;
|
||||
genAreaAxes();
|
||||
}
|
||||
/** Gets the width (local x size) of the emitter. */
|
||||
Ogre::Real getWidth(void) const
|
||||
{ return mSize.x; }
|
||||
/** Sets the height (local y size) of the emitter. */
|
||||
void setHeight(Ogre::Real height)
|
||||
{
|
||||
mSize.y = height;
|
||||
genAreaAxes();
|
||||
}
|
||||
/** Gets the height (local y size) of the emitter. */
|
||||
Ogre::Real getHeight(void) const
|
||||
{ return mSize.y; }
|
||||
/** Sets the depth (local y size) of the emitter. */
|
||||
void setDepth(Ogre::Real depth)
|
||||
{
|
||||
mSize.z = depth;
|
||||
genAreaAxes();
|
||||
}
|
||||
/** Gets the depth (local y size) of the emitter. */
|
||||
Ogre::Real getDepth(void) const
|
||||
{ return mSize.z; }
|
||||
|
||||
void setVerticalDirection(Ogre::Radian vdir)
|
||||
{ mVerticalDir = vdir; }
|
||||
Ogre::Radian getVerticalDirection(void) const
|
||||
{ return mVerticalDir; }
|
||||
|
||||
void setVerticalAngle(Ogre::Radian vangle)
|
||||
{ mVerticalAngle = vangle; }
|
||||
Ogre::Radian getVerticalAngle(void) const
|
||||
{ return mVerticalAngle; }
|
||||
|
||||
void setHorizontalDirection(Ogre::Radian hdir)
|
||||
{ mHorizontalDir = hdir; }
|
||||
Ogre::Radian getHorizontalDirection(void) const
|
||||
{ return mHorizontalDir; }
|
||||
|
||||
void setHorizontalAngle(Ogre::Radian hangle)
|
||||
{ mHorizontalAngle = hangle; }
|
||||
Ogre::Radian getHorizontalAngle(void) const
|
||||
{ return mHorizontalAngle; }
|
||||
|
||||
|
||||
protected:
|
||||
/// Size of the area
|
||||
Ogre::Vector3 mSize;
|
||||
|
||||
Ogre::Radian mVerticalDir;
|
||||
Ogre::Radian mVerticalAngle;
|
||||
Ogre::Radian mHorizontalDir;
|
||||
Ogre::Radian mHorizontalAngle;
|
||||
|
||||
/// Local axes, not normalised, their magnitude reflects area size
|
||||
Ogre::Vector3 mXRange, mYRange, mZRange;
|
||||
|
||||
/// Internal method for generating the area axes
|
||||
void genAreaAxes(void)
|
||||
{
|
||||
Ogre::Vector3 mLeft = mUp.crossProduct(mDirection);
|
||||
mXRange = mLeft * (mSize.x * 0.5f);
|
||||
mYRange = mUp * (mSize.y * 0.5f);
|
||||
mZRange = mDirection * (mSize.z * 0.5f);
|
||||
}
|
||||
|
||||
/** Internal for initializing some defaults and parameters
|
||||
@return True if custom parameters need initialising
|
||||
*/
|
||||
bool initDefaults(const Ogre::String &t)
|
||||
{
|
||||
// Defaults
|
||||
mDirection = Ogre::Vector3::UNIT_Z;
|
||||
mUp = Ogre::Vector3::UNIT_Y;
|
||||
setSize(100.0f, 100.0f, 100.0f);
|
||||
mType = t;
|
||||
|
||||
// Set up parameters
|
||||
if(createParamDictionary(mType + "Emitter"))
|
||||
{
|
||||
addBaseParameters();
|
||||
Ogre::ParamDictionary *dict = getParamDictionary();
|
||||
|
||||
// Custom params
|
||||
dict->addParameter(Ogre::ParameterDef("width",
|
||||
"Width of the shape in world coordinates.",
|
||||
Ogre::PT_REAL),
|
||||
&msWidthCmd);
|
||||
dict->addParameter(Ogre::ParameterDef("height",
|
||||
"Height of the shape in world coordinates.",
|
||||
Ogre::PT_REAL),
|
||||
&msHeightCmd);
|
||||
dict->addParameter(Ogre::ParameterDef("depth",
|
||||
"Depth of the shape in world coordinates.",
|
||||
Ogre::PT_REAL),
|
||||
&msDepthCmd);
|
||||
|
||||
dict->addParameter(Ogre::ParameterDef("vertical_direction",
|
||||
"Vertical direction of emitted particles (in degrees).",
|
||||
Ogre::PT_REAL),
|
||||
&msVerticalDirCmd);
|
||||
dict->addParameter(Ogre::ParameterDef("vertical_angle",
|
||||
"Vertical direction variance of emitted particles (in degrees).",
|
||||
Ogre::PT_REAL),
|
||||
&msVerticalAngleCmd);
|
||||
dict->addParameter(Ogre::ParameterDef("horizontal_direction",
|
||||
"Horizontal direction of emitted particles (in degrees).",
|
||||
Ogre::PT_REAL),
|
||||
&msHorizontalDirCmd);
|
||||
dict->addParameter(Ogre::ParameterDef("horizontal_angle",
|
||||
"Horizontal direction variance of emitted particles (in degrees).",
|
||||
Ogre::PT_REAL),
|
||||
&msHorizontalAngleCmd);
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Command objects
|
||||
static CmdWidth msWidthCmd;
|
||||
static CmdHeight msHeightCmd;
|
||||
static CmdDepth msDepthCmd;
|
||||
static CmdVerticalDir msVerticalDirCmd;
|
||||
static CmdVerticalAngle msVerticalAngleCmd;
|
||||
static CmdHorizontalDir msHorizontalDirCmd;
|
||||
static CmdHorizontalAngle msHorizontalAngleCmd;
|
||||
};
|
||||
NifEmitter::CmdWidth NifEmitter::msWidthCmd;
|
||||
NifEmitter::CmdHeight NifEmitter::msHeightCmd;
|
||||
NifEmitter::CmdDepth NifEmitter::msDepthCmd;
|
||||
NifEmitter::CmdVerticalDir NifEmitter::msVerticalDirCmd;
|
||||
NifEmitter::CmdVerticalAngle NifEmitter::msVerticalAngleCmd;
|
||||
NifEmitter::CmdHorizontalDir NifEmitter::msHorizontalDirCmd;
|
||||
NifEmitter::CmdHorizontalAngle NifEmitter::msHorizontalAngleCmd;
|
||||
|
||||
Ogre::ParticleEmitter* NifEmitterFactory::createEmitter(Ogre::ParticleSystem *psys)
|
||||
{
|
||||
Ogre::ParticleEmitter *emitter = OGRE_NEW NifEmitter(psys);
|
||||
mEmitters.push_back(emitter);
|
||||
return emitter;
|
||||
}
|
||||
|
||||
|
||||
class GrowFadeAffector : public Ogre::ParticleAffector
|
||||
{
|
||||
public:
|
||||
/** Command object for grow_time (see Ogre::ParamCommand).*/
|
||||
class CmdGrowTime : public Ogre::ParamCommand
|
||||
{
|
||||
public:
|
||||
Ogre::String doGet(const void *target) const
|
||||
{
|
||||
const GrowFadeAffector *self = static_cast<const GrowFadeAffector*>(target);
|
||||
return Ogre::StringConverter::toString(self->getGrowTime());
|
||||
}
|
||||
void doSet(void *target, const Ogre::String &val)
|
||||
{
|
||||
GrowFadeAffector *self = static_cast<GrowFadeAffector*>(target);
|
||||
self->setGrowTime(Ogre::StringConverter::parseReal(val));
|
||||
}
|
||||
};
|
||||
|
||||
/** Command object for fade_time (see Ogre::ParamCommand).*/
|
||||
class CmdFadeTime : public Ogre::ParamCommand
|
||||
{
|
||||
public:
|
||||
Ogre::String doGet(const void *target) const
|
||||
{
|
||||
const GrowFadeAffector *self = static_cast<const GrowFadeAffector*>(target);
|
||||
return Ogre::StringConverter::toString(self->getFadeTime());
|
||||
}
|
||||
void doSet(void *target, const Ogre::String &val)
|
||||
{
|
||||
GrowFadeAffector *self = static_cast<GrowFadeAffector*>(target);
|
||||
self->setFadeTime(Ogre::StringConverter::parseReal(val));
|
||||
}
|
||||
};
|
||||
|
||||
/** Default constructor. */
|
||||
GrowFadeAffector(Ogre::ParticleSystem *psys) : ParticleAffector(psys)
|
||||
{
|
||||
mGrowTime = 0.0f;
|
||||
mFadeTime = 0.0f;
|
||||
|
||||
mType = "GrowFade";
|
||||
|
||||
// Init parameters
|
||||
if(createParamDictionary("GrowFadeAffector"))
|
||||
{
|
||||
Ogre::ParamDictionary *dict = getParamDictionary();
|
||||
|
||||
Ogre::String grow_title("grow_time");
|
||||
Ogre::String fade_title("fade_time");
|
||||
Ogre::String grow_descr("Time from begin to reach full size.");
|
||||
Ogre::String fade_descr("Time from end to shrink.");
|
||||
|
||||
dict->addParameter(Ogre::ParameterDef(grow_title, grow_descr, Ogre::PT_REAL), &msGrowCmd);
|
||||
dict->addParameter(Ogre::ParameterDef(fade_title, fade_descr, Ogre::PT_REAL), &msFadeCmd);
|
||||
}
|
||||
}
|
||||
|
||||
/** See Ogre::ParticleAffector. */
|
||||
void _initParticle(Ogre::Particle *particle)
|
||||
{
|
||||
#if OGRE_VERSION >= (1 << 16 | 10 << 8 | 0)
|
||||
const Ogre::Real life_time = particle->mTotalTimeToLive;
|
||||
Ogre::Real particle_time = particle->mTimeToLive;
|
||||
#else
|
||||
const Ogre::Real life_time = particle->totalTimeToLive;
|
||||
Ogre::Real particle_time = particle->timeToLive;
|
||||
#endif
|
||||
Ogre::Real width = mParent->getDefaultWidth();
|
||||
Ogre::Real height = mParent->getDefaultHeight();
|
||||
if(life_time-particle_time < mGrowTime)
|
||||
{
|
||||
Ogre::Real scale = (life_time-particle_time) / mGrowTime;
|
||||
assert (scale >= 0);
|
||||
// HACK: don't allow zero-sized particles which can rarely cause an AABB assertion in Ogre to fail
|
||||
scale = std::max(scale, 0.00001f);
|
||||
width *= scale;
|
||||
height *= scale;
|
||||
}
|
||||
if(particle_time < mFadeTime)
|
||||
{
|
||||
Ogre::Real scale = particle_time / mFadeTime;
|
||||
assert (scale >= 0);
|
||||
// HACK: don't allow zero-sized particles which can rarely cause an AABB assertion in Ogre to fail
|
||||
scale = std::max(scale, 0.00001f);
|
||||
width *= scale;
|
||||
height *= scale;
|
||||
}
|
||||
particle->setDimensions(width, height);
|
||||
}
|
||||
|
||||
/** See Ogre::ParticleAffector. */
|
||||
void _affectParticles(Ogre::ParticleSystem *psys, Ogre::Real timeElapsed)
|
||||
{
|
||||
Ogre::ParticleIterator pi = psys->_getIterator();
|
||||
while (!pi.end())
|
||||
{
|
||||
Ogre::Particle *p = pi.getNext();
|
||||
#if OGRE_VERSION >= (1 << 16 | 10 << 8 | 0)
|
||||
const Ogre::Real life_time = p->mTotalTimeToLive;
|
||||
Ogre::Real particle_time = p->mTimeToLive;
|
||||
#else
|
||||
const Ogre::Real life_time = p->totalTimeToLive;
|
||||
Ogre::Real particle_time = p->timeToLive;
|
||||
#endif
|
||||
Ogre::Real width = mParent->getDefaultWidth();
|
||||
Ogre::Real height = mParent->getDefaultHeight();
|
||||
if(life_time-particle_time < mGrowTime)
|
||||
{
|
||||
Ogre::Real scale = (life_time-particle_time) / mGrowTime;
|
||||
assert (scale >= 0);
|
||||
// HACK: don't allow zero-sized particles which can rarely cause an AABB assertion in Ogre to fail
|
||||
scale = std::max(scale, 0.00001f);
|
||||
width *= scale;
|
||||
height *= scale;
|
||||
}
|
||||
if(particle_time < mFadeTime)
|
||||
{
|
||||
Ogre::Real scale = particle_time / mFadeTime;
|
||||
assert (scale >= 0);
|
||||
// HACK: don't allow zero-sized particles which can rarely cause an AABB assertion in Ogre to fail
|
||||
scale = std::max(scale, 0.00001f);
|
||||
width *= scale;
|
||||
height *= scale;
|
||||
}
|
||||
p->setDimensions(width, height);
|
||||
}
|
||||
}
|
||||
|
||||
void setGrowTime(Ogre::Real time)
|
||||
{
|
||||
mGrowTime = time;
|
||||
}
|
||||
Ogre::Real getGrowTime() const
|
||||
{ return mGrowTime; }
|
||||
|
||||
void setFadeTime(Ogre::Real time)
|
||||
{
|
||||
mFadeTime = time;
|
||||
}
|
||||
Ogre::Real getFadeTime() const
|
||||
{ return mFadeTime; }
|
||||
|
||||
static CmdGrowTime msGrowCmd;
|
||||
static CmdFadeTime msFadeCmd;
|
||||
|
||||
protected:
|
||||
Ogre::Real mGrowTime;
|
||||
Ogre::Real mFadeTime;
|
||||
};
|
||||
GrowFadeAffector::CmdGrowTime GrowFadeAffector::msGrowCmd;
|
||||
GrowFadeAffector::CmdFadeTime GrowFadeAffector::msFadeCmd;
|
||||
|
||||
Ogre::ParticleAffector *GrowFadeAffectorFactory::createAffector(Ogre::ParticleSystem *psys)
|
||||
{
|
||||
Ogre::ParticleAffector *p = new GrowFadeAffector(psys);
|
||||
mAffectors.push_back(p);
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
class GravityAffector : public Ogre::ParticleAffector
|
||||
{
|
||||
enum ForceType {
|
||||
Type_Wind,
|
||||
Type_Point
|
||||
};
|
||||
|
||||
public:
|
||||
Ogre::Bone* mEmitterBone;
|
||||
Ogre::Bone* mParticleBone;
|
||||
|
||||
Ogre::ParticleSystem* getPartSys() { return mParent; }
|
||||
|
||||
/** Command object for force (see Ogre::ParamCommand).*/
|
||||
class CmdForce : public Ogre::ParamCommand
|
||||
{
|
||||
public:
|
||||
Ogre::String doGet(const void *target) const
|
||||
{
|
||||
const GravityAffector *self = static_cast<const GravityAffector*>(target);
|
||||
return Ogre::StringConverter::toString(self->getForce());
|
||||
}
|
||||
void doSet(void *target, const Ogre::String &val)
|
||||
{
|
||||
GravityAffector *self = static_cast<GravityAffector*>(target);
|
||||
self->setForce(Ogre::StringConverter::parseReal(val));
|
||||
}
|
||||
};
|
||||
|
||||
/** Command object for force_type (see Ogre::ParamCommand).*/
|
||||
class CmdForceType : public Ogre::ParamCommand
|
||||
{
|
||||
static ForceType getTypeFromString(const Ogre::String &type)
|
||||
{
|
||||
if(type == "wind")
|
||||
return Type_Wind;
|
||||
if(type == "point")
|
||||
return Type_Point;
|
||||
OGRE_EXCEPT(Ogre::Exception::ERR_INVALIDPARAMS, "Invalid force type string: "+type,
|
||||
"CmdForceType::getTypeFromString");
|
||||
}
|
||||
|
||||
static Ogre::String getStringFromType(ForceType type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case Type_Wind: return "wind";
|
||||
case Type_Point: return "point";
|
||||
}
|
||||
OGRE_EXCEPT(Ogre::Exception::ERR_INVALIDPARAMS, "Invalid force type enum: "+Ogre::StringConverter::toString(type),
|
||||
"CmdForceType::getStringFromType");
|
||||
}
|
||||
|
||||
public:
|
||||
Ogre::String doGet(const void *target) const
|
||||
{
|
||||
const GravityAffector *self = static_cast<const GravityAffector*>(target);
|
||||
return getStringFromType(self->getForceType());
|
||||
}
|
||||
void doSet(void *target, const Ogre::String &val)
|
||||
{
|
||||
GravityAffector *self = static_cast<GravityAffector*>(target);
|
||||
self->setForceType(getTypeFromString(val));
|
||||
}
|
||||
};
|
||||
|
||||
/** Command object for direction (see Ogre::ParamCommand).*/
|
||||
class CmdDirection : public Ogre::ParamCommand
|
||||
{
|
||||
public:
|
||||
Ogre::String doGet(const void *target) const
|
||||
{
|
||||
const GravityAffector *self = static_cast<const GravityAffector*>(target);
|
||||
return Ogre::StringConverter::toString(self->getDirection());
|
||||
}
|
||||
void doSet(void *target, const Ogre::String &val)
|
||||
{
|
||||
GravityAffector *self = static_cast<GravityAffector*>(target);
|
||||
self->setDirection(Ogre::StringConverter::parseVector3(val));
|
||||
}
|
||||
};
|
||||
|
||||
/** Command object for position (see Ogre::ParamCommand).*/
|
||||
class CmdPosition : public Ogre::ParamCommand
|
||||
{
|
||||
public:
|
||||
Ogre::String doGet(const void *target) const
|
||||
{
|
||||
const GravityAffector *self = static_cast<const GravityAffector*>(target);
|
||||
return Ogre::StringConverter::toString(self->getPosition());
|
||||
}
|
||||
void doSet(void *target, const Ogre::String &val)
|
||||
{
|
||||
GravityAffector *self = static_cast<GravityAffector*>(target);
|
||||
self->setPosition(Ogre::StringConverter::parseVector3(val));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** Default constructor. */
|
||||
GravityAffector(Ogre::ParticleSystem *psys)
|
||||
: ParticleAffector(psys)
|
||||
, mForce(0.0f)
|
||||
, mForceType(Type_Wind)
|
||||
, mPosition(0.0f)
|
||||
, mDirection(0.0f)
|
||||
{
|
||||
std::vector<Ogre::Bone*> bones = Ogre::any_cast<NiNodeHolder>(psys->getUserObjectBindings().getUserAny()).mBones;
|
||||
assert (!bones.empty());
|
||||
mEmitterBone = bones[0];
|
||||
Ogre::TagPoint* tag = static_cast<Ogre::TagPoint*>(mParent->getParentNode());
|
||||
mParticleBone = static_cast<Ogre::Bone*>(tag->getParent());
|
||||
|
||||
mType = "Gravity";
|
||||
|
||||
// Init parameters
|
||||
if(createParamDictionary("GravityAffector"))
|
||||
{
|
||||
Ogre::ParamDictionary *dict = getParamDictionary();
|
||||
|
||||
Ogre::String force_title("force");
|
||||
Ogre::String force_descr("Amount of force applied to particles.");
|
||||
Ogre::String force_type_title("force_type");
|
||||
Ogre::String force_type_descr("Type of force applied to particles (point or wind).");
|
||||
Ogre::String direction_title("direction");
|
||||
Ogre::String direction_descr("Direction of wind forces.");
|
||||
Ogre::String position_title("position");
|
||||
Ogre::String position_descr("Position of point forces.");
|
||||
|
||||
dict->addParameter(Ogre::ParameterDef(force_title, force_descr, Ogre::PT_REAL), &msForceCmd);
|
||||
dict->addParameter(Ogre::ParameterDef(force_type_title, force_type_descr, Ogre::PT_STRING), &msForceTypeCmd);
|
||||
dict->addParameter(Ogre::ParameterDef(direction_title, direction_descr, Ogre::PT_VECTOR3), &msDirectionCmd);
|
||||
dict->addParameter(Ogre::ParameterDef(position_title, position_descr, Ogre::PT_VECTOR3), &msPositionCmd);
|
||||
}
|
||||
}
|
||||
|
||||
/** See Ogre::ParticleAffector. */
|
||||
void _affectParticles(Ogre::ParticleSystem *psys, Ogre::Real timeElapsed)
|
||||
{
|
||||
switch(mForceType)
|
||||
{
|
||||
case Type_Wind:
|
||||
applyWindForce(psys, timeElapsed);
|
||||
break;
|
||||
case Type_Point:
|
||||
applyPointForce(psys, timeElapsed);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void setForce(Ogre::Real force)
|
||||
{ mForce = force; }
|
||||
Ogre::Real getForce() const
|
||||
{ return mForce; }
|
||||
|
||||
void setForceType(ForceType type)
|
||||
{ mForceType = type; }
|
||||
ForceType getForceType() const
|
||||
{ return mForceType; }
|
||||
|
||||
void setDirection(const Ogre::Vector3 &dir)
|
||||
{ mDirection = dir; }
|
||||
const Ogre::Vector3 &getDirection() const
|
||||
{ return mDirection; }
|
||||
|
||||
void setPosition(const Ogre::Vector3 &pos)
|
||||
{ mPosition = pos; }
|
||||
const Ogre::Vector3 &getPosition() const
|
||||
{ return mPosition; }
|
||||
|
||||
static CmdForce msForceCmd;
|
||||
static CmdForceType msForceTypeCmd;
|
||||
static CmdDirection msDirectionCmd;
|
||||
static CmdPosition msPositionCmd;
|
||||
|
||||
protected:
|
||||
void applyWindForce(Ogre::ParticleSystem *psys, Ogre::Real timeElapsed)
|
||||
{
|
||||
const Ogre::Vector3 vec = mDirection * mForce * timeElapsed;
|
||||
Ogre::ParticleIterator pi = psys->_getIterator();
|
||||
while (!pi.end())
|
||||
{
|
||||
Ogre::Particle *p = pi.getNext();
|
||||
#if OGRE_VERSION >= (1 << 16 | 10 << 8 | 0)
|
||||
p->mDirection += vec;
|
||||
#else
|
||||
p->direction += vec;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void applyPointForce(Ogre::ParticleSystem *psys, Ogre::Real timeElapsed)
|
||||
{
|
||||
const Ogre::Real force = mForce * timeElapsed;
|
||||
Ogre::ParticleIterator pi = psys->_getIterator();
|
||||
while (!pi.end())
|
||||
{
|
||||
Ogre::Particle *p = pi.getNext();
|
||||
#if OGRE_VERSION >= (1 << 16 | 10 << 8 | 0)
|
||||
Ogre::Vector3 position = p->mPosition;
|
||||
#else
|
||||
Ogre::Vector3 position = p->position;
|
||||
#endif
|
||||
|
||||
Ogre::Vector3 vec = (mPosition - position).normalisedCopy() * force;
|
||||
#if OGRE_VERSION >= (1 << 16 | 10 << 8 | 0)
|
||||
p->mDirection += vec;
|
||||
#else
|
||||
p->direction += vec;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
float mForce;
|
||||
|
||||
ForceType mForceType;
|
||||
|
||||
Ogre::Vector3 mPosition;
|
||||
Ogre::Vector3 mDirection;
|
||||
};
|
||||
GravityAffector::CmdForce GravityAffector::msForceCmd;
|
||||
GravityAffector::CmdForceType GravityAffector::msForceTypeCmd;
|
||||
GravityAffector::CmdDirection GravityAffector::msDirectionCmd;
|
||||
GravityAffector::CmdPosition GravityAffector::msPositionCmd;
|
||||
|
||||
Ogre::ParticleAffector *GravityAffectorFactory::createAffector(Ogre::ParticleSystem *psys)
|
||||
{
|
||||
Ogre::ParticleAffector *p = new GravityAffector(psys);
|
||||
mAffectors.push_back(p);
|
||||
return p;
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
#ifndef OENGINE_OGRE_PARTICLES_H
|
||||
#define OENGINE_OGRE_PARTICLES_H
|
||||
|
||||
#include <OgreParticleEmitterFactory.h>
|
||||
#include <OgreParticleAffectorFactory.h>
|
||||
|
||||
/** Factory class for NifEmitter. */
|
||||
class NifEmitterFactory : public Ogre::ParticleEmitterFactory
|
||||
{
|
||||
public:
|
||||
/** See ParticleEmitterFactory */
|
||||
Ogre::String getName() const
|
||||
{ return "Nif"; }
|
||||
|
||||
/** See ParticleEmitterFactory */
|
||||
Ogre::ParticleEmitter* createEmitter(Ogre::ParticleSystem *psys);
|
||||
};
|
||||
|
||||
/** Factory class for GrowFadeAffector. */
|
||||
class GrowFadeAffectorFactory : public Ogre::ParticleAffectorFactory
|
||||
{
|
||||
/** See Ogre::ParticleAffectorFactory */
|
||||
Ogre::String getName() const
|
||||
{ return "GrowFade"; }
|
||||
|
||||
/** See Ogre::ParticleAffectorFactory */
|
||||
Ogre::ParticleAffector *createAffector(Ogre::ParticleSystem *psys);
|
||||
};
|
||||
|
||||
/** Factory class for GravityAffector. */
|
||||
class GravityAffectorFactory : public Ogre::ParticleAffectorFactory
|
||||
{
|
||||
/** See Ogre::ParticleAffectorFactory */
|
||||
Ogre::String getName() const
|
||||
{ return "Gravity"; }
|
||||
|
||||
/** See Ogre::ParticleAffectorFactory */
|
||||
Ogre::ParticleAffector *createAffector(Ogre::ParticleSystem *psys);
|
||||
};
|
||||
|
||||
struct NiNodeHolder
|
||||
{
|
||||
std::vector<Ogre::Bone*> mBones;
|
||||
|
||||
// Ogre::Any needs this for some reason
|
||||
friend std::ostream& operator<<(std::ostream& o, const NiNodeHolder& r)
|
||||
{ return o; }
|
||||
};
|
||||
|
||||
#endif /* OENGINE_OGRE_PARTICLES_H */
|
@ -1,179 +0,0 @@
|
||||
#include "skeleton.hpp"
|
||||
|
||||
#include <OgreSkeletonManager.h>
|
||||
#include <OgreResource.h>
|
||||
#include <OgreSkeleton.h>
|
||||
#include <OgreBone.h>
|
||||
|
||||
#include <components/nif/node.hpp>
|
||||
#include <components/nifcache/nifcache.hpp>
|
||||
#include <components/misc/stringops.hpp>
|
||||
|
||||
namespace NifOgre
|
||||
{
|
||||
|
||||
void NIFSkeletonLoader::buildBones(Ogre::Skeleton *skel, const Nif::Node *node, Ogre::Bone *parent)
|
||||
{
|
||||
Ogre::Bone *bone;
|
||||
if (node->name.empty())
|
||||
{
|
||||
// HACK: use " " instead of empty name, otherwise Ogre will replace it with an auto-generated
|
||||
// name in SkeletonInstance::cloneBoneAndChildren.
|
||||
static const char* emptyname = " ";
|
||||
if (!skel->hasBone(emptyname))
|
||||
bone = skel->createBone(emptyname);
|
||||
else
|
||||
bone = skel->createBone();
|
||||
}
|
||||
else
|
||||
{
|
||||
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_NiBSAnimationNode || /* Handled in the object loader */
|
||||
node->recType == Nif::RC_NiBillboardNode || /* Handled in the object loader */
|
||||
node->recType == Nif::RC_NiBSParticleNode ||
|
||||
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_NiParticleSystemController ||
|
||||
ctrl->recType == Nif::RC_NiBSPArrayController ||
|
||||
ctrl->recType == Nif::RC_NiVisController ||
|
||||
ctrl->recType == Nif::RC_NiUVController ||
|
||||
ctrl->recType == Nif::RC_NiKeyframeController ||
|
||||
ctrl->recType == Nif::RC_NiGeomMorpherController
|
||||
))
|
||||
warn("Unhandled "+ctrl->recName+" from node "+node->name+" in "+skel->getName());
|
||||
ctrl = ctrl->next;
|
||||
}
|
||||
|
||||
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(), 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::NIFFilePtr nif(Nif::Cache::getInstance().load(skel->getName()));
|
||||
const Nif::Node *node = static_cast<const Nif::Node*>(nif->getRoot(0));
|
||||
|
||||
try {
|
||||
buildBones(skel, node);
|
||||
}
|
||||
catch(std::exception &e) {
|
||||
std::cerr<< "Exception while loading "<<skel->getName() <<std::endl;
|
||||
std::cerr<< e.what() <<std::endl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool NIFSkeletonLoader::needSkeleton(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 keyframe controllers, there
|
||||
* are no nodes named "AttachLight" or "ArrowBone", and the tree consists of NiNode,
|
||||
* NiTriShape, and RootCollisionNode types only.
|
||||
*/
|
||||
if(node->boneTrafo)
|
||||
return true;
|
||||
|
||||
if(!node->controller.empty())
|
||||
{
|
||||
Nif::ControllerPtr ctrl = node->controller;
|
||||
do {
|
||||
if(ctrl->recType == Nif::RC_NiKeyframeController && ctrl->flags & Nif::NiNode::ControllerFlag_Active)
|
||||
return true;
|
||||
} while(!(ctrl=ctrl->next).empty());
|
||||
}
|
||||
|
||||
if (node->name == "AttachLight" || node->name == "ArrowBone")
|
||||
return true;
|
||||
|
||||
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())
|
||||
{
|
||||
if(needSkeleton(children[i].getPtr()))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if(node->recType == Nif::RC_NiTriShape)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Ogre::SkeletonPtr NIFSkeletonLoader::createSkeleton(const std::string &name, const std::string &group, const Nif::Node *node)
|
||||
{
|
||||
bool forceskel = false;
|
||||
std::string::size_type extpos = name.rfind('.');
|
||||
if(extpos != std::string::npos && name.compare(extpos, name.size()-extpos, ".nif") == 0)
|
||||
{
|
||||
Ogre::ResourceGroupManager &resMgr = Ogre::ResourceGroupManager::getSingleton();
|
||||
forceskel = resMgr.resourceExistsInAnyGroup(name.substr(0, extpos)+".kf");
|
||||
}
|
||||
|
||||
if(forceskel || needSkeleton(node))
|
||||
{
|
||||
Ogre::SkeletonManager &skelMgr = Ogre::SkeletonManager::getSingleton();
|
||||
return skelMgr.create(name, group, true, &sLoaders[name]);
|
||||
}
|
||||
|
||||
return Ogre::SkeletonPtr();
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
#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();
|
||||
}
|
||||
|
||||
void buildBones(Ogre::Skeleton *skel, const Nif::Node *node, Ogre::Bone *parent=NULL);
|
||||
|
||||
static bool needSkeleton(const Nif::Node *node);
|
||||
|
||||
// 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…
Reference in New Issue