From 8efbdeaa57f43038167b4ff0d3dd375ddc35bdfd Mon Sep 17 00:00:00 2001 From: capostrophic Date: Thu, 8 Aug 2019 13:11:24 +0300 Subject: [PATCH 01/12] Load NiTriStrips/NiTriStripsData (don't do anything yet) --- components/nif/data.cpp | 19 ++++++++++++++++ components/nif/data.hpp | 9 ++++++++ components/nif/niffile.cpp | 2 ++ components/nif/node.hpp | 23 +++++++++++++++++++ components/nif/record.hpp | 2 ++ components/nif/recordptr.hpp | 2 ++ components/nifbullet/bulletnifloader.cpp | 11 ++++++++++ components/nifbullet/bulletnifloader.hpp | 2 ++ components/nifosg/nifloader.cpp | 28 +++++++++++++++++++++++- 9 files changed, 97 insertions(+), 1 deletion(-) diff --git a/components/nif/data.cpp b/components/nif/data.cpp index 086021930..485a2ddf8 100644 --- a/components/nif/data.cpp +++ b/components/nif/data.cpp @@ -90,6 +90,25 @@ void NiTriShapeData::read(NIFStream *nif) } } +void NiTriStripsData::read(NIFStream *nif) +{ + ShapeData::read(nif); + + // Every strip with n points defines n-2 triangles, so this should be unnecessary. + /*int tris =*/ nif->getUShort(); + + // Number of triangle strips + int numStrips = nif->getUShort(); + // Number of points in each strip + int lengths = nif->getUShort(); + for (int i = 0; i < numStrips; i++) + { + std::vector strip; + nif->getUShorts(strip, lengths); + strips.emplace_back(strip); + } +} + void NiAutoNormalParticlesData::read(NIFStream *nif) { ShapeData::read(nif); diff --git a/components/nif/data.hpp b/components/nif/data.hpp index 6b7aa579b..fb1199cff 100644 --- a/components/nif/data.hpp +++ b/components/nif/data.hpp @@ -53,6 +53,15 @@ public: void read(NIFStream *nif); }; +class NiTriStripsData : public ShapeData +{ +public: + // Triangle strips, series of vertex indices. + std::vector> strips; + + void read(NIFStream *nif); +}; + class NiAutoNormalParticlesData : public ShapeData { public: diff --git a/components/nif/niffile.cpp b/components/nif/niffile.cpp index 6675fef08..4b3760ba2 100644 --- a/components/nif/niffile.cpp +++ b/components/nif/niffile.cpp @@ -54,6 +54,7 @@ static std::map makeFactory() newFactory.insert(makeEntry("NiBSAnimationNode", &construct , RC_NiBSAnimationNode )); newFactory.insert(makeEntry("NiBillboardNode", &construct , RC_NiBillboardNode )); newFactory.insert(makeEntry("NiTriShape", &construct , RC_NiTriShape )); + newFactory.insert(makeEntry("NiTriStrips", &construct , RC_NiTriStrips )); newFactory.insert(makeEntry("NiRotatingParticles", &construct , RC_NiRotatingParticles )); newFactory.insert(makeEntry("NiAutoNormalParticles", &construct , RC_NiAutoNormalParticles )); newFactory.insert(makeEntry("NiCamera", &construct , RC_NiCamera )); @@ -96,6 +97,7 @@ static std::map makeFactory() newFactory.insert(makeEntry("NiParticleRotation", &construct , RC_NiParticleRotation )); newFactory.insert(makeEntry("NiFloatData", &construct , RC_NiFloatData )); newFactory.insert(makeEntry("NiTriShapeData", &construct , RC_NiTriShapeData )); + newFactory.insert(makeEntry("NiTriStripsData", &construct , RC_NiTriStripsData )); newFactory.insert(makeEntry("NiVisData", &construct , RC_NiVisData )); newFactory.insert(makeEntry("NiColorData", &construct , RC_NiColorData )); newFactory.insert(makeEntry("NiPixelData", &construct , RC_NiPixelData )); diff --git a/components/nif/node.hpp b/components/nif/node.hpp index cc1871d83..5e5f445cf 100644 --- a/components/nif/node.hpp +++ b/components/nif/node.hpp @@ -156,6 +156,29 @@ struct NiTriShape : Node } }; +struct NiTriStrips : Node +{ + NiTriStripsDataPtr data; + NiSkinInstancePtr skin; + + void read(NIFStream *nif) + { + Node::read(nif); + data.read(nif); + skin.read(nif); + } + + void post(NIFFile *nif) + { + Node::post(nif); + data.post(nif); + skin.post(nif); + if (!skin.empty()) + nif->setUseSkinning(true); + } +}; + + struct NiCamera : Node { struct Camera diff --git a/components/nif/record.hpp b/components/nif/record.hpp index 4a044ac47..909c268bb 100644 --- a/components/nif/record.hpp +++ b/components/nif/record.hpp @@ -41,6 +41,7 @@ enum RecordType RC_NiBillboardNode, RC_AvoidNode, RC_NiTriShape, + RC_NiTriStrips, RC_NiRotatingParticles, RC_NiAutoNormalParticles, RC_NiBSParticleNode, @@ -80,6 +81,7 @@ enum RecordType RC_NiParticleRotation, RC_NiFloatData, RC_NiTriShapeData, + RC_NiTriStripsData, RC_NiVisData, RC_NiColorData, RC_NiPixelData, diff --git a/components/nif/recordptr.hpp b/components/nif/recordptr.hpp index e8aa8cb5b..977973517 100644 --- a/components/nif/recordptr.hpp +++ b/components/nif/recordptr.hpp @@ -135,6 +135,7 @@ class NiPixelData; class NiColorData; struct NiKeyframeData; class NiTriShapeData; +class NiTriStripsData; class NiSkinInstance; class NiSourceTexture; class NiRotatingParticlesData; @@ -154,6 +155,7 @@ typedef RecordPtrT NiFloatDataPtr; typedef RecordPtrT NiColorDataPtr; typedef RecordPtrT NiKeyframeDataPtr; typedef RecordPtrT NiTriShapeDataPtr; +typedef RecordPtrT NiTriStripsDataPtr; typedef RecordPtrT NiSkinInstancePtr; typedef RecordPtrT NiSourceTexturePtr; typedef RecordPtrT NiRotatingParticlesDataPtr; diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index 8f98174ab..e35968d09 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -248,6 +248,10 @@ void BulletNifLoader::handleNode(const std::string& fileName, const Nif::Node *n { handleNiTriShape(static_cast(node), flags, getWorldTransform(node), isAnimated, avoid); } + if(!node->hasBounds && node->recType == Nif::RC_NiTriStrips) + { + handleNiTriStrips(static_cast(node), flags, getWorldTransform(node), isAnimated, avoid); + } } // For NiNodes, loop through children @@ -330,4 +334,11 @@ void BulletNifLoader::handleNiTriShape(const Nif::NiTriShape *shape, int flags, } } + +void BulletNifLoader::handleNiTriStrips(const Nif::NiTriStrips *shape, int flags, const osg::Matrixf &transform, + bool isAnimated, bool avoid) +{ + // do nothing for now +} + } // namespace NifBullet diff --git a/components/nifbullet/bulletnifloader.hpp b/components/nifbullet/bulletnifloader.hpp index de7e6bdcd..afc8772cd 100644 --- a/components/nifbullet/bulletnifloader.hpp +++ b/components/nifbullet/bulletnifloader.hpp @@ -26,6 +26,7 @@ namespace Nif class Node; struct Transformation; struct NiTriShape; + struct NiTriStrips; } namespace NifBullet @@ -59,6 +60,7 @@ private: bool hasAutoGeneratedCollision(const Nif::Node *rootNode); void handleNiTriShape(const Nif::NiTriShape *shape, int flags, const osg::Matrixf& transform, bool isAnimated, bool avoid); + void handleNiTriStrips(const Nif::NiTriStrips *shape, int flags, const osg::Matrixf& transform, bool isAnimated, bool avoid); std::unique_ptr mCompoundShape; diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 69840117d..576aa5d18 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -452,6 +452,7 @@ namespace NifOsg break; } case Nif::RC_NiTriShape: + case Nif::RC_NiTriStrips: case Nif::RC_NiAutoNormalParticles: case Nif::RC_NiRotatingParticles: // Leaf nodes in the NIF hierarchy, so won't be able to dynamically attach children. @@ -580,6 +581,11 @@ namespace NifOsg node->setDataVariance(osg::Object::DYNAMIC); } + if (nifNode->recType == Nif::RC_NiTriStrips && isAnimated) // the same thing for animated NiTriStrips + { + node->setDataVariance(osg::Object::DYNAMIC); + } + osg::ref_ptr composite = new SceneUtil::CompositeStateSetUpdater; applyNodeProperties(nifNode, node, composite, imageManager, boundTextures, animflags); @@ -603,6 +609,25 @@ namespace NifOsg handleMeshControllers(nifNode, node, composite, boundTextures, animflags); } } + if (nifNode->recType == Nif::RC_NiTriStrips && !skipMeshes) + { + const Nif::NiTriStrips* triStrips = static_cast(nifNode); + const std::string nodeName = Misc::StringUtils::lowerCase(triStrips->name); + static const std::string markerName = "tri editormarker"; + static const std::string shadowName = "shadow"; + static const std::string shadowName2 = "tri shadow"; + const bool isMarker = hasMarkers && !nodeName.compare(0, markerName.size(), markerName); + if (!isMarker && nodeName.compare(0, shadowName.size(), shadowName) && nodeName.compare(0, shadowName2.size(), shadowName2)) + { + if (triStrips->skin.empty()) {} + /*handleTriShape(triShape, node, composite, boundTextures, animflags);*/ + else {} + /*handleSkinnedTriShape(triShape, node, composite, boundTextures, animflags);*/ + + if (!nifNode->controller.empty()) {} + /*handleMeshControllers(nifNode, node, composite, boundTextures, animflags);*/ + } + } if(nifNode->recType == Nif::RC_NiAutoNormalParticles || nifNode->recType == Nif::RC_NiRotatingParticles) handleParticleSystem(nifNode, node, composite, animflags, rootNode); @@ -612,7 +637,8 @@ namespace NifOsg // Note: NiTriShapes are not allowed to have KeyframeControllers (the vanilla engine just crashes when there is one). // We can take advantage of this constraint for optimizations later. - if (nifNode->recType != Nif::RC_NiTriShape && !nifNode->controller.empty() && node->getDataVariance() == osg::Object::DYNAMIC) + if (nifNode->recType != Nif::RC_NiTriShape && nifNode->recType != Nif::RC_NiTriStrips + && !nifNode->controller.empty() && node->getDataVariance() == osg::Object::DYNAMIC) handleNodeControllers(nifNode, static_cast(node.get()), animflags); const Nif::NiNode *ninode = dynamic_cast(nifNode); From adb06913f7b6fec721d4de6326dded55807efa4e Mon Sep 17 00:00:00 2001 From: capostrophic Date: Thu, 8 Aug 2019 15:19:01 +0300 Subject: [PATCH 02/12] Add basic NiTriStrips rendering (no physics yet) --- components/nifosg/nifloader.cpp | 146 ++++++++++++++++++++++++++++++-- 1 file changed, 139 insertions(+), 7 deletions(-) diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 576aa5d18..95f5fabf9 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -619,13 +619,13 @@ namespace NifOsg const bool isMarker = hasMarkers && !nodeName.compare(0, markerName.size(), markerName); if (!isMarker && nodeName.compare(0, shadowName.size(), shadowName) && nodeName.compare(0, shadowName2.size(), shadowName2)) { - if (triStrips->skin.empty()) {} - /*handleTriShape(triShape, node, composite, boundTextures, animflags);*/ - else {} - /*handleSkinnedTriShape(triShape, node, composite, boundTextures, animflags);*/ + if (triStrips->skin.empty()) + handleTriStrips(triStrips, node, composite, boundTextures, animflags); + else + handleSkinnedTriStrips(triStrips, node, composite, boundTextures, animflags); - if (!nifNode->controller.empty()) {} - /*handleMeshControllers(nifNode, node, composite, boundTextures, animflags);*/ + if (!nifNode->controller.empty()) + handleMeshControllers(nifNode, node, composite, boundTextures, animflags); } } @@ -699,7 +699,7 @@ namespace NifOsg handleVisController(static_cast(ctrl.getPtr()), node, animflags); } else if(ctrl->recType == Nif::RC_NiGeomMorpherController) - {} // handled in handleTriShape + {} // handled in handleTriShape/handleTriStrips else Log(Debug::Info) << "Unhandled controller " << ctrl->recName << " on node " << nifNode->recIndex << " in " << mFilename; } @@ -1097,6 +1097,51 @@ namespace NifOsg applyDrawableProperties(parentNode, drawableProps, composite, !data->colors.empty(), animflags, false); } + void triStripsToGeometry(const Nif::NiTriStrips *triStrips, osg::Geometry *geometry, osg::Node* parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector& boundTextures, int animflags) + { + const Nif::NiTriStripsData* data = triStrips->data.getPtr(); + + if (!data->vertices.empty()) + geometry->setVertexArray(new osg::Vec3Array(data->vertices.size(), data->vertices.data())); + if (!data->normals.empty()) + geometry->setNormalArray(new osg::Vec3Array(data->normals.size(), data->normals.data()), osg::Array::BIND_PER_VERTEX); + + int textureStage = 0; + for (const int uvSet : boundTextures) + { + if (uvSet >= (int)data->uvlist.size()) + { + Log(Debug::Verbose) << "Out of bounds UV set " << uvSet << " on TriStrips \"" << triStrips->name << "\" in " << mFilename; + if (!data->uvlist.empty()) + geometry->setTexCoordArray(textureStage, new osg::Vec2Array(data->uvlist[0].size(), data->uvlist[0].data()), osg::Array::BIND_PER_VERTEX); + continue; + } + + geometry->setTexCoordArray(textureStage, new osg::Vec2Array(data->uvlist[uvSet].size(), data->uvlist[uvSet].data()), osg::Array::BIND_PER_VERTEX); + textureStage++; + } + + if (!data->colors.empty()) + geometry->setColorArray(new osg::Vec4Array(data->colors.size(), data->colors.data()), osg::Array::BIND_PER_VERTEX); + + if (!data->strips.empty()) + { + for (const std::vector& strip : data->strips) + { + geometry->addPrimitiveSet(new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLE_STRIP, + strip.size(), (unsigned short*)strip.data())); + } + } + + // 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 drawableProps; + collectDrawableProperties(triStrips, drawableProps); + applyDrawableProperties(parentNode, drawableProps, composite, !data->colors.empty(), animflags, false); + } + void handleTriShape(const Nif::NiTriShape* triShape, osg::Group* parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector& boundTextures, int animflags) { osg::ref_ptr drawable; @@ -1128,6 +1173,37 @@ namespace NifOsg parentNode->addChild(drawable); } + void handleTriStrips(const Nif::NiTriStrips* triStrips, osg::Group* parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector& boundTextures, int animflags) + { + osg::ref_ptr drawable; + for (Nif::ControllerPtr ctrl = triStrips->controller; !ctrl.empty(); ctrl = ctrl->next) + { + if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active)) + continue; + if(ctrl->recType == Nif::RC_NiGeomMorpherController) + { + drawable = handleMorphGeometry(static_cast(ctrl.getPtr()), triStrips, parentNode, composite, boundTextures, animflags); + + osg::ref_ptr morphctrl = new GeomMorpherController( + static_cast(ctrl.getPtr())->data.getPtr()); + setupController(ctrl.getPtr(), morphctrl, animflags); + drawable->setUpdateCallback(morphctrl); + break; + } + } + + if (!drawable.get()) + { + osg::ref_ptr geom (new osg::Geometry); + drawable = geom; + triStripsToGeometry(triStrips, geom, parentNode, composite, boundTextures, animflags); + } + + drawable->setName(triStrips->name); + + parentNode->addChild(drawable); + } + osg::ref_ptr handleMorphGeometry(const Nif::NiGeomMorpherController* morpher, const Nif::NiTriShape *triShape, osg::Node* parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector& boundTextures, int animflags) { osg::ref_ptr morphGeom = new SceneUtil::MorphGeometry; @@ -1146,6 +1222,24 @@ namespace NifOsg return morphGeom; } + osg::ref_ptr handleMorphGeometry(const Nif::NiGeomMorpherController* morpher, const Nif::NiTriStrips *triStrips, osg::Node* parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector& boundTextures, int animflags) + { + osg::ref_ptr morphGeom = new SceneUtil::MorphGeometry; + + osg::ref_ptr sourceGeometry (new osg::Geometry); + triStripsToGeometry(triStrips, sourceGeometry, parentNode, composite, boundTextures, animflags); + morphGeom->setSourceGeometry(sourceGeometry); + + const std::vector& morphs = morpher->data.getPtr()->mMorphs; + if (morphs.empty()) + return morphGeom; + // Note we are not interested in morph 0, which just contains the original vertices + for (unsigned int i = 1; i < morphs.size(); ++i) + morphGeom->addMorphTarget(new osg::Vec3Array(morphs[i].mVertices.size(), morphs[i].mVertices.data()), 0.f); + + return morphGeom; + } + void handleSkinnedTriShape(const Nif::NiTriShape *triShape, osg::Group *parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector& boundTextures, int animflags) { @@ -1183,6 +1277,44 @@ namespace NifOsg parentNode->addChild(rig); } + void handleSkinnedTriStrips(const Nif::NiTriStrips *triStrips, osg::Group *parentNode, SceneUtil::CompositeStateSetUpdater* composite, + const std::vector& boundTextures, int animflags) + { + osg::ref_ptr geometry (new osg::Geometry); + triStripsToGeometry(triStrips, geometry, parentNode, composite, boundTextures, animflags); + + osg::ref_ptr rig(new SceneUtil::RigGeometry); + rig->setSourceGeometry(geometry); + rig->setName(triStrips->name); + + const Nif::NiSkinInstance *skin = triStrips->skin.getPtr(); + + // Assign bone weights + osg::ref_ptr map (new SceneUtil::RigGeometry::InfluenceMap); + + 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 = Misc::StringUtils::lowerCase(bones[i].getPtr()->name); + + SceneUtil::RigGeometry::BoneInfluence influence; + const std::vector &weights = data->bones[i].weights; + for(size_t j = 0;j < weights.size();j++) + { + influence.mWeights.emplace_back(weights[j].vertex, weights[j].weight); + } + influence.mInvBindMatrix = data->bones[i].trafo.toMatrix(); + influence.mBoundSphere = osg::BoundingSpheref(data->bones[i].boundSphereCenter, data->bones[i].boundSphereRadius); + + map->mData.emplace_back(boneName, influence); + } + rig->setInfluenceMap(map); + + parentNode->addChild(rig); + } + + osg::BlendFunc::BlendFuncMode getBlendMode(int mode) { switch(mode) From 6599a28ecf46e81d5dec628edadf9ac70919c9ee Mon Sep 17 00:00:00 2001 From: capostrophic Date: Thu, 8 Aug 2019 17:03:11 +0300 Subject: [PATCH 03/12] Generate collision shape based on NiTriStrips --- components/nifbullet/bulletnifloader.cpp | 109 ++++++++++++++++++++++- 1 file changed, 107 insertions(+), 2 deletions(-) diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index e35968d09..a33374ff5 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -54,11 +54,59 @@ void fillTriangleMeshWithTransform(btTriangleMesh& mesh, const Nif::NiTriShapeDa } } +void fillTriangleMeshWithTransform(btTriangleMesh& mesh, const Nif::NiTriStripsData& data, const osg::Matrixf &transform) +{ + const std::vector &vertices = data.vertices; + const std::vector> &strips = data.strips; + if (vertices.empty() || strips.empty()) + return; + + // Assume every strip has the same size + const int singleStripSize = static_cast(strips[0].size()); + // Can't make a triangle from 2 vertices or less. + if (singleStripSize < 3) + return; + + mesh.preallocateVertices(static_cast(data.vertices.size())); + // There are N+2*M vertex indices in sum in all strips + // where N is the number of triangles and M is the number of strips + mesh.preallocateIndices(static_cast((strips.size()-2) * singleStripSize)); + + // It's triangulation time. Totally not a NifSkope spell ripoff. + for (const std::vector& strip : strips) + { + unsigned short a = strip[0]; + unsigned short b = strip[0]; + unsigned short c = strip[1]; + bool flip = false; + + for (int i = 2; i < static_cast(strip.size()); i++) + { + a = b; + b = c; + c = strip[i]; + if (a != b && b != c && a != c) + { + if (!flip) + mesh.addTriangle(getbtVector(vertices[a]), getbtVector(vertices[b]), getbtVector(vertices[c])); + else + mesh.addTriangle(getbtVector(vertices[a]), getbtVector(vertices[c]), getbtVector(vertices[b])); + } + flip = !flip; + } + } +} + void fillTriangleMesh(btTriangleMesh& mesh, const Nif::NiTriShapeData& data) { fillTriangleMeshWithTransform(mesh, data, osg::Matrixf()); } +void fillTriangleMesh(btTriangleMesh& mesh, const Nif::NiTriStripsData& data) +{ + fillTriangleMeshWithTransform(mesh, data, osg::Matrixf()); +} + } namespace NifBullet @@ -248,7 +296,7 @@ void BulletNifLoader::handleNode(const std::string& fileName, const Nif::Node *n { handleNiTriShape(static_cast(node), flags, getWorldTransform(node), isAnimated, avoid); } - if(!node->hasBounds && node->recType == Nif::RC_NiTriStrips) + else if(!node->hasBounds && node->recType == Nif::RC_NiTriStrips) { handleNiTriStrips(static_cast(node), flags, getWorldTransform(node), isAnimated, avoid); } @@ -338,7 +386,64 @@ void BulletNifLoader::handleNiTriShape(const Nif::NiTriShape *shape, int flags, void BulletNifLoader::handleNiTriStrips(const Nif::NiTriStrips *shape, int flags, const osg::Matrixf &transform, bool isAnimated, bool avoid) { - // do nothing for now + assert(shape != nullptr); + + // If the object was marked "NCO" earlier, it shouldn't collide with + // anything. So don't do anything. + if ((flags & 0x800)) + return; + if (!shape->skin.empty()) + isAnimated = false; + if (shape->data.empty()) + return; + if (shape->data->strips.empty()) + return; + + if (isAnimated) + { + if (!mCompoundShape) + mCompoundShape.reset(new btCompoundShape); + + std::unique_ptr childMesh(new btTriangleMesh); + + fillTriangleMesh(*childMesh, shape->data.get()); + + std::unique_ptr childShape(new Resource::TriangleMeshShape(childMesh.get(), true)); + childMesh.release(); + + float scale = shape->trafo.scale; + const Nif::Node* parent = shape; + while (parent->parent) + { + parent = parent->parent; + scale *= parent->trafo.scale; + } + osg::Quat q = transform.getRotate(); + osg::Vec3f v = transform.getTrans(); + childShape->setLocalScaling(btVector3(scale, scale, scale)); + + btTransform trans(btQuaternion(q.x(), q.y(), q.z(), q.w()), btVector3(v.x(), v.y(), v.z())); + + mShape->mAnimatedShapes.emplace(shape->recIndex, mCompoundShape->getNumChildShapes()); + + mCompoundShape->addChildShape(trans, childShape.get()); + childShape.release(); + } + else if (avoid) + { + if (!mAvoidStaticMesh) + mAvoidStaticMesh.reset(new btTriangleMesh(false)); + + fillTriangleMeshWithTransform(*mAvoidStaticMesh, shape->data.get(), transform); + } + else + { + if (!mStaticMesh) + mStaticMesh.reset(new btTriangleMesh(false)); + + // Static shape, just transform all vertices into position + fillTriangleMeshWithTransform(*mStaticMesh, shape->data.get(), transform); + } } } // namespace NifBullet From b4f54651f8920933c6780bcdc7c19349fe760ac6 Mon Sep 17 00:00:00 2001 From: capostrophic Date: Thu, 8 Aug 2019 15:41:09 +0300 Subject: [PATCH 04/12] Make sure strips have valid size --- components/nifbullet/bulletnifloader.cpp | 2 +- components/nifosg/nifloader.cpp | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index a33374ff5..7cfdbf40f 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -63,7 +63,7 @@ void fillTriangleMeshWithTransform(btTriangleMesh& mesh, const Nif::NiTriStripsD // Assume every strip has the same size const int singleStripSize = static_cast(strips[0].size()); - // Can't make a triangle from 2 vertices or less. + // Can't make a triangle from less than three vertices. if (singleStripSize < 3) return; diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 95f5fabf9..0e3c63515 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -1128,6 +1128,10 @@ namespace NifOsg { for (const std::vector& strip : data->strips) { + // Can't make a triangle from less than three vertices. + // All strips have the same size. + if (strip.size() < 3) + break; geometry->addPrimitiveSet(new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLE_STRIP, strip.size(), (unsigned short*)strip.data())); } From bd6c455fd4e11ae86de11596e7c898ce46f39b33 Mon Sep 17 00:00:00 2001 From: capostrophic Date: Thu, 8 Aug 2019 17:38:02 +0300 Subject: [PATCH 05/12] Reduce code duplication for non-skinned shapes --- components/nifosg/nifloader.cpp | 126 +++++++++++--------------------- 1 file changed, 44 insertions(+), 82 deletions(-) diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 0e3c63515..3e6262025 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -590,39 +590,37 @@ namespace NifOsg applyNodeProperties(nifNode, node, composite, imageManager, boundTextures, animflags); - if (nifNode->recType == Nif::RC_NiTriShape && !skipMeshes) + if ((nifNode->recType == Nif::RC_NiTriShape || nifNode->recType == Nif::RC_NiTriStrips) && !skipMeshes) { - const Nif::NiTriShape* triShape = static_cast(nifNode); - const std::string nodeName = Misc::StringUtils::lowerCase(triShape->name); + const std::string nodeName = Misc::StringUtils::lowerCase(nifNode->name); static const std::string markerName = "tri editormarker"; static const std::string shadowName = "shadow"; static const std::string shadowName2 = "tri shadow"; const bool isMarker = hasMarkers && !nodeName.compare(0, markerName.size(), markerName); if (!isMarker && nodeName.compare(0, shadowName.size(), shadowName) && nodeName.compare(0, shadowName2.size(), shadowName2)) { - if (triShape->skin.empty()) - handleTriShape(triShape, node, composite, boundTextures, animflags); - else - handleSkinnedTriShape(triShape, node, composite, boundTextures, animflags); + bool skinned = false; + if (nifNode->recType == Nif::RC_NiTriShape) + { + const Nif::NiTriShape* triShape = static_cast(nifNode); + if (!triShape->skin.empty()) + { + skinned = true; + handleSkinnedTriShape(triShape, node, composite, boundTextures, animflags); + } + } + else // if (nifNode->recType == Nif::RC_NiTriStrips) + { + const Nif::NiTriStrips* triStrips = static_cast(nifNode); + if (!triStrips->skin.empty()) + { + skinned = true; + handleSkinnedTriStrips(triStrips, node, composite, boundTextures, animflags); + } + } - if (!nifNode->controller.empty()) - handleMeshControllers(nifNode, node, composite, boundTextures, animflags); - } - } - if (nifNode->recType == Nif::RC_NiTriStrips && !skipMeshes) - { - const Nif::NiTriStrips* triStrips = static_cast(nifNode); - const std::string nodeName = Misc::StringUtils::lowerCase(triStrips->name); - static const std::string markerName = "tri editormarker"; - static const std::string shadowName = "shadow"; - static const std::string shadowName2 = "tri shadow"; - const bool isMarker = hasMarkers && !nodeName.compare(0, markerName.size(), markerName); - if (!isMarker && nodeName.compare(0, shadowName.size(), shadowName) && nodeName.compare(0, shadowName2.size(), shadowName2)) - { - if (triStrips->skin.empty()) - handleTriStrips(triStrips, node, composite, boundTextures, animflags); - else - handleSkinnedTriStrips(triStrips, node, composite, boundTextures, animflags); + if (!skinned) + handleTriShape(nifNode, node, composite, boundTextures, animflags); if (!nifNode->controller.empty()) handleMeshControllers(nifNode, node, composite, boundTextures, animflags); @@ -699,7 +697,7 @@ namespace NifOsg handleVisController(static_cast(ctrl.getPtr()), node, animflags); } else if(ctrl->recType == Nif::RC_NiGeomMorpherController) - {} // handled in handleTriShape/handleTriStrips + {} // handled in handleMorphController else Log(Debug::Info) << "Unhandled controller " << ctrl->recName << " on node " << nifNode->recIndex << " in " << mFilename; } @@ -1146,47 +1144,43 @@ namespace NifOsg applyDrawableProperties(parentNode, drawableProps, composite, !data->colors.empty(), animflags, false); } - void handleTriShape(const Nif::NiTriShape* triShape, osg::Group* parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector& boundTextures, int animflags) + void handleTriShape(const Nif::Node* nifNode, osg::Group* parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector& boundTextures, int animflags) { + if (nifNode->recType != Nif::RC_NiTriShape && nifNode->recType != Nif::RC_NiTriStrips) + return; + osg::ref_ptr drawable; - for (Nif::ControllerPtr ctrl = triShape->controller; !ctrl.empty(); ctrl = ctrl->next) + osg::ref_ptr geom (new osg::Geometry); + if (nifNode->recType == Nif::RC_NiTriShape) { - if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active)) - continue; - if(ctrl->recType == Nif::RC_NiGeomMorpherController) - { - drawable = handleMorphGeometry(static_cast(ctrl.getPtr()), triShape, parentNode, composite, boundTextures, animflags); - - osg::ref_ptr morphctrl = new GeomMorpherController( - static_cast(ctrl.getPtr())->data.getPtr()); - setupController(ctrl.getPtr(), morphctrl, animflags); - drawable->setUpdateCallback(morphctrl); - break; - } + const Nif::NiTriShape* triShape = static_cast(nifNode); + triShapeToGeometry(triShape, geom, parentNode, composite, boundTextures, animflags); + handleMorphController(triShape->controller, drawable, geom, parentNode, composite, boundTextures, animflags); + } + else + { + const Nif::NiTriStrips* triStrips = static_cast(nifNode); + triStripsToGeometry(triStrips, geom, parentNode, composite, boundTextures, animflags); + handleMorphController(triStrips->controller, drawable, geom, parentNode, composite, boundTextures, animflags); } if (!drawable.get()) - { - osg::ref_ptr geom (new osg::Geometry); drawable = geom; - triShapeToGeometry(triShape, geom, parentNode, composite, boundTextures, animflags); - } - drawable->setName(triShape->name); + drawable->setName(nifNode->name); parentNode->addChild(drawable); } - void handleTriStrips(const Nif::NiTriStrips* triStrips, osg::Group* parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector& boundTextures, int animflags) + void handleMorphController(Nif::ControllerPtr ctrl, osg::Drawable *drawable, osg::ref_ptr geom, osg::Node* parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector& boundTextures, int animflags) { - osg::ref_ptr drawable; - for (Nif::ControllerPtr ctrl = triStrips->controller; !ctrl.empty(); ctrl = ctrl->next) + for (; !ctrl.empty(); ctrl = ctrl->next) { if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active)) continue; if(ctrl->recType == Nif::RC_NiGeomMorpherController) { - drawable = handleMorphGeometry(static_cast(ctrl.getPtr()), triStrips, parentNode, composite, boundTextures, animflags); + drawable = handleMorphGeometry(static_cast(ctrl.getPtr()), geom, parentNode, composite, boundTextures, animflags); osg::ref_ptr morphctrl = new GeomMorpherController( static_cast(ctrl.getPtr())->data.getPtr()); @@ -1195,43 +1189,11 @@ namespace NifOsg break; } } - - if (!drawable.get()) - { - osg::ref_ptr geom (new osg::Geometry); - drawable = geom; - triStripsToGeometry(triStrips, geom, parentNode, composite, boundTextures, animflags); - } - - drawable->setName(triStrips->name); - - parentNode->addChild(drawable); - } - - osg::ref_ptr handleMorphGeometry(const Nif::NiGeomMorpherController* morpher, const Nif::NiTriShape *triShape, osg::Node* parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector& boundTextures, int animflags) - { - osg::ref_ptr morphGeom = new SceneUtil::MorphGeometry; - - osg::ref_ptr sourceGeometry (new osg::Geometry); - triShapeToGeometry(triShape, sourceGeometry, parentNode, composite, boundTextures, animflags); - morphGeom->setSourceGeometry(sourceGeometry); - - const std::vector& morphs = morpher->data.getPtr()->mMorphs; - if (morphs.empty()) - return morphGeom; - // Note we are not interested in morph 0, which just contains the original vertices - for (unsigned int i = 1; i < morphs.size(); ++i) - morphGeom->addMorphTarget(new osg::Vec3Array(morphs[i].mVertices.size(), morphs[i].mVertices.data()), 0.f); - - return morphGeom; } - osg::ref_ptr handleMorphGeometry(const Nif::NiGeomMorpherController* morpher, const Nif::NiTriStrips *triStrips, osg::Node* parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector& boundTextures, int animflags) + osg::ref_ptr handleMorphGeometry(const Nif::NiGeomMorpherController* morpher, osg::ref_ptr sourceGeometry, osg::Node* parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector& boundTextures, int animflags) { osg::ref_ptr morphGeom = new SceneUtil::MorphGeometry; - - osg::ref_ptr sourceGeometry (new osg::Geometry); - triStripsToGeometry(triStrips, sourceGeometry, parentNode, composite, boundTextures, animflags); morphGeom->setSourceGeometry(sourceGeometry); const std::vector& morphs = morpher->data.getPtr()->mMorphs; From 7cc70ffb5057b93a90fd4400c51cd56ee58e01fa Mon Sep 17 00:00:00 2001 From: capostrophic Date: Thu, 8 Aug 2019 18:37:42 +0300 Subject: [PATCH 06/12] Reduce skinned shape code duplication --- components/nifosg/nifloader.cpp | 83 +++++++++------------------------ 1 file changed, 23 insertions(+), 60 deletions(-) diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 3e6262025..25e5a3864 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -599,28 +599,16 @@ namespace NifOsg const bool isMarker = hasMarkers && !nodeName.compare(0, markerName.size(), markerName); if (!isMarker && nodeName.compare(0, shadowName.size(), shadowName) && nodeName.compare(0, shadowName2.size(), shadowName2)) { - bool skinned = false; + Nif::NiSkinInstancePtr skin; if (nifNode->recType == Nif::RC_NiTriShape) - { - const Nif::NiTriShape* triShape = static_cast(nifNode); - if (!triShape->skin.empty()) - { - skinned = true; - handleSkinnedTriShape(triShape, node, composite, boundTextures, animflags); - } - } + skin = static_cast(nifNode)->skin; else // if (nifNode->recType == Nif::RC_NiTriStrips) - { - const Nif::NiTriStrips* triStrips = static_cast(nifNode); - if (!triStrips->skin.empty()) - { - skinned = true; - handleSkinnedTriStrips(triStrips, node, composite, boundTextures, animflags); - } - } + skin = static_cast(nifNode)->skin; - if (!skinned) + if (skin.empty()) handleTriShape(nifNode, node, composite, boundTextures, animflags); + else + handleSkinnedTriShape(nifNode, node, composite, boundTextures, animflags); if (!nifNode->controller.empty()) handleMeshControllers(nifNode, node, composite, boundTextures, animflags); @@ -1206,58 +1194,34 @@ namespace NifOsg return morphGeom; } - void handleSkinnedTriShape(const Nif::NiTriShape *triShape, osg::Group *parentNode, SceneUtil::CompositeStateSetUpdater* composite, + void handleSkinnedTriShape(const Nif::Node *nifNode, osg::Group *parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector& boundTextures, int animflags) { - osg::ref_ptr geometry (new osg::Geometry); - triShapeToGeometry(triShape, geometry, parentNode, composite, boundTextures, animflags); - - osg::ref_ptr rig(new SceneUtil::RigGeometry); - rig->setSourceGeometry(geometry); - rig->setName(triShape->name); - - const Nif::NiSkinInstance *skin = triShape->skin.getPtr(); - - // Assign bone weights - osg::ref_ptr map (new SceneUtil::RigGeometry::InfluenceMap); + if (nifNode->recType != Nif::RC_NiTriShape && nifNode->recType != Nif::RC_NiTriStrips) + return; - const Nif::NiSkinData *data = skin->data.getPtr(); - const Nif::NodeList &bones = skin->bones; - for(size_t i = 0;i < bones.length();i++) + osg::ref_ptr geometry (new osg::Geometry); + Nif::NiSkinInstancePtr skinPtr; + if (nifNode->recType != Nif::RC_NiTriShape) { - std::string boneName = Misc::StringUtils::lowerCase(bones[i].getPtr()->name); - - SceneUtil::RigGeometry::BoneInfluence influence; - const std::vector &weights = data->bones[i].weights; - for(size_t j = 0;j < weights.size();j++) - { - influence.mWeights.emplace_back(weights[j].vertex, weights[j].weight); - } - influence.mInvBindMatrix = data->bones[i].trafo.toMatrix(); - influence.mBoundSphere = osg::BoundingSpheref(data->bones[i].boundSphereCenter, data->bones[i].boundSphereRadius); - - map->mData.emplace_back(boneName, influence); + const Nif::NiTriShape* triShape = static_cast(nifNode); + triShapeToGeometry(triShape, geometry, parentNode, composite, boundTextures, animflags); + skinPtr = triShape->skin; + } + else + { + const Nif::NiTriStrips* triStrips = static_cast(nifNode); + triStripsToGeometry(triStrips, geometry, parentNode, composite, boundTextures, animflags); + skinPtr = triStrips->skin; } - rig->setInfluenceMap(map); - - parentNode->addChild(rig); - } - - void handleSkinnedTriStrips(const Nif::NiTriStrips *triStrips, osg::Group *parentNode, SceneUtil::CompositeStateSetUpdater* composite, - const std::vector& boundTextures, int animflags) - { - osg::ref_ptr geometry (new osg::Geometry); - triStripsToGeometry(triStrips, geometry, parentNode, composite, boundTextures, animflags); - osg::ref_ptr rig(new SceneUtil::RigGeometry); rig->setSourceGeometry(geometry); - rig->setName(triStrips->name); - - const Nif::NiSkinInstance *skin = triStrips->skin.getPtr(); + rig->setName(nifNode->name); // Assign bone weights osg::ref_ptr map (new SceneUtil::RigGeometry::InfluenceMap); + const Nif::NiSkinInstance *skin = skinPtr.getPtr(); const Nif::NiSkinData *data = skin->data.getPtr(); const Nif::NodeList &bones = skin->bones; for(size_t i = 0;i < bones.length();i++) @@ -1280,7 +1244,6 @@ namespace NifOsg parentNode->addChild(rig); } - osg::BlendFunc::BlendFuncMode getBlendMode(int mode) { switch(mode) From ed154f0e2c3a38bd0d7923ee409ecacc4e214b93 Mon Sep 17 00:00:00 2001 From: capostrophic Date: Thu, 8 Aug 2019 18:41:43 +0300 Subject: [PATCH 07/12] Fix typo and replace failsave with an assert --- components/nifosg/nifloader.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 25e5a3864..d4ecee0e8 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -1134,8 +1134,7 @@ namespace NifOsg void handleTriShape(const Nif::Node* nifNode, osg::Group* parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector& boundTextures, int animflags) { - if (nifNode->recType != Nif::RC_NiTriShape && nifNode->recType != Nif::RC_NiTriStrips) - return; + assert(nifNode->recType == Nif::RC_NiTriShape || nifNode->recType == Nif::RC_NiTriStrips); osg::ref_ptr drawable; osg::ref_ptr geom (new osg::Geometry); @@ -1197,12 +1196,11 @@ namespace NifOsg void handleSkinnedTriShape(const Nif::Node *nifNode, osg::Group *parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector& boundTextures, int animflags) { - if (nifNode->recType != Nif::RC_NiTriShape && nifNode->recType != Nif::RC_NiTriStrips) - return; + assert(nifNode->recType == Nif::RC_NiTriShape || nifNode->recType == Nif::RC_NiTriStrips); osg::ref_ptr geometry (new osg::Geometry); Nif::NiSkinInstancePtr skinPtr; - if (nifNode->recType != Nif::RC_NiTriShape) + if (nifNode->recType == Nif::RC_NiTriShape) { const Nif::NiTriShape* triShape = static_cast(nifNode); triShapeToGeometry(triShape, geometry, parentNode, composite, boundTextures, animflags); From 91efdf18a91a16975f611d9a646f75bfaf383e40 Mon Sep 17 00:00:00 2001 From: capostrophic Date: Thu, 8 Aug 2019 19:02:08 +0300 Subject: [PATCH 08/12] Reduce Bullet part code duplication --- components/nifbullet/bulletnifloader.cpp | 128 +++++++++-------------- components/nifbullet/bulletnifloader.hpp | 3 +- components/nifosg/nifloader.cpp | 8 +- 3 files changed, 50 insertions(+), 89 deletions(-) diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index 7cfdbf40f..40a9134f3 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -292,13 +292,9 @@ void BulletNifLoader::handleNode(const std::string& fileName, const Nif::Node *n // NOTE: a trishape with hasBounds=true, but no BBoxCollision flag should NOT go through handleNiTriShape! // It must be ignored completely. // (occurs in tr_ex_imp_wall_arch_04.nif) - if(!node->hasBounds && node->recType == Nif::RC_NiTriShape) + if(!node->hasBounds && (node->recType == Nif::RC_NiTriShape || node->recType == Nif::RC_NiTriStrips)) { - handleNiTriShape(static_cast(node), flags, getWorldTransform(node), isAnimated, avoid); - } - else if(!node->hasBounds && node->recType == Nif::RC_NiTriStrips) - { - handleNiTriStrips(static_cast(node), flags, getWorldTransform(node), isAnimated, avoid); + handleNiTriShape(node, flags, getWorldTransform(node), isAnimated, avoid); } } @@ -315,90 +311,38 @@ void BulletNifLoader::handleNode(const std::string& fileName, const Nif::Node *n } } -void BulletNifLoader::handleNiTriShape(const Nif::NiTriShape *shape, int flags, const osg::Matrixf &transform, +void BulletNifLoader::handleNiTriShape(const Nif::Node *nifNode, int flags, const osg::Matrixf &transform, bool isAnimated, bool avoid) { - assert(shape != nullptr); + assert(nifNode != nullptr); // If the object was marked "NCO" earlier, it shouldn't collide with // anything. So don't do anything. if ((flags & 0x800)) - { - return; - } - - if (!shape->skin.empty()) - isAnimated = false; - - if (shape->data.empty()) - return; - if (shape->data->triangles.empty()) return; - if (isAnimated) - { - if (!mCompoundShape) - mCompoundShape.reset(new btCompoundShape); - - std::unique_ptr childMesh(new btTriangleMesh); - - fillTriangleMesh(*childMesh, shape->data.get()); - - std::unique_ptr childShape(new Resource::TriangleMeshShape(childMesh.get(), true)); - childMesh.release(); - - float scale = shape->trafo.scale; - const Nif::Node* parent = shape; - while (parent->parent) - { - parent = parent->parent; - scale *= parent->trafo.scale; - } - osg::Quat q = transform.getRotate(); - osg::Vec3f v = transform.getTrans(); - childShape->setLocalScaling(btVector3(scale, scale, scale)); - - btTransform trans(btQuaternion(q.x(), q.y(), q.z(), q.w()), btVector3(v.x(), v.y(), v.z())); - - mShape->mAnimatedShapes.insert(std::make_pair(shape->recIndex, mCompoundShape->getNumChildShapes())); - - mCompoundShape->addChildShape(trans, childShape.get()); - childShape.release(); - } - else if (avoid) + if (nifNode->recType == Nif::RC_NiTriShape) { - if (!mAvoidStaticMesh) - mAvoidStaticMesh.reset(new btTriangleMesh(false)); - - fillTriangleMeshWithTransform(*mAvoidStaticMesh, shape->data.get(), transform); + const Nif::NiTriShape* shape = static_cast(nifNode); + if (!shape->skin.empty()) + isAnimated = false; + if (shape->data.empty()) + return; + if (shape->data->triangles.empty()) + return; } else { - if (!mStaticMesh) - mStaticMesh.reset(new btTriangleMesh(false)); - - // Static shape, just transform all vertices into position - fillTriangleMeshWithTransform(*mStaticMesh, shape->data.get(), transform); + const Nif::NiTriStrips* shape = static_cast(nifNode); + if (!shape->skin.empty()) + isAnimated = false; + if (shape->data.empty()) + return; + if (shape->data->strips.empty()) + return; } -} -void BulletNifLoader::handleNiTriStrips(const Nif::NiTriStrips *shape, int flags, const osg::Matrixf &transform, - bool isAnimated, bool avoid) -{ - assert(shape != nullptr); - - // If the object was marked "NCO" earlier, it shouldn't collide with - // anything. So don't do anything. - if ((flags & 0x800)) - return; - if (!shape->skin.empty()) - isAnimated = false; - if (shape->data.empty()) - return; - if (shape->data->strips.empty()) - return; - if (isAnimated) { if (!mCompoundShape) @@ -406,13 +350,16 @@ void BulletNifLoader::handleNiTriStrips(const Nif::NiTriStrips *shape, int flags std::unique_ptr childMesh(new btTriangleMesh); - fillTriangleMesh(*childMesh, shape->data.get()); + if (nifNode->recType == Nif::RC_NiTriShape) + fillTriangleMesh(*childMesh, static_cast(nifNode)->data.get()); + else + fillTriangleMesh(*childMesh, static_cast(nifNode)->data.get()); std::unique_ptr childShape(new Resource::TriangleMeshShape(childMesh.get(), true)); childMesh.release(); - float scale = shape->trafo.scale; - const Nif::Node* parent = shape; + float scale = nifNode->trafo.scale; + const Nif::Node* parent = nifNode; while (parent->parent) { parent = parent->parent; @@ -424,7 +371,7 @@ void BulletNifLoader::handleNiTriStrips(const Nif::NiTriStrips *shape, int flags btTransform trans(btQuaternion(q.x(), q.y(), q.z(), q.w()), btVector3(v.x(), v.y(), v.z())); - mShape->mAnimatedShapes.emplace(shape->recIndex, mCompoundShape->getNumChildShapes()); + mShape->mAnimatedShapes.emplace(nifNode->recIndex, mCompoundShape->getNumChildShapes()); mCompoundShape->addChildShape(trans, childShape.get()); childShape.release(); @@ -434,7 +381,16 @@ void BulletNifLoader::handleNiTriStrips(const Nif::NiTriStrips *shape, int flags if (!mAvoidStaticMesh) mAvoidStaticMesh.reset(new btTriangleMesh(false)); - fillTriangleMeshWithTransform(*mAvoidStaticMesh, shape->data.get(), transform); + if (nifNode->recType == Nif::RC_NiTriShape) + { + const Nif::NiTriShape* shape = static_cast(nifNode); + fillTriangleMeshWithTransform(*mAvoidStaticMesh, shape->data.get(), transform); + } + else + { + const Nif::NiTriStrips* shape = static_cast(nifNode); + fillTriangleMeshWithTransform(*mAvoidStaticMesh, shape->data.get(), transform); + } } else { @@ -442,7 +398,17 @@ void BulletNifLoader::handleNiTriStrips(const Nif::NiTriStrips *shape, int flags mStaticMesh.reset(new btTriangleMesh(false)); // Static shape, just transform all vertices into position - fillTriangleMeshWithTransform(*mStaticMesh, shape->data.get(), transform); + + if (nifNode->recType == Nif::RC_NiTriShape) + { + const Nif::NiTriShape* shape = static_cast(nifNode); + fillTriangleMeshWithTransform(*mStaticMesh, shape->data.get(), transform); + } + else + { + const Nif::NiTriStrips* shape = static_cast(nifNode); + fillTriangleMeshWithTransform(*mStaticMesh, shape->data.get(), transform); + } } } diff --git a/components/nifbullet/bulletnifloader.hpp b/components/nifbullet/bulletnifloader.hpp index afc8772cd..e423e5149 100644 --- a/components/nifbullet/bulletnifloader.hpp +++ b/components/nifbullet/bulletnifloader.hpp @@ -59,8 +59,7 @@ private: bool hasAutoGeneratedCollision(const Nif::Node *rootNode); - void handleNiTriShape(const Nif::NiTriShape *shape, int flags, const osg::Matrixf& transform, bool isAnimated, bool avoid); - void handleNiTriStrips(const Nif::NiTriStrips *shape, int flags, const osg::Matrixf& transform, bool isAnimated, bool avoid); + void handleNiTriShape(const Nif::Node *nifNode, int flags, const osg::Matrixf& transform, bool isAnimated, bool avoid); std::unique_ptr mCompoundShape; diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index d4ecee0e8..f5f36a36d 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -576,12 +576,8 @@ namespace NifOsg node->setDataVariance(osg::Object::DYNAMIC); } - if (nifNode->recType == Nif::RC_NiTriShape && isAnimated) // the same thing for animated NiTriShapes - { - node->setDataVariance(osg::Object::DYNAMIC); - } - - if (nifNode->recType == Nif::RC_NiTriStrips && isAnimated) // the same thing for animated NiTriStrips + // Same thing for animated shapes + if ((nifNode->recType == Nif::RC_NiTriShape || nifNode->recType == Nif::RC_NiTriStrips) && isAnimated) { node->setDataVariance(osg::Object::DYNAMIC); } From 7fc3153f62718e17c00c0f4478d3b33380e1d419 Mon Sep 17 00:00:00 2001 From: capostrophic Date: Thu, 8 Aug 2019 19:26:50 +0300 Subject: [PATCH 09/12] Reduce code duplication further --- components/nif/data.cpp | 1 - components/nifbullet/bulletnifloader.cpp | 71 +++-------- components/nifosg/nifloader.cpp | 143 ++++++++--------------- 3 files changed, 66 insertions(+), 149 deletions(-) diff --git a/components/nif/data.cpp b/components/nif/data.cpp index 485a2ddf8..3ed7d5c3c 100644 --- a/components/nif/data.cpp +++ b/components/nif/data.cpp @@ -96,7 +96,6 @@ void NiTriStripsData::read(NIFStream *nif) // Every strip with n points defines n-2 triangles, so this should be unnecessary. /*int tris =*/ nif->getUShort(); - // Number of triangle strips int numStrips = nif->getUShort(); // Number of points in each strip diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index 40a9134f3..93429ef93 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -58,28 +58,17 @@ void fillTriangleMeshWithTransform(btTriangleMesh& mesh, const Nif::NiTriStripsD { const std::vector &vertices = data.vertices; const std::vector> &strips = data.strips; - if (vertices.empty() || strips.empty()) + // Can't make a triangle from less than three vertices. All strips have the same size. + if (vertices.empty() || strips.empty() || strips[0].size() < 3) return; - - // Assume every strip has the same size - const int singleStripSize = static_cast(strips[0].size()); - // Can't make a triangle from less than three vertices. - if (singleStripSize < 3) - return; - mesh.preallocateVertices(static_cast(data.vertices.size())); - // There are N+2*M vertex indices in sum in all strips - // where N is the number of triangles and M is the number of strips - mesh.preallocateIndices(static_cast((strips.size()-2) * singleStripSize)); + // There are N+2*M vertex indices overall, so we must substract 2*M (N triangles, M strips) + mesh.preallocateIndices(static_cast((strips.size()-2) * strips[0].size())); // It's triangulation time. Totally not a NifSkope spell ripoff. for (const std::vector& strip : strips) { - unsigned short a = strip[0]; - unsigned short b = strip[0]; - unsigned short c = strip[1]; - bool flip = false; - + unsigned short a = strip[0], b = strip[0], c = strip[1]; for (int i = 2; i < static_cast(strip.size()); i++) { a = b; @@ -87,24 +76,26 @@ void fillTriangleMeshWithTransform(btTriangleMesh& mesh, const Nif::NiTriStripsD c = strip[i]; if (a != b && b != c && a != c) { - if (!flip) + if (i%2==0) mesh.addTriangle(getbtVector(vertices[a]), getbtVector(vertices[b]), getbtVector(vertices[c])); else mesh.addTriangle(getbtVector(vertices[a]), getbtVector(vertices[c]), getbtVector(vertices[b])); } - flip = !flip; } } } -void fillTriangleMesh(btTriangleMesh& mesh, const Nif::NiTriShapeData& data) +void fillTriangleMeshWithTransform(btTriangleMesh& mesh, const Nif::Node* nifNode, const osg::Matrixf &transform) { - fillTriangleMeshWithTransform(mesh, data, osg::Matrixf()); + if (nifNode->recType == Nif::RC_NiTriShape) + fillTriangleMeshWithTransform(mesh, static_cast(nifNode)->data.get(), transform); + else // if (nifNode->recType == Nif::RC_NiTriStrips) + fillTriangleMeshWithTransform(mesh, static_cast(nifNode)->data.get(), transform); } -void fillTriangleMesh(btTriangleMesh& mesh, const Nif::NiTriStripsData& data) +void fillTriangleMesh(btTriangleMesh& mesh, const Nif::Node* node) { - fillTriangleMeshWithTransform(mesh, data, osg::Matrixf()); + fillTriangleMeshWithTransform(mesh, node, osg::Matrixf()); } } @@ -326,9 +317,7 @@ void BulletNifLoader::handleNiTriShape(const Nif::Node *nifNode, int flags, cons const Nif::NiTriShape* shape = static_cast(nifNode); if (!shape->skin.empty()) isAnimated = false; - if (shape->data.empty()) - return; - if (shape->data->triangles.empty()) + if (shape->data.empty() || shape->data->triangles.empty()) return; } else @@ -336,9 +325,7 @@ void BulletNifLoader::handleNiTriShape(const Nif::Node *nifNode, int flags, cons const Nif::NiTriStrips* shape = static_cast(nifNode); if (!shape->skin.empty()) isAnimated = false; - if (shape->data.empty()) - return; - if (shape->data->strips.empty()) + if (shape->data.empty() || shape->data->strips.empty()) return; } @@ -350,10 +337,7 @@ void BulletNifLoader::handleNiTriShape(const Nif::Node *nifNode, int flags, cons std::unique_ptr childMesh(new btTriangleMesh); - if (nifNode->recType == Nif::RC_NiTriShape) - fillTriangleMesh(*childMesh, static_cast(nifNode)->data.get()); - else - fillTriangleMesh(*childMesh, static_cast(nifNode)->data.get()); + fillTriangleMesh(*childMesh, nifNode); std::unique_ptr childShape(new Resource::TriangleMeshShape(childMesh.get(), true)); childMesh.release(); @@ -381,16 +365,7 @@ void BulletNifLoader::handleNiTriShape(const Nif::Node *nifNode, int flags, cons if (!mAvoidStaticMesh) mAvoidStaticMesh.reset(new btTriangleMesh(false)); - if (nifNode->recType == Nif::RC_NiTriShape) - { - const Nif::NiTriShape* shape = static_cast(nifNode); - fillTriangleMeshWithTransform(*mAvoidStaticMesh, shape->data.get(), transform); - } - else - { - const Nif::NiTriStrips* shape = static_cast(nifNode); - fillTriangleMeshWithTransform(*mAvoidStaticMesh, shape->data.get(), transform); - } + fillTriangleMeshWithTransform(*mAvoidStaticMesh, nifNode, transform); } else { @@ -398,17 +373,7 @@ void BulletNifLoader::handleNiTriShape(const Nif::Node *nifNode, int flags, cons mStaticMesh.reset(new btTriangleMesh(false)); // Static shape, just transform all vertices into position - - if (nifNode->recType == Nif::RC_NiTriShape) - { - const Nif::NiTriShape* shape = static_cast(nifNode); - fillTriangleMeshWithTransform(*mStaticMesh, shape->data.get(), transform); - } - else - { - const Nif::NiTriStrips* shape = static_cast(nifNode); - fillTriangleMeshWithTransform(*mStaticMesh, shape->data.get(), transform); - } + fillTriangleMeshWithTransform(*mStaticMesh, nifNode, transform); } } diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index f5f36a36d..77e9688c7 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -576,8 +576,7 @@ namespace NifOsg node->setDataVariance(osg::Object::DYNAMIC); } - // Same thing for animated shapes - if ((nifNode->recType == Nif::RC_NiTriShape || nifNode->recType == Nif::RC_NiTriStrips) && isAnimated) + if ((nifNode->recType == Nif::RC_NiTriShape || nifNode->recType == Nif::RC_NiTriStrips) && isAnimated) // Same thing for animated shapes { node->setDataVariance(osg::Object::DYNAMIC); } @@ -1038,85 +1037,55 @@ namespace NifOsg } } - void triShapeToGeometry(const Nif::NiTriShape *triShape, osg::Geometry *geometry, osg::Node* parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector& boundTextures, int animflags) + void triCommonToGeometry(osg::Geometry *geometry, const std::vector& vertices, const std::vector& normals, const std::vector>& uvlist, const std::vector& colors, const std::vector& boundTextures, const std::string& name) { - const Nif::NiTriShapeData* data = triShape->data.getPtr(); - - if (!data->vertices.empty()) - geometry->setVertexArray(new osg::Vec3Array(data->vertices.size(), data->vertices.data())); - if (!data->normals.empty()) - geometry->setNormalArray(new osg::Vec3Array(data->normals.size(), data->normals.data()), osg::Array::BIND_PER_VERTEX); + if (!vertices.empty()) + geometry->setVertexArray(new osg::Vec3Array(vertices.size(), vertices.data())); + if (!normals.empty()) + geometry->setNormalArray(new osg::Vec3Array(normals.size(), normals.data()), osg::Array::BIND_PER_VERTEX); + if (!colors.empty()) + geometry->setColorArray(new osg::Vec4Array(colors.size(), colors.data()), osg::Array::BIND_PER_VERTEX); int textureStage = 0; - for (std::vector::const_iterator it = boundTextures.begin(); it != boundTextures.end(); ++it,++textureStage) + for (const int uvSet : boundTextures) { - int uvSet = *it; - if (uvSet >= (int)data->uvlist.size()) + if (uvSet >= (int)uvlist.size()) { - Log(Debug::Verbose) << "Out of bounds UV set " << uvSet << " on TriShape \"" << triShape->name << "\" in " << mFilename; - if (!data->uvlist.empty()) - geometry->setTexCoordArray(textureStage, new osg::Vec2Array(data->uvlist[0].size(), data->uvlist[0].data()), osg::Array::BIND_PER_VERTEX); + Log(Debug::Verbose) << "Out of bounds UV set " << uvSet << " on shape \"" << name << "\" in " << mFilename; + if (!uvlist.empty()) + geometry->setTexCoordArray(textureStage, new osg::Vec2Array(uvlist[0].size(), uvlist[0].data()), osg::Array::BIND_PER_VERTEX); continue; } - geometry->setTexCoordArray(textureStage, new osg::Vec2Array(data->uvlist[uvSet].size(), data->uvlist[uvSet].data()), osg::Array::BIND_PER_VERTEX); + geometry->setTexCoordArray(textureStage, new osg::Vec2Array(uvlist[uvSet].size(), uvlist[uvSet].data()), osg::Array::BIND_PER_VERTEX); + textureStage++; } - - if (!data->colors.empty()) - geometry->setColorArray(new osg::Vec4Array(data->colors.size(), data->colors.data()), osg::Array::BIND_PER_VERTEX); - - if (!data->triangles.empty()) - geometry->addPrimitiveSet(new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLES, - data->triangles.size(), - (unsigned short*)data->triangles.data())); - - // 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 drawableProps; - collectDrawableProperties(triShape, drawableProps); - applyDrawableProperties(parentNode, drawableProps, composite, !data->colors.empty(), animflags, false); } - void triStripsToGeometry(const Nif::NiTriStrips *triStrips, osg::Geometry *geometry, osg::Node* parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector& boundTextures, int animflags) + void triShapeToGeometry(const Nif::Node *nifNode, osg::Geometry *geometry, osg::Node* parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector& boundTextures, int animflags) { - const Nif::NiTriStripsData* data = triStrips->data.getPtr(); - - if (!data->vertices.empty()) - geometry->setVertexArray(new osg::Vec3Array(data->vertices.size(), data->vertices.data())); - if (!data->normals.empty()) - geometry->setNormalArray(new osg::Vec3Array(data->normals.size(), data->normals.data()), osg::Array::BIND_PER_VERTEX); - - int textureStage = 0; - for (const int uvSet : boundTextures) + bool vertexColorsPresent = false; + if (nifNode->recType == Nif::RC_NiTriShape) { - if (uvSet >= (int)data->uvlist.size()) - { - Log(Debug::Verbose) << "Out of bounds UV set " << uvSet << " on TriStrips \"" << triStrips->name << "\" in " << mFilename; - if (!data->uvlist.empty()) - geometry->setTexCoordArray(textureStage, new osg::Vec2Array(data->uvlist[0].size(), data->uvlist[0].data()), osg::Array::BIND_PER_VERTEX); - continue; - } - - geometry->setTexCoordArray(textureStage, new osg::Vec2Array(data->uvlist[uvSet].size(), data->uvlist[uvSet].data()), osg::Array::BIND_PER_VERTEX); - textureStage++; + const Nif::NiTriShape* triShape = static_cast(nifNode); + const Nif::NiTriShapeData* data = triShape->data.getPtr(); + vertexColorsPresent = !data->colors.empty(); + triCommonToGeometry(geometry, data->vertices, data->normals, data->uvlist, data->colors, boundTextures, triShape->name); + if (!data->triangles.empty()) + geometry->addPrimitiveSet(new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLES, data->triangles.size(), + (unsigned short*)data->triangles.data())); } - - if (!data->colors.empty()) - geometry->setColorArray(new osg::Vec4Array(data->colors.size(), data->colors.data()), osg::Array::BIND_PER_VERTEX); - - if (!data->strips.empty()) + else { - for (const std::vector& strip : data->strips) - { - // Can't make a triangle from less than three vertices. - // All strips have the same size. - if (strip.size() < 3) - break; - geometry->addPrimitiveSet(new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLE_STRIP, - strip.size(), (unsigned short*)strip.data())); - } + const Nif::NiTriStrips* triStrips = static_cast(nifNode); + const Nif::NiTriStripsData* data = triStrips->data.getPtr(); + vertexColorsPresent = !data->colors.empty(); + triCommonToGeometry(geometry, data->vertices, data->normals, data->uvlist, data->colors, boundTextures, triStrips->name); + // Can't make a triangle from less than three vertices. All strips have the same size. + if (!data->strips.empty() && data->strips[0].size() < 3) + for (const std::vector& strip : data->strips) + geometry->addPrimitiveSet(new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLE_STRIP, strip.size(), + (unsigned short*)strip.data())); } // osg::Material properties are handled here for two reasons: @@ -1124,34 +1093,26 @@ namespace NifOsg // - there are 3 "overlapping" nif properties that all affect the osg::Material, handling them // above the actual renderable would be tedious. std::vector drawableProps; - collectDrawableProperties(triStrips, drawableProps); - applyDrawableProperties(parentNode, drawableProps, composite, !data->colors.empty(), animflags, false); + collectDrawableProperties(nifNode, drawableProps); + applyDrawableProperties(parentNode, drawableProps, composite, vertexColorsPresent, animflags, false); } void handleTriShape(const Nif::Node* nifNode, osg::Group* parentNode, SceneUtil::CompositeStateSetUpdater* composite, const std::vector& boundTextures, int animflags) { assert(nifNode->recType == Nif::RC_NiTriShape || nifNode->recType == Nif::RC_NiTriStrips); - osg::ref_ptr drawable; osg::ref_ptr geom (new osg::Geometry); + triShapeToGeometry(nifNode, geom, parentNode, composite, boundTextures, animflags); + Nif::ControllerPtr ctrl; if (nifNode->recType == Nif::RC_NiTriShape) - { - const Nif::NiTriShape* triShape = static_cast(nifNode); - triShapeToGeometry(triShape, geom, parentNode, composite, boundTextures, animflags); - handleMorphController(triShape->controller, drawable, geom, parentNode, composite, boundTextures, animflags); - } + ctrl = static_cast(nifNode)->controller; else - { - const Nif::NiTriStrips* triStrips = static_cast(nifNode); - triStripsToGeometry(triStrips, geom, parentNode, composite, boundTextures, animflags); - handleMorphController(triStrips->controller, drawable, geom, parentNode, composite, boundTextures, animflags); - } + ctrl = static_cast(nifNode)->controller; + handleMorphController(ctrl, drawable, geom, parentNode, composite, boundTextures, animflags); if (!drawable.get()) drawable = geom; - drawable->setName(nifNode->name); - parentNode->addChild(drawable); } @@ -1193,21 +1154,8 @@ namespace NifOsg const std::vector& boundTextures, int animflags) { assert(nifNode->recType == Nif::RC_NiTriShape || nifNode->recType == Nif::RC_NiTriStrips); - osg::ref_ptr geometry (new osg::Geometry); - Nif::NiSkinInstancePtr skinPtr; - if (nifNode->recType == Nif::RC_NiTriShape) - { - const Nif::NiTriShape* triShape = static_cast(nifNode); - triShapeToGeometry(triShape, geometry, parentNode, composite, boundTextures, animflags); - skinPtr = triShape->skin; - } - else - { - const Nif::NiTriStrips* triStrips = static_cast(nifNode); - triStripsToGeometry(triStrips, geometry, parentNode, composite, boundTextures, animflags); - skinPtr = triStrips->skin; - } + triShapeToGeometry(nifNode, geometry, parentNode, composite, boundTextures, animflags); osg::ref_ptr rig(new SceneUtil::RigGeometry); rig->setSourceGeometry(geometry); rig->setName(nifNode->name); @@ -1215,6 +1163,11 @@ namespace NifOsg // Assign bone weights osg::ref_ptr map (new SceneUtil::RigGeometry::InfluenceMap); + Nif::NiSkinInstancePtr skinPtr; + if (nifNode->recType == Nif::RC_NiTriShape) + skinPtr = static_cast(nifNode)->skin; + else + skinPtr = static_cast(nifNode)->skin; const Nif::NiSkinInstance *skin = skinPtr.getPtr(); const Nif::NiSkinData *data = skin->data.getPtr(); const Nif::NodeList &bones = skin->bones; From 93f73fe87bfae3b88300a526857645877294e9ae Mon Sep 17 00:00:00 2001 From: capostrophic Date: Thu, 8 Aug 2019 20:41:46 +0300 Subject: [PATCH 10/12] Fix comparison --- components/nifosg/nifloader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 77e9688c7..e10322f46 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -1082,7 +1082,7 @@ namespace NifOsg vertexColorsPresent = !data->colors.empty(); triCommonToGeometry(geometry, data->vertices, data->normals, data->uvlist, data->colors, boundTextures, triStrips->name); // Can't make a triangle from less than three vertices. All strips have the same size. - if (!data->strips.empty() && data->strips[0].size() < 3) + if (!data->strips.empty() && data->strips[0].size() >= 3) for (const std::vector& strip : data->strips) geometry->addPrimitiveSet(new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLE_STRIP, strip.size(), (unsigned short*)strip.data())); From fa910299d04874c90253c87ac00642871435969e Mon Sep 17 00:00:00 2001 From: capostrophic Date: Thu, 8 Aug 2019 20:57:03 +0300 Subject: [PATCH 11/12] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 57551301f..e36be793b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -158,6 +158,7 @@ Feature #5051: Provide a separate textures for scrollbars Feature #5094: Unix like console hotkeys Feature #5098: Allow user controller bindings + Feature #5121: Handle NiTriStrips and NiTriStripsData Feature #5122: Use magic glow for enchanted arrows Task #4686: Upgrade media decoder to a more current FFmpeg API Task #4695: Optimize Distant Terrain memory consumption From 9e93c9ecd2a0ceeb0e654a26842a160f252c942a Mon Sep 17 00:00:00 2001 From: capostrophic Date: Thu, 8 Aug 2019 22:33:56 +0300 Subject: [PATCH 12/12] Fix multiple-strip NiTriStrips loading --- components/nif/data.cpp | 8 +++++--- components/nifbullet/bulletnifloader.cpp | 17 +++++++++++++---- components/nifosg/nifloader.cpp | 10 ++++++++-- 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/components/nif/data.cpp b/components/nif/data.cpp index 3ed7d5c3c..4a9266239 100644 --- a/components/nif/data.cpp +++ b/components/nif/data.cpp @@ -98,12 +98,14 @@ void NiTriStripsData::read(NIFStream *nif) /*int tris =*/ nif->getUShort(); // Number of triangle strips int numStrips = nif->getUShort(); - // Number of points in each strip - int lengths = nif->getUShort(); + + std::vector lengths; + nif->getUShorts(lengths, numStrips); + for (int i = 0; i < numStrips; i++) { std::vector strip; - nif->getUShorts(strip, lengths); + nif->getUShorts(strip, lengths[i]); strips.emplace_back(strip); } } diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index 93429ef93..2f24a4067 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -58,16 +58,25 @@ void fillTriangleMeshWithTransform(btTriangleMesh& mesh, const Nif::NiTriStripsD { const std::vector &vertices = data.vertices; const std::vector> &strips = data.strips; - // Can't make a triangle from less than three vertices. All strips have the same size. - if (vertices.empty() || strips.empty() || strips[0].size() < 3) + if (vertices.empty() || strips.empty()) return; mesh.preallocateVertices(static_cast(data.vertices.size())); - // There are N+2*M vertex indices overall, so we must substract 2*M (N triangles, M strips) - mesh.preallocateIndices(static_cast((strips.size()-2) * strips[0].size())); + int numTriangles = 0; + for (const std::vector& strip : strips) + { + // Each strip with N points contains information about N-2 triangles. + if (strip.size() >= 3) + numTriangles += static_cast(strip.size()-2); + } + mesh.preallocateIndices(static_cast(numTriangles)); // It's triangulation time. Totally not a NifSkope spell ripoff. for (const std::vector& strip : strips) { + // Can't make a triangle from less than 3 points. + if (strip.size() < 3) + continue; + unsigned short a = strip[0], b = strip[0], c = strip[1]; for (int i = 2; i < static_cast(strip.size()); i++) { diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index e10322f46..60644c758 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -1081,11 +1081,17 @@ namespace NifOsg const Nif::NiTriStripsData* data = triStrips->data.getPtr(); vertexColorsPresent = !data->colors.empty(); triCommonToGeometry(geometry, data->vertices, data->normals, data->uvlist, data->colors, boundTextures, triStrips->name); - // Can't make a triangle from less than three vertices. All strips have the same size. - if (!data->strips.empty() && data->strips[0].size() >= 3) + if (!data->strips.empty()) + { for (const std::vector& strip : data->strips) + { + // Can't make a triangle from less than three vertices. + if (strip.size() < 3) + continue; geometry->addPrimitiveSet(new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLE_STRIP, strip.size(), (unsigned short*)strip.data())); + } + } } // osg::Material properties are handled here for two reasons: