diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index a37a132615..c00b4abe11 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -285,24 +285,6 @@ namespace int mSourceIndex; }; - osg::ref_ptr handleMorphGeometry(const Nif::NiGeomMorpherController* morpher) - { - osg::ref_ptr morphGeom = new osgAnimation::MorphGeometry; - morphGeom->setMethod(osgAnimation::MorphGeometry::RELATIVE); - // No normals available in the MorphData - morphGeom->setMorphNormals(false); - - const std::vector& 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 morphTarget = new osg::Geometry; - morphTarget->setVertexArray(new osg::Vec3Array(morphs[i].mVertices.size(), &morphs[i].mVertices[0])); - morphGeom->addMorphTarget(morphTarget, 0.f); - } - return morphGeom; - } - void extractTextKeys(const Nif::NiTextKeyExtraData *tk, NifOsg::TextKeyMap &textkeys) { for(size_t i = 0;i < tk->list.size();i++) @@ -352,959 +334,1016 @@ namespace NifOsg sShowMarkers = show; } - void Loader::loadKf(Nif::NIFFilePtr nif, osg::Node *rootNode, int sourceIndex, TextKeyMap& textKeys) + class LoaderImpl { - if(nif->numRoots() < 1) - { - nif->warn("Found no root nodes"); - return; - } - - const Nif::Record *r = nif->getRoot(0); - assert(r != NULL); + public: + const VFS::Manager* mResourceManager; + bool mShowMarkers; - if(r->recType != Nif::RC_NiSequenceStreamHelper) + LoaderImpl(const VFS::Manager* resourceManager, bool showMarkers) + : mResourceManager(resourceManager) + , mShowMarkers(showMarkers) { - nif->warn("First root was not a NiSequenceStreamHelper, but a "+ - r->recName+"."); - return; } - const Nif::NiSequenceStreamHelper *seq = static_cast(r); - Nif::ExtraPtr extra = seq->extra; - if(extra.empty() || extra->recType != Nif::RC_NiTextKeyExtraData) + void loadKf(Nif::NIFFilePtr nif, osg::Node *rootNode, int sourceIndex, TextKeyMap& textKeys) { - nif->warn("First extra data was not a NiTextKeyExtraData, but a "+ - (extra.empty() ? std::string("nil") : extra->recName)+"."); - return; - } - - extractTextKeys(static_cast(extra.getPtr()), textKeys); + if(nif->numRoots() < 1) + { + nif->warn("Found no root nodes"); + return; + } - std::map controllerMap; + const Nif::Record *r = nif->getRoot(0); + assert(r != NULL); - extra = extra->extra; - Nif::ControllerPtr ctrl = seq->controller; - for(;!extra.empty() && !ctrl.empty();(extra=extra->extra),(ctrl=ctrl->next)) - { - if(extra->recType != Nif::RC_NiStringExtraData || ctrl->recType != Nif::RC_NiKeyframeController) + if(r->recType != Nif::RC_NiSequenceStreamHelper) { - nif->warn("Unexpected extra data "+extra->recName+" with controller "+ctrl->recName); - continue; + nif->warn("First root was not a NiSequenceStreamHelper, but a "+ + r->recName+"."); + return; } + const Nif::NiSequenceStreamHelper *seq = static_cast(r); - if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active)) - continue; + Nif::ExtraPtr extra = seq->extra; + if(extra.empty() || extra->recType != Nif::RC_NiTextKeyExtraData) + { + nif->warn("First extra data was not a NiTextKeyExtraData, but a "+ + (extra.empty() ? std::string("nil") : extra->recName)+"."); + return; + } - const Nif::NiStringExtraData *strdata = static_cast(extra.getPtr()); - const Nif::NiKeyframeController *key = static_cast(ctrl.getPtr()); + extractTextKeys(static_cast(extra.getPtr()), textKeys); - if(key->data.empty()) - continue; + std::map controllerMap; - controllerMap[strdata->string] = key; - } - - LoadKfVisitor visitor(controllerMap, sourceIndex); - rootNode->accept(visitor); - } + extra = extra->extra; + Nif::ControllerPtr ctrl = seq->controller; + for(;!extra.empty() && !ctrl.empty();(extra=extra->extra),(ctrl=ctrl->next)) + { + if(extra->recType != Nif::RC_NiStringExtraData || ctrl->recType != Nif::RC_NiKeyframeController) + { + nif->warn("Unexpected extra data "+extra->recName+" with controller "+ctrl->recName); + continue; + } - osg::Node* Loader::load(Nif::NIFFilePtr nif, osg::Group *parentNode, TextKeyMap* textKeys) - { - mNif = nif; + if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active)) + continue; - if (nif->numRoots() < 1) - nif->fail("Found no root nodes"); + const Nif::NiStringExtraData *strdata = static_cast(extra.getPtr()); + const Nif::NiKeyframeController *key = static_cast(ctrl.getPtr()); - const Nif::Record* r = nif->getRoot(0); + if(key->data.empty()) + continue; - const Nif::Node* nifNode = dynamic_cast(r); - if (nifNode == NULL) - nif->fail("First root was not a node, but a " + r->recName); + controllerMap[strdata->string] = key; + } - mRootNode = parentNode; + LoadKfVisitor visitor(controllerMap, sourceIndex); + rootNode->accept(visitor); + } - osg::Node* created = handleNode(nifNode, parentNode, false, std::map(), 0, 0, false, textKeys); - return created; - } + osg::Node* load(Nif::NIFFilePtr nif, osg::Group *parentNode, TextKeyMap* textKeys) + { + if (nif->numRoots() < 1) + nif->fail("Found no root nodes"); - osg::Node* Loader::loadAsSkeleton(Nif::NIFFilePtr nif, osg::Group *parentNode, TextKeyMap* textKeys) - { - mNif = nif; + const Nif::Record* r = nif->getRoot(0); - if (nif->numRoots() < 1) - nif->fail("Found no root nodes"); + const Nif::Node* nifNode = dynamic_cast(r); + if (nifNode == NULL) + nif->fail("First root was not a node, but a " + r->recName); - const Nif::Record* r = nif->getRoot(0); - assert(r != NULL); + osg::Node* created = handleNode(nifNode, parentNode, false, std::map(), 0, 0, false, textKeys); + return created; + } - const Nif::Node* nifNode = dynamic_cast(r); - if (nifNode == NULL) - nif->fail("First root was not a node, but a " + r->recName); + osg::Node* loadAsSkeleton(Nif::NIFFilePtr nif, osg::Group *parentNode, TextKeyMap* textKeys) + { + if (nif->numRoots() < 1) + nif->fail("Found no root nodes"); - osg::ref_ptr skel = new osgAnimation::Skeleton; - parentNode->addChild(skel); + const Nif::Record* r = nif->getRoot(0); + assert(r != NULL); - mRootNode = parentNode; + const Nif::Node* nifNode = dynamic_cast(r); + if (nifNode == NULL) + nif->fail("First root was not a node, but a " + r->recName); - handleNode(nifNode, skel, true, std::map(), 0, 0, false, textKeys); + osg::ref_ptr skel = new osgAnimation::Skeleton; + parentNode->addChild(skel); - return skel; - } + handleNode(nifNode, skel, true, std::map(), 0, 0, false, textKeys); - void Loader::applyNodeProperties(const Nif::Node *nifNode, osg::Node *applyTo, std::map& boundTextures, int animflags) - { - const Nif::PropertyList& props = nifNode->props; - for (size_t i = 0; i mSource = boost::shared_ptr(new FrameTimeSource); - - toSetup->mFunction = boost::shared_ptr(new ControllerFunction(ctrl, 1 /*autoPlay*/)); - } - osg::Node* Loader::handleNode(const Nif::Node* nifNode, osg::Group* parentNode, bool createSkeleton, - std::map boundTextures, int animflags, int particleflags, bool skipMeshes, TextKeyMap* textKeys) - { - osg::ref_ptr transformNode; - if (nifNode->recType == Nif::RC_NiBillboardNode) - { - transformNode = new BillboardNode(toMatrix(nifNode->trafo)); - } - else if (createSkeleton) + void applyNodeProperties(const Nif::Node *nifNode, osg::Node *applyTo, std::map& boundTextures, int animflags) { - osgAnimation::Bone* bone = new osgAnimation::Bone; - transformNode = bone; - bone->setMatrix(toMatrix(nifNode->trafo)); - bone->setName(nifNode->name); - bone->setInvBindMatrixInSkeletonSpace(osg::Matrixf::inverse(getWorldTransform(nifNode))); + const Nif::PropertyList& props = nifNode->props; + for (size_t i = 0; i trafo)); - } - // Ignoring name for non-bone nodes for now. We might need it later in isolated cases, e.g. AttachLight. + //bool autoPlay = animflags & Nif::NiNode::AnimFlag_AutoPlay; + //if (autoPlay) + toSetup->mSource = boost::shared_ptr(new FrameTimeSource); - // Insert bones at position 0 to prevent update order problems (see comment in osg Skeleton.cpp) - parentNode->insertChild(0, transformNode); + toSetup->mFunction = boost::shared_ptr(new ControllerFunction(ctrl, 1 /*autoPlay*/)); + } - // UserData used for a variety of features: - // - finding the correct emitter node for a particle system - // - establishing connections to the animated collision shapes, which are handled in a separate loader - // - finding a random child NiNode in NiBspArrayController - // - storing the previous 3x3 rotation and scale values for when a KeyframeController wants to - // change only certain elements of the 4x4 transform - transformNode->getOrCreateUserDataContainer()->addUserObject( - new NodeUserData(nifNode->recIndex, nifNode->trafo.scale, nifNode->trafo.rotation)); - for (Nif::ExtraPtr e = nifNode->extra; !e.empty(); e = e->extra) + osg::Node* handleNode(const Nif::Node* nifNode, osg::Group* parentNode, bool createSkeleton, + std::map boundTextures, int animflags, int particleflags, bool skipMeshes, TextKeyMap* textKeys, osg::Node* rootNode=NULL) { - if(e->recType == Nif::RC_NiTextKeyExtraData && textKeys) + osg::ref_ptr transformNode; + if (nifNode->recType == Nif::RC_NiBillboardNode) + { + transformNode = new BillboardNode(toMatrix(nifNode->trafo)); + } + else if (createSkeleton) { - const Nif::NiTextKeyExtraData *tk = static_cast(e.getPtr()); - extractTextKeys(tk, *textKeys); + osgAnimation::Bone* bone = new osgAnimation::Bone; + transformNode = bone; + bone->setMatrix(toMatrix(nifNode->trafo)); + bone->setName(nifNode->name); + bone->setInvBindMatrixInSkeletonSpace(osg::Matrixf::inverse(getWorldTransform(nifNode))); } - else if(e->recType == Nif::RC_NiStringExtraData) + else { - const Nif::NiStringExtraData *sd = static_cast(e.getPtr()); - // String markers may contain important information - // affecting the entire subtree of this obj - // TODO: implement show markers flag - if(sd->string == "MRK" && !sShowMarkers) + transformNode = new osg::MatrixTransform(toMatrix(nifNode->trafo)); + } + + if (!rootNode) + rootNode = transformNode; + + // Ignoring name for non-bone nodes for now. We might need it later in isolated cases, e.g. AttachLight. + + // Insert bones at position 0 to prevent update order problems (see comment in osg Skeleton.cpp) + parentNode->insertChild(0, transformNode); + + // UserData used for a variety of features: + // - finding the correct emitter node for a particle system + // - establishing connections to the animated collision shapes, which are handled in a separate loader + // - finding a random child NiNode in NiBspArrayController + // - storing the previous 3x3 rotation and scale values for when a KeyframeController wants to + // change only certain elements of the 4x4 transform + transformNode->getOrCreateUserDataContainer()->addUserObject( + new NodeUserData(nifNode->recIndex, nifNode->trafo.scale, nifNode->trafo.rotation)); + + for (Nif::ExtraPtr e = nifNode->extra; !e.empty(); e = e->extra) + { + if(e->recType == Nif::RC_NiTextKeyExtraData && textKeys) { - // Marker objects. These meshes are only visible in the editor. - skipMeshes = true; + const Nif::NiTextKeyExtraData *tk = static_cast(e.getPtr()); + extractTextKeys(tk, *textKeys); + } + else if(e->recType == Nif::RC_NiStringExtraData) + { + const Nif::NiStringExtraData *sd = static_cast(e.getPtr()); + // String markers may contain important information + // affecting the entire subtree of this obj + // TODO: implement show markers flag + if(sd->string == "MRK" && !mShowMarkers) + { + // Marker objects. These meshes are only visible in the editor. + skipMeshes = true; + } } } - } - if (nifNode->recType == Nif::RC_NiBSAnimationNode) - animflags |= nifNode->flags; - if (nifNode->recType == Nif::RC_NiBSParticleNode) - particleflags |= nifNode->flags; + if (nifNode->recType == Nif::RC_NiBSAnimationNode) + animflags |= nifNode->flags; + if (nifNode->recType == Nif::RC_NiBSParticleNode) + particleflags |= nifNode->flags; - // Hide collision shapes, but don't skip the subgraph - // We still need to animate the hidden bones so the physics system can access them - if (nifNode->recType == Nif::RC_RootCollisionNode) - { - skipMeshes = true; - // Leave mask for UpdateVisitor enabled - transformNode->setNodeMask(0x1); - } + // Hide collision shapes, but don't skip the subgraph + // We still need to animate the hidden bones so the physics system can access them + if (nifNode->recType == Nif::RC_RootCollisionNode) + { + skipMeshes = true; + // Leave mask for UpdateVisitor enabled + transformNode->setNodeMask(0x1); + } - // 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(0x1); // Leave mask for UpdateVisitor enabled + // 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(0x1); // Leave mask for UpdateVisitor enabled - applyNodeProperties(nifNode, transformNode, boundTextures, animflags); + applyNodeProperties(nifNode, transformNode, boundTextures, animflags); - if (nifNode->recType == Nif::RC_NiTriShape && !skipMeshes) - { - const Nif::NiTriShape* triShape = static_cast(nifNode); - if (!createSkeleton || triShape->skin.empty()) - handleTriShape(triShape, transformNode, boundTextures, animflags); - else - handleSkinnedTriShape(triShape, transformNode, boundTextures, animflags); + if (nifNode->recType == Nif::RC_NiTriShape && !skipMeshes) + { + const Nif::NiTriShape* triShape = static_cast(nifNode); + if (!createSkeleton || triShape->skin.empty()) + handleTriShape(triShape, transformNode, boundTextures, animflags); + else + handleSkinnedTriShape(triShape, transformNode, boundTextures, animflags); - if (!nifNode->controller.empty()) - handleMeshControllers(nifNode, transformNode, boundTextures, animflags); - } + if (!nifNode->controller.empty()) + handleMeshControllers(nifNode, transformNode, boundTextures, animflags); + } - if(nifNode->recType == Nif::RC_NiAutoNormalParticles || nifNode->recType == Nif::RC_NiRotatingParticles) - handleParticleSystem(nifNode, transformNode, animflags, particleflags); + if(nifNode->recType == Nif::RC_NiAutoNormalParticles || nifNode->recType == Nif::RC_NiRotatingParticles) + handleParticleSystem(nifNode, transformNode, animflags, particleflags, rootNode); - if (!nifNode->controller.empty()) - handleNodeControllers(nifNode, transformNode, animflags); + if (!nifNode->controller.empty()) + handleNodeControllers(nifNode, transformNode, animflags); - // Added last so the changes from KeyframeControllers are taken into account - if (osgAnimation::Bone* bone = dynamic_cast(transformNode.get())) - bone->addUpdateCallback(new UpdateBone); + // Added last so the changes from KeyframeControllers are taken into account + if (osgAnimation::Bone* bone = dynamic_cast(transformNode.get())) + bone->addUpdateCallback(new UpdateBone); - const Nif::NiNode *ninode = dynamic_cast(nifNode); - if(ninode) - { - const Nif::NodeList &children = ninode->children; - for(size_t i = 0;i < children.length();++i) + const Nif::NiNode *ninode = dynamic_cast(nifNode); + if(ninode) { - if(!children[i].empty()) - handleNode(children[i].getPtr(), transformNode, createSkeleton, boundTextures, animflags, particleflags, skipMeshes, textKeys); + 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, animflags, particleflags, skipMeshes, textKeys, rootNode); + } } - } - return transformNode; - } + return transformNode; + } - void Loader::handleMeshControllers(const Nif::Node *nifNode, osg::MatrixTransform *transformNode, const std::map &boundTextures, int animflags) - { - for (Nif::ControllerPtr ctrl = nifNode->controller; !ctrl.empty(); ctrl = ctrl->next) + void handleMeshControllers(const Nif::Node *nifNode, osg::MatrixTransform *transformNode, const std::map &boundTextures, int animflags) { - if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active)) - continue; - if (ctrl->recType == Nif::RC_NiUVController) + for (Nif::ControllerPtr ctrl = nifNode->controller; !ctrl.empty(); ctrl = ctrl->next) { - const Nif::NiUVController *uvctrl = static_cast(ctrl.getPtr()); - std::set texUnits; - for (std::map::const_iterator it = boundTextures.begin(); it != boundTextures.end(); ++it) - texUnits.insert(it->first); + if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active)) + continue; + if (ctrl->recType == Nif::RC_NiUVController) + { + const Nif::NiUVController *uvctrl = static_cast(ctrl.getPtr()); + std::set texUnits; + for (std::map::const_iterator it = boundTextures.begin(); it != boundTextures.end(); ++it) + texUnits.insert(it->first); - osg::ref_ptr ctrl = new UVController(uvctrl->data.getPtr(), texUnits); - setupController(uvctrl, ctrl, animflags); - transformNode->getOrCreateStateSet()->setDataVariance(osg::StateSet::DYNAMIC); + osg::ref_ptr ctrl = new UVController(uvctrl->data.getPtr(), texUnits); + setupController(uvctrl, ctrl, animflags); + transformNode->getOrCreateStateSet()->setDataVariance(osg::StateSet::DYNAMIC); - transformNode->addUpdateCallback(ctrl); + transformNode->addUpdateCallback(ctrl); + } } } - } - void Loader::handleNodeControllers(const Nif::Node* nifNode, osg::MatrixTransform* transformNode, int animflags) - { - for (Nif::ControllerPtr ctrl = nifNode->controller; !ctrl.empty(); ctrl = ctrl->next) + void handleNodeControllers(const Nif::Node* nifNode, osg::MatrixTransform* transformNode, int animflags) { - if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active)) - continue; - if (ctrl->recType == Nif::RC_NiKeyframeController) + for (Nif::ControllerPtr ctrl = nifNode->controller; !ctrl.empty(); ctrl = ctrl->next) { - const Nif::NiKeyframeController *key = static_cast(ctrl.getPtr()); - if(!key->data.empty()) + if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active)) + continue; + if (ctrl->recType == Nif::RC_NiKeyframeController) { - osg::ref_ptr callback(new KeyframeController(key->data.getPtr())); + const Nif::NiKeyframeController *key = static_cast(ctrl.getPtr()); + if(!key->data.empty()) + { + osg::ref_ptr callback(new KeyframeController(key->data.getPtr())); - setupController(key, callback, animflags); - transformNode->addUpdateCallback(callback); + setupController(key, callback, animflags); + transformNode->addUpdateCallback(callback); + } } - } - else if (ctrl->recType == Nif::RC_NiVisController) - { - const Nif::NiVisController* visctrl = static_cast(ctrl.getPtr()); + else if (ctrl->recType == Nif::RC_NiVisController) + { + const Nif::NiVisController* visctrl = static_cast(ctrl.getPtr()); - osg::ref_ptr callback(new VisController(visctrl->data.getPtr())); - setupController(visctrl, callback, animflags); - transformNode->addUpdateCallback(callback); + osg::ref_ptr callback(new VisController(visctrl->data.getPtr())); + setupController(visctrl, callback, animflags); + transformNode->addUpdateCallback(callback); + } } } - } - void Loader::handleMaterialControllers(const Nif::Property *materialProperty, osg::Node* node, osg::StateSet *stateset, int animflags) - { - for (Nif::ControllerPtr ctrl = materialProperty->controller; !ctrl.empty(); ctrl = ctrl->next) + + void handleMaterialControllers(const Nif::Property *materialProperty, osg::Node* node, osg::StateSet *stateset, int animflags) { - if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active)) - continue; - if (ctrl->recType == Nif::RC_NiAlphaController) + for (Nif::ControllerPtr ctrl = materialProperty->controller; !ctrl.empty(); ctrl = ctrl->next) { - const Nif::NiAlphaController* alphactrl = static_cast(ctrl.getPtr()); - osg::ref_ptr ctrl(new AlphaController(alphactrl->data.getPtr())); - setupController(alphactrl, ctrl, animflags); - stateset->setDataVariance(osg::StateSet::DYNAMIC); - node->addUpdateCallback(ctrl); - } - else if (ctrl->recType == Nif::RC_NiMaterialColorController) - { - const Nif::NiMaterialColorController* matctrl = static_cast(ctrl.getPtr()); - osg::ref_ptr ctrl(new MaterialColorController(matctrl->data.getPtr())); - setupController(matctrl, ctrl, animflags); - stateset->setDataVariance(osg::StateSet::DYNAMIC); - node->addUpdateCallback(ctrl); + if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active)) + continue; + if (ctrl->recType == Nif::RC_NiAlphaController) + { + const Nif::NiAlphaController* alphactrl = static_cast(ctrl.getPtr()); + osg::ref_ptr ctrl(new AlphaController(alphactrl->data.getPtr())); + setupController(alphactrl, ctrl, animflags); + stateset->setDataVariance(osg::StateSet::DYNAMIC); + node->addUpdateCallback(ctrl); + } + else if (ctrl->recType == Nif::RC_NiMaterialColorController) + { + const Nif::NiMaterialColorController* matctrl = static_cast(ctrl.getPtr()); + osg::ref_ptr ctrl(new MaterialColorController(matctrl->data.getPtr())); + setupController(matctrl, ctrl, animflags); + stateset->setDataVariance(osg::StateSet::DYNAMIC); + node->addUpdateCallback(ctrl); + } + else + std::cerr << "Unexpected material controller " << ctrl->recType << std::endl; } - else - std::cerr << "Unexpected material controller " << ctrl->recType << std::endl; } - } - void Loader::handleTextureControllers(const Nif::Property *texProperty, osg::Node* node, osg::StateSet *stateset, int animflags) - { - for (Nif::ControllerPtr ctrl = texProperty->controller; !ctrl.empty(); ctrl = ctrl->next) + + void handleTextureControllers(const Nif::Property *texProperty, osg::Node* node, osg::StateSet *stateset, int animflags) { - if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active)) - continue; - if (ctrl->recType == Nif::RC_NiFlipController) + for (Nif::ControllerPtr ctrl = texProperty->controller; !ctrl.empty(); ctrl = ctrl->next) { - const Nif::NiFlipController* flipctrl = static_cast(ctrl.getPtr()); - std::vector > textures; - for (unsigned int i=0; imSources.length(); ++i) + if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active)) + continue; + if (ctrl->recType == Nif::RC_NiFlipController) { - Nif::NiSourceTexturePtr st = flipctrl->mSources[i]; - if (st.empty()) - continue; - - std::string filename = Misc::ResourceHelpers::correctTexturePath(st->filename, resourceManager); - - // 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->get(filename.c_str()), opts); - textures.push_back(osg::ref_ptr(result.getImage())); + const Nif::NiFlipController* flipctrl = static_cast(ctrl.getPtr()); + std::vector > textures; + for (unsigned int i=0; imSources.length(); ++i) + { + Nif::NiSourceTexturePtr st = flipctrl->mSources[i]; + if (st.empty()) + continue; + + std::string filename = Misc::ResourceHelpers::correctTexturePath(st->filename, mResourceManager); + + // TODO: replace with texture manager + // 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(*mResourceManager->get(filename.c_str()), opts); + textures.push_back(osg::ref_ptr(result.getImage())); + } + osg::ref_ptr callback(new FlipController(flipctrl, textures)); + setupController(ctrl.getPtr(), callback, animflags); + stateset->setDataVariance(osg::StateSet::DYNAMIC); + node->addUpdateCallback(callback); } - osg::ref_ptr callback(new FlipController(flipctrl, textures)); - setupController(ctrl.getPtr(), callback, animflags); - stateset->setDataVariance(osg::StateSet::DYNAMIC); - node->addUpdateCallback(callback); + else + std::cerr << "Unexpected texture controller " << ctrl->recName << std::endl; } - else - std::cerr << "Unexpected texture controller " << ctrl->recName << std::endl; } - } - void Loader::handleParticleSystem(const Nif::Node *nifNode, osg::Group *parentNode, int animflags, int particleflags) - { - osg::ref_ptr partsys (new ParticleSystem); - partsys->setSortMode(osgParticle::ParticleSystem::SORT_BACK_TO_FRONT); - - const Nif::NiAutoNormalParticlesData *particledata = NULL; - if(nifNode->recType == Nif::RC_NiAutoNormalParticles) - particledata = static_cast(nifNode)->data.getPtr(); - else if(nifNode->recType == Nif::RC_NiRotatingParticles) - particledata = static_cast(nifNode)->data.getPtr(); - else - return; - - const Nif::NiParticleSystemController* partctrl = NULL; - for (Nif::ControllerPtr ctrl = nifNode->controller; !ctrl.empty(); ctrl = ctrl->next) + void handleParticleAffectors(Nif::ExtraPtr e, osg::Group *attachTo, osgParticle::ParticleSystem* partsys, osgParticle::ParticleProcessor::ReferenceFrame rf) { - if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active)) - continue; - if(ctrl->recType == Nif::RC_NiParticleSystemController || ctrl->recType == Nif::RC_NiBSPArrayController) - partctrl = static_cast(ctrl.getPtr()); - } - if (!partctrl) - { - std::cerr << "No particle controller found " << std::endl; - return; + osgParticle::ModularProgram* program = new osgParticle::ModularProgram; + attachTo->addChild(program); + program->setParticleSystem(partsys); + program->setReferenceFrame(rf); + for (; !e.empty(); e = e->extra) + { + if (e->recType == Nif::RC_NiParticleGrowFade) + { + const Nif::NiParticleGrowFade *gf = static_cast(e.getPtr()); + GrowFadeAffector* affector = new GrowFadeAffector(gf->growTime, gf->fadeTime); + program->addOperator(affector); + } + else if (e->recType == Nif::RC_NiGravity) + { + const Nif::NiGravity* gr = static_cast(e.getPtr()); + GravityAffector* affector = new GravityAffector(gr); + program->addOperator(affector); + } + else if (e->recType == Nif::RC_NiParticleColorModifier) + { + const Nif::NiParticleColorModifier *cl = static_cast(e.getPtr()); + const Nif::NiColorData *clrdata = cl->data.getPtr(); + ParticleColorAffector* affector = new ParticleColorAffector(clrdata); + program->addOperator(affector); + } + else if (e->recType == Nif::RC_NiParticleRotation) + { + // TODO: Implement? + } + else + std::cerr << "Unhandled particle modifier " << e->recName << std::endl; + } } - std::vector targets; - if (partctrl->recType == Nif::RC_NiBSPArrayController) + + void handleParticleSystem(const Nif::Node *nifNode, osg::Group *parentNode, int animflags, int particleflags, osg::Node* rootNode) { - getAllNiNodes(partctrl->emitter.getPtr(), targets); - } + osg::ref_ptr partsys (new ParticleSystem); + partsys->setSortMode(osgParticle::ParticleSystem::SORT_BACK_TO_FRONT); + + const Nif::NiAutoNormalParticlesData *particledata = NULL; + if(nifNode->recType == Nif::RC_NiAutoNormalParticles) + particledata = static_cast(nifNode)->data.getPtr(); + else if(nifNode->recType == Nif::RC_NiRotatingParticles) + particledata = static_cast(nifNode)->data.getPtr(); + else + return; - osgParticle::ParticleProcessor::ReferenceFrame rf = (particleflags & Nif::NiNode::ParticleFlag_LocalSpace) - ? osgParticle::ParticleProcessor::RELATIVE_RF - : osgParticle::ParticleProcessor::ABSOLUTE_RF; + const Nif::NiParticleSystemController* partctrl = NULL; + for (Nif::ControllerPtr ctrl = nifNode->controller; !ctrl.empty(); ctrl = ctrl->next) + { + if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active)) + continue; + if(ctrl->recType == Nif::RC_NiParticleSystemController || ctrl->recType == Nif::RC_NiBSPArrayController) + partctrl = static_cast(ctrl.getPtr()); + } + if (!partctrl) + { + std::cerr << "No particle controller found " << std::endl; + return; + } - // TODO: also take into account the transform by placement in the scene - osg::Matrix particletransform; - if (rf == osgParticle::ParticleProcessor::ABSOLUTE_RF) - particletransform = getWorldTransform(nifNode); + std::vector targets; + if (partctrl->recType == Nif::RC_NiBSPArrayController) + { + getAllNiNodes(partctrl->emitter.getPtr(), targets); + } - int i=0; - for (std::vector::const_iterator it = partctrl->particles.begin(); - iactiveCount && it != partctrl->particles.end(); ++it, ++i) - { - const Nif::NiParticleSystemController::Particle& particle = *it; + osgParticle::ParticleProcessor::ReferenceFrame rf = (particleflags & Nif::NiNode::ParticleFlag_LocalSpace) + ? osgParticle::ParticleProcessor::RELATIVE_RF + : osgParticle::ParticleProcessor::ABSOLUTE_RF; - ParticleAgeSetter particletemplate(std::max(0.f, particle.lifetime)); + // TODO: also take into account the transform by placement in the scene + osg::Matrix particletransform; + if (rf == osgParticle::ParticleProcessor::ABSOLUTE_RF) + particletransform = getWorldTransform(nifNode); - osgParticle::Particle* created = partsys->createParticle(&particletemplate); - created->setLifeTime(std::max(0.f, particle.lifespan)); - osg::Vec4f adjustedVelocity = osg::Vec4f(particle.velocity, 0.f) * particletransform; - created->setVelocity(osg::Vec3f(adjustedVelocity.x(), adjustedVelocity.y(), adjustedVelocity.z())); - created->setPosition(particledata->vertices.at(particle.vertex) * particletransform); + int i=0; + for (std::vector::const_iterator it = partctrl->particles.begin(); + iactiveCount && it != partctrl->particles.end(); ++it, ++i) + { + const Nif::NiParticleSystemController::Particle& particle = *it; - osg::Vec4f partcolor (1.f,1.f,1.f,1.f); - if (particle.vertex < int(particledata->colors.size())) - partcolor = particledata->colors.at(particle.vertex); + ParticleAgeSetter particletemplate(std::max(0.f, particle.lifetime)); - float size = particledata->sizes.at(particle.vertex) * partctrl->size; + osgParticle::Particle* created = partsys->createParticle(&particletemplate); + created->setLifeTime(std::max(0.f, particle.lifespan)); + osg::Vec4f adjustedVelocity = osg::Vec4f(particle.velocity, 0.f) * particletransform; + created->setVelocity(osg::Vec3f(adjustedVelocity.x(), adjustedVelocity.y(), adjustedVelocity.z())); + created->setPosition(particledata->vertices.at(particle.vertex) * particletransform); - created->setSizeRange(osgParticle::rangef(size, size)); - } + osg::Vec4f partcolor (1.f,1.f,1.f,1.f); + if (particle.vertex < int(particledata->colors.size())) + partcolor = particledata->colors.at(particle.vertex); - partsys->setQuota(partctrl->numParticles); + float size = particledata->sizes.at(particle.vertex) * partctrl->size; - partsys->getDefaultParticleTemplate().setSizeRange(osgParticle::rangef(partctrl->size, partctrl->size)); - partsys->getDefaultParticleTemplate().setColorRange(osgParticle::rangev4(osg::Vec4f(1.f,1.f,1.f,1.f), osg::Vec4f(1.f,1.f,1.f,1.f))); - partsys->getDefaultParticleTemplate().setAlphaRange(osgParticle::rangef(1.f, 1.f)); + created->setSizeRange(osgParticle::rangef(size, size)); + } - // ---- emitter + partsys->setQuota(partctrl->numParticles); - osg::ref_ptr emitter = new Emitter(targets); - emitter->setParticleSystem(partsys); - emitter->setReferenceFrame(osgParticle::ParticleProcessor::RELATIVE_RF); + partsys->getDefaultParticleTemplate().setSizeRange(osgParticle::rangef(partctrl->size, partctrl->size)); + partsys->getDefaultParticleTemplate().setColorRange(osgParticle::rangev4(osg::Vec4f(1.f,1.f,1.f,1.f), osg::Vec4f(1.f,1.f,1.f,1.f))); + partsys->getDefaultParticleTemplate().setAlphaRange(osgParticle::rangef(1.f, 1.f)); - osgParticle::ConstantRateCounter* counter = new osgParticle::ConstantRateCounter; - if (partctrl->emitFlags & Nif::NiParticleSystemController::NoAutoAdjust) - counter->setNumberOfParticlesPerSecondToCreate(partctrl->emitRate); - else - counter->setNumberOfParticlesPerSecondToCreate(partctrl->numParticles / (partctrl->lifetime + partctrl->lifetimeRandom/2)); + // ---- emitter - emitter->setCounter(counter); + osg::ref_ptr emitter = new Emitter(targets); + emitter->setParticleSystem(partsys); + emitter->setReferenceFrame(osgParticle::ParticleProcessor::RELATIVE_RF); - ParticleShooter* shooter = new ParticleShooter(partctrl->velocity - partctrl->velocityRandom*0.5f, - partctrl->velocity + partctrl->velocityRandom*0.5f, - partctrl->horizontalDir, partctrl->horizontalAngle, - partctrl->verticalDir, partctrl->verticalAngle, - partctrl->lifetime, partctrl->lifetimeRandom); - emitter->setShooter(shooter); + osgParticle::ConstantRateCounter* counter = new osgParticle::ConstantRateCounter; + if (partctrl->emitFlags & Nif::NiParticleSystemController::NoAutoAdjust) + counter->setNumberOfParticlesPerSecondToCreate(partctrl->emitRate); + else + counter->setNumberOfParticlesPerSecondToCreate(partctrl->numParticles / (partctrl->lifetime + partctrl->lifetimeRandom/2)); - osgParticle::BoxPlacer* placer = new osgParticle::BoxPlacer; - placer->setXRange(-partctrl->offsetRandom.x(), partctrl->offsetRandom.x()); - placer->setYRange(-partctrl->offsetRandom.y(), partctrl->offsetRandom.y()); - placer->setZRange(-partctrl->offsetRandom.z(), partctrl->offsetRandom.z()); + emitter->setCounter(counter); - emitter->setPlacer(placer); + ParticleShooter* shooter = new ParticleShooter(partctrl->velocity - partctrl->velocityRandom*0.5f, + partctrl->velocity + partctrl->velocityRandom*0.5f, + partctrl->horizontalDir, partctrl->horizontalAngle, + partctrl->verticalDir, partctrl->verticalAngle, + partctrl->lifetime, partctrl->lifetimeRandom); + emitter->setShooter(shooter); - // Note: we assume that the Emitter node is placed *before* the Particle node in the scene graph. - // This seems to be true for all NIF files in the game that I've checked, suggesting that NIFs work similar to OSG with regards to update order. - // If something ever violates this assumption, the worst that could happen is the culling being one frame late, which wouldn't be a disaster. + osgParticle::BoxPlacer* placer = new osgParticle::BoxPlacer; + placer->setXRange(-partctrl->offsetRandom.x(), partctrl->offsetRandom.x()); + placer->setYRange(-partctrl->offsetRandom.y(), partctrl->offsetRandom.y()); + placer->setZRange(-partctrl->offsetRandom.z(), partctrl->offsetRandom.z()); - // Creating emitters will need to be changed when cloning a scenegraph is implemented, the particleSystem pointer would become invalid - FindRecIndexVisitor find (partctrl->emitter->recIndex); - mRootNode->accept(find); - if (!find.mFound) - { - std::cerr << "can't find emitter node, wrong node order?" << std::endl; - return; - } - osg::Group* emitterNode = find.mFound; - - // Emitter attached to the emitter node. Note one side effect of the emitter using the CullVisitor is that hiding its node - // actually causes the emitter to stop firing. Convenient, because MW behaves this way too! - emitterNode->addChild(emitter); - - osg::ref_ptr callback(new ParticleSystemController(partctrl)); - setupController(partctrl, callback, animflags); - emitter->setUpdateCallback(callback); - - // ----------- affector (must be after emitters in the scene graph) - osgParticle::ModularProgram* program = new osgParticle::ModularProgram; - program->setParticleSystem(partsys); - program->setReferenceFrame(rf); - emitterNode->addChild(program); - for (Nif::ExtraPtr e = partctrl->extra; !e.empty(); e = e->extra) - { - if (e->recType == Nif::RC_NiParticleGrowFade) - { - const Nif::NiParticleGrowFade *gf = static_cast(e.getPtr()); - GrowFadeAffector* affector = new GrowFadeAffector(gf->growTime, gf->fadeTime); - program->addOperator(affector); - } - else if (e->recType == Nif::RC_NiGravity) - { - const Nif::NiGravity* gr = static_cast(e.getPtr()); - GravityAffector* affector = new GravityAffector(gr); - program->addOperator(affector); - } - else if (e->recType == Nif::RC_NiParticleColorModifier) - { - const Nif::NiParticleColorModifier *cl = static_cast(e.getPtr()); - const Nif::NiColorData *clrdata = cl->data.getPtr(); - ParticleColorAffector* affector = new ParticleColorAffector(clrdata); - program->addOperator(affector); - } - else if (e->recType == Nif::RC_NiParticleRotation) + emitter->setPlacer(placer); + + // Note: we assume that the Emitter node is placed *before* the Particle node in the scene graph. + // This seems to be true for all NIF files in the game that I've checked, suggesting that NIFs work similar to OSG with regards to update order. + // If something ever violates this assumption, the worst that could happen is the culling being one frame late, which wouldn't be a disaster. + + // Creating emitters will need to be changed when cloning a scenegraph is implemented, the particleSystem pointer would become invalid + FindRecIndexVisitor find (partctrl->emitter->recIndex); + rootNode->accept(find); + if (!find.mFound) { - // TODO: Implement? + std::cerr << "can't find emitter node, wrong node order?" << std::endl; + return; } - else - std::cerr << "Unhandled particle modifier " << e->recName << std::endl; - } + osg::Group* emitterNode = find.mFound; - // ----------- + // Emitter attached to the emitter node. Note one side effect of the emitter using the CullVisitor is that hiding its node + // actually causes the emitter to stop firing. Convenient, because MW behaves this way too! + emitterNode->addChild(emitter); - osg::ref_ptr geode (new osg::Geode); - geode->addDrawable(partsys); + osg::ref_ptr callback(new ParticleSystemController(partctrl)); + setupController(partctrl, callback, animflags); + emitter->setUpdateCallback(callback); - std::vector materialProps; - collectMaterialProperties(nifNode, materialProps); - applyMaterialProperties(geode, materialProps, true, animflags); + // affectors must be attached *after* the emitter in the scene graph for correct update order + handleParticleAffectors(partctrl->extra, emitterNode, partsys.get(), rf); - partsys->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); - partsys->getOrCreateStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); + osg::ref_ptr geode (new osg::Geode); + geode->addDrawable(partsys); + std::vector materialProps; + collectMaterialProperties(nifNode, materialProps); + applyMaterialProperties(geode, materialProps, true, animflags); - if (rf == osgParticle::ParticleProcessor::RELATIVE_RF) - parentNode->addChild(geode); - else - { - osg::MatrixTransform* trans = new osg::MatrixTransform; - trans->setUpdateCallback(new InverseWorldMatrix); - trans->addChild(geode); - parentNode->addChild(trans); - } + partsys->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); + partsys->getOrCreateStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); - // particle system updater (after the emitters and affectors in the scene graph) - osgParticle::ParticleSystemUpdater* updater = new osgParticle::ParticleSystemUpdater; - updater->addParticleSystem(partsys); - parentNode->addChild(updater); - } - void Loader::triShapeToGeometry(const Nif::NiTriShape *triShape, osg::Geometry *geometry, osg::Geode* parentGeode, const std::map& boundTextures, int animflags) - { - const Nif::NiTriShapeData* data = triShape->data.getPtr(); + if (rf == osgParticle::ParticleProcessor::RELATIVE_RF) + parentNode->addChild(geode); + else + { + osg::MatrixTransform* trans = new osg::MatrixTransform; + trans->setUpdateCallback(new InverseWorldMatrix); + trans->addChild(geode); + parentNode->addChild(trans); + } - const Nif::NiSkinInstance *skin = (triShape->skin.empty() ? NULL : triShape->skin.getPtr()); - if (skin) + // particle system updater (after the emitters and affectors in the scene graph) + osgParticle::ParticleSystemUpdater* updater = new osgParticle::ParticleSystemUpdater; + updater->addParticleSystem(partsys); + parentNode->addChild(updater); + } + + void triShapeToGeometry(const Nif::NiTriShape *triShape, osg::Geometry *geometry, osg::Geode* parentGeode, const std::map& boundTextures, int animflags) { - // 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 newVerts (new osg::Vec3Array(data->vertices.size())); - osg::ref_ptr newNormals (new osg::Vec3Array(data->normals.size())); + const Nif::NiTriShapeData* data = triShape->data.getPtr(); - const Nif::NiSkinData *skinData = skin->data.getPtr(); - const Nif::NodeList &bones = skin->bones; - for(size_t b = 0;b < bones.length();b++) + const Nif::NiSkinInstance *skin = (triShape->skin.empty() ? NULL : triShape->skin.getPtr()); + if (skin) { - osg::Matrixf mat = toMatrix(skinData->bones[b].trafo); - - mat = mat * getWorldTransform(bones[b].getPtr()); - - const std::vector &weights = skinData->bones[b].weights; - for(size_t i = 0;i < weights.size();i++) + // 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 newVerts (new osg::Vec3Array(data->vertices.size())); + osg::ref_ptr 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++) { - size_t index = weights[i].vertex; - float weight = weights[i].weight; + osg::Matrixf mat = toMatrix(skinData->bones[b].trafo); + + mat = mat * getWorldTransform(bones[b].getPtr()); - 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) + const std::vector &weights = skinData->bones[b].weights; + for(size_t i = 0;i < weights.size();i++) { - 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()); + 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;isize();++i) + // (*newNormals)[i].normalize(); + + geometry->setVertexArray(newVerts); + if (!data->normals.empty()) + geometry->setNormalArray(newNormals, osg::Array::BIND_PER_VERTEX); } - // 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;isize();++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::const_iterator it = boundTextures.begin(); it != boundTextures.end(); ++it) - { - int textureStage = it->first; - int uvSet = it->second; - if (uvSet >= (int)data->uvlist.size()) + else { - // 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->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); } - geometry->setTexCoordArray(textureStage, new osg::Vec2Array(data->uvlist[uvSet].size(), &data->uvlist[uvSet][0]), osg::Array::BIND_PER_VERTEX); - } - - 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])); - - // osg::Material properties are handled here for two reasons: - // - if there are no vertex colors, we need to disable colorMode. - // - there are 3 "overlapping" nif properties that all affect the osg::Material, handling them - // above the actual renderable would be tedious. - std::vector materialProps; - collectMaterialProperties(triShape, materialProps); - applyMaterialProperties(parentGeode, materialProps, !data->colors.empty(), animflags); - } - - void Loader::handleTriShape(const Nif::NiTriShape* triShape, osg::Group* parentNode, const std::map& boundTextures, int animflags) - { - osg::ref_ptr geometry; - if(!triShape->controller.empty()) - { - Nif::ControllerPtr ctrl = triShape->controller; - do { - if(ctrl->recType == Nif::RC_NiGeomMorpherController && ctrl->flags & Nif::NiNode::ControllerFlag_Active) + for (std::map::const_iterator it = boundTextures.begin(); it != boundTextures.end(); ++it) + { + int textureStage = it->first; + int uvSet = it->second; + if (uvSet >= (int)data->uvlist.size()) { - geometry = handleMorphGeometry(static_cast(ctrl.getPtr())); - - osg::ref_ptr morphctrl = new GeomMorpherController( - static_cast(ctrl.getPtr())->data.getPtr()); - setupController(ctrl.getPtr(), morphctrl, animflags); - geometry->setUpdateCallback(morphctrl); - break; + // 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; } - } while(!(ctrl=ctrl->next).empty()); - } - if (!geometry.get()) - geometry = new osg::Geometry; + geometry->setTexCoordArray(textureStage, new osg::Vec2Array(data->uvlist[uvSet].size(), &data->uvlist[uvSet][0]), osg::Array::BIND_PER_VERTEX); + } - osg::ref_ptr geode (new osg::Geode); - geode->setName(triShape->name); // name will be used for part filtering - triShapeToGeometry(triShape, geometry, geode, boundTextures, animflags); + if (!data->colors.empty()) + geometry->setColorArray(new osg::Vec4Array(data->colors.size(), &data->colors[0]), osg::Array::BIND_PER_VERTEX); - geode->addDrawable(geometry); + geometry->addPrimitiveSet(new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLES, + data->triangles.size(), + (unsigned short*)&data->triangles[0])); - parentNode->addChild(geode); - } + // osg::Material properties are handled here for two reasons: + // - if there are no vertex colors, we need to disable colorMode. + // - there are 3 "overlapping" nif properties that all affect the osg::Material, handling them + // above the actual renderable would be tedious. + std::vector materialProps; + collectMaterialProperties(triShape, materialProps); + applyMaterialProperties(parentGeode, materialProps, !data->colors.empty(), animflags); + } - void Loader::handleSkinnedTriShape(const Nif::NiTriShape *triShape, osg::Group *parentNode, const std::map& boundTextures, int animflags) - { - osg::ref_ptr geode (new osg::Geode); - geode->setName(triShape->name); // name will be used for part filtering - osg::ref_ptr geometry (new osg::Geometry); - triShapeToGeometry(triShape, geometry, geode, boundTextures, animflags); - - osg::ref_ptr 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 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++) + void handleTriShape(const Nif::NiTriShape* triShape, osg::Group* parentNode, const std::map& boundTextures, int animflags) { - std::string boneName = bones[i].getPtr()->name; - - osgAnimation::VertexInfluence influence; - influence.setName(boneName); - const std::vector &weights = data->bones[i].weights; - influence.reserve(weights.size()); - for(size_t j = 0;j < weights.size();j++) + osg::ref_ptr geometry; + if(!triShape->controller.empty()) { - osgAnimation::VertexIndexWeight indexWeight = std::make_pair(weights[j].vertex, weights[j].weight); - influence.push_back(indexWeight); - } + Nif::ControllerPtr ctrl = triShape->controller; + do { + if(ctrl->recType == Nif::RC_NiGeomMorpherController && ctrl->flags & Nif::NiNode::ControllerFlag_Active) + { + geometry = handleMorphGeometry(static_cast(ctrl.getPtr())); - map->insert(std::make_pair(boneName, influence)); - } - rig->setInfluenceMap(map); + osg::ref_ptr morphctrl = new GeomMorpherController( + static_cast(ctrl.getPtr())->data.getPtr()); + setupController(ctrl.getPtr(), morphctrl, animflags); + geometry->setUpdateCallback(morphctrl); + break; + } + } while(!(ctrl=ctrl->next).empty()); + } - osg::ref_ptr trans(new osg::MatrixTransform); - trans->setUpdateCallback(new InvertBoneMatrix()); + if (!geometry.get()) + geometry = new osg::Geometry; - geode->addDrawable(rig); + osg::ref_ptr geode (new osg::Geode); + geode->setName(triShape->name); // name will be used for part filtering + triShapeToGeometry(triShape, geometry, geode, boundTextures, animflags); - trans->addChild(geode); - parentNode->addChild(trans); - } + geode->addDrawable(geometry); - void Loader::handleProperty(const Nif::Property *property, const Nif::Node* nifNode, - osg::Node *node, std::map& boundTextures, int animflags) - { - osg::StateSet* stateset = node->getOrCreateStateSet(); + parentNode->addChild(geode); + } - switch (property->recType) + osg::ref_ptr handleMorphGeometry(const Nif::NiGeomMorpherController* morpher) { - case Nif::RC_NiStencilProperty: + osg::ref_ptr morphGeom = new osgAnimation::MorphGeometry; + morphGeom->setMethod(osgAnimation::MorphGeometry::RELATIVE); + // No normals available in the MorphData + morphGeom->setMorphNormals(false); + + const std::vector& 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 morphTarget = new osg::Geometry; + morphTarget->setVertexArray(new osg::Vec3Array(morphs[i].mVertices.size(), &morphs[i].mVertices[0])); + morphGeom->addMorphTarget(morphTarget, 0.f); + } + return morphGeom; + } + + void handleSkinnedTriShape(const Nif::NiTriShape *triShape, osg::Group *parentNode, const std::map& boundTextures, int animflags) { - const Nif::NiStencilProperty* stencilprop = static_cast(property); - osg::FrontFace* frontFace = new osg::FrontFace; - switch (stencilprop->data.drawMode) + osg::ref_ptr geode (new osg::Geode); + geode->setName(triShape->name); // name will be used for part filtering + osg::ref_ptr geometry (new osg::Geometry); + triShapeToGeometry(triShape, geometry, geode, boundTextures, animflags); + + osg::ref_ptr 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 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++) { - case 1: - frontFace->setMode(osg::FrontFace::CLOCKWISE); - break; - case 0: - case 2: - default: - frontFace->setMode(osg::FrontFace::COUNTER_CLOCKWISE); - break; + std::string boneName = bones[i].getPtr()->name; + + osgAnimation::VertexInfluence influence; + influence.setName(boneName); + const std::vector &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); - stateset->setAttribute(frontFace, osg::StateAttribute::ON); - stateset->setMode(GL_CULL_FACE, stencilprop->data.drawMode == 3 ? osg::StateAttribute::OFF - : osg::StateAttribute::ON); + osg::ref_ptr trans(new osg::MatrixTransform); + trans->setUpdateCallback(new InvertBoneMatrix()); - // 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); + geode->addDrawable(rig); - 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(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(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: - { - // Handled in handleTriShape so we know whether vertex colors are available - break; + trans->addChild(geode); + parentNode->addChild(trans); } - case Nif::RC_NiAlphaProperty: + + + void handleProperty(const Nif::Property *property, const Nif::Node* nifNode, + osg::Node *node, std::map& boundTextures, int animflags) { - const Nif::NiAlphaProperty* alphaprop = static_cast(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); + osg::StateSet* stateset = node->getOrCreateStateSet(); - bool noSort = (alphaprop->flags>>13)&1; - if (!noSort) + switch (property->recType) + { + case Nif::RC_NiStencilProperty: + { + const Nif::NiStencilProperty* stencilprop = static_cast(property); + osg::FrontFace* frontFace = new osg::FrontFace; + switch (stencilprop->data.drawMode) { - stateset->setNestRenderBins(false); - stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); + 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); + */ } - else + case Nif::RC_NiWireframeProperty: { - stateset->setAttributeAndModes(blendfunc, osg::StateAttribute::OFF); - stateset->setNestRenderBins(false); - stateset->setRenderingHint(osg::StateSet::OPAQUE_BIN); + const Nif::NiWireframeProperty* wireprop = static_cast(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; } - - osg::AlphaFunc* alphafunc = new osg::AlphaFunc; - if((alphaprop->flags>>9)&1) + case Nif::RC_NiZBufferProperty: { - alphafunc->setFunction(getTestMode((alphaprop->flags>>10)&0x7), alphaprop->data.threshold/255.f); - stateset->setAttributeAndModes(alphafunc, osg::StateAttribute::ON); + const Nif::NiZBufferProperty* zprop = static_cast(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; } - else - stateset->setAttributeAndModes(alphafunc, osg::StateAttribute::OFF); - break; - } - case Nif::RC_NiTexturingProperty: - { - const Nif::NiTexturingProperty* texprop = static_cast(property); - for (int i=0; itextures[i].inUse) + // Handled in handleTriShape so we know whether vertex colors are available + break; + } + case Nif::RC_NiAlphaProperty: + { + const Nif::NiAlphaProperty* alphaprop = static_cast(property); + osg::BlendFunc* blendfunc = new osg::BlendFunc; + if (alphaprop->flags&1) { - if (i != Nif::NiTexturingProperty::BaseTexture - && i != Nif::NiTexturingProperty::GlowTexture - && i != Nif::NiTexturingProperty::DarkTexture - && i != Nif::NiTexturingProperty::DetailTexture) - { - std::cerr << "Warning: unhandled texture stage " << i << std::endl; - continue; - } + blendfunc->setFunction(getBlendMode((alphaprop->flags>>1)&0xf), + getBlendMode((alphaprop->flags>>5)&0xf)); + stateset->setAttributeAndModes(blendfunc, osg::StateAttribute::ON); - const Nif::NiTexturingProperty::Texture& tex = texprop->textures[i]; - if(tex.texture.empty()) + bool noSort = (alphaprop->flags>>13)&1; + if (!noSort) { - std::cerr << "Warning: texture layer " << i << " is in use but empty " << std::endl; - continue; + stateset->setNestRenderBins(false); + stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); } - const Nif::NiSourceTexture *st = tex.texture.getPtr(); - if (!st->external) - { - std::cerr << "Warning: unhandled internal texture " << std::endl; - continue; - } - - std::string filename = Misc::ResourceHelpers::correctTexturePath(st->filename, resourceManager); - - // 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->get(filename.c_str()), opts); - osg::Image* image = result.getImage(); - osg::Texture2D* texture2d = new osg::Texture2D; - texture2d->setUnRefImageDataAfterApply(true); - texture2d->setImage(image); - - unsigned int clamp = static_cast(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); + } + else + { + stateset->setAttributeAndModes(blendfunc, osg::StateAttribute::OFF); + stateset->setNestRenderBins(false); + stateset->setRenderingHint(osg::StateSet::OPAQUE_BIN); + } - if (i == Nif::NiTexturingProperty::GlowTexture) - { - osg::TexEnv* texEnv = new osg::TexEnv; - texEnv->setMode(osg::TexEnv::ADD); - stateset->setTextureAttributeAndModes(i, texEnv, osg::StateAttribute::ON); - } - else if (i == Nif::NiTexturingProperty::DarkTexture) + 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(property); + for (int i=0; itextures[i].inUse) { - // untested - osg::TexEnv* texEnv = new osg::TexEnv; - texEnv->setMode(osg::TexEnv::MODULATE); - stateset->setTextureAttributeAndModes(i, texEnv, osg::StateAttribute::ON); + if (i != Nif::NiTexturingProperty::BaseTexture + && i != Nif::NiTexturingProperty::GlowTexture + && i != Nif::NiTexturingProperty::DarkTexture + && i != Nif::NiTexturingProperty::DetailTexture) + { + std::cerr << "Warning: unhandled texture stage " << i << std::endl; + continue; + } + + 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(); + if (!st->external) + { + std::cerr << "Warning: unhandled internal texture " << std::endl; + continue; + } + + std::string filename = Misc::ResourceHelpers::correctTexturePath(st->filename, mResourceManager); + + // TODO: replace with texture manager + // 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(*mResourceManager->get(filename.c_str()), opts); + osg::Image* image = result.getImage(); + osg::Texture2D* texture2d = new osg::Texture2D; + texture2d->setUnRefImageDataAfterApply(true); + texture2d->setImage(image); + + unsigned int clamp = static_cast(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); + + if (i == Nif::NiTexturingProperty::GlowTexture) + { + osg::TexEnv* texEnv = new osg::TexEnv; + texEnv->setMode(osg::TexEnv::ADD); + stateset->setTextureAttributeAndModes(i, texEnv, osg::StateAttribute::ON); + } + else if (i == Nif::NiTexturingProperty::DarkTexture) + { + // untested + osg::TexEnv* texEnv = new osg::TexEnv; + texEnv->setMode(osg::TexEnv::MODULATE); + stateset->setTextureAttributeAndModes(i, texEnv, osg::StateAttribute::ON); + } + else if (i == Nif::NiTexturingProperty::DetailTexture) + { + // untested + osg::TexEnvCombine* texEnv = new osg::TexEnvCombine; + texEnv->setScale_RGB(2.f); + texEnv->setCombine_Alpha(GL_MODULATE); + texEnv->setOperand0_Alpha(GL_SRC_ALPHA); + texEnv->setOperand1_Alpha(GL_SRC_ALPHA); + texEnv->setSource0_Alpha(GL_PREVIOUS); + texEnv->setSource1_Alpha(GL_TEXTURE); + texEnv->setCombine_RGB(GL_MODULATE); + texEnv->setOperand0_RGB(GL_SRC_COLOR); + texEnv->setOperand1_RGB(GL_SRC_COLOR); + texEnv->setSource0_RGB(GL_PREVIOUS); + texEnv->setSource1_RGB(GL_TEXTURE); + stateset->setTextureAttributeAndModes(i, texEnv, osg::StateAttribute::ON); + } + + boundTextures[i] = tex.uvSet; } - else if (i == Nif::NiTexturingProperty::DetailTexture) + else if (boundTextures.find(i) != boundTextures.end()) { - // untested - osg::TexEnvCombine* texEnv = new osg::TexEnvCombine; - texEnv->setScale_RGB(2.f); - texEnv->setCombine_Alpha(GL_MODULATE); - texEnv->setOperand0_Alpha(GL_SRC_ALPHA); - texEnv->setOperand1_Alpha(GL_SRC_ALPHA); - texEnv->setSource0_Alpha(GL_PREVIOUS); - texEnv->setSource1_Alpha(GL_TEXTURE); - texEnv->setCombine_RGB(GL_MODULATE); - texEnv->setOperand0_RGB(GL_SRC_COLOR); - texEnv->setOperand1_RGB(GL_SRC_COLOR); - texEnv->setSource0_RGB(GL_PREVIOUS); - texEnv->setSource1_RGB(GL_TEXTURE); - stateset->setTextureAttributeAndModes(i, texEnv, osg::StateAttribute::ON); + stateset->setTextureAttributeAndModes(i, new osg::Texture2D, osg::StateAttribute::OFF); + boundTextures.erase(i); } - - boundTextures[i] = tex.uvSet; + handleTextureControllers(texprop, node, stateset, animflags); } - else if (boundTextures.find(i) != boundTextures.end()) - { - stateset->setTextureAttributeAndModes(i, new osg::Texture2D, osg::StateAttribute::OFF); - boundTextures.erase(i); - } - handleTextureControllers(texprop, node, stateset, animflags); + break; } - 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; - } - } - - void Loader::applyMaterialProperties(osg::Node* node, const std::vector& properties, - bool hasVertexColors, int animflags) - { - osg::StateSet* stateset = node->getOrCreateStateSet(); - - int specFlags = 0; // Specular is disabled by default, even if there's a specular color in the NiMaterialProperty - osg::Material* mat = new osg::Material; - mat->setColorMode(hasVertexColors ? osg::Material::AMBIENT_AND_DIFFUSE : osg::Material::OFF); - // TODO: check if the OpenGL default material values are actually the default NIF material values, for when there's no NiMaterialProperty - for (std::vector::const_reverse_iterator it = properties.rbegin(); it != properties.rend(); ++it) - { - const Nif::Property* property = *it; - switch (property->recType) + case Nif::RC_NiDitherProperty: { - case Nif::RC_NiSpecularProperty: - { - specFlags = property->flags; + stateset->setMode(GL_DITHER, property->flags != 0 ? osg::StateAttribute::ON + : osg::StateAttribute::OFF); break; } - case Nif::RC_NiMaterialProperty: + default: + std::cerr << "Unhandled " << property->recName << std::endl; + break; + } + } + + void applyMaterialProperties(osg::Node* node, const std::vector& properties, + bool hasVertexColors, int animflags) + { + osg::StateSet* stateset = node->getOrCreateStateSet(); + + int specFlags = 0; // Specular is disabled by default, even if there's a specular color in the NiMaterialProperty + osg::Material* mat = new osg::Material; + mat->setColorMode(hasVertexColors ? osg::Material::AMBIENT_AND_DIFFUSE : osg::Material::OFF); + // TODO: check if the OpenGL default material values are actually the default NIF material values, for when there's no NiMaterialProperty + for (std::vector::const_reverse_iterator it = properties.rbegin(); it != properties.rend(); ++it) { - const Nif::NiMaterialProperty* matprop = static_cast(property); + 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(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->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); + mat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(matprop->data.specular, 1.f)); + mat->setShininess(osg::Material::FRONT_AND_BACK, matprop->data.glossiness); - if (!matprop->controller.empty()) - handleMaterialControllers(matprop, node, stateset, animflags); + if (!matprop->controller.empty()) + handleMaterialControllers(matprop, node, stateset, animflags); - break; - } - case Nif::RC_NiVertexColorProperty: - { - const Nif::NiVertexColorProperty* vertprop = static_cast(property); - if (!hasVertexColors) break; - switch (vertprop->flags) + } + case Nif::RC_NiVertexColorProperty: { - 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; + const Nif::NiVertexColorProperty* vertprop = static_cast(property); + if (!hasVertexColors) + break; + 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); } - if (specFlags == 0) - mat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f,0.f,0.f,0.f)); + }; + + osg::Node* Loader::load(Nif::NIFFilePtr file, osg::Group *parentNode, TextKeyMap *textKeys) + { + LoaderImpl loader(resourceManager, sShowMarkers); + return loader.load(file, parentNode, textKeys); + } + + osg::Node* Loader::loadAsSkeleton(Nif::NIFFilePtr file, osg::Group *parentNode, TextKeyMap *textKeys) + { + LoaderImpl loader(resourceManager, sShowMarkers); + return loader.loadAsSkeleton(file, parentNode, textKeys); + } - stateset->setAttributeAndModes(mat, osg::StateAttribute::ON); + void Loader::loadKf(Nif::NIFFilePtr kf, osg::Node *rootNode, int sourceIndex, TextKeyMap &textKeys) + { + LoaderImpl loader(resourceManager, sShowMarkers); + loader.loadKf(kf, rootNode, sourceIndex, textKeys); } + } diff --git a/components/nifosg/nifloader.hpp b/components/nifosg/nifloader.hpp index 8f9efe6b37..7ccbbfd2cc 100644 --- a/components/nifosg/nifloader.hpp +++ b/components/nifosg/nifloader.hpp @@ -9,24 +9,8 @@ namespace osg { - class Geometry; class Group; class Node; - class MatrixTransform; - class StateSet; - class Geode; -} -namespace osgAnimation -{ - class Bone; -} - -namespace Nif -{ - class Node; - class NiTriShape; - class Property; - class Controller; } namespace NifOsg @@ -62,45 +46,6 @@ namespace NifOsg private: - /// @param createSkeleton If true, use an osgAnimation::Bone for NIF nodes, otherwise an osg::MatrixTransform. - osg::Node* handleNode(const Nif::Node* nifNode, osg::Group* parentNode, bool createSkeleton, - std::map boundTextures, int animflags, int particleflags, bool skipMeshes, TextKeyMap* textKeys); - - void handleMeshControllers(const Nif::Node* nifNode, osg::MatrixTransform* transformNode, const std::map& boundTextures, int animflags); - - void handleNodeControllers(const Nif::Node* nifNode, osg::MatrixTransform* transformNode, int animflags); - - void handleMaterialControllers(const Nif::Property* materialProperty, osg::Node* node, osg::StateSet* stateset, int animflags); - - void handleTextureControllers(const Nif::Property* texProperty, osg::Node* node, osg::StateSet* stateset, int animflags); - - void handleProperty (const Nif::Property* property, const Nif::Node* nifNode, - osg::Node* node, std::map& boundTextures, int animflags); - - void handleParticleSystem(const Nif::Node* nifNode, osg::Group* parentNode, int animflags, int particleflags); - - // 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& boundTextures, int animflags); - - // Fills the vertex data for the given TriShape into the given Geometry. - void triShapeToGeometry(const Nif::NiTriShape* triShape, osg::Geometry* geom, osg::Geode* parentGeode, const std::map& boundTextures, int animflags); - - // 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& boundTextures, int animflags); - - // 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& boundTextures, int animflags); - - void applyMaterialProperties(osg::Node* node, const std::vector& properties, - bool hasVertexColors, int animflags); - - // Set up the default input and controller function for the given controller. - void setupController(const Nif::Controller* ctrl, Controller* toSetup, int animflags); - - Nif::NIFFilePtr mNif; - - osg::Group* mRootNode; - static bool sShowMarkers; };