mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-04-01 04:36:44 +00:00
More NIF adjustments
Constant interpolation support
This commit is contained in:
parent
a5289035e2
commit
e654a52b70
12 changed files with 236 additions and 130 deletions
|
@ -35,16 +35,16 @@ void ShapeData::read(NIFStream *nif)
|
||||||
{
|
{
|
||||||
int verts = nif->getUShort();
|
int verts = nif->getUShort();
|
||||||
|
|
||||||
if(nif->getInt())
|
if (nif->getBoolean())
|
||||||
nif->getVector3s(vertices, verts);
|
nif->getVector3s(vertices, verts);
|
||||||
|
|
||||||
if(nif->getInt())
|
if (nif->getBoolean())
|
||||||
nif->getVector3s(normals, verts);
|
nif->getVector3s(normals, verts);
|
||||||
|
|
||||||
center = nif->getVector3();
|
center = nif->getVector3();
|
||||||
radius = nif->getFloat();
|
radius = nif->getFloat();
|
||||||
|
|
||||||
if(nif->getInt())
|
if (nif->getBoolean())
|
||||||
nif->getVector4s(colors, verts);
|
nif->getVector4s(colors, verts);
|
||||||
|
|
||||||
// Only the first 6 bits are used as a count. I think the rest are
|
// Only the first 6 bits are used as a count. I think the rest are
|
||||||
|
@ -120,7 +120,7 @@ void NiAutoNormalParticlesData::read(NIFStream *nif)
|
||||||
particleRadius = nif->getFloat();
|
particleRadius = nif->getFloat();
|
||||||
activeCount = nif->getUShort();
|
activeCount = nif->getUShort();
|
||||||
|
|
||||||
if(nif->getInt())
|
if (nif->getBoolean())
|
||||||
{
|
{
|
||||||
// Particle sizes
|
// Particle sizes
|
||||||
nif->getFloats(sizes, vertices.size());
|
nif->getFloats(sizes, vertices.size());
|
||||||
|
@ -131,7 +131,7 @@ void NiRotatingParticlesData::read(NIFStream *nif)
|
||||||
{
|
{
|
||||||
NiAutoNormalParticlesData::read(nif);
|
NiAutoNormalParticlesData::read(nif);
|
||||||
|
|
||||||
if(nif->getInt())
|
if (nif->getBoolean())
|
||||||
{
|
{
|
||||||
// Rotation quaternions.
|
// Rotation quaternions.
|
||||||
nif->getQuaternions(rotations, vertices.size());
|
nif->getQuaternions(rotations, vertices.size());
|
||||||
|
@ -176,7 +176,7 @@ void NiPixelData::read(NIFStream *nif)
|
||||||
|
|
||||||
numberOfMipmaps = nif->getUInt();
|
numberOfMipmaps = nif->getUInt();
|
||||||
|
|
||||||
// Bytes per pixel, should be bpp * 8
|
// Bytes per pixel, should be bpp / 8
|
||||||
/* int bytes = */ nif->getUInt();
|
/* int bytes = */ nif->getUInt();
|
||||||
|
|
||||||
for(unsigned int i=0; i<numberOfMipmaps; i++)
|
for(unsigned int i=0; i<numberOfMipmaps; i++)
|
||||||
|
@ -228,10 +228,8 @@ void NiSkinData::read(NIFStream *nif)
|
||||||
nif->getInt(); // -1
|
nif->getInt(); // -1
|
||||||
|
|
||||||
bones.resize(boneNum);
|
bones.resize(boneNum);
|
||||||
for(int i=0;i<boneNum;i++)
|
for (BoneInfo &bi : bones)
|
||||||
{
|
{
|
||||||
BoneInfo &bi = bones[i];
|
|
||||||
|
|
||||||
bi.trafo.rotation = nif->getMatrix3();
|
bi.trafo.rotation = nif->getMatrix3();
|
||||||
bi.trafo.pos = nif->getVector3();
|
bi.trafo.pos = nif->getVector3();
|
||||||
bi.trafo.scale = nif->getFloat();
|
bi.trafo.scale = nif->getFloat();
|
||||||
|
@ -267,7 +265,7 @@ void NiKeyframeData::read(NIFStream *nif)
|
||||||
{
|
{
|
||||||
mRotations = std::make_shared<QuaternionKeyMap>();
|
mRotations = std::make_shared<QuaternionKeyMap>();
|
||||||
mRotations->read(nif);
|
mRotations->read(nif);
|
||||||
if(mRotations->mInterpolationType == Vector3KeyMap::sXYZInterpolation)
|
if(mRotations->mInterpolationType == Vector3KeyMap::XYZ)
|
||||||
{
|
{
|
||||||
//Chomp unused float
|
//Chomp unused float
|
||||||
nif->getFloat();
|
nif->getFloat();
|
||||||
|
|
|
@ -9,9 +9,7 @@ namespace Nif
|
||||||
|
|
||||||
/// Open a NIF stream. The name is used for error messages.
|
/// Open a NIF stream. The name is used for error messages.
|
||||||
NIFFile::NIFFile(Files::IStreamPtr stream, const std::string &name)
|
NIFFile::NIFFile(Files::IStreamPtr stream, const std::string &name)
|
||||||
: ver(0)
|
: filename(name)
|
||||||
, filename(name)
|
|
||||||
, mUseSkinning(false)
|
|
||||||
{
|
{
|
||||||
parse(stream);
|
parse(stream);
|
||||||
}
|
}
|
||||||
|
@ -139,27 +137,18 @@ void NIFFile::parse(Files::IStreamPtr stream)
|
||||||
// Check the header string
|
// Check the header string
|
||||||
std::string head = nif.getVersionString();
|
std::string head = nif.getVersionString();
|
||||||
if(head.compare(0, 22, "NetImmerse File Format") != 0)
|
if(head.compare(0, 22, "NetImmerse File Format") != 0)
|
||||||
fail("Invalid NIF header: " + head);
|
fail("Invalid NIF header: " + head);
|
||||||
|
|
||||||
// Get BCD version
|
// Get BCD version
|
||||||
ver = nif.getUInt();
|
ver = nif.getUInt();
|
||||||
// 4.0.0.0 is an older, practically identical version of the format.
|
// 4.0.0.0 is an older, practically identical version of the format.
|
||||||
// It's not used by Morrowind assets but Morrowind supports it.
|
// It's not used by Morrowind assets but Morrowind supports it.
|
||||||
if(ver != 0x04000000 && ver != VER_MW)
|
if(ver != VER_4_0_0_0 && ver != VER_MW)
|
||||||
fail("Unsupported NIF version: " + printVersion(ver));
|
fail("Unsupported NIF version: " + printVersion(ver));
|
||||||
// Number of records
|
// Number of records
|
||||||
size_t recNum = nif.getInt();
|
size_t recNum = nif.getInt();
|
||||||
records.resize(recNum);
|
records.resize(recNum);
|
||||||
|
|
||||||
/* The format for 10.0.1.0 seems to be a bit different. After the
|
|
||||||
header, it contains the number of records, r (int), just like
|
|
||||||
4.0.0.2, but following that it contains a short x, followed by x
|
|
||||||
strings. Then again by r shorts, one for each record, giving
|
|
||||||
which of the above strings to use to identify the record. After
|
|
||||||
this follows two ints (zero?) and then the record data. However
|
|
||||||
we do not support or plan to support other versions yet.
|
|
||||||
*/
|
|
||||||
|
|
||||||
for(size_t i = 0;i < recNum;i++)
|
for(size_t i = 0;i < recNum;i++)
|
||||||
{
|
{
|
||||||
Record *r = nullptr;
|
Record *r = nullptr;
|
||||||
|
|
|
@ -26,21 +26,27 @@ struct File
|
||||||
|
|
||||||
virtual size_t numRoots() const = 0;
|
virtual size_t numRoots() const = 0;
|
||||||
|
|
||||||
|
virtual std::string getString(size_t index) const = 0;
|
||||||
|
|
||||||
virtual void setUseSkinning(bool skinning) = 0;
|
virtual void setUseSkinning(bool skinning) = 0;
|
||||||
|
|
||||||
virtual bool getUseSkinning() const = 0;
|
virtual bool getUseSkinning() const = 0;
|
||||||
|
|
||||||
virtual std::string getFilename() const = 0;
|
virtual std::string getFilename() const = 0;
|
||||||
|
|
||||||
|
virtual unsigned int getVersion() const = 0;
|
||||||
|
|
||||||
|
virtual unsigned int getUserVersion() const = 0;
|
||||||
|
|
||||||
|
virtual unsigned int getBethVersion() const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class NIFFile final : public File
|
class NIFFile final : public File
|
||||||
{
|
{
|
||||||
enum NIFVersion {
|
/// File version, user version, Bethesda version
|
||||||
VER_MW = 0x04000002 // Morrowind NIFs
|
unsigned int ver = 0;
|
||||||
};
|
unsigned int userVer = 0;
|
||||||
|
unsigned int bethVer = 0;
|
||||||
/// Nif file version
|
|
||||||
unsigned int ver;
|
|
||||||
|
|
||||||
/// File name, used for error messages and opening the file
|
/// File name, used for error messages and opening the file
|
||||||
std::string filename;
|
std::string filename;
|
||||||
|
@ -51,7 +57,10 @@ class NIFFile final : public File
|
||||||
/// Root list. This is a select portion of the pointers from records
|
/// Root list. This is a select portion of the pointers from records
|
||||||
std::vector<Record*> roots;
|
std::vector<Record*> roots;
|
||||||
|
|
||||||
bool mUseSkinning;
|
/// String table
|
||||||
|
std::vector<std::string> strings;
|
||||||
|
|
||||||
|
bool mUseSkinning = false;
|
||||||
|
|
||||||
/// Parse the file
|
/// Parse the file
|
||||||
void parse(Files::IStreamPtr stream);
|
void parse(Files::IStreamPtr stream);
|
||||||
|
@ -66,6 +75,34 @@ class NIFFile final : public File
|
||||||
void operator = (NIFFile const &);
|
void operator = (NIFFile const &);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
enum NIFVersion
|
||||||
|
{
|
||||||
|
// Feature-relevant
|
||||||
|
VER_4_1_0_0 = 0x04010000, // 1-byte booleans (previously 4-byte)
|
||||||
|
VER_5_0_0_1 = 0x05000001, // Optimized record type listings
|
||||||
|
VER_5_0_0_6 = 0x05000006, // Record groups
|
||||||
|
VER_10_0_1_8 = 0x0A000108, // The last version without user version
|
||||||
|
VER_20_1_0_1 = 0x14010001, // String tables
|
||||||
|
VER_20_2_0_5 = 0x14020005, // Record sizes
|
||||||
|
// Game-relevant
|
||||||
|
VER_4_0_0_0 = 0x04000000, // Freedom Force NIFs, supported by Morrowind
|
||||||
|
VER_MW = 0x04000002, // 4.0.0.2. Morrowind and Freedom Force NIFs
|
||||||
|
VER_4_2_1_0 = 0x04020100, // Used in Civ4 and Dark Age of Camelot
|
||||||
|
VER_CI = 0x04020200, // 4.2.2.0. Main Culpa Innata NIF version, also used in Civ4
|
||||||
|
VER_ZT2 = 0x0A000100, // 10.0.1.0. Main Zoo Tycoon 2 NIF version, also used in Oblivion and Civ4
|
||||||
|
VER_OB_OLD = 0x0A000102, // 10.0.1.2. Main older Oblivion NIF version
|
||||||
|
VER_GAMEBRYO = 0x0A010000, // 10.1.0.0. Lots of games use it. The first version that has Gamebryo File Format header.
|
||||||
|
VER_10_2_0_0 = 0x0A020000, // Lots of games use this version as well.
|
||||||
|
VER_CIV4 = 0x14000004, // 20.0.0.4. Main Civilization IV NIF version.
|
||||||
|
VER_OB = 0x14000005, // 20.0.0.5. Main Oblivion NIF version
|
||||||
|
VER_BGS = 0x14020007 // 20.2.0.7. Main Fallout 3/4/76/New Vegas and Skyrim/SkyrimSE NIF version.
|
||||||
|
};
|
||||||
|
enum BethVersion
|
||||||
|
{
|
||||||
|
BETHVER_FO3 = 34, // Fallout 3
|
||||||
|
BETHVER_FO4 = 130 // Fallout 4
|
||||||
|
};
|
||||||
|
|
||||||
/// Used if file parsing fails
|
/// Used if file parsing fails
|
||||||
void fail(const std::string &msg) const
|
void fail(const std::string &msg) const
|
||||||
{
|
{
|
||||||
|
@ -101,6 +138,12 @@ public:
|
||||||
/// Number of roots
|
/// Number of roots
|
||||||
size_t numRoots() const override { return roots.size(); }
|
size_t numRoots() const override { return roots.size(); }
|
||||||
|
|
||||||
|
/// Get a given string from the file's string table
|
||||||
|
std::string getString(size_t index) const override
|
||||||
|
{
|
||||||
|
return strings.at(index);
|
||||||
|
}
|
||||||
|
|
||||||
/// Set whether there is skinning contained in this NIF file.
|
/// Set whether there is skinning contained in this NIF file.
|
||||||
/// @note This is just a hint for users of the NIF file and has no effect on the loading procedure.
|
/// @note This is just a hint for users of the NIF file and has no effect on the loading procedure.
|
||||||
void setUseSkinning(bool skinning) override;
|
void setUseSkinning(bool skinning) override;
|
||||||
|
@ -109,8 +152,17 @@ public:
|
||||||
|
|
||||||
/// Get the name of the file
|
/// Get the name of the file
|
||||||
std::string getFilename() const override { return filename; }
|
std::string getFilename() const override { return filename; }
|
||||||
|
|
||||||
|
/// Get the version of the NIF format used
|
||||||
|
unsigned int getVersion() const override { return ver; }
|
||||||
|
|
||||||
|
/// Get the user version of the NIF format used
|
||||||
|
unsigned int getUserVersion() const override { return userVer; }
|
||||||
|
|
||||||
|
/// Get the Bethesda version of the NIF format used
|
||||||
|
unsigned int getBethVersion() const override { return bethVer; }
|
||||||
};
|
};
|
||||||
typedef std::shared_ptr<const Nif::NIFFile> NIFFilePtr;
|
using NIFFilePtr = std::shared_ptr<const Nif::NIFFile>;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -26,34 +26,37 @@ struct KeyT {
|
||||||
float mContinuity; // Only for TBC interpolation
|
float mContinuity; // Only for TBC interpolation
|
||||||
*/
|
*/
|
||||||
};
|
};
|
||||||
typedef KeyT<float> FloatKey;
|
using FloatKey = KeyT<float>;
|
||||||
typedef KeyT<osg::Vec3f> Vector3Key;
|
using Vector3Key = KeyT<osg::Vec3f>;
|
||||||
typedef KeyT<osg::Vec4f> Vector4Key;
|
using Vector4Key = KeyT<osg::Vec4f>;
|
||||||
typedef KeyT<osg::Quat> QuaternionKey;
|
using QuaternionKey = KeyT<osg::Quat>;
|
||||||
|
|
||||||
template<typename T, T (NIFStream::*getValue)()>
|
template<typename T, T (NIFStream::*getValue)()>
|
||||||
struct KeyMapT {
|
struct KeyMapT {
|
||||||
typedef std::map< float, KeyT<T> > MapType;
|
using MapType = std::map<float, KeyT<T>>;
|
||||||
|
|
||||||
typedef T ValueType;
|
using ValueType = T;
|
||||||
typedef KeyT<T> KeyType;
|
using KeyType = KeyT<T>;
|
||||||
|
|
||||||
static const unsigned int sLinearInterpolation = 1;
|
enum InterpolationType
|
||||||
static const unsigned int sQuadraticInterpolation = 2;
|
{
|
||||||
static const unsigned int sTBCInterpolation = 3;
|
Unknown = 0,
|
||||||
static const unsigned int sXYZInterpolation = 4;
|
Linear = 1,
|
||||||
|
Quadratic = 2,
|
||||||
|
TBC = 3,
|
||||||
|
XYZ = 4,
|
||||||
|
Constant = 5
|
||||||
|
};
|
||||||
|
|
||||||
unsigned int mInterpolationType;
|
unsigned int mInterpolationType = Linear;
|
||||||
MapType mKeys;
|
MapType mKeys;
|
||||||
|
|
||||||
KeyMapT() : mInterpolationType(sLinearInterpolation) {}
|
|
||||||
|
|
||||||
//Read in a KeyGroup (see http://niftools.sourceforge.net/doc/nif/NiKeyframeData.html)
|
//Read in a KeyGroup (see http://niftools.sourceforge.net/doc/nif/NiKeyframeData.html)
|
||||||
void read(NIFStream *nif, bool force=false)
|
void read(NIFStream *nif, bool force=false)
|
||||||
{
|
{
|
||||||
assert(nif);
|
assert(nif);
|
||||||
|
|
||||||
mInterpolationType = 0;
|
mInterpolationType = Unknown;
|
||||||
|
|
||||||
size_t count = nif->getUInt();
|
size_t count = nif->getUInt();
|
||||||
if(count == 0 && !force)
|
if(count == 0 && !force)
|
||||||
|
@ -66,7 +69,7 @@ struct KeyMapT {
|
||||||
KeyT<T> key;
|
KeyT<T> key;
|
||||||
NIFStream &nifReference = *nif;
|
NIFStream &nifReference = *nif;
|
||||||
|
|
||||||
if(mInterpolationType == sLinearInterpolation)
|
if (mInterpolationType == Linear || mInterpolationType == Constant)
|
||||||
{
|
{
|
||||||
for(size_t i = 0;i < count;i++)
|
for(size_t i = 0;i < count;i++)
|
||||||
{
|
{
|
||||||
|
@ -75,7 +78,7 @@ struct KeyMapT {
|
||||||
mKeys[time] = key;
|
mKeys[time] = key;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(mInterpolationType == sQuadraticInterpolation)
|
else if (mInterpolationType == Quadratic)
|
||||||
{
|
{
|
||||||
for(size_t i = 0;i < count;i++)
|
for(size_t i = 0;i < count;i++)
|
||||||
{
|
{
|
||||||
|
@ -84,7 +87,7 @@ struct KeyMapT {
|
||||||
mKeys[time] = key;
|
mKeys[time] = key;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(mInterpolationType == sTBCInterpolation)
|
else if (mInterpolationType == TBC)
|
||||||
{
|
{
|
||||||
for(size_t i = 0;i < count;i++)
|
for(size_t i = 0;i < count;i++)
|
||||||
{
|
{
|
||||||
|
@ -98,7 +101,7 @@ struct KeyMapT {
|
||||||
// Eats a floating point number, then
|
// Eats a floating point number, then
|
||||||
// Re-runs the read function 3 more times.
|
// Re-runs the read function 3 more times.
|
||||||
// When it does that it's reading in a bunch of sLinearInterpolation keys, not sXYZInterpolation.
|
// When it does that it's reading in a bunch of sLinearInterpolation keys, not sXYZInterpolation.
|
||||||
else if(mInterpolationType == sXYZInterpolation)
|
else if(mInterpolationType == XYZ)
|
||||||
{
|
{
|
||||||
//Don't try to read XYZ keys into the wrong part
|
//Don't try to read XYZ keys into the wrong part
|
||||||
if ( count != 1 )
|
if ( count != 1 )
|
||||||
|
@ -109,7 +112,7 @@ struct KeyMapT {
|
||||||
nif->file->fail(error.str());
|
nif->file->fail(error.str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (0 == mInterpolationType)
|
else if (mInterpolationType == Unknown)
|
||||||
{
|
{
|
||||||
if (count != 0)
|
if (count != 0)
|
||||||
nif->file->fail("Interpolation type 0 doesn't work with keys");
|
nif->file->fail("Interpolation type 0 doesn't work with keys");
|
||||||
|
@ -149,15 +152,17 @@ private:
|
||||||
/*key.mContinuity = */nif.getFloat();
|
/*key.mContinuity = */nif.getFloat();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
typedef KeyMapT<float,&NIFStream::getFloat> FloatKeyMap;
|
using FloatKeyMap = KeyMapT<float,&NIFStream::getFloat>;
|
||||||
typedef KeyMapT<osg::Vec3f,&NIFStream::getVector3> Vector3KeyMap;
|
using Vector3KeyMap = KeyMapT<osg::Vec3f,&NIFStream::getVector3>;
|
||||||
typedef KeyMapT<osg::Vec4f,&NIFStream::getVector4> Vector4KeyMap;
|
using Vector4KeyMap = KeyMapT<osg::Vec4f,&NIFStream::getVector4>;
|
||||||
typedef KeyMapT<osg::Quat,&NIFStream::getQuaternion> QuaternionKeyMap;
|
using QuaternionKeyMap = KeyMapT<osg::Quat,&NIFStream::getQuaternion>;
|
||||||
|
using ByteKeyMap = KeyMapT<char,&NIFStream::getChar>;
|
||||||
|
|
||||||
typedef std::shared_ptr<FloatKeyMap> FloatKeyMapPtr;
|
using FloatKeyMapPtr = std::shared_ptr<FloatKeyMap>;
|
||||||
typedef std::shared_ptr<Vector3KeyMap> Vector3KeyMapPtr;
|
using Vector3KeyMapPtr = std::shared_ptr<Vector3KeyMap>;
|
||||||
typedef std::shared_ptr<Vector4KeyMap> Vector4KeyMapPtr;
|
using Vector4KeyMapPtr = std::shared_ptr<Vector4KeyMap>;
|
||||||
typedef std::shared_ptr<QuaternionKeyMap> QuaternionKeyMapPtr;
|
using QuaternionKeyMapPtr = std::shared_ptr<QuaternionKeyMap>;
|
||||||
|
using ByteKeyMapPtr = std::shared_ptr<ByteKeyMap>;
|
||||||
|
|
||||||
} // Namespace
|
} // Namespace
|
||||||
#endif //#ifndef OPENMW_COMPONENTS_NIF_NIFKEY_HPP
|
#endif //#ifndef OPENMW_COMPONENTS_NIF_NIFKEY_HPP
|
||||||
|
|
|
@ -24,4 +24,21 @@ namespace Nif
|
||||||
t.scale = getFloat();
|
t.scale = getFloat();
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///Currently specific for 4.0.0.2 and earlier
|
||||||
|
bool NIFStream::getBoolean()
|
||||||
|
{
|
||||||
|
return !!getInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
///Read in a string, either from the string table using the index (currently absent) or from the stream using the specified length
|
||||||
|
std::string NIFStream::getString()
|
||||||
|
{
|
||||||
|
return getSizedString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convenience utility functions: get the versions of the currently read file
|
||||||
|
unsigned int NIFStream::getVersion() { return file->getVersion(); }
|
||||||
|
unsigned int NIFStream::getUserVersion() { return file->getBethVersion(); }
|
||||||
|
unsigned int NIFStream::getBethVersion() { return file->getBethVersion(); }
|
||||||
}
|
}
|
||||||
|
|
|
@ -155,8 +155,16 @@ public:
|
||||||
|
|
||||||
Transformation getTrafo();
|
Transformation getTrafo();
|
||||||
|
|
||||||
|
bool getBoolean();
|
||||||
|
|
||||||
|
std::string getString();
|
||||||
|
|
||||||
|
unsigned int getVersion();
|
||||||
|
unsigned int getUserVersion();
|
||||||
|
unsigned int getBethVersion();
|
||||||
|
|
||||||
///Read in a string of the given length
|
///Read in a string of the given length
|
||||||
std::string getString(size_t length)
|
std::string getSizedString(size_t length)
|
||||||
{
|
{
|
||||||
std::vector<char> str(length + 1, 0);
|
std::vector<char> str(length + 1, 0);
|
||||||
|
|
||||||
|
@ -165,11 +173,19 @@ public:
|
||||||
return str.data();
|
return str.data();
|
||||||
}
|
}
|
||||||
///Read in a string of the length specified in the file
|
///Read in a string of the length specified in the file
|
||||||
std::string getString()
|
std::string getSizedString()
|
||||||
{
|
{
|
||||||
size_t size = readLittleEndianType<uint32_t,uint32_t>(inp);
|
size_t size = readLittleEndianType<uint32_t,uint32_t>(inp);
|
||||||
return getString(size);
|
return getSizedString(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///Specific to Bethesda headers, uses a byte for length
|
||||||
|
std::string getExportString()
|
||||||
|
{
|
||||||
|
size_t size = static_cast<size_t>(readLittleEndianType<uint8_t,uint8_t>(inp));
|
||||||
|
return getSizedString(size);
|
||||||
|
}
|
||||||
|
|
||||||
///This is special since the version string doesn't start with a number, and ends with "\n"
|
///This is special since the version string doesn't start with a number, and ends with "\n"
|
||||||
std::string getVersionString()
|
std::string getVersionString()
|
||||||
{
|
{
|
||||||
|
@ -190,6 +206,18 @@ public:
|
||||||
readLittleEndianDynamicBufferOfType<float,uint32_t>(inp, vec.data(), size);
|
readLittleEndianDynamicBufferOfType<float,uint32_t>(inp, vec.data(), size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void getInts(std::vector<int> &vec, size_t size)
|
||||||
|
{
|
||||||
|
vec.resize(size);
|
||||||
|
readLittleEndianDynamicBufferOfType<int,int>(inp, vec.data(), size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void getUInts(std::vector<unsigned int> &vec, size_t size)
|
||||||
|
{
|
||||||
|
vec.resize(size);
|
||||||
|
readLittleEndianDynamicBufferOfType<unsigned int,unsigned int>(inp, vec.data(), size);
|
||||||
|
}
|
||||||
|
|
||||||
void getVector2s(std::vector<osg::Vec2f> &vec, size_t size)
|
void getVector2s(std::vector<osg::Vec2f> &vec, size_t size)
|
||||||
{
|
{
|
||||||
vec.resize(size);
|
vec.resize(size);
|
||||||
|
@ -217,6 +245,20 @@ public:
|
||||||
for (size_t i = 0;i < quat.size();i++)
|
for (size_t i = 0;i < quat.size();i++)
|
||||||
quat[i] = getQuaternion();
|
quat[i] = getQuaternion();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void getStrings(std::vector<std::string> &vec, size_t size)
|
||||||
|
{
|
||||||
|
vec.resize(size);
|
||||||
|
for (size_t i = 0; i < vec.size(); i++)
|
||||||
|
vec[i] = getString();
|
||||||
|
}
|
||||||
|
/// We need to use this when the string table isn't actually initialized.
|
||||||
|
void getSizedStrings(std::vector<std::string> &vec, size_t size)
|
||||||
|
{
|
||||||
|
vec.resize(size);
|
||||||
|
for (size_t i = 0; i < vec.size(); i++)
|
||||||
|
vec[i] = getSizedString();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ class Node : public Named
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// Node flags. Interpretation depends somewhat on the type of node.
|
// Node flags. Interpretation depends somewhat on the type of node.
|
||||||
int flags;
|
unsigned int flags;
|
||||||
Transformation trafo;
|
Transformation trafo;
|
||||||
osg::Vec3f velocity; // Unused? Might be a run-time game state
|
osg::Vec3f velocity; // Unused? Might be a run-time game state
|
||||||
PropertyList props;
|
PropertyList props;
|
||||||
|
|
|
@ -14,7 +14,7 @@ void Property::read(NIFStream *nif)
|
||||||
|
|
||||||
void NiTexturingProperty::Texture::read(NIFStream *nif)
|
void NiTexturingProperty::Texture::read(NIFStream *nif)
|
||||||
{
|
{
|
||||||
inUse = !!nif->getInt();
|
inUse = nif->getBoolean();
|
||||||
if(!inUse) return;
|
if(!inUse) return;
|
||||||
|
|
||||||
texture.read(nif);
|
texture.read(nif);
|
||||||
|
|
|
@ -167,6 +167,7 @@ using NiParticleModifierPtr = RecordPtrT<NiParticleModifier>;
|
||||||
|
|
||||||
using NodeList = RecordListT<Node>;
|
using NodeList = RecordListT<Node>;
|
||||||
using PropertyList = RecordListT<Property>;
|
using PropertyList = RecordListT<Property>;
|
||||||
|
using ExtraList = RecordListT<Extra>;
|
||||||
using NiSourceTextureList = RecordListT<NiSourceTexture>;
|
using NiSourceTextureList = RecordListT<NiSourceTexture>;
|
||||||
|
|
||||||
} // Namespace
|
} // Namespace
|
||||||
|
|
|
@ -35,17 +35,33 @@ namespace NifOsg
|
||||||
{
|
{
|
||||||
|
|
||||||
// interpolation of keyframes
|
// interpolation of keyframes
|
||||||
template <typename MapT, typename InterpolationFunc>
|
template <typename MapT>
|
||||||
class ValueInterpolator
|
class ValueInterpolator
|
||||||
{
|
{
|
||||||
public:
|
typename MapT::MapType::const_iterator retrieveKey(float time) const
|
||||||
typedef typename MapT::ValueType ValueT;
|
|
||||||
|
|
||||||
ValueInterpolator()
|
|
||||||
: mDefaultVal(ValueT())
|
|
||||||
{
|
{
|
||||||
|
// retrieve the current position in the map, optimized for the most common case
|
||||||
|
// where time moves linearly along the keyframe track
|
||||||
|
if (mLastHighKey != mKeys->mKeys.end())
|
||||||
|
{
|
||||||
|
if (time > mLastHighKey->first)
|
||||||
|
{
|
||||||
|
// try if we're there by incrementing one
|
||||||
|
++mLastLowKey;
|
||||||
|
++mLastHighKey;
|
||||||
|
}
|
||||||
|
if (mLastHighKey != mKeys->mKeys.end() && time >= mLastLowKey->first && time <= mLastHighKey->first)
|
||||||
|
return mLastHighKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mKeys->mKeys.lower_bound(time);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
using ValueT = typename MapT::ValueType;
|
||||||
|
|
||||||
|
ValueInterpolator() = default;
|
||||||
|
|
||||||
ValueInterpolator(std::shared_ptr<const MapT> keys, ValueT defaultVal = ValueT())
|
ValueInterpolator(std::shared_ptr<const MapT> keys, ValueT defaultVal = ValueT())
|
||||||
: mKeys(keys)
|
: mKeys(keys)
|
||||||
, mDefaultVal(defaultVal)
|
, mDefaultVal(defaultVal)
|
||||||
|
@ -67,44 +83,21 @@ namespace NifOsg
|
||||||
if(time <= keys.begin()->first)
|
if(time <= keys.begin()->first)
|
||||||
return keys.begin()->second.mValue;
|
return keys.begin()->second.mValue;
|
||||||
|
|
||||||
// retrieve the current position in the map, optimized for the most common case
|
typename MapT::MapType::const_iterator it = retrieveKey(time);
|
||||||
// where time moves linearly along the keyframe track
|
|
||||||
typename MapT::MapType::const_iterator it = mLastHighKey;
|
|
||||||
if (mLastHighKey != keys.end())
|
|
||||||
{
|
|
||||||
if (time > mLastHighKey->first)
|
|
||||||
{
|
|
||||||
// try if we're there by incrementing one
|
|
||||||
++mLastLowKey;
|
|
||||||
++mLastHighKey;
|
|
||||||
it = mLastHighKey;
|
|
||||||
}
|
|
||||||
if (mLastHighKey == keys.end() || (time < mLastLowKey->first || time > mLastHighKey->first))
|
|
||||||
it = keys.lower_bound(time); // still not there, reorient by performing lower_bound check on the whole map
|
|
||||||
}
|
|
||||||
else
|
|
||||||
it = keys.lower_bound(time);
|
|
||||||
|
|
||||||
// now do the actual interpolation
|
// now do the actual interpolation
|
||||||
if (it != keys.end())
|
if (it != keys.end())
|
||||||
{
|
{
|
||||||
float aTime = it->first;
|
|
||||||
const typename MapT::KeyType* aKey = &it->second;
|
|
||||||
|
|
||||||
// cache for next time
|
// cache for next time
|
||||||
mLastHighKey = it;
|
mLastHighKey = it;
|
||||||
|
mLastLowKey = --it;
|
||||||
|
|
||||||
typename MapT::MapType::const_iterator last = --it;
|
float a = (time - mLastLowKey->first) / (mLastHighKey->first - mLastLowKey->first);
|
||||||
mLastLowKey = last;
|
|
||||||
float aLastTime = last->first;
|
|
||||||
const typename MapT::KeyType* aLastKey = &last->second;
|
|
||||||
|
|
||||||
float a = (time - aLastTime) / (aTime - aLastTime);
|
return interpolate(mLastLowKey->second, mLastHighKey->second, a, mKeys->mInterpolationType);
|
||||||
|
|
||||||
return InterpolationFunc()(aLastKey->mValue, aKey->mValue, a);
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
return keys.rbegin()->second.mValue;
|
return keys.rbegin()->second.mValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool empty() const
|
bool empty() const
|
||||||
|
@ -113,36 +106,44 @@ namespace NifOsg
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
template <typename ValueType>
|
||||||
|
ValueType interpolate(const Nif::KeyT<ValueType>& a, const Nif::KeyT<ValueType>& b, float fraction, unsigned int type) const
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case 5:
|
||||||
|
return fraction > 0.5f ? b.mValue : a.mValue;
|
||||||
|
default:
|
||||||
|
return a.mValue + ((b.mValue - a.mValue) * fraction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
osg::Quat interpolate(const Nif::KeyT<osg::Quat>& a, const Nif::KeyT<osg::Quat>& b, float fraction, unsigned int type) const
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case 5:
|
||||||
|
return fraction > 0.5f ? b.mValue : a.mValue;
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
osg::Quat result;
|
||||||
|
result.slerp(fraction, a.mValue, b.mValue);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mutable typename MapT::MapType::const_iterator mLastLowKey;
|
mutable typename MapT::MapType::const_iterator mLastLowKey;
|
||||||
mutable typename MapT::MapType::const_iterator mLastHighKey;
|
mutable typename MapT::MapType::const_iterator mLastHighKey;
|
||||||
|
|
||||||
std::shared_ptr<const MapT> mKeys;
|
std::shared_ptr<const MapT> mKeys;
|
||||||
|
|
||||||
ValueT mDefaultVal;
|
ValueT mDefaultVal = ValueT();
|
||||||
};
|
};
|
||||||
|
|
||||||
struct LerpFunc
|
using QuaternionInterpolator = ValueInterpolator<Nif::QuaternionKeyMap>;
|
||||||
{
|
using FloatInterpolator = ValueInterpolator<Nif::FloatKeyMap>;
|
||||||
template <typename ValueType>
|
using Vec3Interpolator = ValueInterpolator<Nif::Vector3KeyMap>;
|
||||||
inline ValueType operator()(const ValueType& a, const ValueType& b, float fraction)
|
using Vec4Interpolator = ValueInterpolator<Nif::Vector4KeyMap>;
|
||||||
{
|
|
||||||
return a + ((b - a) * fraction);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct QuaternionSlerpFunc
|
|
||||||
{
|
|
||||||
inline osg::Quat operator()(const osg::Quat& a, const osg::Quat& b, float fraction)
|
|
||||||
{
|
|
||||||
osg::Quat result;
|
|
||||||
result.slerp(fraction, a, b);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef ValueInterpolator<Nif::QuaternionKeyMap, QuaternionSlerpFunc> QuaternionInterpolator;
|
|
||||||
typedef ValueInterpolator<Nif::FloatKeyMap, LerpFunc> FloatInterpolator;
|
|
||||||
typedef ValueInterpolator<Nif::Vector3KeyMap, LerpFunc> Vec3Interpolator;
|
|
||||||
|
|
||||||
class ControllerFunction : public SceneUtil::ControllerFunction
|
class ControllerFunction : public SceneUtil::ControllerFunction
|
||||||
{
|
{
|
||||||
|
|
|
@ -184,14 +184,16 @@ namespace NifOsg
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// @param filename used for warning messages.
|
/// @param filename used for warning messages.
|
||||||
LoaderImpl(const std::string& filename)
|
LoaderImpl(const std::string& filename, unsigned int ver, unsigned int userver, unsigned int bethver)
|
||||||
: mFilename(filename), mFirstRootTextureIndex(-1), mFoundFirstRootTexturingProperty(false)
|
: mFilename(filename), mVersion(ver), mUserVersion(userver), mBethVersion(bethver)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
std::string mFilename;
|
std::string mFilename;
|
||||||
size_t mFirstRootTextureIndex;
|
unsigned int mVersion, mUserVersion, mBethVersion;
|
||||||
bool mFoundFirstRootTexturingProperty;
|
|
||||||
|
size_t mFirstRootTextureIndex = -1;
|
||||||
|
bool mFoundFirstRootTexturingProperty = false;
|
||||||
|
|
||||||
static void loadKf(Nif::NIFFilePtr nif, KeyframeHolder& target)
|
static void loadKf(Nif::NIFFilePtr nif, KeyframeHolder& target)
|
||||||
{
|
{
|
||||||
|
@ -1846,13 +1848,13 @@ namespace NifOsg
|
||||||
|
|
||||||
osg::ref_ptr<osg::Node> Loader::load(Nif::NIFFilePtr file, Resource::ImageManager* imageManager)
|
osg::ref_ptr<osg::Node> Loader::load(Nif::NIFFilePtr file, Resource::ImageManager* imageManager)
|
||||||
{
|
{
|
||||||
LoaderImpl impl(file->getFilename());
|
LoaderImpl impl(file->getFilename(), file->getVersion(), file->getUserVersion(), file->getBethVersion());
|
||||||
return impl.load(file, imageManager);
|
return impl.load(file, imageManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Loader::loadKf(Nif::NIFFilePtr kf, KeyframeHolder& target)
|
void Loader::loadKf(Nif::NIFFilePtr kf, KeyframeHolder& target)
|
||||||
{
|
{
|
||||||
LoaderImpl impl(kf->getFilename());
|
LoaderImpl impl(kf->getFilename(), kf->getVersion(), kf->getUserVersion(), kf->getBethVersion());
|
||||||
impl.loadKf(kf, target);
|
impl.loadKf(kf, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -151,7 +151,6 @@ namespace NifOsg
|
||||||
float mCachedDefaultSize;
|
float mCachedDefaultSize;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef ValueInterpolator<Nif::Vector4KeyMap, LerpFunc> Vec4Interpolator;
|
|
||||||
class ParticleColorAffector : public osgParticle::Operator
|
class ParticleColorAffector : public osgParticle::Operator
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
Loading…
Reference in a new issue