diff --git a/include/obj/NiTriBasedGeom.h b/include/obj/NiTriBasedGeom.h index 66c543e9206a929ddc3b8422a3496593be4b82a8..350d94a60582da80285223f5ce00183bb7bfa859 100644 --- a/include/obj/NiTriBasedGeom.h +++ b/include/obj/NiTriBasedGeom.h @@ -71,6 +71,12 @@ public: vector<Vector3> GetSkinInfluencedVertices() const; + /*! + * Generate or update a NiStringExtraData block with precalculated + * tangent and binormal data (Oblivion specific) + */ + void UpdateTangentSpace(); + protected: list< Ref<NiNode> > NiTriBasedGeom::ListAncestors( const Ref<NiNode> & leaf ) const; diff --git a/src/obj/NiTriBasedGeom.cpp b/src/obj/NiTriBasedGeom.cpp index be4ab01aaa182afd33653486db5efac1072fa28f..5608f8fc565f7da6628eaefacfe9b91e0273eea8 100644 --- a/src/obj/NiTriBasedGeom.cpp +++ b/src/obj/NiTriBasedGeom.cpp @@ -7,6 +7,8 @@ All rights reserved. Please see niflib.h for licence. */ #include "../../include/obj/NiObject.h" #include "../../include/obj/NiSkinData.h" #include "../../include/obj/NiSkinPartition.h" +#include "../../include/obj/NiExtraData.h" +#include "../../include/obj/NiBinaryExtraData.h" using namespace Niflib; //Definition of TYPE constant @@ -387,4 +389,158 @@ void NiTriBasedGeom::GenHardwareSkinInfo( int max_bones_per_partition /*= 4*/, i skinData->SetSkinPartition( skinPart ); } } -} \ No newline at end of file +} + +void NiTriBasedGeom::UpdateTangentSpace() { + /* No data, no tangent space */ + if(this->data == NULL) { + return; + } + + vector<Vector3> verts = this->data->GetVertices(); + vector<Vector3> norms = this->data->GetNormals(); + vector<TexCoord> uvs = this->data->GetUVSet(0); + vector<Color4> colors = this->data->GetColors(); + vector<Triangle> tris = this->data->GetTriangles(); + + /* check for data validity */ + if( + verts.empty() || + verts.size() != norms.size() || + verts.size() != uvs.size() || + tris.empty() + ) + { + return; + } + + vector<Vector3> tangents(verts.size()); + vector<Vector3> binormals(verts.size()); + + int dups = 0; + + multimap<int, int> vmap; + + for(int t = 0; t < (int)tris.size(); t++) { + Triangle & tri = tris[t]; + + int i1 = tri[0]; + int i2 = tri[1]; + int i3 = tri[2]; + + const Vector3 v1 = verts[i1]; + const Vector3 v2 = verts[i2]; + const Vector3 v3 = verts[i3]; + + const TexCoord w1 = uvs[i1]; + const TexCoord w2 = uvs[i2]; + const TexCoord w3 = uvs[i3]; + + Vector3 v2v1 = v2 - v1; + Vector3 v3v1 = v3 - v1; + + TexCoord w2w1(w2.u - w1.u, w2.v - w1.v); + TexCoord w3w1(w3.u - w1.u, w3.v - w1.v); + + float r = w2w1.u * w3w1.v - w3w1.u * w2w1.v; + + if ( abs( r ) <= 10e-5 ){ + continue; + } + + r = 1.0f / r; + + Vector3 sdir( + ( w3w1.v * v2v1.x - w2w1.v * v3v1.x ) * r, + ( w3w1.v * v2v1.y - w2w1.v * v3v1.y ) * r, + ( w3w1.v * v2v1.z - w2w1.v * v3v1.z ) * r + ); + + Vector3 tdir( + ( w2w1.u * v3v1.x - w3w1.u * v2v1.x ) * r, + ( w2w1.u * v3v1.y - w3w1.u * v2v1.y ) * r, + ( w2w1.u * v3v1.z - w3w1.u * v2v1.z ) * r + ); + + for ( int j = 0; j < 3; j++ ) + { // no duplication, just smoothing + int i = tri[j]; + + tangents[i] += sdir.Normalized(); + binormals[i] += tdir.Normalized(); + } + } + + for ( int i = 0; i < (int)verts.size(); i++ ) + { // for each vertex calculate tangent and binormal + const Vector3 & n = norms[i]; + + Vector3 & t = tangents[i]; + Vector3 & b = binormals[i]; + + if ( t == Vector3() || b == Vector3() ) + { + t.x = n.y; + t.y = n.z; + t.z = n.x; + b = n.CrossProduct(t); + } + + else + { + t = ( t - n * n.DotProduct(t) ); + t = t.Normalized(); + b = ( b - n * n.DotProduct(b) ); + b = b.Normalized(); + } + } + + // generate the byte data + int vCount = (int)verts.size(); + int fSize = sizeof(float[3]); + vector<byte> binData(2 * vCount * fSize); + + for(int i = 0; i < (int)verts.size(); i++) { + float tan_xyz[3], bin_xyz[3]; + + tan_xyz[0] = tangents[i].x; + tan_xyz[1] = tangents[i].y; + tan_xyz[2] = tangents[i].z; + + bin_xyz[0] = binormals[i].x; + bin_xyz[1] = binormals[i].y; + bin_xyz[2] = binormals[i].z; + + char * tan_Bytes = (char *) tan_xyz; + char * bin_Bytes = (char *) bin_xyz; + + for(int j = 0; j < fSize; j++) + { + binData[ i * fSize + j] = tan_Bytes[j]; + binData[(i + vCount) * fSize + j] = bin_Bytes[j]; + } + } + + // update or create the tangent space extra data + NiBinaryExtraDataRef TSpaceRef; + + std::list<NiExtraDataRef> props = this->GetExtraData(); + std::list<NiExtraDataRef>::iterator prop; + + for(prop = props.begin(); + prop != props.end(); + prop++) + { + if((*prop)->GetName() == "Tangent space (binormal & tangent vectors)") { + TSpaceRef = DynamicCast<NiBinaryExtraData>(*prop); + break; + } + } + + if(TSpaceRef == NULL) { + TSpaceRef = new NiBinaryExtraData(); + this->AddExtraData(DynamicCast<NiExtraData>(TSpaceRef)); + } + + TSpaceRef->SetData(binData); +}