mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-02-06 19:45:33 +00:00
Merge pull request #2649 from Capostrophic/nifstreamline
Yet more NIF adjustments
This commit is contained in:
commit
95f9e4f4c9
14 changed files with 246 additions and 135 deletions
|
@ -266,9 +266,13 @@ namespace
|
|||
MOCK_CONST_METHOD0(numRecords, std::size_t ());
|
||||
MOCK_CONST_METHOD1(getRoot, Nif::Record* (std::size_t));
|
||||
MOCK_CONST_METHOD0(numRoots, std::size_t ());
|
||||
MOCK_CONST_METHOD1(getString, std::string (std::size_t));
|
||||
MOCK_METHOD1(setUseSkinning, void (bool));
|
||||
MOCK_CONST_METHOD0(getUseSkinning, bool ());
|
||||
MOCK_CONST_METHOD0(getFilename, std::string ());
|
||||
MOCK_CONST_METHOD0(getVersion, unsigned int ());
|
||||
MOCK_CONST_METHOD0(getUserVersion, unsigned int ());
|
||||
MOCK_CONST_METHOD0(getBethVersion, unsigned int ());
|
||||
};
|
||||
|
||||
struct RecordMock : Nif::Record
|
||||
|
|
|
@ -9,7 +9,7 @@ namespace Nif
|
|||
{
|
||||
Named::read(nif);
|
||||
|
||||
external = !!nif->getChar();
|
||||
external = nif->getChar() != 0;
|
||||
if(external)
|
||||
filename = nif->getString();
|
||||
else
|
||||
|
|
|
@ -35,16 +35,16 @@ void ShapeData::read(NIFStream *nif)
|
|||
{
|
||||
int verts = nif->getUShort();
|
||||
|
||||
if(nif->getInt())
|
||||
if (nif->getBoolean())
|
||||
nif->getVector3s(vertices, verts);
|
||||
|
||||
if(nif->getInt())
|
||||
if (nif->getBoolean())
|
||||
nif->getVector3s(normals, verts);
|
||||
|
||||
center = nif->getVector3();
|
||||
radius = nif->getFloat();
|
||||
|
||||
if(nif->getInt())
|
||||
if (nif->getBoolean())
|
||||
nif->getVector4s(colors, verts);
|
||||
|
||||
// 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();
|
||||
activeCount = nif->getUShort();
|
||||
|
||||
if(nif->getInt())
|
||||
if (nif->getBoolean())
|
||||
{
|
||||
// Particle sizes
|
||||
nif->getFloats(sizes, vertices.size());
|
||||
|
@ -131,7 +131,7 @@ void NiRotatingParticlesData::read(NIFStream *nif)
|
|||
{
|
||||
NiAutoNormalParticlesData::read(nif);
|
||||
|
||||
if(nif->getInt())
|
||||
if (nif->getBoolean())
|
||||
{
|
||||
// Rotation quaternions.
|
||||
nif->getQuaternions(rotations, vertices.size());
|
||||
|
@ -176,7 +176,7 @@ void NiPixelData::read(NIFStream *nif)
|
|||
|
||||
numberOfMipmaps = nif->getUInt();
|
||||
|
||||
// Bytes per pixel, should be bpp * 8
|
||||
// Bytes per pixel, should be bpp / 8
|
||||
/* int bytes = */ nif->getUInt();
|
||||
|
||||
for(unsigned int i=0; i<numberOfMipmaps; i++)
|
||||
|
@ -228,10 +228,8 @@ void NiSkinData::read(NIFStream *nif)
|
|||
nif->getInt(); // -1
|
||||
|
||||
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.pos = nif->getVector3();
|
||||
bi.trafo.scale = nif->getFloat();
|
||||
|
@ -267,7 +265,7 @@ void NiKeyframeData::read(NIFStream *nif)
|
|||
{
|
||||
mRotations = std::make_shared<QuaternionKeyMap>();
|
||||
mRotations->read(nif);
|
||||
if(mRotations->mInterpolationType == Vector3KeyMap::sXYZInterpolation)
|
||||
if(mRotations->mInterpolationType == InterpolationType_XYZ)
|
||||
{
|
||||
//Chomp unused float
|
||||
nif->getFloat();
|
||||
|
|
|
@ -9,9 +9,7 @@ namespace Nif
|
|||
|
||||
/// Open a NIF stream. The name is used for error messages.
|
||||
NIFFile::NIFFile(Files::IStreamPtr stream, const std::string &name)
|
||||
: ver(0)
|
||||
, filename(name)
|
||||
, mUseSkinning(false)
|
||||
: filename(name)
|
||||
{
|
||||
parse(stream);
|
||||
}
|
||||
|
@ -145,21 +143,12 @@ void NIFFile::parse(Files::IStreamPtr stream)
|
|||
ver = nif.getUInt();
|
||||
// 4.0.0.0 is an older, practically identical version of the format.
|
||||
// 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));
|
||||
// Number of records
|
||||
size_t recNum = nif.getInt();
|
||||
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++)
|
||||
{
|
||||
Record *r = nullptr;
|
||||
|
|
|
@ -26,21 +26,27 @@ struct File
|
|||
|
||||
virtual size_t numRoots() const = 0;
|
||||
|
||||
virtual std::string getString(size_t index) const = 0;
|
||||
|
||||
virtual void setUseSkinning(bool skinning) = 0;
|
||||
|
||||
virtual bool getUseSkinning() 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
|
||||
{
|
||||
enum NIFVersion {
|
||||
VER_MW = 0x04000002 // Morrowind NIFs
|
||||
};
|
||||
|
||||
/// Nif file version
|
||||
unsigned int ver;
|
||||
/// File version, user version, Bethesda version
|
||||
unsigned int ver = 0;
|
||||
unsigned int userVer = 0;
|
||||
unsigned int bethVer = 0;
|
||||
|
||||
/// File name, used for error messages and opening the file
|
||||
std::string filename;
|
||||
|
@ -51,7 +57,10 @@ class NIFFile final : public File
|
|||
/// Root list. This is a select portion of the pointers from records
|
||||
std::vector<Record*> roots;
|
||||
|
||||
bool mUseSkinning;
|
||||
/// String table
|
||||
std::vector<std::string> strings;
|
||||
|
||||
bool mUseSkinning = false;
|
||||
|
||||
/// Parse the file
|
||||
void parse(Files::IStreamPtr stream);
|
||||
|
@ -66,6 +75,34 @@ class NIFFile final : public File
|
|||
void operator = (NIFFile const &);
|
||||
|
||||
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
|
||||
void fail(const std::string &msg) const
|
||||
{
|
||||
|
@ -101,6 +138,12 @@ public:
|
|||
/// Number of roots
|
||||
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.
|
||||
/// @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;
|
||||
|
@ -109,8 +152,17 @@ public:
|
|||
|
||||
/// Get the name of the file
|
||||
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>;
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -13,6 +13,16 @@
|
|||
namespace Nif
|
||||
{
|
||||
|
||||
enum InterpolationType
|
||||
{
|
||||
InterpolationType_Unknown = 0,
|
||||
InterpolationType_Linear = 1,
|
||||
InterpolationType_Quadratic = 2,
|
||||
InterpolationType_TBC = 3,
|
||||
InterpolationType_XYZ = 4,
|
||||
InterpolationType_Constant = 5
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct KeyT {
|
||||
T mValue;
|
||||
|
@ -26,34 +36,27 @@ struct KeyT {
|
|||
float mContinuity; // Only for TBC interpolation
|
||||
*/
|
||||
};
|
||||
typedef KeyT<float> FloatKey;
|
||||
typedef KeyT<osg::Vec3f> Vector3Key;
|
||||
typedef KeyT<osg::Vec4f> Vector4Key;
|
||||
typedef KeyT<osg::Quat> QuaternionKey;
|
||||
using FloatKey = KeyT<float>;
|
||||
using Vector3Key = KeyT<osg::Vec3f>;
|
||||
using Vector4Key = KeyT<osg::Vec4f>;
|
||||
using QuaternionKey = KeyT<osg::Quat>;
|
||||
|
||||
template<typename T, T (NIFStream::*getValue)()>
|
||||
struct KeyMapT {
|
||||
typedef std::map< float, KeyT<T> > MapType;
|
||||
using MapType = std::map<float, KeyT<T>>;
|
||||
|
||||
typedef T ValueType;
|
||||
typedef KeyT<T> KeyType;
|
||||
using ValueType = T;
|
||||
using KeyType = KeyT<T>;
|
||||
|
||||
static const unsigned int sLinearInterpolation = 1;
|
||||
static const unsigned int sQuadraticInterpolation = 2;
|
||||
static const unsigned int sTBCInterpolation = 3;
|
||||
static const unsigned int sXYZInterpolation = 4;
|
||||
|
||||
unsigned int mInterpolationType;
|
||||
unsigned int mInterpolationType = InterpolationType_Linear;
|
||||
MapType mKeys;
|
||||
|
||||
KeyMapT() : mInterpolationType(sLinearInterpolation) {}
|
||||
|
||||
//Read in a KeyGroup (see http://niftools.sourceforge.net/doc/nif/NiKeyframeData.html)
|
||||
void read(NIFStream *nif, bool force=false)
|
||||
{
|
||||
assert(nif);
|
||||
|
||||
mInterpolationType = 0;
|
||||
mInterpolationType = InterpolationType_Unknown;
|
||||
|
||||
size_t count = nif->getUInt();
|
||||
if(count == 0 && !force)
|
||||
|
@ -66,7 +69,8 @@ struct KeyMapT {
|
|||
KeyT<T> key;
|
||||
NIFStream &nifReference = *nif;
|
||||
|
||||
if(mInterpolationType == sLinearInterpolation)
|
||||
if (mInterpolationType == InterpolationType_Linear
|
||||
|| mInterpolationType == InterpolationType_Constant)
|
||||
{
|
||||
for(size_t i = 0;i < count;i++)
|
||||
{
|
||||
|
@ -75,7 +79,7 @@ struct KeyMapT {
|
|||
mKeys[time] = key;
|
||||
}
|
||||
}
|
||||
else if(mInterpolationType == sQuadraticInterpolation)
|
||||
else if (mInterpolationType == InterpolationType_Quadratic)
|
||||
{
|
||||
for(size_t i = 0;i < count;i++)
|
||||
{
|
||||
|
@ -84,7 +88,7 @@ struct KeyMapT {
|
|||
mKeys[time] = key;
|
||||
}
|
||||
}
|
||||
else if(mInterpolationType == sTBCInterpolation)
|
||||
else if (mInterpolationType == InterpolationType_TBC)
|
||||
{
|
||||
for(size_t i = 0;i < count;i++)
|
||||
{
|
||||
|
@ -94,11 +98,11 @@ struct KeyMapT {
|
|||
}
|
||||
}
|
||||
//XYZ keys aren't actually read here.
|
||||
//data.hpp sees that the last type read was sXYZInterpolation and:
|
||||
//data.hpp sees that the last type read was InterpolationType_XYZ and:
|
||||
// Eats a floating point number, then
|
||||
// Re-runs the read function 3 more times.
|
||||
// When it does that it's reading in a bunch of sLinearInterpolation keys, not sXYZInterpolation.
|
||||
else if(mInterpolationType == sXYZInterpolation)
|
||||
// When it does that it's reading in a bunch of InterpolationType_Linear keys, not InterpolationType_XYZ.
|
||||
else if(mInterpolationType == InterpolationType_XYZ)
|
||||
{
|
||||
//Don't try to read XYZ keys into the wrong part
|
||||
if ( count != 1 )
|
||||
|
@ -109,7 +113,7 @@ struct KeyMapT {
|
|||
nif->file->fail(error.str());
|
||||
}
|
||||
}
|
||||
else if (0 == mInterpolationType)
|
||||
else if (mInterpolationType == InterpolationType_Unknown)
|
||||
{
|
||||
if (count != 0)
|
||||
nif->file->fail("Interpolation type 0 doesn't work with keys");
|
||||
|
@ -149,15 +153,17 @@ private:
|
|||
/*key.mContinuity = */nif.getFloat();
|
||||
}
|
||||
};
|
||||
typedef KeyMapT<float,&NIFStream::getFloat> FloatKeyMap;
|
||||
typedef KeyMapT<osg::Vec3f,&NIFStream::getVector3> Vector3KeyMap;
|
||||
typedef KeyMapT<osg::Vec4f,&NIFStream::getVector4> Vector4KeyMap;
|
||||
typedef KeyMapT<osg::Quat,&NIFStream::getQuaternion> QuaternionKeyMap;
|
||||
using FloatKeyMap = KeyMapT<float,&NIFStream::getFloat>;
|
||||
using Vector3KeyMap = KeyMapT<osg::Vec3f,&NIFStream::getVector3>;
|
||||
using Vector4KeyMap = KeyMapT<osg::Vec4f,&NIFStream::getVector4>;
|
||||
using QuaternionKeyMap = KeyMapT<osg::Quat,&NIFStream::getQuaternion>;
|
||||
using ByteKeyMap = KeyMapT<char,&NIFStream::getChar>;
|
||||
|
||||
typedef std::shared_ptr<FloatKeyMap> FloatKeyMapPtr;
|
||||
typedef std::shared_ptr<Vector3KeyMap> Vector3KeyMapPtr;
|
||||
typedef std::shared_ptr<Vector4KeyMap> Vector4KeyMapPtr;
|
||||
typedef std::shared_ptr<QuaternionKeyMap> QuaternionKeyMapPtr;
|
||||
using FloatKeyMapPtr = std::shared_ptr<FloatKeyMap>;
|
||||
using Vector3KeyMapPtr = std::shared_ptr<Vector3KeyMap>;
|
||||
using Vector4KeyMapPtr = std::shared_ptr<Vector4KeyMap>;
|
||||
using QuaternionKeyMapPtr = std::shared_ptr<QuaternionKeyMap>;
|
||||
using ByteKeyMapPtr = std::shared_ptr<ByteKeyMap>;
|
||||
|
||||
} // Namespace
|
||||
#endif //#ifndef OPENMW_COMPONENTS_NIF_NIFKEY_HPP
|
||||
|
|
|
@ -24,4 +24,21 @@ namespace Nif
|
|||
t.scale = getFloat();
|
||||
return t;
|
||||
}
|
||||
|
||||
///Currently specific for 4.0.0.2 and earlier
|
||||
bool NIFStream::getBoolean()
|
||||
{
|
||||
return getInt() != 0;
|
||||
}
|
||||
|
||||
///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();
|
||||
|
||||
bool getBoolean();
|
||||
|
||||
std::string getString();
|
||||
|
||||
unsigned int getVersion();
|
||||
unsigned int getUserVersion();
|
||||
unsigned int getBethVersion();
|
||||
|
||||
///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);
|
||||
|
||||
|
@ -165,11 +173,19 @@ public:
|
|||
return str.data();
|
||||
}
|
||||
///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);
|
||||
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"
|
||||
std::string getVersionString()
|
||||
{
|
||||
|
@ -190,6 +206,18 @@ public:
|
|||
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)
|
||||
{
|
||||
vec.resize(size);
|
||||
|
@ -217,6 +245,20 @@ public:
|
|||
for (size_t i = 0;i < quat.size();i++)
|
||||
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:
|
||||
// Node flags. Interpretation depends somewhat on the type of node.
|
||||
int flags;
|
||||
unsigned int flags;
|
||||
Transformation trafo;
|
||||
osg::Vec3f velocity; // Unused? Might be a run-time game state
|
||||
PropertyList props;
|
||||
|
@ -44,7 +44,7 @@ public:
|
|||
velocity = nif->getVector3();
|
||||
props.read(nif);
|
||||
|
||||
hasBounds = !!nif->getInt();
|
||||
hasBounds = nif->getBoolean();
|
||||
if(hasBounds)
|
||||
{
|
||||
nif->getInt(); // always 1
|
||||
|
|
|
@ -14,7 +14,7 @@ void Property::read(NIFStream *nif)
|
|||
|
||||
void NiTexturingProperty::Texture::read(NIFStream *nif)
|
||||
{
|
||||
inUse = !!nif->getInt();
|
||||
inUse = nif->getBoolean();
|
||||
if(!inUse) return;
|
||||
|
||||
texture.read(nif);
|
||||
|
|
|
@ -167,6 +167,7 @@ using NiParticleModifierPtr = RecordPtrT<NiParticleModifier>;
|
|||
|
||||
using NodeList = RecordListT<Node>;
|
||||
using PropertyList = RecordListT<Property>;
|
||||
using ExtraList = RecordListT<Extra>;
|
||||
using NiSourceTextureList = RecordListT<NiSourceTexture>;
|
||||
|
||||
} // Namespace
|
||||
|
|
|
@ -35,16 +35,32 @@ namespace NifOsg
|
|||
{
|
||||
|
||||
// interpolation of keyframes
|
||||
template <typename MapT, typename InterpolationFunc>
|
||||
template <typename MapT>
|
||||
class ValueInterpolator
|
||||
{
|
||||
public:
|
||||
typedef typename MapT::ValueType ValueT;
|
||||
|
||||
ValueInterpolator()
|
||||
: mDefaultVal(ValueT())
|
||||
typename MapT::MapType::const_iterator retrieveKey(float time) const
|
||||
{
|
||||
// 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())
|
||||
: mKeys(keys)
|
||||
|
@ -67,43 +83,20 @@ namespace NifOsg
|
|||
if(time <= keys.begin()->first)
|
||||
return keys.begin()->second.mValue;
|
||||
|
||||
// retrieve the current position in the map, optimized for the most common case
|
||||
// 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);
|
||||
typename MapT::MapType::const_iterator it = retrieveKey(time);
|
||||
|
||||
// now do the actual interpolation
|
||||
if (it != keys.end())
|
||||
{
|
||||
float aTime = it->first;
|
||||
const typename MapT::KeyType* aKey = &it->second;
|
||||
|
||||
// cache for next time
|
||||
mLastHighKey = it;
|
||||
mLastLowKey = --it;
|
||||
|
||||
typename MapT::MapType::const_iterator last = --it;
|
||||
mLastLowKey = last;
|
||||
float aLastTime = last->first;
|
||||
const typename MapT::KeyType* aLastKey = &last->second;
|
||||
float a = (time - mLastLowKey->first) / (mLastHighKey->first - mLastLowKey->first);
|
||||
|
||||
float a = (time - aLastTime) / (aTime - aLastTime);
|
||||
|
||||
return InterpolationFunc()(aLastKey->mValue, aKey->mValue, a);
|
||||
return interpolate(mLastLowKey->second, mLastHighKey->second, a, mKeys->mInterpolationType);
|
||||
}
|
||||
else
|
||||
|
||||
return keys.rbegin()->second.mValue;
|
||||
}
|
||||
|
||||
|
@ -113,36 +106,44 @@ namespace NifOsg
|
|||
}
|
||||
|
||||
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 Nif::InterpolationType_Constant:
|
||||
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 Nif::InterpolationType_Constant:
|
||||
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 mLastHighKey;
|
||||
|
||||
std::shared_ptr<const MapT> mKeys;
|
||||
|
||||
ValueT mDefaultVal;
|
||||
ValueT mDefaultVal = ValueT();
|
||||
};
|
||||
|
||||
struct LerpFunc
|
||||
{
|
||||
template <typename ValueType>
|
||||
inline ValueType operator()(const ValueType& a, const ValueType& b, float fraction)
|
||||
{
|
||||
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;
|
||||
using QuaternionInterpolator = ValueInterpolator<Nif::QuaternionKeyMap>;
|
||||
using FloatInterpolator = ValueInterpolator<Nif::FloatKeyMap>;
|
||||
using Vec3Interpolator = ValueInterpolator<Nif::Vector3KeyMap>;
|
||||
using Vec4Interpolator = ValueInterpolator<Nif::Vector4KeyMap>;
|
||||
|
||||
class ControllerFunction : public SceneUtil::ControllerFunction
|
||||
{
|
||||
|
|
|
@ -184,14 +184,16 @@ namespace NifOsg
|
|||
{
|
||||
public:
|
||||
/// @param filename used for warning messages.
|
||||
LoaderImpl(const std::string& filename)
|
||||
: mFilename(filename), mFirstRootTextureIndex(-1), mFoundFirstRootTexturingProperty(false)
|
||||
LoaderImpl(const std::string& filename, unsigned int ver, unsigned int userver, unsigned int bethver)
|
||||
: mFilename(filename), mVersion(ver), mUserVersion(userver), mBethVersion(bethver)
|
||||
{
|
||||
|
||||
}
|
||||
std::string mFilename;
|
||||
size_t mFirstRootTextureIndex;
|
||||
bool mFoundFirstRootTexturingProperty;
|
||||
unsigned int mVersion, mUserVersion, mBethVersion;
|
||||
|
||||
size_t mFirstRootTextureIndex = -1;
|
||||
bool mFoundFirstRootTexturingProperty = false;
|
||||
|
||||
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)
|
||||
{
|
||||
LoaderImpl impl(file->getFilename());
|
||||
LoaderImpl impl(file->getFilename(), file->getVersion(), file->getUserVersion(), file->getBethVersion());
|
||||
return impl.load(file, imageManager);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -151,7 +151,6 @@ namespace NifOsg
|
|||
float mCachedDefaultSize;
|
||||
};
|
||||
|
||||
typedef ValueInterpolator<Nif::Vector4KeyMap, LerpFunc> Vec4Interpolator;
|
||||
class ParticleColorAffector : public osgParticle::Operator
|
||||
{
|
||||
public:
|
||||
|
|
Loading…
Reference in a new issue