diff --git a/include/obj/NiGeometry.h b/include/obj/NiGeometry.h index c1929af1833262a3514cb509a2d466d4c1591b3e..a1042b240687d38bfb0de4bf6296edc18c0c927d 100644 --- a/include/obj/NiGeometry.h +++ b/include/obj/NiGeometry.h @@ -49,7 +49,7 @@ public: * NiSkinInstance and NiSkinData class. The bones must have a common * ancestor in the scenegraph. This becomes the skeleton root. */ - void BindSkin( vector< Ref<NiNode> > bone_nodes, bool bind_to_scene = false ); + void BindSkin( vector< Ref<NiNode> > bone_nodes ); void UnbindSkin(); /*! * Sets the skin weights in the attached NiSkinData object. @@ -69,8 +69,20 @@ public: string GetShader() const; void SetShader( const string & n ); + /* + * Returns the position of the verticies and values of the normals after they + * have been deformed by the positions of their skin influences. + * \param[out] vertices A vector that will be filled with the skin deformed position of the verticies. + * \param[out] normals A vector thta will be filled with the skin deformed normal values. + */ void GetSkinDeformation( vector<Vector3> & vertices, vector<Vector3> & normals ) const; + /* + * Applies the local transform values to the vertices of the geometry and + * zeros them out to the identity. + */ + void ApplyTransforms(); + protected: list< Ref<NiNode> > ListAncestors( const Ref<NiNode> & leaf ) const; NI_GEOMETRY_MEMBERS diff --git a/include/obj/NiGeometryData.h b/include/obj/NiGeometryData.h index 3ae06bb5dcd7c8e91b002882b6f244c22f9638dd..fbc03b2735a2e29aee371c28109aa596fb2977c5 100644 --- a/include/obj/NiGeometryData.h +++ b/include/obj/NiGeometryData.h @@ -127,6 +127,13 @@ public: */ void SetUVSet( int index, const vector<TexCoord> & in ); + /*! + * Used to apply a transformation directly to all the vertices and normals in + * this mesh. + * \param[in] transform The 4x4 transformation matrix to apply to the vertices and normals in this mesh. Normals are only affected by the rotation portion of this matrix. + */ + void Transform( Matrix44 & transform ); + protected: NI_GEOMETRY_DATA_MEMBERS private: diff --git a/include/obj/NiNode.h b/include/obj/NiNode.h index 51b72a4e84424537976e8e136487c66f4dc5ce97..68d24b7fa6169887e0edf5fb3772c2a163d3e4c3 100644 --- a/include/obj/NiNode.h +++ b/include/obj/NiNode.h @@ -72,11 +72,18 @@ public: bool IsSplitMeshProxy() const; - /*! Causes all children's transforms to be changed so that all the skin + /*! + * Causes all children's transforms to be changed so that all the skin * pieces line up without any vertex transformations. */ void GoToSkeletonBindPosition(); + /*! + * Applies the local transforms of this node to its children, + * causing itself to be cleared to identity transforms. + */ + void PropagateTransform(); + /*! * Should only be called by NiTriBasedGeom * Adds a new SkinInstance to the specified mesh. diff --git a/src/obj/NiGeometry.cpp b/src/obj/NiGeometry.cpp index ced04547db5de77d5363bc93ea8f3dacfc983243..8ffc6b4d5747e6ea0294375aba3288cd334fa303 100644 --- a/src/obj/NiGeometry.cpp +++ b/src/obj/NiGeometry.cpp @@ -74,7 +74,7 @@ Ref<NiSkinInstance> NiGeometry::GetSkinInstance() const { return skinInstance; } -void NiGeometry::BindSkin( vector< Ref<NiNode> > bone_nodes, bool bind_to_scene ) { +void NiGeometry::BindSkin( vector< Ref<NiNode> > bone_nodes ) { //Ensure skin is not aleady bound if ( skinInstance != 0 ) { throw runtime_error("You have attempted to re-bind a skin that is already bound. Unbind it first."); @@ -110,68 +110,55 @@ void NiGeometry::BindSkin( vector< Ref<NiNode> > bone_nodes, bool bind_to_scene throw runtime_error("Shape and all skin influence bones must be part of the same tree before skin bind can take place."); } - NiNodeRef skeleton_root; - if (bind_to_scene) { - // Just parent to the scene - NiNodeRef parent = GetParent(); - while (parent != NULL) { - skeleton_root = parent; - parent = parent->GetParent(); - } - - } else { - skeleton_root = ancestors[0].front(); - - //Make sure bone and shapes are part of the same tree - for ( int i = 1; i < num_lists; ++i ) { - if ( ancestors[i].size() == 0 ) { - throw runtime_error("Shape and all skin influence bones must be part of the same tree before skin bind can take place."); - } - if ( ancestors[i].front() != skeleton_root ) { - throw runtime_error("Shape and all skin influence bones must be part of the same tree before skin bind can take place."); - } + NiNodeRef skeleton_root = ancestors[0].front(); + //Make sure bone and shapes are part of the same tree + for ( int i = 1; i < num_lists; ++i ) { + if ( ancestors[i].size() == 0 ) { + throw runtime_error("Shape and all skin influence bones must be part of the same tree before skin bind can take place."); } - - //Since the first items have been shown to match, pop all the stacks - for ( int i = 0; i < num_lists; ++i ) { - ancestors[i].pop_front(); + if ( ancestors[i].front() != skeleton_root ) { + throw runtime_error("Shape and all skin influence bones must be part of the same tree before skin bind can take place."); } + } - //Now search for the common ancestor + //Since the first items have been shown to match, pop all the stacks + for ( int i = 0; i < num_lists; ++i ) { + ancestors[i].pop_front(); + } - while(true) { - bool all_same = true; - if ( ancestors[0].size() == 0 ) { + //Now search for the common ancestor + while(true) { + bool all_same = true; + if ( ancestors[0].size() == 0 ) { + //This list is over, so the last top is the common ancestor + //break out of the loop + break; + } + NiNodeRef first_ancestor = ancestors[0].front(); + for ( int i = 1; i < num_lists; ++i ) { + if ( ancestors[i].size() == 0 ) { //This list is over, so the last top is the common ancestor //break out of the loop + all_same = false; break; } - NiNodeRef first_ancestor = ancestors[0].front(); - for ( int i = 1; i < num_lists; ++i ) { - if ( ancestors[i].size() == 0 ) { - //This list is over, so the last top is the common ancestor - //break out of the loop - all_same = false; - break; - } - if ( ancestors[i].front() != first_ancestor ) { - all_same = false; - } + if ( ancestors[i].front() != first_ancestor ) { + all_same = false; } + } - if ( all_same == true ) { - //They're all the same, so set the top, pop all the stacks - //and look again - - skeleton_root = ancestors[0].front(); - for ( int i = 0; i < num_lists; ++i ) { - ancestors[i].pop_front(); - } - } else { - //One is different, so the last top is the common ancestor. - //break out of the loop - break; + if ( all_same == true ) { + //They're all the same, so set the top, pop all the stacks + //and look again + + skeleton_root = ancestors[0].front(); + for ( int i = 0; i < num_lists; ++i ) { + ancestors[i].pop_front(); } + } else { + //One is different, so the last top is the common ancestor. + //break out of the loop + break; } } @@ -179,6 +166,15 @@ void NiGeometry::BindSkin( vector< Ref<NiNode> > bone_nodes, bool bind_to_scene throw runtime_error("Failed to find suitable skeleton root."); } + //Ancestors[bone_nodes.size()] should now hold all nodes between (but not including) the + //skeleton root and this mesh. Cycle through and propogate their transforms + for ( list<NiNodeRef>::iterator it = ancestors[bone_nodes.size()].begin(); it != ancestors[bone_nodes.size()].end(); ++it ) { + (*it)->PropagateTransform(); + } + + //Now apply the transforms to the vertices and normals of this mesh + this->ApplyTransforms(); + //Create a skin instance using the bone and root data skinInstance = new NiSkinInstance( skeleton_root, bone_nodes ); @@ -287,6 +283,20 @@ void NiGeometry::GetSkinDeformation( vector<Vector3> & vertices, vector<Vector3> } } +void NiGeometry::ApplyTransforms() { + //Get Data + NiGeometryDataRef geom_data = this->GetData(); + if ( geom_data == NULL ) { + throw runtime_error( "Called ApplyTransform on a NiGeometry object that has no NiGeometryData attached."); + } + + //Transform the vertices by the local transform of this mesh + geom_data->Transform( this->GetLocalTransform() ); + + //Now that the transforms have been applied, clear them to the identity + this->SetLocalTransform( Matrix44::IDENTITY ); +} + // Calculate bounding sphere using minimum-volume axis-align bounding box. Its fast but not a very good fit. static void CalcAxisAlignedBox(const vector<SkinWeight> & n, const vector<Vector3>& vertices, Vector3& center, float& radius) { diff --git a/src/obj/NiGeometryData.cpp b/src/obj/NiGeometryData.cpp index 82c58494863b0b876c773e5ef1b7170fd526b804..b2992d005c1f18d753a5aac5b89523c10e5814eb 100644 --- a/src/obj/NiGeometryData.cpp +++ b/src/obj/NiGeometryData.cpp @@ -119,17 +119,22 @@ void NiGeometryData::SetUVSet( int index, const vector<TexCoord> & in ) { uvSets[index] = in; } -/*! Returns the 3D center of the mesh. - * \return The center of this mesh. - */ Vector3 NiGeometryData::GetCenter() const { return center; } -/*! Returns the radius of the mesh. That is the distance from the center to - * the farthest point from the center. - * \return The radius of this mesh. - */ float NiGeometryData::GetRadius() const { return radius; } + +void NiGeometryData::Transform( Matrix44 & transform ) { + Matrix44 rotation = Matrix44( transform.GetRotation() ); + + //Apply the transformations + for ( uint i = 0; i < vertices.size(); ++i ) { + vertices[i] = transform * vertices[i]; + } + for ( uint i = 0; i < normals.size(); ++i ) { + normals[i] = rotation * normals[i]; + } +} \ No newline at end of file diff --git a/src/obj/NiNode.cpp b/src/obj/NiNode.cpp index 82768908a14c27572649a4ab00198dc00d4ed42d..a1c92d8e88ef40109baef1fc0dc79fc08b80fe6f 100644 --- a/src/obj/NiNode.cpp +++ b/src/obj/NiNode.cpp @@ -213,6 +213,21 @@ void NiNode::GoToSkeletonBindPosition() { } } +void NiNode::PropagateTransform() { + + Matrix44 par_trans = this->GetLocalTransform(); + + //Loop through each child and apply this node's transform to it + for ( unsigned i = 0; i < children.size(); ++i ) { + children[i]->SetLocalTransform( + children[i]->GetLocalTransform() * par_trans + ); + } + + //Nowthat the transforms have been propogated, clear them out + this->SetLocalTransform( Matrix44::IDENTITY ); +} + bool NiNode::IsSplitMeshProxy() const { //Let us guess that a node is a split mesh proxy if: // 1) All its children are NiTriBasedGeom derived objects. diff --git a/src/obj/NiTriShapeData.cpp b/src/obj/NiTriShapeData.cpp index b67fad8be458cbac4aa463526f45e06603a9de58..b8464bc55f8c6ace304d40126d5a7edbe052da9a 100644 --- a/src/obj/NiTriShapeData.cpp +++ b/src/obj/NiTriShapeData.cpp @@ -78,7 +78,10 @@ void NiTriShapeData::SetTriangles( const vector<Triangle> & in ) { hasTriangles = ( triangles.size() != 0 ); + //Set nuber of triangles + numTriangles = uint(triangles.size()); + //Set number of trianble points to the number of triangles times 3 - numTrianglePoints = uint(triangles.size()) * 3; + numTrianglePoints = numTriangles * 3; }