Add OSG nifloader, currently supports geometry, materials, basic texturing, skinning, morphing, and most controllers.

c++11
scrawl 10 years ago
parent 6b36e55a4e
commit 8e01d8cb19

@ -221,7 +221,7 @@ if (${OGRE_VERSION} VERSION_LESS "1.9")
message(FATAL_ERROR "OpenMW requires Ogre 1.9 or later, please install the latest stable version from http://ogre3d.org")
endif()
find_package(OpenSceneGraph 3.2.0 REQUIRED osgDB)
find_package(OpenSceneGraph 3.2.0 REQUIRED osgDB osgViewer osgGA osgAnimation)
include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS})
find_package(MyGUI REQUIRED)
@ -592,6 +592,8 @@ if (BUILD_NIFTEST)
endif(BUILD_NIFTEST)
# Apps and tools
add_subdirectory( apps/nifosgtest )
if (BUILD_OPENMW)
add_subdirectory( apps/openmw )
endif()

@ -0,0 +1,7 @@
set (FILES
test.cpp
)
add_executable (test ${FILES})
target_link_libraries (test ${OPENSCENEGRAPH_LIBRARIES} "components")

@ -0,0 +1,100 @@
#include <osgViewer/Viewer>
#include <components/bsa/bsa_file.hpp>
#include <components/nif/niffile.hpp>
#include <components/nifosg/nifloader.hpp>
#include <osgGA/TrackballManipulator>
#include <osgDB/Registry>
#include <osgDB/WriteFile>
#include <osg/PolygonMode>
// EventHandler to toggle wireframe when 'w' key is pressed
class EventHandler : public osgGA::GUIEventHandler
{
public:
EventHandler(osg::Node* node)
: mWireframe(false)
, mNode(node)
{
}
virtual bool handle(const osgGA::GUIEventAdapter& adapter,osgGA::GUIActionAdapter& action)
{
switch (adapter.getEventType())
{
case osgGA::GUIEventAdapter::KEYDOWN:
if (adapter.getKey() == osgGA::GUIEventAdapter::KEY_W)
{
mWireframe = !mWireframe;
osg::PolygonMode* mode = new osg::PolygonMode;
mode->setMode(osg::PolygonMode::FRONT_AND_BACK,
mWireframe ? osg::PolygonMode::LINE : osg::PolygonMode::FILL);
mNode->getOrCreateStateSet()->setAttributeAndModes(mode, osg::StateAttribute::ON);
mNode->getOrCreateStateSet()->setMode(GL_CULL_FACE, mWireframe ? osg::StateAttribute::OFF
: osg::StateAttribute::ON);
return true;
}
default:
break;
}
return false;
}
private:
bool mWireframe;
osg::Node* mNode;
};
int main(int argc, char** argv)
{
if (argc < 3)
{
std::cout << "Usage: " << argv[0] << " <BSA file> <NIF file>" << std::endl;
return 1;
}
Bsa::BSAFile bsa;
bsa.open(argv[1]);
Nif::NIFFilePtr nif(new Nif::NIFFile(bsa.getFile(argv[2]), std::string(argv[2])));
osgViewer::Viewer viewer;
osg::ref_ptr<osg::Group> root(new osg::Group());
root->getOrCreateStateSet()->setMode(GL_CULL_FACE, osg::StateAttribute::ON);
// To prevent lighting issues with scaled meshes
root->getOrCreateStateSet()->setMode(GL_NORMALIZE, osg::StateAttribute::ON);
osg::Group* newNode = new osg::Group;
NifOsg::Loader loader;
loader.resourceManager = &bsa;
loader.loadAsSkeleton(nif, newNode);
//osgDB::writeNodeFile(*newNode, "out.osg");
for (int x=0; x<1;++x)
{
root->addChild(newNode);
}
viewer.setSceneData(root);
viewer.setUpViewInWindow(0, 0, 800, 600);
viewer.realize();
viewer.setCameraManipulator(new osgGA::TrackballManipulator());
viewer.addEventHandler(new EventHandler(root));
while (!viewer.done())
{
viewer.frame();
for (unsigned int i=0; i<loader.mControllers.size(); ++i)
loader.mControllers[i].update();
}
return 0;
}

@ -38,6 +38,10 @@ add_component_dir (nif
controlled effect niftypes record controller extra node record_ptr data niffile property nifkey data node base nifstream
)
add_component_dir (nifosg
nifloader controller
)
#add_component_dir (nifcache
# nifcache
# )

@ -0,0 +1,88 @@
#include "controlled.hpp"
#include "data.hpp"
namespace Nif
{
void NiSourceTexture::read(NIFStream *nif)
{
Named::read(nif);
external = !!nif->getChar();
if(external)
filename = nif->getString();
else
{
nif->getChar(); // always 1
data.read(nif);
}
pixel = nif->getInt();
mipmap = nif->getInt();
alpha = nif->getInt();
nif->getChar(); // always 1
}
void NiSourceTexture::post(NIFFile *nif)
{
Named::post(nif);
data.post(nif);
}
void NiParticleGrowFade::read(NIFStream *nif)
{
Controlled::read(nif);
growTime = nif->getFloat();
fadeTime = nif->getFloat();
}
void NiParticleColorModifier::read(NIFStream *nif)
{
Controlled::read(nif);
data.read(nif);
}
void NiParticleColorModifier::post(NIFFile *nif)
{
Controlled::post(nif);
data.post(nif);
}
void NiGravity::read(NIFStream *nif)
{
Controlled::read(nif);
/*unknown*/nif->getFloat();
mForce = nif->getFloat();
mType = nif->getUInt();
mPosition = nif->getVector3();
mDirection = nif->getVector3();
}
void NiPlanarCollider::read(NIFStream *nif)
{
Controlled::read(nif);
// (I think) 4 floats + 4 vectors
nif->skip(4*16);
}
void NiParticleRotation::read(NIFStream *nif)
{
Controlled::read(nif);
/*
byte (0 or 1)
float (1)
float*3
*/
nif->skip(17);
}
}

@ -0,0 +1,203 @@
#include "controller.hpp"
#include "node.hpp"
#include "data.hpp"
namespace Nif
{
void Controller::read(NIFStream *nif)
{
next.read(nif);
flags = nif->getUShort();
frequency = nif->getFloat();
phase = nif->getFloat();
timeStart = nif->getFloat();
timeStop = nif->getFloat();
target.read(nif);
}
void Controller::post(NIFFile *nif)
{
Record::post(nif);
next.post(nif);
target.post(nif);
}
void NiParticleSystemController::read(NIFStream *nif)
{
Controller::read(nif);
velocity = nif->getFloat();
velocityRandom = nif->getFloat();
verticalDir = nif->getFloat();
verticalAngle = nif->getFloat();
horizontalDir = nif->getFloat();
horizontalAngle = nif->getFloat();
/*normal?*/ nif->getVector3();
/*color?*/ nif->getVector4();
size = nif->getFloat();
startTime = nif->getFloat();
stopTime = nif->getFloat();
nif->getChar();
emitRate = nif->getFloat();
lifetime = nif->getFloat();
lifetimeRandom = nif->getFloat();
emitFlags = nif->getUShort();
offsetRandom = nif->getVector3();
emitter.read(nif);
/* Unknown Short, 0?
* Unknown Float, 1.0?
* Unknown Int, 1?
* Unknown Int, 0?
* Unknown Short, 0?
*/
nif->skip(16);
numParticles = nif->getUShort();
activeCount = nif->getUShort();
particles.resize(numParticles);
for(size_t i = 0;i < particles.size();i++)
{
particles[i].velocity = nif->getVector3();
nif->getVector3(); /* unknown */
particles[i].lifetime = nif->getFloat();
particles[i].lifespan = nif->getFloat();
particles[i].timestamp = nif->getFloat();
nif->getUShort(); /* unknown */
particles[i].vertex = nif->getUShort();
}
nif->getUInt(); /* -1? */
extra.read(nif);
nif->getUInt(); /* -1? */
nif->getChar();
}
void NiParticleSystemController::post(NIFFile *nif)
{
Controller::post(nif);
emitter.post(nif);
extra.post(nif);
}
void NiMaterialColorController::read(NIFStream *nif)
{
Controller::read(nif);
data.read(nif);
}
void NiMaterialColorController::post(NIFFile *nif)
{
Controller::post(nif);
data.post(nif);
}
void NiPathController::read(NIFStream *nif)
{
Controller::read(nif);
/*
int = 1
2xfloat
short = 0 or 1
*/
nif->skip(14);
posData.read(nif);
floatData.read(nif);
}
void NiPathController::post(NIFFile *nif)
{
Controller::post(nif);
posData.post(nif);
floatData.post(nif);
}
void NiUVController::read(NIFStream *nif)
{
Controller::read(nif);
nif->getUShort(); // always 0
data.read(nif);
}
void NiUVController::post(NIFFile *nif)
{
Controller::post(nif);
data.post(nif);
}
void NiKeyframeController::read(NIFStream *nif)
{
Controller::read(nif);
data.read(nif);
}
void NiKeyframeController::post(NIFFile *nif)
{
Controller::post(nif);
data.post(nif);
}
void NiAlphaController::read(NIFStream *nif)
{
Controller::read(nif);
data.read(nif);
}
void NiAlphaController::post(NIFFile *nif)
{
Controller::post(nif);
data.post(nif);
}
void NiGeomMorpherController::read(NIFStream *nif)
{
Controller::read(nif);
data.read(nif);
nif->getChar(); // always 0
}
void NiGeomMorpherController::post(NIFFile *nif)
{
Controller::post(nif);
data.post(nif);
}
void NiVisController::read(NIFStream *nif)
{
Controller::read(nif);
data.read(nif);
}
void NiVisController::post(NIFFile *nif)
{
Controller::post(nif);
data.post(nif);
}
void NiFlipController::read(NIFStream *nif)
{
Controller::read(nif);
mTexSlot = nif->getUInt();
/*unknown=*/nif->getUInt();/*0?*/
mDelta = nif->getFloat();
mSources.read(nif);
}
void NiFlipController::post(NIFFile *nif)
{
Controller::post(nif);
mSources.post(nif);
}
}

@ -0,0 +1,61 @@
#include "effect.hpp"
#include "node.hpp"
namespace Nif
{
void NiLight::SLight::read(NIFStream *nif)
{
dimmer = nif->getFloat();
ambient = nif->getVector3();
diffuse = nif->getVector3();
specular = nif->getVector3();
}
void NiLight::read(NIFStream *nif)
{
Effect::read(nif);
nif->getInt(); // 1
nif->getInt(); // 1?
light.read(nif);
}
void NiTextureEffect::read(NIFStream *nif)
{
Effect::read(nif);
int tmp = nif->getInt();
if(tmp) nif->getInt(); // always 1?
/*
3 x Vector4 = [1,0,0,0]
int = 2
int = 0 or 3
int = 2
int = 2
*/
nif->skip(16*4);
texture.read(nif);
/*
byte = 0
vector4 = [1,0,0,0]
short = 0
short = -75
short = 0
*/
nif->skip(23);
}
void NiTextureEffect::post(NIFFile *nif)
{
Effect::post(nif);
texture.post(nif);
}
}

@ -0,0 +1,43 @@
#include "extra.hpp"
namespace Nif
{
void NiStringExtraData::read(NIFStream *nif)
{
Extra::read(nif);
nif->getInt(); // size of string + 4. Really useful...
string = nif->getString();
}
void NiTextKeyExtraData::read(NIFStream *nif)
{
Extra::read(nif);
nif->getInt(); // 0
int keynum = nif->getInt();
list.resize(keynum);
for(int i=0; i<keynum; i++)
{
list[i].time = nif->getFloat();
list[i].text = nif->getString();
}
}
void NiVertWeightsExtraData::read(NIFStream *nif)
{
Extra::read(nif);
// We should have s*4+2 == i, for some reason. Might simply be the
// size of the rest of the record, unhelpful as that may be.
/*int i =*/ nif->getInt();
int s = nif->getUShort();
nif->skip(s * sizeof(float)); // vertex weights I guess
}
}

@ -0,0 +1,111 @@
#include "property.hpp"
#include "data.hpp"
#include "controlled.hpp"
namespace Nif
{
void Property::read(NIFStream *nif)
{
Named::read(nif);
flags = nif->getUShort();
}
void NiTexturingProperty::Texture::read(NIFStream *nif)
{
inUse = !!nif->getInt();
if(!inUse) return;
texture.read(nif);
clamp = nif->getInt();
filter = nif->getInt();
uvSet = nif->getInt();
// I have no idea, but I think these are actually two
// PS2-specific shorts (ps2L and ps2K), followed by an unknown
// short.
nif->skip(6);
}
void NiTexturingProperty::Texture::post(NIFFile *nif)
{
texture.post(nif);
}
void NiTexturingProperty::read(NIFStream *nif)
{
Property::read(nif);
apply = nif->getInt();
// Unknown, always 7. Probably the number of textures to read
// below
nif->getInt();
textures[0].read(nif); // Base
textures[1].read(nif); // Dark
textures[2].read(nif); // Detail
textures[3].read(nif); // Gloss (never present)
textures[4].read(nif); // Glow
textures[5].read(nif); // Bump map
if(textures[5].inUse)
{
// Ignore these at the moment
/*float lumaScale =*/ nif->getFloat();
/*float lumaOffset =*/ nif->getFloat();
/*const Vector4 *lumaMatrix =*/ nif->getVector4();
}
textures[6].read(nif); // Decal
}
void NiTexturingProperty::post(NIFFile *nif)
{
Property::post(nif);
for(int i = 0;i < 7;i++)
textures[i].post(nif);
}
void NiFogProperty::read(NIFStream *nif)
{
Property::read(nif);
mFogDepth = nif->getFloat();
mColour = nif->getVector3();
}
void S_MaterialProperty::read(NIFStream *nif)
{
ambient = nif->getVector3();
diffuse = nif->getVector3();
specular = nif->getVector3();
emissive = nif->getVector3();
glossiness = nif->getFloat();
alpha = nif->getFloat();
}
void S_VertexColorProperty::read(NIFStream *nif)
{
vertmode = nif->getInt();
lightmode = nif->getInt();
}
void S_AlphaProperty::read(NIFStream *nif)
{
threshold = nif->getChar();
}
void S_StencilProperty::read(NIFStream *nif)
{
enabled = nif->getChar();
compareFunc = nif->getInt();
stencilRef = nif->getUInt();
stencilMask = nif->getUInt();
failAction = nif->getInt();
zFailAction = nif->getInt();
zPassAction = nif->getInt();
drawMode = nif->getInt();
}
}

@ -0,0 +1,275 @@
#include "controller.hpp"
#include <osg/MatrixTransform>
#include <osg/TexMat>
#include <osgAnimation/MorphGeometry>
#include <components/nif/data.hpp>
namespace NifOsg
{
float ValueInterpolator::interpKey(const Nif::FloatKeyMap::MapType &keys, float time, float def) 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;
}
osg::Vec3f ValueInterpolator::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::KeyT<osg::Vec3f>* 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::KeyT<osg::Vec3f>* aLastKey = &last->second;
float a = (time - aLastTime) / (aTime - aLastTime);
return aLastKey->mValue + ((aKey->mValue - aLastKey->mValue) * a);
}
else
return keys.rbegin()->second.mValue;
}
ControllerFunction::ControllerFunction(const Nif::Controller *ctrl, bool deltaInput)
: mDeltaInput(deltaInput)
, mFrequency(ctrl->frequency)
, mPhase(ctrl->phase)
, mStartTime(ctrl->timeStart)
, mStopTime(ctrl->timeStop)
, mDeltaCount(0.f)
{
if(mDeltaInput)
mDeltaCount = mPhase;
}
float ControllerFunction::calculate(float 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;
}
osg::Quat KeyframeController::Value::interpKey(const Nif::QuaternionKeyMap::MapType &keys, float time)
{
if(time <= keys.begin()->first)
return keys.begin()->second.mValue;
Nif::QuaternionKeyMap::MapType::const_iterator it = keys.lower_bound(time);
if (it != keys.end())
{
float aTime = it->first;
const Nif::QuaternionKey* aKey = &it->second;
assert (it != keys.begin()); // Shouldn't happen, was checked at beginning of this function
Nif::QuaternionKeyMap::MapType::const_iterator last = --it;
float aLastTime = last->first;
const Nif::QuaternionKey* aLastKey = &last->second;
float a = (time - aLastTime) / (aTime - aLastTime);
osg::Quat v1 = aLastKey->mValue;
osg::Quat v2 = aKey->mValue;
// don't take the long path
if (v1.x()*v2.x() + v1.y()*v2.y() + v1.z()*v2.z() + v1.w()*v2.w() < 0) // dotProduct(v1,v2)
v1 = -v1;
osg::Quat result;
result.slerp(a, v1, v2);
return result;
}
else
return keys.rbegin()->second.mValue;
}
osg::Quat KeyframeController::Value::getXYZRotation(float time) const
{
float xrot = interpKey(mXRotations->mKeys, time);
float yrot = interpKey(mYRotations->mKeys, time);
float zrot = interpKey(mZRotations->mKeys, time);
osg::Quat xr(xrot, osg::Vec3f(1,0,0));
osg::Quat yr(yrot, osg::Vec3f(0,1,0));
osg::Quat zr(zrot, osg::Vec3f(0,0,1));
return (zr*yr*xr);
}
KeyframeController::Value::Value(osg::Node *target, const Nif::NIFFilePtr &nif, const Nif::NiKeyframeData *data,
osg::Quat initialQuat, float initialScale)
: NodeTargetValue(target)
, mRotations(&data->mRotations)
, mXRotations(&data->mXRotations)
, mYRotations(&data->mYRotations)
, mZRotations(&data->mZRotations)
, mTranslations(&data->mTranslations)
, mScales(&data->mScales)
, mNif(nif)
, mInitialQuat(initialQuat)
, mInitialScale(initialScale)
{ }
osg::Vec3f KeyframeController::Value::getTranslation(float time) const
{
if(mTranslations->mKeys.size() > 0)
return interpKey(mTranslations->mKeys, time);
osg::MatrixTransform* trans = static_cast<osg::MatrixTransform*>(mNode);
return trans->getMatrix().getTrans();
}
void KeyframeController::Value::setValue(float time)
{
osg::MatrixTransform* trans = static_cast<osg::MatrixTransform*>(mNode);
osg::Matrix mat = trans->getMatrix();
if(mRotations->mKeys.size() > 0)
mat.setRotate(interpKey(mRotations->mKeys, time));
else if (!mXRotations->mKeys.empty() || !mYRotations->mKeys.empty() || !mZRotations->mKeys.empty())
mat.setRotate(getXYZRotation(time));
else
mat.setRotate(mInitialQuat);
// let's hope no one's using multiple KeyframeControllers on the same node (not that would make any sense...)
float scale = mInitialScale;
if(mScales->mKeys.size() > 0)
scale = interpKey(mScales->mKeys, time);
for (int i=0;i<3;++i)
for (int j=0;j<3;++j)
mat(i,j) *= scale;
if(mTranslations->mKeys.size() > 0)
mat.setTrans(interpKey(mTranslations->mKeys, time));
trans->setMatrix(mat);
}
Controller::Controller(boost::shared_ptr<ControllerSource> src, boost::shared_ptr<ControllerValue> dest, boost::shared_ptr<ControllerFunction> function)
: mSource(src)
, mDestValue(dest)
, mFunction(function)
{
}
void Controller::update()
{
if (mSource.get())
{
mDestValue->setValue(mFunction->calculate(mSource->getValue()));
}
}
GeomMorpherController::Value::Value(osgAnimation::MorphGeometry *geom, const Nif::NiMorphData* morphData)
: mGeom(geom)
, mMorphs(morphData->mMorphs)
{
}
void GeomMorpherController::Value::setValue(float time)
{
if (mMorphs.size() <= 1)
return;
int i = 0;
for (std::vector<Nif::NiMorphData::MorphData>::iterator it = mMorphs.begin()+1; it != mMorphs.end(); ++it,++i)
{
float val = 0;
if (!it->mData.mKeys.empty())
val = interpKey(it->mData.mKeys, time);
val = std::max(0.f, std::min(1.f, val));
mGeom->setWeight(i, val);
}
}
UVController::Value::Value(osg::StateSet *target, const Nif::NiUVData *data, std::set<int> textureUnits)
: mStateSet(target)
, mUTrans(data->mKeyList[0])
, mVTrans(data->mKeyList[1])
, mUScale(data->mKeyList[2])
, mVScale(data->mKeyList[3])
, mTextureUnits(textureUnits)
{
}
void UVController::Value::setValue(float value)
{
float uTrans = interpKey(mUTrans.mKeys, value, 0.0f);
float vTrans = interpKey(mVTrans.mKeys, value, 0.0f);
float uScale = interpKey(mUScale.mKeys, value, 1.0f);
float vScale = interpKey(mVScale.mKeys, value, 1.0f);
osg::Matrixf mat = osg::Matrixf::scale(uScale, vScale, 1);
mat.setTrans(uTrans, vTrans, 0);
osg::TexMat* texMat = new osg::TexMat;
texMat->setMatrix(mat);
for (std::set<int>::const_iterator it = mTextureUnits.begin(); it != mTextureUnits.end(); ++it)
{
mStateSet->setTextureAttributeAndModes(*it, texMat, osg::StateAttribute::ON);
}
}
bool VisController::Value::calculate(float time) const
{
if(mData.size() == 0)
return true;
for(size_t i = 1;i < mData.size();i++)
{
if(mData[i].time > time)
return mData[i-1].isSet;
}
return mData.back().isSet;
}
void VisController::Value::setValue(float time)
{
bool vis = calculate(time);
mNode->setNodeMask(vis ? ~0 : 0);
}
}

@ -0,0 +1,214 @@
#ifndef COMPONENTS_NIFOSG_CONTROLLER_H
#define COMPONENTS_NIFOSG_CONTROLLER_H
#include <components/nif/niffile.hpp>
#include <components/nif/nifkey.hpp>
#include <components/nif/controller.hpp>
#include <components/nif/data.hpp>
#include <components/nifcache/nifcache.hpp>
#include <boost/shared_ptr.hpp>
#include <set> //UVController
#include <osg/Timer>
namespace osg
{
class Node;
class StateSet;
}
namespace osgAnimation
{
class MorphGeometry;
}
namespace NifOsg
{
// FIXME: Should not be here. We might also want to use this for non-NIF model formats
class ValueInterpolator
{
protected:
float interpKey(const Nif::FloatKeyMap::MapType &keys, float time, float def=0.f) const;
osg::Vec3f interpKey(const Nif::Vector3KeyMap::MapType &keys, float time) const;
};
// FIXME: Should not be here. We might also want to use this for non-NIF model formats
class ControllerFunction
{
private:
float mFrequency;
float mPhase;
float mStartTime;
bool mDeltaInput;
float mDeltaCount;
public:
float mStopTime;
public:
ControllerFunction(const Nif::Controller *ctrl, bool deltaInput);
float calculate(float value);
};
typedef ControllerFunction DefaultFunction;
class ControllerSource
{
public:
virtual float getValue() const = 0;
};
// FIXME: Should return a dt instead of time
class FrameTimeSource : public ControllerSource
{
public:
virtual float getValue() const
{
return mTimer.time_s();
}
private:
osg::Timer mTimer;
};
class ControllerValue
{
public:
virtual void setValue(float value) = 0;
};
class Controller
{
public:
Controller (boost::shared_ptr<ControllerSource> src, boost::shared_ptr<ControllerValue> dest,
boost::shared_ptr<ControllerFunction> function);
virtual void update();
boost::shared_ptr<ControllerSource> mSource;
boost::shared_ptr<ControllerValue> mDestValue;
// The source value gets passed through this function before it's passed on to the DestValue.
boost::shared_ptr<ControllerFunction> mFunction;
};
// FIXME: Should be with other general extensions.
class NodeTargetValue : public ControllerValue
{
protected:
osg::Node *mNode;
public:
NodeTargetValue(osg::Node *target) : mNode(target)
{ }
virtual osg::Vec3f getTranslation(float value) const = 0;
osg::Node *getNode() const
{ return mNode; }
};
class GeomMorpherController
{
public:
class Value : public ControllerValue, public ValueInterpolator
{
public:
// FIXME: don't copy the morph data?
Value(osgAnimation::MorphGeometry* geom, const Nif::NiMorphData *data);
virtual void setValue(float time);
private:
osgAnimation::MorphGeometry* mGeom;
std::vector<Nif::NiMorphData::MorphData> mMorphs;
};
};
class KeyframeController
{
public:
class Value : public NodeTargetValue, public ValueInterpolator
{
private:
const Nif::QuaternionKeyMap* mRotations;
const Nif::FloatKeyMap* mXRotations;
const Nif::FloatKeyMap* mYRotations;
const Nif::FloatKeyMap* mZRotations;
const Nif::Vector3KeyMap* mTranslations;
const Nif::FloatKeyMap* mScales;
Nif::NIFFilePtr mNif; // Hold a SharedPtr to make sure key lists stay valid
osg::Quat mInitialQuat;
float mInitialScale;
using ValueInterpolator::interpKey;
osg::Quat interpKey(const Nif::QuaternionKeyMap::MapType &keys, float time);
osg::Quat getXYZRotation(float time) const;
public:
/// @note The NiKeyFrameData must be valid as long as this KeyframeController exists.
Value(osg::Node *target, const Nif::NIFFilePtr& nif, const Nif::NiKeyframeData *data,
osg::Quat initialQuat, float initialScale);
virtual osg::Vec3f getTranslation(float time) const;
virtual void setValue(float time);
};
};
class UVController
{
public:
class Value : public ControllerValue, ValueInterpolator
{
private:
osg::StateSet* mStateSet;
Nif::FloatKeyMap mUTrans;
Nif::FloatKeyMap mVTrans;
Nif::FloatKeyMap mUScale;
Nif::FloatKeyMap mVScale;
std::set<int> mTextureUnits;
public:
Value(osg::StateSet* target, const Nif::NiUVData *data, std::set<int> textureUnits);
virtual void setValue(float value);
};
};
class VisController
{
public:
class Value : public NodeTargetValue
{
private:
std::vector<Nif::NiVisData::VisData> mData;
bool calculate(float time) const;
public:
Value(osg::Node *target, const Nif::NiVisData *data)
: NodeTargetValue(target)
, mData(data->mVis)
{ }
virtual osg::Vec3f getTranslation(float time) const
{ return osg::Vec3f(); }
virtual void setValue(float time);
};
};
}
#endif

@ -0,0 +1,759 @@
#include "nifloader.hpp"
#include <osg/Matrixf>
#include <osg/MatrixTransform>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/Array>
// resource
#include <components/bsa/bsa_file.hpp>
#include <osgDB/Registry>
#include <osg/io_utils>
#include <components/misc/stringops.hpp>
// skel
#include <osgAnimation/Skeleton>
#include <osgAnimation/Bone>
#include <osgAnimation/RigGeometry>
#include <osgAnimation/MorphGeometry>
#include <osg/BlendFunc>
#include <osg/AlphaFunc>
#include <osg/Depth>
#include <osg/PolygonMode>
#include <osg/FrontFace>
#include <osg/Stencil>
#include <osg/Material>
#include <osg/Texture2D>
#include <components/nif/node.hpp>
namespace
{
osg::Matrixf toMatrix(const Nif::Transformation& nifTrafo)
{
osg::Matrixf transform;
transform.setTrans(nifTrafo.pos);
for (int i=0;i<3;++i)
for (int j=0;j<3;++j)
transform(j,i) = nifTrafo.rotation.mValues[i][j] * nifTrafo.scale; // NB column/row major difference
return transform;
}
osg::Matrixf getWorldTransform(const Nif::Node* node)
{
if(node->parent != NULL)
return toMatrix(node->trafo) * getWorldTransform(node->parent);
return toMatrix(node->trafo);
}
osg::BlendFunc::BlendFuncMode getBlendMode(int mode)
{
switch(mode)
{
case 0: return osg::BlendFunc::ONE;
case 1: return osg::BlendFunc::ZERO;
case 2: return osg::BlendFunc::SRC_COLOR;
case 3: return osg::BlendFunc::ONE_MINUS_SRC_COLOR;
case 4: return osg::BlendFunc::DST_COLOR;
case 5: return osg::BlendFunc::ONE_MINUS_DST_COLOR;
case 6: return osg::BlendFunc::SRC_ALPHA;
case 7: return osg::BlendFunc::ONE_MINUS_SRC_ALPHA;
case 8: return osg::BlendFunc::DST_ALPHA;
case 9: return osg::BlendFunc::ONE_MINUS_DST_ALPHA;
case 10: return osg::BlendFunc::SRC_ALPHA_SATURATE;
default:
std::cerr<< "Unexpected blend mode: "<< mode << std::endl;
return osg::BlendFunc::SRC_ALPHA;
}
}
osg::AlphaFunc::ComparisonFunction getTestMode(int mode)
{
switch (mode)
{
case 0: return osg::AlphaFunc::ALWAYS;
case 1: return osg::AlphaFunc::LESS;
case 2: return osg::AlphaFunc::EQUAL;
case 3: return osg::AlphaFunc::LEQUAL;
case 4: return osg::AlphaFunc::GREATER;
case 5: return osg::AlphaFunc::NOTEQUAL;
case 6: return osg::AlphaFunc::GEQUAL;
case 7: return osg::AlphaFunc::NEVER;
default:
std::cerr << "Unexpected blend mode: " << mode << std::endl;
return osg::AlphaFunc::LEQUAL;
}
}
// Collect all properties affecting the given node that should be applied to an osg::Material.
void collectMaterialProperties(const Nif::Node* nifNode, std::vector<const Nif::Property*>& out)
{
const Nif::PropertyList& props = nifNode->props;
for (size_t i = 0; i <props.length();++i)
{
if (!props[i].empty())
{
switch (props[i]->recType)
{
case Nif::RC_NiMaterialProperty:
case Nif::RC_NiVertexColorProperty:
case Nif::RC_NiSpecularProperty:
out.push_back(props[i].getPtr());
break;
default:
break;
}
}
}
if (nifNode->parent)
collectMaterialProperties(nifNode->parent, out);
}
void updateMaterialProperties(osg::StateSet* stateset, const std::vector<const Nif::Property*>& properties)
{
int specFlags = 0; // Specular is disabled by default, even if there's a specular color in the NiMaterialProperty
osg::Material* mat = new osg::Material;
// FIXME: color mode should be disabled if the TriShape has no vertex colors
mat->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE);
for (std::vector<const Nif::Property*>::const_reverse_iterator it = properties.rbegin(); it != properties.rend(); ++it)
{
const Nif::Property* property = *it;
switch (property->recType)
{
case Nif::RC_NiSpecularProperty:
{
specFlags = property->flags;
break;
}
case Nif::RC_NiMaterialProperty:
{
const Nif::NiMaterialProperty* matprop = static_cast<const Nif::NiMaterialProperty*>(property);
mat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(matprop->data.diffuse, matprop->data.alpha));
mat->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(matprop->data.ambient, 1.f));
mat->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(matprop->data.emissive, 1.f));
mat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(matprop->data.specular, 1.f));
mat->setShininess(osg::Material::FRONT_AND_BACK, matprop->data.glossiness);
break;
}
case Nif::RC_NiVertexColorProperty:
{
const Nif::NiVertexColorProperty* vertprop = static_cast<const Nif::NiVertexColorProperty*>(property);
switch (vertprop->flags)
{
case 0:
mat->setColorMode(osg::Material::OFF);
break;
case 1:
mat->setColorMode(osg::Material::EMISSION);
break;
case 2:
mat->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE);
break;
}
}
}
}
if (specFlags == 0)
mat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f,0.f,0.f,0.f));
stateset->setAttributeAndModes(mat, osg::StateAttribute::ON);
}
// NodeCallback used to update the bone matrices in skeleton space as needed for skinning.
class UpdateBone : public osg::NodeCallback
{
public:
// Callback method called by the NodeVisitor when visiting a node.
void operator()(osg::Node* node, osg::NodeVisitor* nv)
{
if (nv && nv->getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR)
{
osgAnimation::Bone* b = dynamic_cast<osgAnimation::Bone*>(node);
if (!b)
{
OSG_WARN << "Warning: UpdateBone set on non-Bone object." << std::endl;
return;
}
osgAnimation::Bone* parent = b->getBoneParent();
if (parent)
b->setMatrixInSkeletonSpace(b->getMatrixInBoneSpace() * parent->getMatrixInSkeletonSpace());
else
b->setMatrixInSkeletonSpace(b->getMatrixInBoneSpace());
}
traverse(node,nv);
}
};
// NodeCallback used to set the inverse of the parent bone's matrix in skeleton space
// on the MatrixTransform that the NodeCallback is attached to. This is used so we can
// attach skinned meshes to their actual parent node, while still having the skinning
// work in skeleton space as expected.
class InvertBoneMatrix : public osg::NodeCallback
{
public:
InvertBoneMatrix(osg::Node* skelRootNode)
: mSkelRoot(skelRootNode)
{
}
void operator()(osg::Node* node, osg::NodeVisitor* nv)
{
if (nv && nv->getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR)
{
osg::NodePath path = nv->getNodePath();
path.pop_back();
osg::MatrixTransform* trans = dynamic_cast<osg::MatrixTransform*>(node);
osg::NodePath::iterator found = std::find(path.begin(), path.end(), mSkelRoot);
if (found != path.end())
{
path.erase(path.begin(),found+1);
osg::Matrix worldMat = osg::computeLocalToWorld( path );
trans->setMatrix(osg::Matrix::inverse(worldMat));
}
}
traverse(node,nv);
}
private:
osg::Node* mSkelRoot;
};
osg::ref_ptr<osg::Geometry> handleMorphGeometry(const Nif::NiGeomMorpherController* morpher)
{
osg::ref_ptr<osgAnimation::MorphGeometry> morphGeom = new osgAnimation::MorphGeometry;
morphGeom->setMethod(osgAnimation::MorphGeometry::RELATIVE);
// NIF format doesn't specify morphed normals
morphGeom->setMorphNormals(false);
const std::vector<Nif::NiMorphData::MorphData>& morphs = morpher->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)
{
osg::ref_ptr<osg::Geometry> morphTarget = new osg::Geometry;
morphTarget->setVertexArray(new osg::Vec3Array(morphs[i].mVertices.size(), &morphs[i].mVertices[0]));
morphGeom->addMorphTarget(morphTarget, 0.f);
}
return morphGeom;
}
}
namespace NifOsg
{
void Loader::load(Nif::NIFFilePtr nif, osg::Group *parentNode)
{
mNif = nif;
if (nif->numRoots() < 1)
{
nif->warn("Found no root nodes");
return;
}
const Nif::Record* r = nif->getRoot(0);
assert(r != NULL);
const Nif::Node* nifNode = dynamic_cast<const Nif::Node*>(r);
if (nifNode == NULL)
{
nif->warn("First root was not a node, but a " + r->recName);
return;
}
mRootNode = parentNode;
handleNode(nifNode, parentNode, false, std::map<int, int>());
}
void Loader::loadAsSkeleton(Nif::NIFFilePtr nif, osg::Group *parentNode)
{
mNif = nif;
if (nif->numRoots() < 1)
{
nif->warn("Found no root nodes");
return;
}
const Nif::Record* r = nif->getRoot(0);
assert(r != NULL);
const Nif::Node* nifNode = dynamic_cast<const Nif::Node*>(r);
if (nifNode == NULL)
{
nif->warn("First root was not a node, but a " + r->recName);
return;
}
mRootNode = parentNode;
osgAnimation::Skeleton* skel = new osgAnimation::Skeleton;
mSkeleton = skel;
mRootNode->addChild(mSkeleton);
handleNode(nifNode, mSkeleton, true, std::map<int, int>());
}
void Loader::applyNodeProperties(const Nif::Node *nifNode, osg::Node *applyTo, std::map<int, int>& boundTextures)
{
const Nif::PropertyList& props = nifNode->props;
for (size_t i = 0; i <props.length();++i)
{
if (!props[i].empty())
handleProperty(props[i].getPtr(), nifNode, applyTo, boundTextures);
}
}
void Loader::createController(const Nif::Controller *ctrl, boost::shared_ptr<ControllerValue> value, int animflags)
{
// FIXME animflags currently not passed to this function
//bool autoPlay = animflags & Nif::NiNode::AnimFlag_AutoPlay;
boost::shared_ptr<ControllerSource> src(new FrameTimeSource); // if autoPlay
boost::shared_ptr<ControllerFunction> function (new ControllerFunction(ctrl
, 0/*autoPlay*/));
//scene->mMaxControllerLength = std::max(function->mStopTime, scene->mMaxControllerLength);
mControllers.push_back(Controller(src, value, function));
}
void Loader::handleNode(const Nif::Node* nifNode, osg::Group* parentNode, bool createSkeleton,
std::map<int, int> boundTextures)
{
osg::ref_ptr<osg::MatrixTransform> transformNode;
if (createSkeleton)
{
osgAnimation::Bone* bone = new osgAnimation::Bone;
transformNode = bone;
bone->setMatrix(toMatrix(nifNode->trafo));
bone->setName(nifNode->name);
bone->setUpdateCallback(new UpdateBone);
bone->setInvBindMatrixInSkeletonSpace(osg::Matrixf::inverse(getWorldTransform(nifNode)));
}
else
{
transformNode = new osg::MatrixTransform;
transformNode->setMatrix(toMatrix(nifNode->trafo));
}
// Hide collision shapes, but don't skip the subgraph
// We still need to animate the hidden bones so the physics system can access them
// FIXME: skip creation of the TriShapes
if (nifNode->recType == Nif::RC_RootCollisionNode)
transformNode->setNodeMask(0);
// We could probably skip hidden nodes entirely if they don't have a VisController that
// might make them visible later
if (nifNode->flags & Nif::NiNode::Flag_Hidden)
transformNode->setNodeMask(0);
// Insert bones at position 0 to prevent update order problems (see comment in osg Skeleton.cpp)
parentNode->insertChild(0, transformNode);
applyNodeProperties(nifNode, transformNode, boundTextures);
if (nifNode->recType == Nif::RC_NiTriShape)
{
const Nif::NiTriShape* triShape = static_cast<const Nif::NiTriShape*>(nifNode);
if (!createSkeleton || triShape->skin.empty())
handleTriShape(triShape, transformNode, boundTextures);
else
handleSkinnedTriShape(triShape, transformNode, boundTextures);
if (!nifNode->controller.empty())
handleMeshControllers(nifNode, transformNode, boundTextures);
}
if (!nifNode->controller.empty())
handleNodeControllers(nifNode, transformNode);
const Nif::NiNode *ninode = dynamic_cast<const Nif::NiNode*>(nifNode);
if(ninode)
{
const Nif::NodeList &children = ninode->children;
for(size_t i = 0;i < children.length();++i)
{
if(!children[i].empty())
handleNode(children[i].getPtr(), transformNode, createSkeleton, boundTextures);
}
}
}
void Loader::handleMeshControllers(const Nif::Node *nifNode, osg::MatrixTransform *transformNode, const std::map<int, int> &boundTextures)
{
for (Nif::ControllerPtr ctrl = nifNode->controller; !ctrl.empty(); ctrl = ctrl->next)
{
if (ctrl->recType == Nif::RC_NiUVController)
{
const Nif::NiUVController *uvctrl = static_cast<const Nif::NiUVController*>(ctrl.getPtr());
std::set<int> texUnits;
for (std::map<int, int>::const_iterator it = boundTextures.begin(); it != boundTextures.end(); ++it)
texUnits.insert(it->first);
boost::shared_ptr<ControllerValue> dest(new UVController::Value(transformNode->getOrCreateStateSet()
, uvctrl->data.getPtr(), texUnits));
createController(uvctrl, dest, 0);
}
}
}
void Loader::handleNodeControllers(const Nif::Node* nifNode, osg::MatrixTransform* transformNode)
{
bool seenKeyframeCtrl = false;
for (Nif::ControllerPtr ctrl = nifNode->controller; !ctrl.empty(); ctrl = ctrl->next)
{
if (ctrl->recType == Nif::RC_NiKeyframeController)
{
const Nif::NiKeyframeController *key = static_cast<const Nif::NiKeyframeController*>(ctrl.getPtr());
if(!key->data.empty())
{
if (seenKeyframeCtrl)
{
std::cerr << "Warning: multiple KeyframeControllers on the same node" << std::endl;
continue;
}
boost::shared_ptr<ControllerValue> dest(new KeyframeController::Value(transformNode, mNif, key->data.getPtr(),
transformNode->getMatrix().getRotate(), nifNode->trafo.scale));
createController(key, dest, 0);
seenKeyframeCtrl = true;
}
}
else if (ctrl->recType == Nif::RC_NiVisController)
{
const Nif::NiVisController* visctrl = static_cast<const Nif::NiVisController*>(ctrl.getPtr());
boost::shared_ptr<ControllerValue> dest(new VisController::Value(transformNode, visctrl->data.getPtr()));
createController(visctrl, dest, 0);
}
}
}
void Loader::triShapeToGeometry(const Nif::NiTriShape *triShape, osg::Geometry *geometry, const std::map<int, int>& boundTextures)
{
const Nif::NiTriShapeData* data = triShape->data.getPtr();
const Nif::NiSkinInstance *skin = (triShape->skin.empty() ? NULL : triShape->skin.getPtr());
if (skin)
{
// 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.
osg::ref_ptr<osg::Vec3Array> newVerts (new osg::Vec3Array(data->vertices.size()));
osg::ref_ptr<osg::Vec3Array> newNormals (new osg::Vec3Array(data->normals.size()));
const Nif::NiSkinData *skinData = skin->data.getPtr();
const Nif::NodeList &bones = skin->bones;
for(size_t b = 0;b < bones.length();b++)
{
osg::Matrixf mat = toMatrix(skinData->bones[b].trafo);
mat = mat * getWorldTransform(bones[b].getPtr());
const std::vector<Nif::NiSkinData::VertWeight> &weights = skinData->bones[b].weights;
for(size_t i = 0;i < weights.size();i++)
{
size_t index = weights[i].vertex;
float weight = weights[i].weight;
osg::Vec4f mult = (osg::Vec4f(data->vertices.at(index),1.f) * mat) * weight;
(*newVerts)[index] += osg::Vec3f(mult.x(),mult.y(),mult.z());
if(newNormals->size() > index)
{
osg::Vec4 normal(data->normals[index].x(), data->normals[index].y(), data->normals[index].z(), 0.f);
normal = (normal * mat) * weight;
(*newNormals)[index] += osg::Vec3f(normal.x(),normal.y(),normal.z());
}
}
}
// Interpolating normalized normals doesn't necessarily give you a normalized result
// Currently we're using GL_NORMALIZE, so this isn't needed
//for (unsigned int i=0;i<newNormals->size();++i)
// (*newNormals)[i].normalize();
geometry->setVertexArray(newVerts);
if (!data->normals.empty())
geometry->setNormalArray(newNormals, osg::Array::BIND_PER_VERTEX);
}
else
{
geometry->setVertexArray(new osg::Vec3Array(data->vertices.size(), &data->vertices[0]));
if (!data->normals.empty())
geometry->setNormalArray(new osg::Vec3Array(data->normals.size(), &data->normals[0]), osg::Array::BIND_PER_VERTEX);
}
for (std::map<int, int>::const_iterator it = boundTextures.begin(); it != boundTextures.end(); ++it)
{
int textureStage = it->first;
int uvSet = it->second;
if (uvSet >= (int)data->uvlist.size())
{
// Occurred in "ascendedsleeper.nif", but only for hidden Shadow nodes, apparently
//std::cerr << "Warning: using an undefined UV set " << uvSet << " on TriShape " << triShape->name << std::endl;
continue;
}
geometry->setTexCoordArray(textureStage, new osg::Vec2Array(data->uvlist[uvSet].size(), &data->uvlist[uvSet][0]), osg::Array::BIND_PER_VERTEX);
}
// FIXME: material ColorMode should be disabled if the TriShape has no vertex colors
if (!data->colors.empty())
geometry->setColorArray(new osg::Vec4Array(data->colors.size(), &data->colors[0]), osg::Array::BIND_PER_VERTEX);
geometry->addPrimitiveSet(new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLES,
data->triangles.size(),
(unsigned short*)&data->triangles[0]));
}
void Loader::handleTriShape(const Nif::NiTriShape* triShape, osg::Group* parentNode, const std::map<int, int>& boundTextures)
{
osg::ref_ptr<osg::Geometry> geometry;
if(!triShape->controller.empty())
{
Nif::ControllerPtr ctrl = triShape->controller;
do {
if(ctrl->recType == Nif::RC_NiGeomMorpherController && ctrl->flags & Nif::NiNode::ControllerFlag_Active)
{
geometry = handleMorphGeometry(static_cast<const Nif::NiGeomMorpherController*>(ctrl.getPtr()));
boost::shared_ptr<ControllerValue> value(
new GeomMorpherController::Value(static_cast<osgAnimation::MorphGeometry*>(geometry.get()),
static_cast<const Nif::NiGeomMorpherController*>(ctrl.getPtr())->data.getPtr()));
createController(ctrl.getPtr(), value, 0);
break;
}
} while(!(ctrl=ctrl->next).empty());
}
if (!geometry.get())
geometry = new osg::Geometry;
triShapeToGeometry(triShape, geometry.get(), boundTextures);
osg::ref_ptr<osg::Geode> geode (new osg::Geode);
geode->addDrawable(geometry.get());
parentNode->addChild(geode.get());
}
void Loader::handleSkinnedTriShape(const Nif::NiTriShape *triShape, osg::Group *parentNode, const std::map<int, int>& boundTextures)
{
osg::ref_ptr<osg::Geometry> geometry (new osg::Geometry);
triShapeToGeometry(triShape, geometry.get(), boundTextures);
osg::ref_ptr<osgAnimation::RigGeometry> rig(new osgAnimation::RigGeometry);
rig->setSourceGeometry(geometry);
// Slightly expand the bounding box to account for movement of the bones
// For more accuracy the skinning should be relative to the parent of the first skinned bone,
// rather than the root bone.
osg::BoundingBox box = geometry->getBound();
box.expandBy(box._min-(box._max-box._min)/2);
box.expandBy(box._max+(box._max-box._min)/2);
rig->setInitialBound(box);
const Nif::NiSkinInstance *skin = triShape->skin.getPtr();
// Assign bone weights
osg::ref_ptr<osgAnimation::VertexInfluenceMap> map (new osgAnimation::VertexInfluenceMap);
const Nif::NiSkinData *data = skin->data.getPtr();
const Nif::NodeList &bones = skin->bones;
for(size_t i = 0;i < bones.length();i++)
{
std::string boneName = bones[i].getPtr()->name;
osgAnimation::VertexInfluence influence;
influence.setName(boneName);
const std::vector<Nif::NiSkinData::VertWeight> &weights = data->bones[i].weights;
influence.reserve(weights.size());
for(size_t j = 0;j < weights.size();j++)
{
osgAnimation::VertexIndexWeight indexWeight = std::make_pair(weights[j].vertex, weights[j].weight);
influence.push_back(indexWeight);
}
map->insert(std::make_pair(boneName, influence));
}
rig->setInfluenceMap(map.get());
osg::ref_ptr<osg::MatrixTransform> trans(new osg::MatrixTransform);
trans->setUpdateCallback(new InvertBoneMatrix(mSkeleton));
osg::ref_ptr<osg::Geode> geode (new osg::Geode);
geode->addDrawable(rig.get());
trans->addChild(geode.get());
parentNode->addChild(trans.get());
}
void Loader::handleProperty(const Nif::Property *property, const Nif::Node* nifNode,
osg::Node *node, std::map<int, int>& boundTextures)
{
osg::StateSet* stateset = node->getOrCreateStateSet();
switch (property->recType)
{
case Nif::RC_NiStencilProperty:
{
const Nif::NiStencilProperty* stencilprop = static_cast<const Nif::NiStencilProperty*>(property);
osg::FrontFace* frontFace = new osg::FrontFace;
switch (stencilprop->data.drawMode)
{
case 1:
frontFace->setMode(osg::FrontFace::CLOCKWISE);
break;
case 0:
case 2:
default:
frontFace->setMode(osg::FrontFace::COUNTER_CLOCKWISE);
break;
}
stateset->setAttribute(frontFace, osg::StateAttribute::ON);
stateset->setMode(GL_CULL_FACE, stencilprop->data.drawMode == 3 ? osg::StateAttribute::OFF
: osg::StateAttribute::ON);
// Stencil settings not enabled yet, not sure if the original engine is actually using them,
// since they might conflict with Morrowind's stencil shadows.
/*
osg::Stencil* stencil = new osg::Stencil;
stencil->setFunction(func, stencilprop->data.stencilRef, stencilprop->data.stencilMask);
stateset->setMode(GL_STENCIL_TEST, stencilprop->data.enabled != 0 ? osg::StateAttribute::ON
: osg::StateAttribute::OFF);
*/
}
case Nif::RC_NiWireframeProperty:
{
const Nif::NiWireframeProperty* wireprop = static_cast<const Nif::NiWireframeProperty*>(property);
osg::PolygonMode* mode = new osg::PolygonMode;
mode->setMode(osg::PolygonMode::FRONT_AND_BACK, wireprop->flags == 0 ? osg::PolygonMode::FILL
: osg::PolygonMode::LINE);
stateset->setAttributeAndModes(mode, osg::StateAttribute::ON);
break;
}
case Nif::RC_NiZBufferProperty:
{
const Nif::NiZBufferProperty* zprop = static_cast<const Nif::NiZBufferProperty*>(property);
// VER_MW doesn't support a DepthFunction according to NifSkope
osg::Depth* depth = new osg::Depth;
depth->setWriteMask((zprop->flags>>1)&1);
stateset->setAttributeAndModes(depth, osg::StateAttribute::ON);
break;
}
// OSG groups the material properties that NIFs have separate, so we have to parse them all again when one changed
case Nif::RC_NiMaterialProperty:
case Nif::RC_NiVertexColorProperty:
case Nif::RC_NiSpecularProperty:
{
// TODO: handle these in handleTriShape so we know whether vertex colors are available
std::vector<const Nif::Property*> materialProps;
collectMaterialProperties(nifNode, materialProps);
updateMaterialProperties(stateset, materialProps);
break;
}
case Nif::RC_NiAlphaProperty:
{
const Nif::NiAlphaProperty* alphaprop = static_cast<const Nif::NiAlphaProperty*>(property);
osg::BlendFunc* blendfunc = new osg::BlendFunc;
if (alphaprop->flags&1)
{
blendfunc->setFunction(getBlendMode((alphaprop->flags>>1)&0xf),
getBlendMode((alphaprop->flags>>5)&0xf));
stateset->setAttributeAndModes(blendfunc, osg::StateAttribute::ON);
bool noSort = (alphaprop->flags>>13)&1;
if (!noSort)
{
stateset->setNestRenderBins(false);
stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
}
}
else
{
stateset->setAttributeAndModes(blendfunc, osg::StateAttribute::OFF);
stateset->setNestRenderBins(false);
stateset->setRenderingHint(osg::StateSet::OPAQUE_BIN);
}
osg::AlphaFunc* alphafunc = new osg::AlphaFunc;
if((alphaprop->flags>>9)&1)
{
alphafunc->setFunction(getTestMode((alphaprop->flags>>10)&0x7), alphaprop->data.threshold/255.f);
stateset->setAttributeAndModes(alphafunc, osg::StateAttribute::ON);
}
else
stateset->setAttributeAndModes(alphafunc, osg::StateAttribute::OFF);
break;
}
case Nif::RC_NiTexturingProperty:
{
const Nif::NiTexturingProperty* texprop = static_cast<const Nif::NiTexturingProperty*>(property);
for (int i=0; i<Nif::NiTexturingProperty::NumTextures; ++i)
{
if (i != Nif::NiTexturingProperty::BaseTexture)
continue; // FIXME: implement other textures
if (texprop->textures[i].inUse)
{
const Nif::NiTexturingProperty::Texture& tex = texprop->textures[i];
if(tex.texture.empty())
{
std::cerr << "Warning: texture layer " << i << " is in use but empty " << std::endl;
continue;
}
const Nif::NiSourceTexture *st = tex.texture.getPtr();
std::string filename (st->filename);
Misc::StringUtils::toLower(filename);
filename = "textures\\" + filename;
size_t found = filename.find(".tga");
if (found == std::string::npos)
found = filename.find(".bmp");
if (found != std::string::npos)
filename.replace(found, 4, ".dds");
// tx_creature_werewolf.dds isn't loading in the correct format without this option
osgDB::Options* opts = new osgDB::Options;
opts->setOptionString("dds_dxt1_detect_rgba");
osgDB::ReaderWriter* reader = osgDB::Registry::instance()->getReaderWriterForExtension("dds");
osgDB::ReaderWriter::ReadResult result = reader->readImage(*resourceManager->getFile(filename.c_str()), opts);
osg::Image* image = result.getImage();
osg::Texture2D* texture2d = new osg::Texture2D;
texture2d->setImage(image);
unsigned int clamp = static_cast<unsigned int>(tex.clamp);
int wrapT = (clamp) & 0x1;
int wrapS = (clamp >> 1) & 0x1;
texture2d->setWrap(osg::Texture::WRAP_S, wrapS ? osg::Texture::REPEAT : osg::Texture::CLAMP);
texture2d->setWrap(osg::Texture::WRAP_T, wrapT ? osg::Texture::REPEAT : osg::Texture::CLAMP);
stateset->setTextureAttributeAndModes(i, texture2d, osg::StateAttribute::ON);
boundTextures[i] = tex.uvSet;
}
else if (boundTextures.find(i) != boundTextures.end())
{
stateset->setTextureAttributeAndModes(i, new osg::Texture2D, osg::StateAttribute::OFF);
boundTextures.erase(i);
}
}
break;
}
case Nif::RC_NiDitherProperty:
{
stateset->setMode(GL_DITHER, property->flags != 0 ? osg::StateAttribute::ON
: osg::StateAttribute::OFF);
break;
}
default:
std::cerr << "Unhandled " << property->recName << std::endl;
break;
}
}
}

@ -0,0 +1,84 @@
#ifndef OPENMW_COMPONENTS_NIFOSG_LOADER
#define OPENMW_COMPONENTS_NIFOSG_LOADER
#include <components/nif/niffile.hpp>
#include <components/nifcache/nifcache.hpp> // NIFFilePtr
#include <osg/Group>
#include "controller.hpp"
namespace osg
{
class Geometry;
}
namespace osgAnimation
{
class Bone;
}
namespace Nif
{
class Node;
class NiTriShape;
class Property;
}
namespace Bsa
{
class BSAFile;
}
namespace NifOsg
{
/// The main class responsible for loading NIF files into an OSG-Scenegraph.
class Loader
{
public:
/// @param node The parent of the root node for the created NIF file.
void load(Nif::NIFFilePtr file, osg::Group* parentNode);
void loadAsSkeleton(Nif::NIFFilePtr file, osg::Group* parentNode);
// FIXME replace with resource system
Bsa::BSAFile* resourceManager;
// FIXME move
std::vector<Controller> mControllers;
private:
/// @param createSkeleton If true, use an osgAnimation::Bone for NIF nodes, otherwise an osg::MatrixTransform.
void handleNode(const Nif::Node* nifNode, osg::Group* parentNode, bool createSkeleton, std::map<int, int> boundTextures);
void handleMeshControllers(const Nif::Node* nifNode, osg::MatrixTransform* transformNode, const std::map<int, int>& boundTextures);
void handleNodeControllers(const Nif::Node* nifNode, osg::MatrixTransform* transformNode);
void handleProperty (const Nif::Property* property, const Nif::Node* nifNode,
osg::Node* node, std::map<int, int>& boundTextures);
// Creates an osg::Geometry object for the given TriShape, populates it, and attaches it to the given node.
void handleTriShape(const Nif::NiTriShape* triShape, osg::Group* parentNode, const std::map<int, int>& boundTextures);
// Fills the vertex data for the given TriShape into the given Geometry.
void triShapeToGeometry(const Nif::NiTriShape* triShape, osg::Geometry* geom, const std::map<int, int>& boundTextures);
// Creates a skinned osg::Geometry object for the given TriShape, populates it, and attaches it to the given node.
void handleSkinnedTriShape(const Nif::NiTriShape* triShape, osg::Group* parentNode, const std::map<int, int>& boundTextures);
// Applies the Properties of the given nifNode onto the StateSet of the given OSG node.
void applyNodeProperties(const Nif::Node* nifNode, osg::Node* applyTo, std::map<int, int>& boundTextures);
void createController(const Nif::Controller* ctrl, boost::shared_ptr<ControllerValue> value, int animflags);
Nif::NIFFilePtr mNif;
osg::Group* mRootNode;
osg::Group* mSkeleton;
};
}
#endif
Loading…
Cancel
Save