|
|
|
@ -319,6 +319,19 @@ namespace NifOsg
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct HandleNodeArgs
|
|
|
|
|
{
|
|
|
|
|
unsigned int mNifVersion;
|
|
|
|
|
Resource::ImageManager* mImageManager;
|
|
|
|
|
SceneUtil::TextKeyMap* mTextKeys;
|
|
|
|
|
std::vector<unsigned int> mBoundTextures = {};
|
|
|
|
|
int mAnimFlags = 0;
|
|
|
|
|
bool mSkipMeshes = false;
|
|
|
|
|
bool mHasMarkers = false;
|
|
|
|
|
bool mHasAnimatedParents = false;
|
|
|
|
|
osg::Node* mRootNode = nullptr;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
osg::ref_ptr<osg::Node> load(Nif::FileView nif, Resource::ImageManager* imageManager)
|
|
|
|
|
{
|
|
|
|
|
const size_t numRoots = nif.numRoots();
|
|
|
|
@ -341,8 +354,10 @@ namespace NifOsg
|
|
|
|
|
created->setDataVariance(osg::Object::STATIC);
|
|
|
|
|
for (const Nif::Node* root : roots)
|
|
|
|
|
{
|
|
|
|
|
auto node = handleNode(nif.getVersion(), root, nullptr, nullptr, imageManager,
|
|
|
|
|
std::vector<unsigned int>(), 0, false, false, false, &textkeys->mTextKeys);
|
|
|
|
|
auto node = handleNode(root, nullptr, nullptr,
|
|
|
|
|
{ .mNifVersion = nif.getVersion(),
|
|
|
|
|
.mImageManager = imageManager,
|
|
|
|
|
.mTextKeys = &textkeys->mTextKeys });
|
|
|
|
|
created->addChild(node);
|
|
|
|
|
}
|
|
|
|
|
if (mHasNightDayLabel)
|
|
|
|
@ -598,19 +613,11 @@ namespace NifOsg
|
|
|
|
|
return node;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
osg::ref_ptr<osg::Node> handleNode(unsigned int nifVersion, const Nif::Node* nifNode, const Nif::Parent* parent,
|
|
|
|
|
osg::Group* parentNode, Resource::ImageManager* imageManager, std::vector<unsigned int> boundTextures,
|
|
|
|
|
int animflags, bool skipMeshes, bool hasMarkers, bool hasAnimatedParents, SceneUtil::TextKeyMap* textKeys,
|
|
|
|
|
osg::Node* rootNode = nullptr)
|
|
|
|
|
osg::ref_ptr<osg::Node> handleNode(
|
|
|
|
|
const Nif::Node* nifNode, const Nif::Parent* parent, osg::Group* parentNode, HandleNodeArgs args)
|
|
|
|
|
{
|
|
|
|
|
if (rootNode)
|
|
|
|
|
{
|
|
|
|
|
if (Misc::StringUtils::ciEqual(nifNode->name, "Bounding Box"))
|
|
|
|
|
return nullptr;
|
|
|
|
|
if (nifVersion > Nif::NIFFile::NIFVersion::VER_MW && !Loader::getShowMarkers()
|
|
|
|
|
&& Misc::StringUtils::ciEqual(nifNode->name, "EditorMarker"))
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
if (args.mRootNode && Misc::StringUtils::ciEqual(nifNode->name, "Bounding Box"))
|
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
|
|
osg::ref_ptr<osg::Group> node = createNode(nifNode);
|
|
|
|
|
|
|
|
|
@ -624,8 +631,8 @@ namespace NifOsg
|
|
|
|
|
if (parentNode)
|
|
|
|
|
parentNode->addChild(node);
|
|
|
|
|
|
|
|
|
|
if (!rootNode)
|
|
|
|
|
rootNode = node;
|
|
|
|
|
if (!args.mRootNode)
|
|
|
|
|
args.mRootNode = node;
|
|
|
|
|
|
|
|
|
|
// The original NIF record index is used for a variety of features:
|
|
|
|
|
// - finding the correct emitter node for a particle system
|
|
|
|
@ -644,10 +651,10 @@ namespace NifOsg
|
|
|
|
|
|
|
|
|
|
for (const auto& e : extraCollection)
|
|
|
|
|
{
|
|
|
|
|
if (e->recType == Nif::RC_NiTextKeyExtraData && textKeys)
|
|
|
|
|
if (e->recType == Nif::RC_NiTextKeyExtraData && args.mTextKeys)
|
|
|
|
|
{
|
|
|
|
|
const Nif::NiTextKeyExtraData* tk = static_cast<const Nif::NiTextKeyExtraData*>(e.getPtr());
|
|
|
|
|
extractTextKeys(tk, *textKeys);
|
|
|
|
|
extractTextKeys(tk, *args.mTextKeys);
|
|
|
|
|
}
|
|
|
|
|
else if (e->recType == Nif::RC_NiStringExtraData)
|
|
|
|
|
{
|
|
|
|
@ -660,7 +667,7 @@ namespace NifOsg
|
|
|
|
|
if (sd->string == "MRK" && !Loader::getShowMarkers())
|
|
|
|
|
{
|
|
|
|
|
// Marker objects. These meshes are only visible in the editor.
|
|
|
|
|
hasMarkers = true;
|
|
|
|
|
args.mHasMarkers = true;
|
|
|
|
|
}
|
|
|
|
|
else if (sd->string == "BONE")
|
|
|
|
|
{
|
|
|
|
@ -672,10 +679,16 @@ namespace NifOsg
|
|
|
|
|
Misc::OsgUserValues::sExtraData, sd->string.substr(extraDataIdentifer.length()));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (e->recType == Nif::RC_BSXFlags)
|
|
|
|
|
{
|
|
|
|
|
auto bsxFlags = static_cast<const Nif::NiIntegerExtraData*>(e.getPtr());
|
|
|
|
|
if (bsxFlags->data & 32) // Editor marker flag
|
|
|
|
|
args.mHasMarkers = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (nifNode->recType == Nif::RC_NiBSAnimationNode || nifNode->recType == Nif::RC_NiBSParticleNode)
|
|
|
|
|
animflags = nifNode->flags;
|
|
|
|
|
args.mAnimFlags = nifNode->flags;
|
|
|
|
|
|
|
|
|
|
if (nifNode->recType == Nif::RC_NiSortAdjustNode)
|
|
|
|
|
{
|
|
|
|
@ -699,7 +712,7 @@ namespace NifOsg
|
|
|
|
|
// We still need to animate the hidden bones so the physics system can access them
|
|
|
|
|
if (nifNode->recType == Nif::RC_RootCollisionNode)
|
|
|
|
|
{
|
|
|
|
|
skipMeshes = true;
|
|
|
|
|
args.mSkipMeshes = true;
|
|
|
|
|
node->setNodeMask(Loader::getHiddenNodeMask());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -716,8 +729,8 @@ namespace NifOsg
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!hasVisController)
|
|
|
|
|
skipMeshes = true; // skip child meshes, but still create the child node hierarchy for animating
|
|
|
|
|
// collision shapes
|
|
|
|
|
args.mSkipMeshes = true; // skip child meshes, but still create the child node hierarchy for
|
|
|
|
|
// animating collision shapes
|
|
|
|
|
|
|
|
|
|
node->setNodeMask(Loader::getHiddenNodeMask());
|
|
|
|
|
}
|
|
|
|
@ -727,37 +740,44 @@ namespace NifOsg
|
|
|
|
|
|
|
|
|
|
osg::ref_ptr<SceneUtil::CompositeStateSetUpdater> composite = new SceneUtil::CompositeStateSetUpdater;
|
|
|
|
|
|
|
|
|
|
applyNodeProperties(nifNode, node, composite, imageManager, boundTextures, animflags);
|
|
|
|
|
applyNodeProperties(nifNode, node, composite, args.mImageManager, args.mBoundTextures, args.mAnimFlags);
|
|
|
|
|
|
|
|
|
|
const bool isGeometry = isTypeGeometry(nifNode->recType);
|
|
|
|
|
|
|
|
|
|
if (isGeometry && !skipMeshes)
|
|
|
|
|
if (isGeometry && !args.mSkipMeshes)
|
|
|
|
|
{
|
|
|
|
|
const bool isMarker = hasMarkers && Misc::StringUtils::ciStartsWith(nifNode->name, "tri editormarker");
|
|
|
|
|
if (!isMarker && !Misc::StringUtils::ciStartsWith(nifNode->name, "shadow")
|
|
|
|
|
&& !Misc::StringUtils::ciStartsWith(nifNode->name, "tri shadow"))
|
|
|
|
|
bool skip;
|
|
|
|
|
if (args.mNifVersion <= Nif::NIFFile::NIFVersion::VER_MW)
|
|
|
|
|
{
|
|
|
|
|
skip = (args.mHasMarkers && Misc::StringUtils::ciStartsWith(nifNode->name, "tri editormarker"))
|
|
|
|
|
|| Misc::StringUtils::ciStartsWith(nifNode->name, "shadow")
|
|
|
|
|
|| Misc::StringUtils::ciStartsWith(nifNode->name, "tri shadow");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
skip = args.mHasMarkers && Misc::StringUtils::ciStartsWith(nifNode->name, "EditorMarker");
|
|
|
|
|
if (!skip)
|
|
|
|
|
{
|
|
|
|
|
Nif::NiSkinInstancePtr skin = static_cast<const Nif::NiGeometry*>(nifNode)->skin;
|
|
|
|
|
|
|
|
|
|
if (skin.empty())
|
|
|
|
|
handleGeometry(nifNode, parent, node, composite, boundTextures, animflags);
|
|
|
|
|
handleGeometry(nifNode, parent, node, composite, args.mBoundTextures, args.mAnimFlags);
|
|
|
|
|
else
|
|
|
|
|
handleSkinnedGeometry(nifNode, parent, node, composite, boundTextures, animflags);
|
|
|
|
|
handleSkinnedGeometry(nifNode, parent, node, composite, args.mBoundTextures, args.mAnimFlags);
|
|
|
|
|
|
|
|
|
|
if (!nifNode->controller.empty())
|
|
|
|
|
handleMeshControllers(nifNode, node, composite, boundTextures, animflags);
|
|
|
|
|
handleMeshControllers(nifNode, node, composite, args.mBoundTextures, args.mAnimFlags);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (nifNode->recType == Nif::RC_NiParticles)
|
|
|
|
|
handleParticleSystem(nifNode, parent, node, composite, animflags);
|
|
|
|
|
handleParticleSystem(nifNode, parent, node, composite, args.mAnimFlags);
|
|
|
|
|
|
|
|
|
|
if (composite->getNumControllers() > 0)
|
|
|
|
|
{
|
|
|
|
|
osg::Callback* cb = composite;
|
|
|
|
|
if (composite->getNumControllers() == 1)
|
|
|
|
|
cb = composite->getController(0);
|
|
|
|
|
if (animflags & Nif::NiNode::AnimFlag_AutoPlay)
|
|
|
|
|
if (args.mAnimFlags & Nif::NiNode::AnimFlag_AutoPlay)
|
|
|
|
|
node->addCullCallback(cb);
|
|
|
|
|
else
|
|
|
|
|
node->addUpdateCallback(
|
|
|
|
@ -765,10 +785,10 @@ namespace NifOsg
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool isAnimated = false;
|
|
|
|
|
handleNodeControllers(nifNode, node, animflags, isAnimated);
|
|
|
|
|
hasAnimatedParents |= isAnimated;
|
|
|
|
|
handleNodeControllers(nifNode, node, args.mAnimFlags, isAnimated);
|
|
|
|
|
args.mHasAnimatedParents |= isAnimated;
|
|
|
|
|
// Make sure empty nodes and animated shapes are not optimized away so the physics system can find them.
|
|
|
|
|
if (isAnimated || (hasAnimatedParents && ((skipMeshes || hasMarkers) || isGeometry)))
|
|
|
|
|
if (isAnimated || (args.mHasAnimatedParents && ((args.mSkipMeshes || args.mHasMarkers) || isGeometry)))
|
|
|
|
|
node->setDataVariance(osg::Object::DYNAMIC);
|
|
|
|
|
|
|
|
|
|
// LOD and Switch nodes must be wrapped by a transform (the current node) to support transformations
|
|
|
|
@ -809,8 +829,7 @@ namespace NifOsg
|
|
|
|
|
const Nif::Parent currentParent{ *ninode, parent };
|
|
|
|
|
for (const auto& child : children)
|
|
|
|
|
if (!child.empty())
|
|
|
|
|
handleNode(nifVersion, child.getPtr(), ¤tParent, currentNode, imageManager, boundTextures,
|
|
|
|
|
animflags, skipMeshes, hasMarkers, hasAnimatedParents, textKeys, rootNode);
|
|
|
|
|
handleNode(child.getPtr(), ¤tParent, currentNode, args);
|
|
|
|
|
|
|
|
|
|
// Propagate effects to the the direct subgraph instead of the node itself
|
|
|
|
|
// This simulates their "affected node list" which Morrowind appears to replace with the subgraph (?)
|
|
|
|
@ -819,7 +838,7 @@ namespace NifOsg
|
|
|
|
|
if (!effect.empty())
|
|
|
|
|
{
|
|
|
|
|
osg::ref_ptr<osg::StateSet> effectStateSet = new osg::StateSet;
|
|
|
|
|
if (handleEffect(effect.getPtr(), effectStateSet, imageManager))
|
|
|
|
|
if (handleEffect(effect.getPtr(), effectStateSet, args.mImageManager))
|
|
|
|
|
for (unsigned int i = 0; i < currentNode->getNumChildren(); ++i)
|
|
|
|
|
currentNode->getChild(i)->getOrCreateStateSet()->merge(*effectStateSet);
|
|
|
|
|
}
|
|
|
|
|