From a0c83456d94b32a510a5013193a5ef8dde5c2687 Mon Sep 17 00:00:00 2001 From: Shon Ferguson <shonferg@users.sourceforge.net> Date: Thu, 17 Nov 2005 06:42:31 +0000 Subject: [PATCH] NiSkinPartition and NiLODNode are now read fully and can be examined by calling the asString function or written back out to a file unchanged. NiTriStripsData has been given a public interface, ITriStripsData, so that the triangle strips it contains can be extracted. --- NIF_Blocks.cpp | 747 +++++++++++++++++++++++++++++++++++-------------- NIF_Blocks.h | 95 +++++-- NIF_IO.cpp | 66 +++++ NIF_IO.h | 97 ++++++- nif_attrs.h | 95 ++++++- niflib.cpp | 9 + niflib.h | 30 +- pyniflib.i | 1 + 8 files changed, 891 insertions(+), 249 deletions(-) diff --git a/NIF_Blocks.cpp b/NIF_Blocks.cpp index 2441fc5a..192c02de 100644 --- a/NIF_Blocks.cpp +++ b/NIF_Blocks.cpp @@ -116,6 +116,8 @@ void ABlock::AddAttr( AttrTypes type, string name, unsigned int first_ver, unsig attr = new SkeletonRootAttr( name, this, first_ver, last_ver ); } else if ( type == attr_particlegroup ) { attr = new ParticleGroupAttr( name, this, first_ver, last_ver ); + } else if ( type == attr_lodrangegroup ) { + attr = new LODRangeGroupAttr( name, this, first_ver, last_ver ); } else { cout << type << endl; throw runtime_error("Unknown attribute type requested."); @@ -159,7 +161,9 @@ void ABlock::Read( ifstream& in, unsigned int version ) { //Read Attributes for (unsigned int i = 0; i < _attr_vect.size(); ++i ) { _attr_vect[i]->Read( in, version ); - //cout << " " << _attr_vect[i]->GetName() << ": " << _attr_vect[i]->asString() << endl; + //if ( _attr_vect[i]->GetType() != "bones" ) { + // cout << " " << _attr_vect[i]->GetName() << ": " << _attr_vect[i]->asString() << endl; + //} } } @@ -1001,16 +1005,24 @@ void NiTriShapeData::Read( ifstream& in, unsigned int version ){ AShapeData::Read( in, version ); short numTriangles = ReadUShort( in ); - if (numTriangles > 0) { - int numVertexIndices = ReadUInt( in ); - triangles.resize( numTriangles ); - for ( int i = 0; i < numTriangles; ++i ){ - triangles[i].v1 = ReadUShort( in ); - triangles[i].v2 = ReadUShort( in ); - triangles[i].v3 = ReadUShort( in ); + int numVertexIndices = ReadUInt( in ); + + //From version 10.1.0.0 on there is a bool to check whether or not there are any triangles + //We already know the answer to this from the numTriangles count, don't we? + //Jus in case, set numTriangles to zero if this is false. + if ( version >= VER_10_1_0_0 ) { + if ( ReadBool( in, version ) == false ) { + numTriangles = 0; } } + triangles.resize( numTriangles ); + for ( uint i = 0; i < triangles.size(); ++i ){ + triangles[i].v1 = ReadUShort( in ); + triangles[i].v2 = ReadUShort( in ); + triangles[i].v3 = ReadUShort( in ); + } + short matchGroupCount = ReadUShort( in ); match_group_mode = ( matchGroupCount != 0 ); // Only record whether or not file prefers to have match data generated @@ -1062,16 +1074,17 @@ void NiTriShapeData::Write( ofstream& out, unsigned int version ){ AShapeData::Write( out, version ); WriteUShort( ushort(triangles.size()), out ); + WriteUInt( ushort(triangles.size()) * 3, out ); + + //From version 10.1.0.0 on there is a bool to check whether or not there are any triangles + if ( version >= VER_10_1_0_0 ) { + WriteBool( triangles.size() > 0, out, version ); + } - if ( triangles.size() > 0 ) { - //Write number of shorts: triCount * 3 - WriteUInt( ushort(triangles.size()) * 3, out ); - - for ( uint i = 0; i < triangles.size(); ++i ){ - WriteUShort( triangles[i].v1, out ); - WriteUShort( triangles[i].v2, out ); - WriteUShort( triangles[i].v3, out ); - } + for ( uint i = 0; i < triangles.size(); ++i ){ + WriteUShort( triangles[i].v1, out ); + WriteUShort( triangles[i].v2, out ); + WriteUShort( triangles[i].v3, out ); } if ( match_group_mode ) { @@ -1138,6 +1151,15 @@ void NiTriStripsData::Read( ifstream& in, unsigned int version ){ strips[i].resize( stripSize ); } + //From version 10.1.0.0 on there is a bool to check whether or not there are any points + //We already know the answer to this from the counts above, don't we? + //Jus in case, clear all strips if this is false. + if ( version >= VER_10_1_0_0 ) { + if ( ReadBool( in, version ) == false ) { + strips.clear(); + } + } + //Read points for ( uint i = 0; i < strips.size(); ++i ) { for ( uint j = 0; j < strips[i].size(); ++j ) { @@ -1150,14 +1172,8 @@ void NiTriStripsData::Write( ofstream& out, unsigned int version ){ AShapeData::Write( out, version ); - //Calculate number of triangles - //Sum of length of each strip - 2 - short numTriangles = 0; - for ( uint i = 0; i < strips.size(); ++i ) { - numTriangles += short(strips[i].size() - 2); - } - //Write number of triangles and strips + short numTriangles = GetTriangleCount(); WriteUShort( numTriangles, out ); WriteUShort( ushort(strips.size()), out ); @@ -1166,6 +1182,11 @@ void NiTriStripsData::Write( ofstream& out, unsigned int version ){ WriteUShort( ushort(strips[i].size()), out ); } + //From version 10.1.0.0 on there is a bool to check whether or not there are any points + if ( version >= VER_10_1_0_0 ) { + WriteBool( numTriangles > 0, out, version ); + } + //Write points for ( uint i = 0; i < strips.size(); ++i ) { for ( uint j = 0; j < strips[i].size(); ++j ) { @@ -1181,14 +1202,7 @@ string NiTriStripsData::asString() { out << AShapeData::asString(); - //Calculate number of triangles - //Sum of length of each strip - 2 - short numTriangles = 0; - for ( uint i = 0; i < strips.size(); ++i ) { - numTriangles += short(strips[i].size() - 2); - } - - out << "Triangles: " << numTriangles << endl + out << "Triangles: " << GetTriangleCount() << endl << "Strips: " << ushort(strips.size()) << endl; if (verbose) { @@ -1205,6 +1219,163 @@ string NiTriStripsData::asString() { return out.str(); } +void * NiTriStripsData::QueryInterface( int id ) { + // Contains TriShapeData Interface + if ( id == ID_TRI_STRIPS_DATA ) { + return (void*)static_cast<ITriStripsData*>(this); + } else { + return AShapeData::QueryInterface( id ); + } +} + +short NiTriStripsData::GetStripCount() { + return short(strips.size()); +} + +void NiTriStripsData::SetStripCount(int n) { + strips.resize( n ); +} + +//Getters +vector<short> NiTriStripsData::GetStrip( int index ) { + return strips[index]; +} + +vector<Triangle> NiTriStripsData::GetTriangles() { + + //Create a vector to hold the triangles + vector<Triangle> triangles( GetTriangleCount() ); + int n = 0; // Current triangle + + //Cycle through all strips + vector< vector<short> >::iterator it; + for (it = strips.begin(); it != strips.end(); ++it ) { + //The first three values in the strip are the first triangle + triangles[n].Set( (*it)[0], (*it)[1], (*it)[2] ); + + //Move to the next triangle + ++n; + + //The remaining triangles use the previous two indices as their first two indices. + for( uint i = 3; i < it->size(); ++i ) { + //Odd numbered triangles need to be reversed to keep the vertices in counter-clockwise order + if ( i % 2 == 0 ) + triangles[n].Set( (*it)[i - 2], (*it)[i - 1], (*it)[i] ); + else + triangles[n].Set( (*it)[i], (*it)[i - 1], (*it)[i - 2] ); + + //Move to the next triangle + ++n; + } + } + + return triangles; +} + +//Setter +void NiTriStripsData::SetStrip( int index, const vector<short> & in ) { + strips[index] = in; +} + +short NiTriStripsData::GetTriangleCount() { + + //Calculate number of triangles + //Sum of length of each strip - 2 + short numTriangles = 0; + for ( uint i = 0; i < strips.size(); ++i ) { + numTriangles += short(strips[i].size() - 2); + } + + return numTriangles; +} + + + +/*********************************************************** + * NiCollisionData methods + **********************************************************/ + +void NiCollisionData::Read( ifstream& in, unsigned int version ){ + //Read parent node but don't store it + ReadUInt( in ); + + unknownInt1 = ReadUInt( in ); + unknownByte = ReadByte( in ); + collisionType = ReadUInt( in ); + + if ( collisionType == 0 ) { + unknownInt2 = ReadUInt( in ); + ReadFVector3( unknown3Floats, in ); + } + else if ( collisionType == 1 ) { + for (int i = 0; i < 15; ++i ) { + unknown15Floats[i] = ReadFloat( in ); + } + } + else if ( collisionType == 2) { + for ( int i = 0; i < 8; ++i ) { + unknown8Floats[i] = ReadFloat( in ); + } + } +} + +void NiCollisionData::Write( ofstream& out, unsigned int version ){ + + //Write Parent node number + WriteUInt( GetParent().get_index(), out ); + + WriteUInt( unknownInt1, out ); + WriteByte( unknownByte, out ); + WriteUInt( collisionType, out ); + + if ( collisionType == 0 ) { + WriteUInt( unknownInt2, out ); + WriteFVector3( unknown3Floats, out ); + } + else if ( collisionType == 1 ) { + for (int i = 0; i < 15; ++i ) { + WriteFloat( unknown15Floats[i], out ); + } + } + else if ( collisionType == 2) { + for ( int i = 0; i < 8; ++i ) { + WriteFloat( unknown8Floats[i], out ); + } + } +} + +string NiCollisionData::asString() { + stringstream out; + out.setf(ios::fixed, ios::floatfield); + out << setprecision(1); + + //Parent is already written, so don't do anything with it + + out << "Unknown Int 1: " << unknownInt1 << endl + << "Unknown Byte: " << unknownByte << endl + << "Collision Type: " << collisionType << endl + << "Collision Data:" << endl; + + if ( collisionType == 0 ) { + out << " Unknown Int 2: " << unknownInt2 << endl + << " Unknown 3 Floats: " << unknown3Floats << endl; + } + else if ( collisionType == 1 ) { + out << " Unknown 15 Floats:" << endl; + for (int i = 0; i < 15; ++i ) { + out << " " << i + 1 << ": " << unknown15Floats[i] << endl; + } + } + else if ( collisionType == 2) { + out << " Unknown 8 Floats:" << endl; + for ( int i = 0; i < 8; ++i ) { + out << " " << i + 1 << ": " << unknown8Floats[i] << endl; + } + } + + return out.str(); +} + /*********************************************************** * NiSkinData methods **********************************************************/ @@ -1219,7 +1390,7 @@ void NiSkinData::Read( ifstream& in, unsigned int version ) { ReadFVector3( translation, in ); scale = ReadFloat( in ); int boneCount = ReadUInt( in ); - unknownInt = ReadUInt( in ); + GetAttr("Skin Partition")->Read( in, version ); //unknownByte exists from version 4.2.1.0 on if ( version >= VER_4_2_1_0 ) { unknownByte = ReadByte( in ); @@ -1256,7 +1427,7 @@ void NiSkinData::Write( ofstream& out, unsigned int version ) { WriteFVector3( translation, out ); WriteFloat( scale, out ); WriteUInt(short(bone_map.size()), out); - WriteUInt(unknownInt, out); + GetAttr("Skin Partition")->Write( out, version ); //unknownByte exists from version 4.2.1.0 on if ( version >= VER_4_2_1_0) { WriteByte( unknownByte, out ); @@ -1340,8 +1511,8 @@ string NiSkinData::asString() { << "Translate: " << translation << endl << "Scale: " << scale << endl << "Bone Count: " << uint(bone_map.size()) << endl - << "Unknown Index: " << unknownInt << endl - << "Unknown Byte: " << unknownByte << endl + << "Skin Partition: " << GetAttr("Skin Partition")->asLink() << endl + << "Unknown Byte: " << int(unknownByte) << endl << "Bones:" << endl; map<IBlock*, Bone>::iterator it; @@ -1732,191 +1903,110 @@ string NiGeomMorpherController::asString() { * NiKeyframeData methods **********************************************************/ -void NiKeyframeData::Read( ifstream& in, unsigned int version ) { +void NiKeyframeData::Read( ifstream& file, unsigned int version ) { - scaleType = rotationType = translationType = KeyType(0); + scaleType = rotationType = translationType = xyzTypes[0] = xyzTypes[1] = xyzTypes[2] = KeyType(0); //--Rotation--// - uint numRotations = ReadUInt( in ); + uint numRotations = ReadUInt( file ); if (numRotations > 0) { - rotationType = KeyType(ReadUInt( in )); - - rotKeys.resize( numRotations ); - for ( unsigned int i = 0; i < numRotations; ++i ) { - rotKeys[i].time = ReadFloat( in ); + NifStream( rotationType, file ); - if (rotationType != 4) { - rotKeys[i].data.w = ReadFloat( in ); - rotKeys[i].data.x = ReadFloat( in ); - rotKeys[i].data.y = ReadFloat( in ); - rotKeys[i].data.z = ReadFloat( in ); + if ( rotationType != 4 ) { + rotKeys.resize( numRotations ); + for ( unsigned int i = 0; i < rotKeys.size(); ++i ) { + NifStream(rotKeys[i], file, rotationType ); } + } + else { + //Read vestigial time and discard + ReadFloat( file ); - if (rotationType == 3) { - rotKeys[i].tension = ReadFloat( in ); - rotKeys[i].bias = ReadFloat( in ); - rotKeys[i].continuity = ReadFloat( in ); - } else if (rotationType == 4) { - throw runtime_error("NiKeyframeData rotation type 4 currently unsupported"); - //cout << "Rotation Type 4 Unsupported - Data will not be read" << endl; - - ////cout << endl; - //for (int j = 0; j < 3; j++) { - // //cout << "--Rotation Group " << j + 1 << "--" << endl; - // int subCount = ReadUInt( in ); - // //cout << "Sub Count: " << subCount << endl; - // int subType = ReadUInt( in ); - // //cout << "Sub Type: " << subType << endl; - - // for (int k = 0; k < subCount; k++) { - // float subTime = ReadFloat( in ); - // //cout << "KeyTime: " << subTime << " "; - // float subUnk1 = ReadFloat( in ); - // //cout << "Data: " << subUnk1; - // if (subType == 2) { - // float subUnk2 = ReadFloat( in ); - // float subUnk3 = ReadFloat( in ); - // //cout << ", " << subUnk2 << ", " << subUnk3; - // } - // //cout << endl; - // } - //} + for (int i = 0; i < 3; i++) { + int subCount = ReadUInt( file ); + NifStream( xyzTypes[i], file ); + + xyzKeys[i].resize( subCount ); + for (uint j = 0; j < xyzKeys[i].size(); j++) { + NifStream(xyzKeys[i][j], file, xyzTypes[i] ); + } } } } //--Translation--// - uint numTranslations = ReadUInt( in ); + uint numTranslations = ReadUInt( file ); if (numTranslations > 0) { - translationType = KeyType(ReadUInt( in )); + NifStream( translationType, file ); transKeys.resize( numTranslations ); - for ( unsigned int i = 0; i < numTranslations; ++i ) { - transKeys[i].time = ReadFloat( in ); - - transKeys[i].data.x = ReadFloat( in ); - transKeys[i].data.y = ReadFloat( in ); - transKeys[i].data.z = ReadFloat( in ); - - if (translationType == 2) { - transKeys[i].forward_tangent.x = ReadFloat( in ); - transKeys[i].forward_tangent.y = ReadFloat( in ); - transKeys[i].forward_tangent.y = ReadFloat( in ); - - transKeys[i].backward_tangent.x = ReadFloat( in ); - transKeys[i].backward_tangent.y = ReadFloat( in ); - transKeys[i].backward_tangent.y = ReadFloat( in ); - }else if (translationType == 3) { - transKeys[i].tension = ReadFloat( in ); - transKeys[i].bias = ReadFloat( in ); - transKeys[i].continuity = ReadFloat( in ); - } + for ( unsigned int i = 0; i < transKeys.size(); ++i ) { + NifStream(transKeys[i], file, translationType ); } } //--Scale--// - uint numScalings = ReadUInt( in ); + uint numScalings = ReadUInt( file ); if (numScalings > 0) { - scaleType = KeyType(ReadUInt( in )); + NifStream( scaleType, file ); scaleKeys.resize( numScalings ); - for ( unsigned int i = 0; i < numScalings; ++i ) { - scaleKeys[i].time = ReadFloat( in ); - - scaleKeys[i].data = ReadFloat( in ); - - if (scaleType == 2) { - scaleKeys[i].forward_tangent = ReadFloat( in ); - scaleKeys[i].backward_tangent = ReadFloat( in ); - } else if (scaleType == 3) { - scaleKeys[i].tension = ReadFloat( in ); - scaleKeys[i].bias = ReadFloat( in ); - scaleKeys[i].continuity = ReadFloat( in ); - } + for ( unsigned int i = 0; i < scaleKeys.size(); ++i ) { + NifStream(scaleKeys[i], file, scaleType ); } } } -void NiKeyframeData::Write( ofstream& out, unsigned int version ) { +void NiKeyframeData::Write( ofstream& file, unsigned int version ) { //--Rotation--// - WriteUInt( uint(rotKeys.size()), out ); + WriteUInt( uint(rotKeys.size()) , file ); - if (rotKeys.size() > 0) { - WriteUInt( rotationType, out ); - - for ( unsigned int i = 0; i < rotKeys.size(); ++i ) { - WriteFloat( rotKeys[i].time, out ); + if ( rotKeys.size() > 0) { + NifStream( rotationType, file ); - if (rotationType != 4) { - WriteFloat( rotKeys[i].data.w, out ); - WriteFloat( rotKeys[i].data.x, out ); - WriteFloat( rotKeys[i].data.y, out ); - WriteFloat( rotKeys[i].data.z, out ); + if ( rotationType != 4 ) { + for ( unsigned int i = 0; i < rotKeys.size(); ++i ) { + NifStream(rotKeys[i], file, rotationType ); } + } + else { + //Write vestigial time + WriteFloat( 0.0, file ); - if (rotationType == 3) { - WriteFloat( rotKeys[i].tension, out ); - WriteFloat( rotKeys[i].bias, out ); - WriteFloat( rotKeys[i].continuity, out ); - } else if (rotationType == 4) { - throw runtime_error("NiKeyframeData rotation type 4 currently unsupported"); + for (int i = 0; i < 3; i++) { + WriteUInt( uint(xyzKeys[i].size()) , file ); + NifStream( xyzTypes[i], file ); + + for (uint j = 0; j < xyzKeys[i].size(); j++) { + NifStream(xyzKeys[i][j], file, xyzTypes[i] ); + } } } } //--Translation--// + WriteUInt( uint(transKeys.size()) , file ); - WriteUInt( uint(transKeys.size()), out ); - - if (transKeys.size() > 0) { - WriteUInt( translationType, out ); + if ( transKeys.size() > 0) { + NifStream( translationType, file ); for ( unsigned int i = 0; i < transKeys.size(); ++i ) { - WriteFloat( transKeys[i].time, out ); - - WriteFloat( transKeys[i].data.x, out ); - WriteFloat( transKeys[i].data.y, out ); - WriteFloat( transKeys[i].data.z, out ); - - if (translationType == 2) { - WriteFloat( transKeys[i].forward_tangent.x, out ); - WriteFloat( transKeys[i].forward_tangent.y, out ); - WriteFloat( transKeys[i].forward_tangent.z, out ); - - WriteFloat( transKeys[i].backward_tangent.x, out ); - WriteFloat( transKeys[i].backward_tangent.y, out ); - WriteFloat( transKeys[i].backward_tangent.z, out ); - }else if (translationType == 3) { - WriteFloat( transKeys[i].tension, out ); - WriteFloat( transKeys[i].bias, out ); - WriteFloat( transKeys[i].continuity, out ); - } + NifStream(transKeys[i], file, translationType ); } } - + //--Scale--// - WriteUInt( uint(scaleKeys.size()), out ); + WriteUInt( uint(scaleKeys.size()), file ); - if ( scaleKeys.size() > 0) { - WriteUInt( scaleType, out ); + if (scaleKeys.size() > 0) { + NifStream( scaleType, file ); for ( unsigned int i = 0; i < scaleKeys.size(); ++i ) { - WriteFloat( scaleKeys[i].time, out ); - - WriteFloat( scaleKeys[i].data, out ); - - if (scaleType == 2) { - WriteFloat( scaleKeys[i].forward_tangent, out ); - WriteFloat( scaleKeys[i].backward_tangent, out ); - } else if (scaleType == 3) { - WriteFloat( scaleKeys[i].tension, out ); - WriteFloat( scaleKeys[i].bias, out ); - WriteFloat( scaleKeys[i].continuity, out ); - } + NifStream(scaleKeys[i], file, scaleType ); } } } @@ -2015,36 +2105,22 @@ string NiKeyframeData::asString() { * NiColorData methods **********************************************************/ -void NiColorData::Read( ifstream& in, unsigned int version ) { - uint colorCount = ReadUInt( in ); - keyType = ReadUInt( in ); - - if (keyType != 1) { - stringstream str; - str << "NiColorData is thought to only support keyType of 1, but this NIF has a keyType of " << keyType << "." << endl; - throw runtime_error( str.str() ); - } +void NiColorData::Read( ifstream& file, unsigned int version ) { + uint keyCount = ReadUInt( file ); + NifStream( keyType, file ); - keys.resize( colorCount ); + keys.resize( keyCount ); for (uint i = 0; i < keys.size(); i++) { - keys[i].time = ReadFloat( in ); - ReadFVector4( keys[i].data, in ); + NifStream( keys[i], file, keyType ); } } -void NiColorData::Write( ofstream& out, unsigned int version ) { - WriteUInt( uint(keys.size()), out ); - WriteUInt( keyType, out ); - - if (keyType != 1) { - stringstream str; - str << "NiColorData is thought to only support keyType of 1, but this NIF has a keyType of " << keyType << "." << endl; - throw runtime_error( str.str() ); - } +void NiColorData::Write( ofstream& file, unsigned int version ) { + WriteUInt( uint(keys.size()), file ); + NifStream( keyType, file ); for (uint i = 0; i < keys.size(); i++) { - WriteFloat( keys[i].time, out ); - WriteFVector4( keys[i].data, out ); + NifStream( keys[i], file, keyType ); } } @@ -2058,9 +2134,9 @@ string NiColorData::asString() { << "Key Type: " << keyType << endl; if (verbose) { - vector<Key<fVector4> >::iterator it; + vector< Key<Color> >::iterator it; for ( it = keys.begin(); it != keys.end(); ++it ) { - out << "Key Time: " << (*it).time << " Color: " << (*it).data << endl; + out << "Key Time: " << it->time << " Color: " << it->data.r << ", " << it->data.g << ", " << it->data.b << ", " << it->data.a << endl; } } else { out << "<<Data Not Shown>>" << endl; @@ -2077,18 +2153,27 @@ void NiFloatData::Read( ifstream& in, unsigned int version ) { uint keyCount = ReadUInt( in ); keyType = ReadUInt( in ); - if (keyType != 2) { - stringstream str; - str << "NiFloatata is thought to only support keyType of 2, but this NIF has a keyType of " << keyType << "."; - throw runtime_error( str.str() ); + if (keyCount > 0 && (keyType < 1 || keyType > 3 ) ) { + stringstream s; + s << "NiFloatData is thought to only support keyType of 1, 2, or 3, but this NIF has a keyType of " << keyType << "."; + throw runtime_error(s.str()); } keys.resize( keyCount ); for (uint i = 0; i < keys.size(); i++) { + //Always read the time and data keys[i].time = ReadFloat( in ); keys[i].data = ReadFloat( in ); - keys[i].forward_tangent = ReadFloat( in ); - keys[i].backward_tangent = ReadFloat( in ); + if ( keyType == 2 ) { + //Uses Quadratic interpolation + keys[i].forward_tangent = ReadFloat( in ); + keys[i].backward_tangent = ReadFloat( in ); + } else if ( keyType == 3 ) { + //Uses TBC interpolation + keys[i].tension = ReadFloat( in ); + keys[i].bias = ReadFloat( in ); + keys[i].continuity = ReadFloat( in ); + } } } @@ -2096,17 +2181,26 @@ void NiFloatData::Write( ofstream& out, unsigned int version ) { WriteUInt( uint(keys.size()), out ); WriteUInt( keyType, out ); - if (keyType != 2) { - stringstream str; - str << "NiFloatata is thought to only support keyType of 2, but this NIF has a keyType of " << keyType << "."; - throw runtime_error( str.str() ); + if (keys.size() > 0 && (keyType < 1 || keyType > 3 ) ) { + stringstream s; + s << "NiFloatData is thought to only support keyType of 1, 2, or 3, but this NIF has a keyType of " << keyType << "."; + throw runtime_error(s.str()); } for (uint i = 0; i < keys.size(); i++) { + //Always write the time and data WriteFloat( keys[i].time, out ); WriteFloat( keys[i].data, out ); - WriteFloat( keys[i].forward_tangent, out ); - WriteFloat( keys[i].backward_tangent, out ); + if ( keyType == 2 ) { + //Uses Quadratic interpolation + WriteFloat( keys[i].forward_tangent, out ); + WriteFloat( keys[i].backward_tangent, out ); + } else if ( keyType == 3 ) { + //Uses TBC interpolation + WriteFloat( keys[i].tension, out ); + WriteFloat( keys[i].bias, out ); + WriteFloat( keys[i].continuity, out ); + } } } @@ -2116,15 +2210,27 @@ string NiFloatData::asString() { out << setprecision(1); out << "Key Count: " << uint(keys.size()) << endl - << "Key Type: " << keyType << endl; + << "Key Type: " << keyType << endl + << "Keys:" << endl; if (verbose) { vector<Key<float> >::iterator it; for ( it = keys.begin(); it != keys.end(); ++it ) { - out << "Key Time: " << it->time << " Data: " << it->data << " Forward: " << it->forward_tangent << " Back: " << it->backward_tangent << endl; + for (uint i = 0; i < keys.size(); i++) { + //Always print the time and data + out << " " << i + 1 << ") Time: " << it->time << " Data: " << it->data; + if ( keyType == 2 ) { + //Uses Quadratic interpolation + out << " FT: " << it->forward_tangent << " BT: " << it->backward_tangent; + } else if ( keyType == 3 ) { + //Uses TBC interpolation + out << " T: " << it->tension << " B: " << it->bias << " C: " << it->continuity; + } + out << endl; + } } } else { - out << "<<Data Not Shown>>" << endl; + out << " <<Data Not Shown>>" << endl; } return out.str(); @@ -2391,6 +2497,223 @@ string NiPalette::asString() { return out.str(); } +/*********************************************************** + * NiSkinPartition methods + **********************************************************/ + +void NiSkinPartition::Read( ifstream& file, unsigned int version ) { + + uint numPartitions = ReadUInt( file ); + partitions.resize( numPartitions ); + + vector<SkinPartition>::iterator it; + for (it = partitions.begin(); it != partitions.end(); ++it ) { + + //Read counts + ushort numVertices = ReadUShort( file ); + ushort numTriangles = ReadUShort( file ); + ushort numBones = ReadUShort( file ); + ushort numStrips = ReadUShort( file ); + ushort numWeights = ReadUShort( file ); + + //Read bones + it->bones.resize( numBones ); + NifStream( it->bones, file ); + + //Read vertex map + //After version 10.1.0.0, the vertex map is conditioned on a bool + bool hasVertexMap = true; + if ( version >= VER_10_1_0_0 ) { + hasVertexMap = ReadBool( file, version ); + } + + if ( hasVertexMap ) { + it->vertexMap.resize( numVertices ); + NifStream( it->vertexMap, file ); + } + + //Read vertex weights + //After version 10.1.0.0, the vertex weights are conditioned on a bool + bool hasVertexWeights = true; + if ( version >= VER_10_1_0_0 ) { + hasVertexWeights = ReadBool( file, version ); + } + + if ( hasVertexWeights ) { + //Resize vectors 2 deep + it->vertexWeights.resize( numVertices ); + for ( uint i = 0; i < it->vertexWeights.size(); ++i ) { + it->vertexWeights[i].resize( numWeights ); + } + //Read it all + NifStream( it->vertexWeights, file ); + } + + //Read strip lenghts, resize strip vectors as we go. + it->strips.resize( numStrips ); + for ( uint i = 0; i < it->strips.size(); ++i ) { + it->strips[i].resize( ReadUShort( file ) ); + } + + //Read triangle strip points + //After version 10.1.0.0, the triangle strip points are conditioned on a bool + bool hasStrips = true; + if ( version >= VER_10_1_0_0 ) { + hasStrips = ReadBool( file, version ); + } + + if ( hasStrips ) { + //Read 2 deep + NifStream( it->strips, file ); + } + + //Read triangles + //Triangles only exist if numStrips == 0 + if ( it->strips.size() == 0 ) { + it->triangles.resize( numTriangles ); + NifStream( it->triangles, file ); + } + + //This bool exists in all versions + bool hasBoneIndices = ReadBool( file, version ); + + if ( hasBoneIndices ) { + //Resize vectors 2 deep + it->boneIndices.resize( numVertices ); + for ( uint i = 0; i < it->vertexWeights.size(); ++i ) { + it->boneIndices[i].resize( numWeights ); + } + //Read it all + NifStream( it->boneIndices, file ); + } + } +} + +void NiSkinPartition::Write( ofstream& file, unsigned int version ) { + + WriteUInt( uint(partitions.size()), file ); + + vector<SkinPartition>::iterator it; + for (it = partitions.begin(); it != partitions.end(); ++it ) { + //Write counts + WriteUShort( ushort( it->vertexMap.size()), file ); + WriteUShort( ushort( it->triangles.size()), file ); + WriteUShort( ushort( it->bones.size()), file ); + WriteUShort( ushort( it->strips.size()), file ); + WriteUShort( ushort( it->vertexWeights.size()), file ); + + //Write bones + NifStream( it->bones, file ); + + //Write vertex map + //After version 10.1.0.0, the vertex map is conditioned on a bool + if ( version >= VER_10_1_0_0 ) { + WriteBool( it->vertexMap.size() > 0, file, version ); + } + + NifStream( it->vertexMap, file ); + + //Write vertex weights + //After version 10.1.0.0, the vertex weights are conditioned on a bool + if ( version >= VER_10_1_0_0 ) { + WriteBool( it->vertexWeights.size() > 0, file, version ); + } + + //Write vertex weights - 2 deep + NifStream( it->vertexWeights, file ); + + //Write strip lenghts + for ( uint i = 0; i < it->strips.size(); ++i ) { + WriteUShort( ushort(it->strips.size()), file ); + } + + //Write triangle strip points + //After version 10.1.0.0, the triangle strip points are conditioned on a bool + if ( version >= VER_10_1_0_0 ) { + WriteBool( it->strips.size() > 0, file, version ); + } + + //Write strip points - 2 deep + NifStream( it->strips, file ); + + //Write triangles + //Triangles only exist if numStrips == 0 + if ( it->strips.size() == 0 ) { + NifStream( it->triangles, file ); + } + + //This bool exists in all versions + WriteBool( it->boneIndices.size() > 0, file, version ); + + //Write bone indices - 2 deep + NifStream( it->boneIndices, file ); + } +} + + +string NiSkinPartition::asString() { + stringstream out; + out.setf(ios::fixed, ios::floatfield); + out << setprecision(1); + + int count = 0; + vector<SkinPartition>::iterator it; + for (it = partitions.begin(); it != partitions.end(); ++it ) { + count++; + //Write counts + out << "Skin Partition " << count << ":" << endl + << " Vertex Count: " << ushort(it->vertexMap.size()) << endl + << " Triangle Count: " << ushort(it->triangles.size()) << endl + << " Bone Count: " << ushort(it->bones.size()) << endl + << " Triangle Strip Count: " << ushort(it->strips.size()) << endl + << " Vertex Weight Count: " << ushort(it->vertexWeights.size()) << endl; + + if (verbose) { + out << " Bones:" << endl; + for ( uint i = 0; i < it->bones.size(); ++i ) { + out << " " << i + 1 << ": " << it->bones[i] << endl; + } + + out << " Vertex Map:" << endl; + for ( uint i = 0; i < it->vertexMap.size(); ++i ) { + out << " " << i + 1 << ": " << it->vertexMap[i] << endl; + } + + out << " Vertex Weights:" << endl; + for ( uint i = 0; i < it->vertexWeights.size(); ++i ) { + out << "Group " << i + 1 << ":" << endl; + for ( uint j = 0; j < it->vertexWeights[i].size(); ++j ) { + out << " " << j + 1 << ": " << it->vertexWeights[i][j] << endl; + } + } + + out << " Triangle Strips:" << endl; + for ( uint i = 0; i < it->strips.size(); ++i ) { + out << "Strip " << i + 1 << ":" << endl; + for ( uint j = 0; j < it->strips[i].size(); ++j ) { + out << " " << j + 1 << ": " << it->strips[i][j] << endl; + } + } + + out << " Triangles:" << endl; + for ( uint i = 0; i < it->triangles.size(); ++i ) { + out << " " << i + 1 << ": " << setw(10) << it->triangles[i].v1 << "," << setw(10) << it->triangles[i].v2 << "," << setw(10) << it->triangles[i].v3 << endl; + } + + out << " Bone Indices:" << endl; + for ( uint i = 0; i < it->boneIndices.size(); ++i ) { + out << "Group " << i + 1 << ":" << endl; + for ( uint j = 0; j < it->boneIndices[i].size(); ++j ) { + out << " " << j + 1 << ": " << it->boneIndices[i][j] << endl; + } + } + } else { + out << " <<Data Not Shown>>" << endl; + } + } + + return out.str(); +} /*********************************************************** * NiPixelData methods diff --git a/NIF_Blocks.h b/NIF_Blocks.h index c1638c6d..3968c025 100644 --- a/NIF_Blocks.h +++ b/NIF_Blocks.h @@ -183,6 +183,13 @@ public: ~AParentNode() {} }; +class AParticleNode : public ANode { +public: + AParticleNode(); + void Init() {} + ~AParticleNode() {} +}; + class AShape : public ANode { public: AShape(); @@ -197,6 +204,13 @@ public: ~AProperty() {} }; +class AParticleProperty : public AControllable { +public: + AParticleProperty(); + void Init() {} + ~AParticleProperty() {} +}; + class AController : public ABlock { public: AController(); @@ -660,7 +674,7 @@ private: /** * NiTriStripsData - Holds mesh data using strips of triangles. */ -class NiTriStripsData : public AShapeData { +class NiTriStripsData : public AShapeData, public ITriStripsData { public: NiTriStripsData() {} ~NiTriStripsData() {} @@ -670,6 +684,19 @@ public: string GetBlockType() { return "NiTriStripsData"; } + void * QueryInterface( int id ); + + //--ITriStripsData--// + //Counts + short GetStripCount(); + void SetStripCount(int n); + short GetTriangleCount(); + //Getters + vector<short> GetStrip( int index ); + vector<Triangle> GetTriangles(); + //Setter + void SetStrip( int index, const vector<short> & in ); + private: vector< vector<short> > strips; }; @@ -679,17 +706,19 @@ private: */ class NiCollisionData : public AData { public: - NiCollisionData() { - AddAttr( attr_int, "Unknown Int 1" ); - AddAttr( attr_int, "Unknown Int 2" ); - AddAttr( attr_byte, "Unknown Byte" ); - AddAttr( attr_int, "Unknown Int 3" ); - AddAttr( attr_int, "Unknown Int 4" ); - AddAttr( attr_float3, "Radius" ); - } + NiCollisionData() {} ~NiCollisionData() {} + void Read( ifstream& in, unsigned int version ); + void Write( ofstream& out, unsigned int version ); + string asString(); string GetBlockType() { return "NiCollisionData"; } +private: + int unknownInt1, collisionType, unknownInt2; + byte unknownByte; + fVector3 unknown3Floats; + float unknown15Floats[15]; + float unknown8Floats[8]; }; @@ -811,7 +840,7 @@ public: * NiAutoNormalParticles */ -class NiAutoNormalParticles : public ANode { +class NiAutoNormalParticles : public AParticleNode { public: NiAutoNormalParticles(); void Init() {} @@ -823,7 +852,7 @@ public: * NiRotatingParticles */ -class NiRotatingParticles : public ANode { +class NiRotatingParticles : public AParticleNode { public: NiRotatingParticles(); void Init() {} @@ -859,7 +888,7 @@ public: * NiParticleMeshes */ -class NiParticleMeshes : public ANode { +class NiParticleMeshes : public AParticleNode { public: NiParticleMeshes(); void Init() {} @@ -871,7 +900,7 @@ public: * NiGravity */ -class NiGravity : public AControllable { +class NiGravity : public AParticleProperty { public: NiGravity(); void Init() {} @@ -883,7 +912,7 @@ public: * NiParticleBomb */ -class NiParticleBomb : public AControllable { +class NiParticleBomb : public AParticleProperty { public: NiParticleBomb(); void Init() {} @@ -895,7 +924,7 @@ public: * NiPlanarCollider */ -class NiPlanarCollider : public AControllable { +class NiPlanarCollider : public AParticleProperty { public: NiPlanarCollider(); void Init() {} @@ -904,10 +933,10 @@ public: }; /** - * NiPlanarCollider + * NiSphericalCollider */ -class NiSphericalCollider : public AControllable { +class NiSphericalCollider : public AParticleProperty { public: NiSphericalCollider(); void Init() {} @@ -919,7 +948,7 @@ public: * NiParticleGrowFade */ -class NiParticleGrowFade : public AControllable { +class NiParticleGrowFade : public AParticleProperty { public: NiParticleGrowFade(); void Init() {} @@ -931,7 +960,7 @@ public: * NiParticleMeshModifier */ -class NiParticleMeshModifier : public AControllable { +class NiParticleMeshModifier : public AParticleProperty { public: NiParticleMeshModifier(); void Init() {} @@ -943,7 +972,7 @@ public: * NiParticleColorModifier */ -class NiParticleColorModifier : public AControllable { +class NiParticleColorModifier : public AParticleProperty { public: NiParticleColorModifier(); void Init() {} @@ -955,7 +984,7 @@ public: * NiGravity */ -class NiParticleRotation : public AControllable { +class NiParticleRotation : public AParticleProperty { public: NiParticleRotation(); void Init() {} @@ -1011,6 +1040,9 @@ class NiKeyframeData : public AData, public IKeyframeData { KeyType scaleType; vector< Key<float> > scaleKeys; + + KeyType xyzTypes[3]; + vector< Key<float> > xyzKeys[3]; }; /** @@ -1038,10 +1070,24 @@ private: class NiSkinPartition : public AData { public: - NiSkinPartition(); + NiSkinPartition() {} void Init() {} ~NiSkinPartition() {} + void Read( ifstream& in, unsigned int version ); + void Write( ofstream& out, unsigned int version ); + string asString(); + string GetBlockType() { return "NiSkinPartition"; } +private: + struct SkinPartition { + vector<ushort> bones; + vector<ushort> vertexMap; + vector< vector<float> > vertexWeights; + vector< vector<ushort> > strips; + vector<Triangle> triangles; + vector< vector<byte> > boneIndices; + }; + vector<SkinPartition> partitions; }; @@ -1102,6 +1148,7 @@ class NiSkinData : public AData, public ISkinData, public ISkinDataInternal { public: NiSkinData() { + AddAttr( attr_link, "Skin Partition" ); SetIdentity33(rotation); translation[0] = 0.0f; translation[1] = 0.0f; @@ -1171,8 +1218,8 @@ public: string GetBlockType() { return "NiColorData"; }; private: - uint keyType; - vector< Key<fVector4> > keys; + KeyType keyType; + vector< Key<Color> > keys; }; /** diff --git a/NIF_IO.cpp b/NIF_IO.cpp index 0bd87225..56046600 100644 --- a/NIF_IO.cpp +++ b/NIF_IO.cpp @@ -356,3 +356,69 @@ ostream & operator<<(ostream & lh, Bin & rh) { } return lh; } + +//--Overloaded versions of Read/Write functions ReadData/WriteData + +void NifStream( uint & val, ifstream& in ) { val = ReadUInt( in ); }; +void NifStream( ushort & val, ifstream& in ) { val = ReadUShort( in ); }; +void NifStream( byte & val, ifstream& in ) { val = ReadByte( in ); }; +void NifStream( float & val, ifstream& in ) { val = ReadFloat( in ); }; +void NifStream( string & val, ifstream& in ) { val = ReadString( in ); }; +void NifStream( KeyType & val, ifstream& in ) { val = KeyType(ReadUInt( in )); }; + +void NifStream( Vector3 & val, ifstream& in ) { + val.x = ReadFloat( in ); + val.y = ReadFloat( in ); + val.z = ReadFloat( in ); +}; + +void NifStream( Quaternion & val, ifstream& in ) { + val.w = ReadFloat( in ); + val.x = ReadFloat( in ); + val.y = ReadFloat( in ); + val.z = ReadFloat( in ); +}; + +void NifStream( Color & val, ifstream& in ) { + val.r = ReadFloat( in ); + val.g = ReadFloat( in ); + val.b = ReadFloat( in ); + val.a = ReadFloat( in ); +}; + +void NifStream( Triangle & val, ifstream& in ) { + val.v1 = ReadUShort( in ); + val.v2 = ReadUShort( in ); + val.v3 = ReadUShort( in ); +}; + + + +void NifStream( uint & val, ofstream& out ) { WriteUInt( val, out ); } +void NifStream( ushort & val, ofstream& out ) { WriteUShort( val, out ); } +void NifStream( byte & val, ofstream& out ) { WriteByte( val, out ); } +void NifStream( float & val, ofstream& out ) { WriteFloat( val, out ); } +void NifStream( string & val, ofstream& out ) { WriteString( val, out ); } +void NifStream( KeyType & val, ofstream& out ) { WriteUInt( val, out ); } +void NifStream( Vector3 & val, ofstream& out ) { + WriteFloat( val.x, out ); + WriteFloat( val.y, out ); + WriteFloat( val.z, out ); +}; +void NifStream( Quaternion & val, ofstream& out ) { + WriteFloat( val.w, out ); + WriteFloat( val.x, out ); + WriteFloat( val.y, out ); + WriteFloat( val.z, out ); +}; +void NifStream( Color & val, ofstream& out ) { + WriteFloat( val.r, out ); + WriteFloat( val.g, out ); + WriteFloat( val.b, out ); + WriteFloat( val.a, out ); +}; +void NifStream( Triangle & val, ofstream& out ) { + WriteUShort( val.v1, out ); + WriteUShort( val.v2, out ); + WriteUShort( val.v3, out ); +}; \ No newline at end of file diff --git a/NIF_IO.h b/NIF_IO.h index b70ba863..431bcc0a 100644 --- a/NIF_IO.h +++ b/NIF_IO.h @@ -40,6 +40,7 @@ POSSIBILITY OF SUCH DAMAGE. */ #include <fstream> #include <iomanip> #include <string> +#include "niflib.h" using namespace std; /* TYPE DEFINITIONS */ @@ -187,25 +188,60 @@ ostream & operator<<(ostream & lh, nifAlphaFormat & rh); * Read utility functions */ uint ReadUInt( ifstream& in ); - ushort ReadUShort( ifstream& in ); - byte ReadByte( ifstream& in ); - float ReadFloat( ifstream &in ); - string ReadString( ifstream &in ); - bool ReadBool( ifstream &in, unsigned int version ); - void ReadUSVector3( usVector3& vec, ifstream& in ); - void ReadFVector2( fVector2& fvec, ifstream& in ); - void ReadFVector3( fVector3& fvec, ifstream& in ); - void ReadFVector4( fVector4& fvec, ifstream& in ); +//Read +void NifStream( uint & val, ifstream& in ); +void NifStream( ushort & val, ifstream& in ); +void NifStream( byte & val, ifstream& in ); +void NifStream( float & val, ifstream& in ); +void NifStream( string & val, ifstream& in ); +void NifStream( Vector3 & val, ifstream& in ); +void NifStream( Quaternion & val, ifstream& in ); +void NifStream( KeyType & val, ifstream& in ); +void NifStream( Color & val, ifstream& in ); +void NifStream( Triangle & val, ifstream& in ); + +template <class T> +void NifStream( Key<T> & key, ifstream& file, KeyType type ) { + key.time = ReadFloat( file ); + + //If key type is not 1, 2, or 3, throw an exception + if ( type < 1 || type > 3 ) { + throw runtime_error("Invalid key type."); + } + + //Read data based on the type of key + NifStream( key.data, file ); + if ( type == QUADRATIC_KEY ) { + //Uses Quadratic interpolation + NifStream( key.forward_tangent, file ); + NifStream( key.backward_tangent, file ); + } else if ( type == TBC_KEY ) { + //Uses TBC interpolation + key.tension = ReadFloat( file ); + key.bias = ReadFloat( file ); + key.continuity = ReadFloat( file ); + } +} + +template <class T> +void NifStream( vector<T> & val, ifstream& file ) { + vector<T>::iterator it; + for ( it = val.begin(); it != val.end(); ++it ) { + NifStream( *it, file ); + } +} + + /** * Write utility functions. */ @@ -231,6 +267,49 @@ void WriteFVector4( fVector4& fvec, ofstream& out ); void WriteBlockName( const char* name, uint nameLength, ofstream& out ); +//Write +void NifStream( uint & val, ofstream& out ); +void NifStream( ushort & val, ofstream& out ); +void NifStream( byte & val, ofstream& out ); +void NifStream( float & val, ofstream& out ); +void NifStream( string & val, ofstream& out ); +void NifStream( Vector3 & val, ofstream& out ); +void NifStream( Quaternion & val, ofstream& out ); +void NifStream( KeyType & val, ofstream& out ); +void NifStream( Color & val, ofstream& out ); +void NifStream( Triangle & val, ofstream& out ); + +template <class T> +void NifStream( Key<T> & key, ofstream& file, KeyType type ) { + WriteFloat( key.time, file ); + + //If key type is not 1, 2, or 3, throw an exception + if ( type < 1 || type > 3 ) { + throw runtime_error("Invalid key type."); + } + + //Read data based on the type of key + NifStream( key.data, file ); + if ( type == QUADRATIC_KEY ) { + //Uses Quadratic interpolation + NifStream( key.forward_tangent, file ); + NifStream( key.backward_tangent, file ); + } else if ( type == TBC_KEY ) { + //Uses TBC interpolation + WriteFloat( key.tension, file); + WriteFloat( key.bias, file); + WriteFloat( key.continuity, file); + } +} + +template <class T> +void NifStream( vector<T> & val, ofstream& file ) { + vector<T>::iterator it; + for ( it = val.begin(); it != val.end(); ++it ) { + NifStream( *it, file ); + } +} + class NIF; class nifIndex { diff --git a/nif_attrs.h b/nif_attrs.h index c613606a..2bc7aa18 100644 --- a/nif_attrs.h +++ b/nif_attrs.h @@ -859,6 +859,19 @@ public: data.unknownShort = ReadUShort( in ); } + //From version 10.1.0.0 and up, this unknown data block may exist + if ( version >= VER_10_1_0_0 ) { + data.hasUnknownData = ReadBool( in, version ); + if ( data.hasUnknownData == true ) { + for (int i = 0; i < 5; ++i ) { + data.unknown5Floats[i] = ReadFloat( in );; + } + data.unknownInt = ReadUInt( in ); + data.unknownFloat1 = ReadFloat( in ); + data.unknownFloat2 = ReadFloat( in ); + } + } + if ( _isBumpMap ) { data.bmLumaScale = ReadFloat( in ); data.bmLumaOffset = ReadFloat( in ); @@ -880,10 +893,25 @@ public: WriteUInt( data.textureSet, out ); WriteUShort( data.PS2_L, out ); WriteUShort( data.PS2_K, out ); + //unknownShort exists up to version 4.1.0.12 if ( version <= VER_4_1_0_12 ) { WriteUShort( data.unknownShort, out ); } + + //From version 10.1.0.0 and up, this unknown data block may exist + if ( version >= VER_10_1_0_0 ) { + WriteBool( data.hasUnknownData, out, version ); + if ( data.hasUnknownData == true ) { + for (int i = 0; i < 5; ++i ) { + WriteFloat( data.unknown5Floats[i], out );; + } + WriteUInt( data.unknownInt, out ); + WriteFloat( data.unknownFloat1, out ); + WriteFloat( data.unknownFloat2, out ); + } + } + if ( _isBumpMap ) { WriteFloat( data.bmLumaScale, out ); WriteFloat( data.bmLumaOffset, out ); @@ -950,7 +978,24 @@ public: << " Texture Set: " << data.textureSet << endl << " PS2 L Setting: " << data.PS2_L << endl << " PS2 K Setting: " << data.PS2_K << endl - << " Unknown Short: " << data.unknownShort; + << " Unknown Short: " << data.unknownShort + << " Unknown Data: "; + + + //From version 10.1.0.0 and up, this unknown data block may exist + if ( data.hasUnknownData == true ) { + out << endl + << " Unknown 5 Floats:" << endl; + for (int i = 0; i < 5; ++i ) { + out << " " << i + 1 << ": " << data.unknown5Floats[i] << endl; + } + out << " Unknown Int: " << data.unknownInt << endl + << " Unknown Float 1: " << data.unknownFloat1 << endl + << " Unknown Float 2: " << data.unknownFloat2 << endl; + } else { + out << "None" << endl; + } + if ( _isBumpMap ) { out << endl << " BumpMap Info:" << endl @@ -1411,4 +1456,52 @@ private: }; + +class LODRangeGroupAttr : public AAttr { +public: + LODRangeGroupAttr( string name, IBlock * owner, unsigned int first_ver, unsigned int last_ver ) : AAttr(name, owner, first_ver, last_ver) {} + ~LODRangeGroupAttr() {} + string GetType() const { return "lodrangegroup"; } + + void ReadAttr( ifstream& in, unsigned int version ) { + int numRanges = ReadUInt( in ); + ranges.resize( numRanges ); + for ( uint i = 0; i < ranges.size(); ++i ) { + ranges[i].near = ReadFloat( in ); + ranges[i].far = ReadFloat( in ); + } + } + + void WriteAttr( ofstream& out, unsigned int version ) { + WriteUInt( uint(ranges.size()), out ); + + for ( uint i = 0; i < ranges.size(); ++i ) { + WriteFloat( ranges[i].near, out ); + WriteFloat( ranges[i].far, out ); + } + } + + string asString() const { + stringstream out; + out.setf(ios::fixed, ios::floatfield); + out << setprecision(1); + + out << uint(ranges.size()) << endl; + + for ( uint i = 0; i < ranges.size(); ++i ) { + out << " " << i + 1 << ") Near: " << ranges[i].near << " Far: " << ranges[i].far << endl; + } + + return out.str(); + } + +private: + struct LODRange { + float near; + float far; + }; + vector<LODRange> ranges; +}; + + #endif diff --git a/niflib.cpp b/niflib.cpp index c9036f78..63369447 100644 --- a/niflib.cpp +++ b/niflib.cpp @@ -183,6 +183,11 @@ vector<blk_ref> ReadNifList( string file_name ) { vector<blk_ref> blocks( numBlocks ); string blockName; for (uint i = 0; i < numBlocks; i++) { + + //Check for EOF + if (in.eof() ) { + throw runtime_error("End of file reached prematurely. This NIF may be corrupt or improperly supported."); + } //There are two ways to read blocks, one before version 5.0.0.1 and one after that if ( version >= 0x05000001 ) { @@ -485,3 +490,7 @@ ITextKeyExtraData * QueryTextKeyExtraData ( blk_ref & block ) { IMorphData * QueryMorphData ( blk_ref & block ) { return (IMorphData*)block->QueryInterface( ID_MORPH_DATA ); } + +ITriStripsData * QueryTriStripsData ( blk_ref & block ) { + return (ITriStripsData*)block->QueryInterface( ID_TRI_STRIPS_DATA ); +} diff --git a/niflib.h b/niflib.h index 03c8cb85..76c0eec4 100644 --- a/niflib.h +++ b/niflib.h @@ -58,6 +58,7 @@ class ISkinData; class IKeyframeData; class ITextKeyExtraData; class IMorphData; +class ITriStripsData; class INode; class blk_ref; class attr_ref; @@ -78,6 +79,7 @@ const int ID_KEYFRAME_DATA = 3; const int ID_TEXT_KEY_EXTRA_DATA = 4; const int ID_MORPH_DATA = 5; const int ID_SHAPE_DATA = 6; +const int ID_TRI_STRIPS_DATA = 7; //Attribute types const enum AttrTypes { @@ -105,7 +107,7 @@ const int VER_20_0_0_4 = 0x14000004; #endif //Key Types -enum KeyType { LINEAR_KEY = 1, QUADRATIC_KEY = 2, TBC_KEY = 3 }; +enum KeyType { LINEAR_KEY = 1, QUADRATIC_KEY = 2, TBC_KEY = 3, XYZ_ROTATION_KEY = 4 }; //--Main Functions--// @@ -139,6 +141,7 @@ INode * QueryNode( blk_ref & block ); IKeyframeData * QueryKeyframeData( blk_ref & block ); ITextKeyExtraData * QueryTextKeyExtraData ( blk_ref & block ); IMorphData * QueryMorphData ( blk_ref & block ); +ITriStripsData * QueryTriStripsData ( blk_ref & block ); //--Simlpe Structures--// @@ -484,6 +487,21 @@ public: virtual void SetTriangles( const vector<Triangle> & in ) = 0; }; +class ITriStripsData { +public: + ITriStripsData() {} + virtual ~ITriStripsData () {} + //Counts + virtual short GetTriangleCount() = 0; + virtual short GetStripCount() = 0; + virtual void SetStripCount(int n) = 0; + //Getter + virtual vector<short> GetStrip( int index ) = 0; + virtual vector<Triangle> GetTriangles() = 0; + //Setter + virtual void SetStrip( int index, const vector<short> & in ) = 0; +}; + class ISkinData { public: ISkinData() {} @@ -1016,10 +1034,16 @@ struct Texture { int textureSet; short PS2_L; short PS2_K; - short unknownShort; + short unknownShort; //exists up to version 4.1.0.12 + //Unknown Block in version 10.1.0.0 and up + bool hasUnknownData; + float unknown5Floats[5]; + int unknownInt; + float unknownFloat1, unknownFloat2; + //Bitmap block - only exists if this texture is in the bitmap slot float bmLumaOffset; float bmLumaScale; - Matrix22 bmMatrix; + Matrix22 bmMatrix; }; struct TextureSource { diff --git a/pyniflib.i b/pyniflib.i index b78954fc..876e636b 100644 --- a/pyniflib.i +++ b/pyniflib.i @@ -62,6 +62,7 @@ struct Key { }; %template(vector_float) std::vector<float>; +%template(vector_short) std::vector<short>; %template(vector_attr_ref) std::vector<attr_ref>; %template(vector_blk_ref) std::vector<blk_ref>; %template(list_blk_ref) std::list<blk_ref>; -- GitLab