diff --git a/gen/SkinData.cpp b/gen/SkinData.cpp index 540d1b34e6e5e165328c8db3734c6e7d76e75c53..07ca9a475778f2eb595278cfb30f30e672122d56 100644 --- a/gen/SkinData.cpp +++ b/gen/SkinData.cpp @@ -6,7 +6,7 @@ All rights reserved. Please see niflib.h for licence. */ using namespace Niflib; //Constructor -SkinData::SkinData() : scale(0.0f), numVertices((ushort)0) {}; +SkinData::SkinData() : scale(0.0f), boundingSphereRadius(0.0f), numVertices((ushort)0) {}; //Destructor SkinData::~SkinData() {}; diff --git a/gen/SkinData.h b/gen/SkinData.h index f33f32b2182f6e7c4f838127592e7dae96e88c05..bf55b03b0d24b870d2ab280156b4ccb723645a56 100644 --- a/gen/SkinData.h +++ b/gen/SkinData.h @@ -33,11 +33,15 @@ struct NIFLIB_API SkinData { */ float scale; /*! - * This has been verified not to be a normalized quaternion. They may or - * may not be related to each other so their specification as an array of - * 4 floats may be misleading. + * Translation offset of a bounding sphere holding all vertices. (Note + * that its a Sphere Containing Axis Aligned Box not a minimum volume + * Sphere) */ - array<float,4> unknown4Floats; + Vector3 boundingSphereOffset; + /*! + * Radius for bounding sphere holding all vertices. + */ + float boundingSphereRadius; /*! * Number of weighted vertices. */ diff --git a/gen/obj_impl.cpp b/gen/obj_impl.cpp index a6d907e3c68e85f52165ca1b969d6f5ccd0a1bd0..cd507bb0f8e14b38bcc149f347896da295249782 100644 --- a/gen/obj_impl.cpp +++ b/gen/obj_impl.cpp @@ -10442,9 +10442,8 @@ void NiSkinData::InternalRead( istream& in, list<uint> & link_stack, unsigned in NifStream( boneList[i1].rotation, in, version ); NifStream( boneList[i1].translation, in, version ); NifStream( boneList[i1].scale, in, version ); - for (uint i2 = 0; i2 < 4; i2++) { - NifStream( boneList[i1].unknown4Floats[i2], in, version ); - }; + NifStream( boneList[i1].boundingSphereOffset, in, version ); + NifStream( boneList[i1].boundingSphereRadius, in, version ); NifStream( boneList[i1].numVertices, in, version ); boneList[i1].vertexWeights.resize(boneList[i1].numVertices); for (uint i2 = 0; i2 < boneList[i1].vertexWeights.size(); i2++) { @@ -10475,9 +10474,8 @@ void NiSkinData::InternalWrite( ostream& out, map<NiObjectRef,uint> link_map, un NifStream( boneList[i1].rotation, out, version ); NifStream( boneList[i1].translation, out, version ); NifStream( boneList[i1].scale, out, version ); - for (uint i2 = 0; i2 < 4; i2++) { - NifStream( boneList[i1].unknown4Floats[i2], out, version ); - }; + NifStream( boneList[i1].boundingSphereOffset, out, version ); + NifStream( boneList[i1].boundingSphereRadius, out, version ); NifStream( boneList[i1].numVertices, out, version ); for (uint i2 = 0; i2 < boneList[i1].vertexWeights.size(); i2++) { NifStream( boneList[i1].vertexWeights[i2].index, out, version ); @@ -10501,13 +10499,8 @@ std::string NiSkinData::InternalAsString( bool verbose ) const { out << " Rotation: " << boneList[i1].rotation << endl; out << " Translation: " << boneList[i1].translation << endl; out << " Scale: " << boneList[i1].scale << endl; - for (uint i2 = 0; i2 < 4; i2++) { - if ( !verbose && ( i2 > MAXARRAYDUMP ) ) { - out << "<Data Truncated. Use verbose mode to see complete listing.>" << endl; - break; - }; - out << " Unknown 4 Floats[" << i2 << "]: " << boneList[i1].unknown4Floats[i2] << endl; - }; + out << " Bounding Sphere Offset: " << boneList[i1].boundingSphereOffset << endl; + out << " Bounding Sphere Radius: " << boneList[i1].boundingSphereRadius << endl; out << " Num Vertices: " << boneList[i1].numVertices << endl; for (uint i2 = 0; i2 < boneList[i1].vertexWeights.size(); i2++) { out << " Index: " << boneList[i1].vertexWeights[i2].index << endl; diff --git a/niflib.h b/niflib.h index 3fca3a49799d756b5aa9222e2421dc1c2403b7b3..79784c8540fdeaddcec59b752859d67318f77cbd 100644 --- a/niflib.h +++ b/niflib.h @@ -264,6 +264,27 @@ NIFLIB_API void SendNifTreeToBindPos( const Ref<NiNode> & root ); */ NIFLIB_API Ref<NiObject> CreateBlock( string block_type ); + +/*! +* Creates a new block of the given type and returns a reference to it +* \param T – The type of block you want to create. Ex. NiNode, NiTriShapeData, NiParticleSystemController, etc. +* \return This function will return a newly created block of the requested type. +* +* <b>Example:</b> +* \code +* NiNodeRef my_block = CreateNiObject<NiNode>(); +* \endcode +* +* sa BlocksInMemory +*/ +#ifndef SWIG +template<typename T> +inline Ref<T> CreateNiObject() { + return DynamicCast<T>(CreateBlock(T::TypeConst().GetTypeName())); +} +#endif + + /*! * Returns whether the requested version is supported. * \param version The version of the nif format to test for availablity. diff --git a/obj/NiAVObject.cpp b/obj/NiAVObject.cpp index 409e7002670e05d3f2aacdcc0474fe0f7960815c..c7d47443bd1d71178536663ae58f17fb7aa77e45 100644 --- a/obj/NiAVObject.cpp +++ b/obj/NiAVObject.cpp @@ -168,6 +168,24 @@ bool NiAVObject::GetHidden() return (bool)NIFLIB_GET_FLAG(flags, 0, 0x01); } +Ref<NiCollisionData > NiAVObject::GetCollisionData() const { + return collisionData; +} + +void NiAVObject::SetCollisionData( Ref<NiCollisionData > value ) { + collisionData = value; +} + + +ef<NiCollisionObject > NiAVObject::GetCollisionObject() const { + + return collisionObject; +} + +void NiAVObject::SetCollisionObject( Ref<NiCollisionObject > value ) { + collisionObject = value; +} + void NiAVObject::SetHidden(bool value) { flags = NIFLIB_MASK_FLAG(flags, value, 0, 0x01); diff --git a/obj/NiAVObject.h b/obj/NiAVObject.h index 55e37a30e9dad44fd25e8c0e2476e778193405c7..159311d73c1d85cc53b0cfd3c5e0fbc19133c65a 100644 --- a/obj/NiAVObject.h +++ b/obj/NiAVObject.h @@ -102,6 +102,12 @@ public: Ref<NiCollisionObject > GetCollisionObject() const; void SetCollisionObject( Ref<NiCollisionObject> value ); + /*! + * Bounding box: refers to NiCollisionData + */ + Ref<NiCollisionData > GetCollisionData() const; + void SetCollisionData( Ref<NiCollisionData> value ); + bool GetHidden(); void SetHidden(bool value); @@ -113,6 +119,7 @@ public: CollisionType GetCollision(); void SetCollsion(CollisionType value); + protected: NiNode * parent; NI_A_V_OBJECT_MEMBERS diff --git a/obj/NiSkinData.cpp b/obj/NiSkinData.cpp index 0bf29e75b5f9c06f00b06c89861a3f1c4f46a6e3..877b4fc2be3e9cf7b0715feba501efe8bd5d06b4 100644 --- a/obj/NiSkinData.cpp +++ b/obj/NiSkinData.cpp @@ -67,10 +67,8 @@ void NiSkinData::SetBoneWeights( uint bone_index, const vector<SkinWeight> & n, } boneList[bone_index].vertexWeights = n; - boneList[bone_index].unknown4Floats[0] = center.x; - boneList[bone_index].unknown4Floats[1] = center.y; - boneList[bone_index].unknown4Floats[2] = center.z; - boneList[bone_index].unknown4Floats[3] = radius; + boneList[bone_index].boundingSphereOffset = center; + boneList[bone_index].boundingSphereRadius = radius; } Matrix44 NiSkinData::GetOverallTransform() const { @@ -121,4 +119,4 @@ NiSkinData::NiSkinData( const Ref<NiTriBasedGeom> & owner ) { //Store result res_mat.Decompose( boneList[i].translation, boneList[i].rotation, boneList[i].scale ); } -} \ No newline at end of file +} diff --git a/obj/NiSkinData.h b/obj/NiSkinData.h index 9d7cc5ff809d383f49828b97e18c8ece009da57a..101ead61a12550e28259b5784e6efc8502f2a4d8 100644 --- a/obj/NiSkinData.h +++ b/obj/NiSkinData.h @@ -51,7 +51,7 @@ public: uint GetBoneCount() const; Matrix44 GetBoneTransform( uint bone_index ) const; vector<SkinWeight> GetBoneWeights( uint bone_index ) const; - void SetBoneWeights( uint bone_index, const vector<SkinWeight> & n, Vector3 center, float radius ); + void SetBoneWeights( uint bone_index, const vector<SkinWeight> & n, Vector3 center, float radius ); protected: NI_SKIN_DATA_MEMBERS diff --git a/obj/NiSkinInstance.cpp b/obj/NiSkinInstance.cpp index 8b8f88578666269a29ddd6adec17d88e2283858c..62a326cf956b9dbe8d37a795088fe12164816503 100644 --- a/obj/NiSkinInstance.cpp +++ b/obj/NiSkinInstance.cpp @@ -126,4 +126,4 @@ uint NiSkinInstance::GetBoneCount() const { Ref<NiNode> NiSkinInstance::GetSkeletonRoot() const { return skeletonRoot; -} \ No newline at end of file +} diff --git a/obj/NiTriBasedGeom.cpp b/obj/NiTriBasedGeom.cpp index 6274fc4c9685a920987dd981b548a443d190d789..a5b75d1bc0e11a178a9cac964f23ec780d73859f 100644 --- a/obj/NiTriBasedGeom.cpp +++ b/obj/NiTriBasedGeom.cpp @@ -261,6 +261,65 @@ vector<Vector3> NiTriBasedGeom::GetSkinInfluencedVertices() const { return skin_verts; } +// 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) +{ + //--Calculate center & radius--// + + //Set lows and highs to first vertex + Vector3 lows = vertices[ n[0].index ]; + Vector3 highs = vertices[ n[0].index ]; + + //Iterate through the vertices, adjusting the stored values + //if a vertex with lower or higher values is found + for ( unsigned int i = 0; i < n.size(); ++i ) { + const Vector3 & v = vertices[ n[i].index ]; + + if ( v.x > highs.x ) highs.x = v.x; + else if ( v.x < lows.x ) lows.x = v.x; + + if ( v.y > highs.y ) highs.y = v.y; + else if ( v.y < lows.y ) lows.y = v.y; + + if ( v.z > highs.z ) highs.z = v.z; + else if ( v.z < lows.z ) lows.z = v.z; + } + + //Now we know the extent of the shape, so the center will be the average + //of the lows and highs + center = (highs + lows) / 2.0f; + + //The radius will be the largest distance from the center + Vector3 diff; + float dist2(0.0f), maxdist2(0.0f); + for ( unsigned int i = 0; i < n.size(); ++i ) { + const Vector3 & v = vertices[ n[i].index ]; + + diff = center - v; + dist2 = diff.x * diff.x + diff.y * diff.y + diff.z * diff.z; + if ( dist2 > maxdist2 ) maxdist2 = dist2; + }; + radius = sqrt(maxdist2); +} + +// Calculate bounding sphere using average position of the points. Better fit but slower. +static void CalcCenteredSphere(const vector<SkinWeight> & n, const vector<Vector3>& vertices, Vector3& center, float& radius) +{ + size_t nv = n.size(); + Vector3 sum; + for (size_t i=0; i<nv; ++i) + sum += vertices[ n[i].index ]; + center = sum / float(nv); + radius = 0.0f; + for (size_t i=0; i<nv; ++i){ + Vector3 diff = vertices[ n[i].index ] - center; + float mag = diff.Magnitude(); + radius = max(radius, mag); + } +} + + + void NiTriBasedGeom::SetBoneWeights( uint bone_index, const vector<SkinWeight> & n ) { if ( n.size() == 0 ) { @@ -288,42 +347,9 @@ void NiTriBasedGeom::SetBoneWeights( uint bone_index, const vector<SkinWeight> & //Get vertex array vector<Vector3> vertices = geomData->GetVertices(); - //--Calculate center & radius--// - - //Set lows and highs to first vertex - Vector3 lows = vertices[ n[0].index ]; - Vector3 highs = vertices[ n[0].index ]; - - //Iterate through the vertices, adjusting the stored values - //if a vertex with lower or higher values is found - for ( unsigned int i = 0; i < n.size(); ++i ) { - const Vector3 & v = vertices[ n[i].index ]; - - if ( v.x > highs.x ) highs.x = v.x; - else if ( v.x < lows.x ) lows.x = v.x; - - if ( v.y > highs.y ) highs.y = v.y; - else if ( v.y < lows.y ) lows.y = v.y; - - if ( v.z > highs.z ) highs.z = v.z; - else if ( v.z < lows.z ) lows.z = v.z; - } - - //Now we know the extent of the shape, so the center will be the average - //of the lows and highs - Vector3 center = highs + lows / 2.0f; - - //The radius will be the largest distance from the center - Vector3 diff; - float dist2(0.0f), maxdist2(0.0f); - for ( unsigned int i = 0; i < n.size(); ++i ) { - const Vector3 & v = vertices[ n[i].index ]; - - diff = center - v; - dist2 = diff.x * diff.x + diff.y * diff.y + diff.z * diff.z; - if ( dist2 > maxdist2 ) maxdist2 = dist2; - }; - float radius = sqrt(maxdist2); + Vector3 center; float radius; + //CalcCenteredSphere(n, vertices, center, radius); + CalcAxisAlignedBox(n, vertices, center, radius); //Translate center by bone matrix center = skinData->GetBoneTransform( bone_index ) * center;