diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index b077c32ff..9df27df32 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -379,6 +379,17 @@ namespace // Note: this assumes only one cull thread is using the given RigGeometry, there would be a race condition otherwise. struct UpdateRigGeometry : public osg::Drawable::CullCallback { + UpdateRigGeometry() + { + } + + UpdateRigGeometry(const UpdateRigGeometry& copy, const osg::CopyOp& copyop) + : osg::Drawable::CullCallback(copy, copyop) + { + } + + META_Object(NifOsg, UpdateRigGeometry) + virtual bool cull(osg::NodeVisitor *, osg::Drawable * drw, osg::State *) const { osgAnimation::RigGeometry* geom = static_cast(drw); @@ -412,6 +423,59 @@ namespace } }; + // Same for MorphGeometry + struct UpdateMorphGeometry : public osg::Drawable::CullCallback + { + UpdateMorphGeometry() + { + } + + UpdateMorphGeometry(const UpdateMorphGeometry& copy, const osg::CopyOp& copyop) + : osg::Drawable::CullCallback(copy, copyop) + { + } + + META_Object(NifOsg, UpdateMorphGeometry) + + virtual bool cull(osg::NodeVisitor *, osg::Drawable * drw, osg::State *) const + { + osgAnimation::MorphGeometry* geom = static_cast(drw); + if (!geom) + return false; + geom->transformSoftwareMethod(); + return false; + } + }; + + // Callback to return a static bounding box for a MorphGeometry. The idea is to not recalculate the bounding box + // every time the morph weights change. To do so we return a maximum containing box that is big enough for all possible combinations of morph targets. + class StaticBoundingBoxCallback : public osg::Drawable::ComputeBoundingBoxCallback + { + public: + StaticBoundingBoxCallback() + { + } + + StaticBoundingBoxCallback(const osg::BoundingBox& bounds) + : mBoundingBox(bounds) + { + } + + StaticBoundingBoxCallback(const StaticBoundingBoxCallback& copy, const osg::CopyOp& copyop) + : osg::Drawable::ComputeBoundingBoxCallback(copy, copyop) + , mBoundingBox(copy.mBoundingBox) + { + } + + virtual osg::BoundingBox computeBound(const osg::Drawable& drawable) const + { + return mBoundingBox; + } + + private: + osg::BoundingBox mBoundingBox; + }; + class RigBoundingBoxCallback : public osg::Drawable::ComputeBoundingBoxCallback { public: @@ -1279,7 +1343,12 @@ namespace NifOsg // No normals available in the MorphData morphGeom->setMorphNormals(false); + morphGeom->setUpdateCallback(NULL); + morphGeom->setCullCallback(new UpdateMorphGeometry); + const std::vector& morphs = morpher->data.getPtr()->mMorphs; + if (!morphs.size()) + 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) { @@ -1287,6 +1356,38 @@ namespace NifOsg morphTarget->setVertexArray(new osg::Vec3Array(morphs[i].mVertices.size(), &morphs[i].mVertices[0])); morphGeom->addMorphTarget(morphTarget, 0.f); } + + // build the bounding box containing all possible morph combinations + + std::vector vertBounds(morphs[0].mVertices.size()); + + // Since we don't know what combinations of morphs are being applied we need to keep track of a bounding box for each vertex. + // The minimum/maximum of the box is the minimum/maximum offset the vertex can have from its starting position. + + // Start with zero offsets which will happen when no morphs are applied. + for (unsigned int i=0; isetComputeBoundingBoxCallback(new StaticBoundingBoxCallback(box)); + return morphGeom; }