1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-06-20 21:11:33 +00:00

Merge branch 'nif' into 'master'

Modernize NIF loader, part 4

See merge request OpenMW/openmw!3427
This commit is contained in:
psi29a 2023-09-15 07:02:38 +00:00
commit 64e4a33400
20 changed files with 648 additions and 697 deletions

View file

@ -20,7 +20,7 @@ namespace Nif::Testing
{ {
value.mExtra = ExtraPtr(nullptr); value.mExtra = ExtraPtr(nullptr);
value.mExtraList = ExtraList(); value.mExtraList = ExtraList();
value.mController = ControllerPtr(nullptr); value.mController = NiTimeControllerPtr(nullptr);
} }
inline void init(NiAVObject& value) inline void init(NiAVObject& value)
@ -55,15 +55,15 @@ namespace Nif::Testing
value.mRoot = NiAVObjectPtr(nullptr); value.mRoot = NiAVObjectPtr(nullptr);
} }
inline void init(Controller& value) inline void init(NiTimeController& value)
{ {
value.next = ControllerPtr(nullptr); value.mNext = NiTimeControllerPtr(nullptr);
value.flags = 0; value.mFlags = 0;
value.frequency = 0; value.mFrequency = 0;
value.phase = 0; value.mPhase = 0;
value.timeStart = 0; value.mTimeStart = 0;
value.timeStop = 0; value.mTimeStop = 0;
value.target = NiObjectNETPtr(nullptr); value.mTarget = NiObjectNETPtr(nullptr);
} }
} }

View file

@ -300,7 +300,7 @@ namespace
Nif::NiStringExtraData mNiStringExtraData; Nif::NiStringExtraData mNiStringExtraData;
Nif::NiStringExtraData mNiStringExtraData2; Nif::NiStringExtraData mNiStringExtraData2;
Nif::NiIntegerExtraData mNiIntegerExtraData; Nif::NiIntegerExtraData mNiIntegerExtraData;
Nif::Controller mController; Nif::NiTimeController mController;
btTransform mTransform{ btMatrix3x3(btQuaternion(btVector3(1, 0, 0), 0.5f)), btVector3(1, 2, 3) }; btTransform mTransform{ btMatrix3x3(btQuaternion(btVector3(1, 0, 0), 0.5f)), btVector3(1, 2, 3) };
btTransform mTransformScale2{ btMatrix3x3(btQuaternion(btVector3(1, 0, 0), 0.5f)), btVector3(2, 4, 6) }; btTransform mTransformScale2{ btMatrix3x3(btQuaternion(btVector3(1, 0, 0), 0.5f)), btVector3(2, 4, 6) };
btTransform mTransformScale3{ btMatrix3x3(btQuaternion(btVector3(1, 0, 0), 0.5f)), btVector3(3, 6, 9) }; btTransform mTransformScale3{ btMatrix3x3(btQuaternion(btVector3(1, 0, 0), 0.5f)), btVector3(3, 6, 9) };
@ -817,11 +817,11 @@ namespace
TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_controller_should_return_animated_shape) TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_controller_should_return_animated_shape)
{ {
mController.recType = Nif::RC_NiKeyframeController; mController.recType = Nif::RC_NiKeyframeController;
mController.flags |= Nif::Controller::Flag_Active; mController.mFlags |= Nif::NiTimeController::Flag_Active;
copy(mTransform, mNiTriShape.mTransform); copy(mTransform, mNiTriShape.mTransform);
mNiTriShape.mTransform.mScale = 3; mNiTriShape.mTransform.mScale = 3;
mNiTriShape.mParents.push_back(&mNiNode); mNiTriShape.mParents.push_back(&mNiNode);
mNiTriShape.mController = Nif::ControllerPtr(&mController); mNiTriShape.mController = Nif::NiTimeControllerPtr(&mController);
mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) }; mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape) };
mNiNode.mTransform.mScale = 4; mNiNode.mTransform.mScale = 4;
@ -847,14 +847,14 @@ namespace
TEST_F(TestBulletNifLoader, for_two_tri_shape_children_nodes_where_one_with_controller_should_return_animated_shape) TEST_F(TestBulletNifLoader, for_two_tri_shape_children_nodes_where_one_with_controller_should_return_animated_shape)
{ {
mController.recType = Nif::RC_NiKeyframeController; mController.recType = Nif::RC_NiKeyframeController;
mController.flags |= Nif::Controller::Flag_Active; mController.mFlags |= Nif::NiTimeController::Flag_Active;
copy(mTransform, mNiTriShape.mTransform); copy(mTransform, mNiTriShape.mTransform);
mNiTriShape.mTransform.mScale = 3; mNiTriShape.mTransform.mScale = 3;
mNiTriShape.mParents.push_back(&mNiNode); mNiTriShape.mParents.push_back(&mNiNode);
copy(mTransform, mNiTriShape2.mTransform); copy(mTransform, mNiTriShape2.mTransform);
mNiTriShape2.mTransform.mScale = 3; mNiTriShape2.mTransform.mScale = 3;
mNiTriShape2.mParents.push_back(&mNiNode); mNiTriShape2.mParents.push_back(&mNiNode);
mNiTriShape2.mController = Nif::ControllerPtr(&mController); mNiTriShape2.mController = Nif::NiTimeControllerPtr(&mController);
mNiNode.mChildren = Nif::NiAVObjectList{ mNiNode.mChildren = Nif::NiAVObjectList{
Nif::NiAVObjectPtr(&mNiTriShape), Nif::NiAVObjectPtr(&mNiTriShape),
Nif::NiAVObjectPtr(&mNiTriShape2), Nif::NiAVObjectPtr(&mNiTriShape2),

View file

@ -21,7 +21,7 @@ namespace Nif
void post(Reader& nif) override { mNext.post(nif); } void post(Reader& nif) override { mNext.post(nif); }
}; };
struct Controller : public Record struct NiTimeController : public Record
{ {
enum Flags enum Flags
{ {
@ -36,17 +36,17 @@ namespace Nif
Mask = 6 Mask = 6
}; };
ControllerPtr next; NiTimeControllerPtr mNext;
int flags; uint16_t mFlags;
float frequency, phase; float mFrequency, mPhase;
float timeStart, timeStop; float mTimeStart, mTimeStop;
NiObjectNETPtr target; NiObjectNETPtr mTarget;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
void post(Reader& nif) override; void post(Reader& nif) override;
bool isActive() const { return flags & Flag_Active; } bool isActive() const { return mFlags & Flag_Active; }
ExtrapolationMode extrapolationMode() const { return static_cast<ExtrapolationMode>(flags & Mask); } ExtrapolationMode extrapolationMode() const { return static_cast<ExtrapolationMode>(mFlags & Mask); }
}; };
/// Abstract object that has a name, extra data and controllers /// Abstract object that has a name, extra data and controllers
@ -55,7 +55,7 @@ namespace Nif
std::string mName; std::string mName;
ExtraPtr mExtra; ExtraPtr mExtra;
ExtraList mExtraList; ExtraList mExtraList;
ControllerPtr mController; NiTimeControllerPtr mController;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
void post(Reader& nif) override; void post(Reader& nif) override;

View file

@ -1,6 +1,7 @@
#include "controller.hpp" #include "controller.hpp"
#include "data.hpp" #include "data.hpp"
#include "exception.hpp"
#include "node.hpp" #include "node.hpp"
#include "particle.hpp" #include "particle.hpp"
#include "texture.hpp" #include "texture.hpp"
@ -8,25 +9,22 @@
namespace Nif namespace Nif
{ {
void Controller::read(NIFStream* nif) void NiTimeController::read(NIFStream* nif)
{ {
next.read(nif); mNext.read(nif);
nif->read(mFlags);
flags = nif->getUShort(); nif->read(mFrequency);
nif->read(mPhase);
frequency = nif->getFloat(); nif->read(mTimeStart);
phase = nif->getFloat(); nif->read(mTimeStop);
timeStart = nif->getFloat(); if (nif->getVersion() >= NIFStream::generateVersion(3, 3, 0, 13))
timeStop = nif->getFloat(); mTarget.read(nif);
target.read(nif);
} }
void Controller::post(Reader& nif) void NiTimeController::post(Reader& nif)
{ {
Record::post(nif); mNext.post(nif);
next.post(nif); mTarget.post(nif);
target.post(nif);
} }
void ControlledBlock::read(NIFStream* nif) void ControlledBlock::read(NIFStream* nif)
@ -44,28 +42,28 @@ namespace Nif
if (nif->getVersion() <= NIFStream::generateVersion(10, 1, 0, 110)) if (nif->getVersion() <= NIFStream::generateVersion(10, 1, 0, 110))
{ {
mBlendInterpolator.read(nif); mBlendInterpolator.read(nif);
mBlendIndex = nif->getUShort(); nif->read(mBlendIndex);
} }
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 106) && nif->getBethVersion() > 0) if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 106) && nif->getBethVersion() > 0)
mPriority = nif->getChar(); nif->read(mPriority);
if (nif->getVersion() >= NIFStream::generateVersion(10, 2, 0, 0) if (nif->getVersion() >= NIFStream::generateVersion(10, 2, 0, 0)
&& nif->getVersion() <= NIFStream::generateVersion(20, 1, 0, 0)) && nif->getVersion() <= NIFStream::generateVersion(20, 1, 0, 0))
{ {
mStringPalette.read(nif); mStringPalette.read(nif);
mNodeNameOffset = nif->getUInt(); nif->read(mNodeNameOffset);
mPropertyTypeOffset = nif->getUInt(); nif->read(mPropertyTypeOffset);
mControllerTypeOffset = nif->getUInt(); nif->read(mControllerTypeOffset);
mControllerIdOffset = nif->getUInt(); nif->read(mControllerIdOffset);
mInterpolatorIdOffset = nif->getUInt(); nif->read(mInterpolatorIdOffset);
} }
else else
{ {
mNodeName = nif->getString(); nif->read(mNodeName);
mPropertyType = nif->getString(); nif->read(mPropertyType);
mControllerType = nif->getString(); nif->read(mControllerType);
mControllerId = nif->getString(); nif->read(mControllerId);
mInterpolatorId = nif->getString(); nif->read(mInterpolatorId);
} }
} }
@ -80,16 +78,15 @@ namespace Nif
void NiSequence::read(NIFStream* nif) void NiSequence::read(NIFStream* nif)
{ {
mName = nif->getString(); nif->read(mName);
if (nif->getVersion() <= NIFStream::generateVersion(10, 1, 0, 103)) if (nif->getVersion() <= NIFStream::generateVersion(10, 1, 0, 103))
{ {
mAccumRootName = nif->getString(); nif->read(mAccumRootName);
mTextKeys.read(nif); mTextKeys.read(nif);
} }
size_t numControlledBlocks = nif->getUInt(); mControlledBlocks.resize(nif->get<uint32_t>());
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 106)) if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 106))
mArrayGrowBy = nif->getUInt(); nif->read(mArrayGrowBy);
mControlledBlocks.resize(numControlledBlocks);
for (ControlledBlock& block : mControlledBlocks) for (ControlledBlock& block : mControlledBlocks)
block.read(nif); block.read(nif);
} }
@ -104,28 +101,30 @@ namespace Nif
void NiControllerSequence::read(NIFStream* nif) void NiControllerSequence::read(NIFStream* nif)
{ {
NiSequence::read(nif); NiSequence::read(nif);
if (nif->getVersion() <= NIFStream::generateVersion(10, 1, 0, 103)) if (nif->getVersion() <= NIFStream::generateVersion(10, 1, 0, 103))
return; return;
mWeight = nif->getFloat(); nif->read(mWeight);
mTextKeys.read(nif); mTextKeys.read(nif);
mExtrapolationMode = static_cast<Controller::ExtrapolationMode>(nif->getUInt()); mExtrapolationMode = static_cast<NiTimeController::ExtrapolationMode>(nif->get<uint32_t>());
mFrequency = nif->getFloat(); nif->read(mFrequency);
if (nif->getVersion() <= NIFStream::generateVersion(10, 4, 0, 1)) if (nif->getVersion() <= NIFStream::generateVersion(10, 4, 0, 1))
mPhase = nif->getFloat(); nif->read(mPhase);
mStartTime = nif->getFloat(); nif->read(mStartTime);
mStopTime = nif->getFloat(); nif->read(mStopTime);
mPlayBackwards = nif->getVersion() == NIFStream::generateVersion(10, 1, 0, 106) && nif->getBoolean(); if (nif->getVersion() == NIFStream::generateVersion(10, 1, 0, 106))
nif->read(mPlayBackwards);
mManager.read(nif); mManager.read(nif);
mAccumRootName = nif->getString(); nif->read(mAccumRootName);
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 113) if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 113)
&& nif->getVersion() <= NIFStream::generateVersion(20, 1, 0, 0)) && nif->getVersion() <= NIFStream::generateVersion(20, 1, 0, 0))
mStringPalette.read(nif); mStringPalette.read(nif);
else if (nif->getVersion() >= NIFFile::NIFVersion::VER_BGS && nif->getBethVersion() >= 24) else if (nif->getVersion() >= NIFFile::NIFVersion::VER_BGS && nif->getBethVersion() >= 24)
{ {
size_t numAnimNotes = 1; uint16_t numAnimNotes = 1;
if (nif->getBethVersion() >= 29) if (nif->getBethVersion() >= 29)
numAnimNotes = nif->getUShort(); nif->read(numAnimNotes);
nif->skip(4 * numAnimNotes); // BSAnimNotes links nif->skip(4 * numAnimNotes); // BSAnimNotes links
} }
@ -134,21 +133,24 @@ namespace Nif
void NiControllerSequence::post(Reader& nif) void NiControllerSequence::post(Reader& nif)
{ {
NiSequence::post(nif); NiSequence::post(nif);
mManager.post(nif); mManager.post(nif);
mStringPalette.post(nif); mStringPalette.post(nif);
} }
void NiInterpController::read(NIFStream* nif) void NiInterpController::read(NIFStream* nif)
{ {
Controller::read(nif); NiTimeController::read(nif);
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 104) if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 104)
&& nif->getVersion() <= NIFStream::generateVersion(10, 1, 0, 108)) && nif->getVersion() <= NIFStream::generateVersion(10, 1, 0, 108))
mManagerControlled = nif->getBoolean(); nif->read(mManagerControlled);
} }
void NiSingleInterpController::read(NIFStream* nif) void NiSingleInterpController::read(NIFStream* nif)
{ {
NiInterpController::read(nif); NiInterpController::read(nif);
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 104)) if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 104))
mInterpolator.read(nif); mInterpolator.read(nif);
} }
@ -156,83 +158,86 @@ namespace Nif
void NiSingleInterpController::post(Reader& nif) void NiSingleInterpController::post(Reader& nif)
{ {
NiInterpController::post(nif); NiInterpController::post(nif);
mInterpolator.post(nif); mInterpolator.post(nif);
} }
void NiParticleInfo::read(NIFStream* nif)
{
nif->read(mVelocity);
if (nif->getVersion() <= NIFStream::generateVersion(10, 4, 0, 1))
nif->read(mRotationAxis);
nif->read(mAge);
nif->read(mLifespan);
nif->read(mLastUpdate);
nif->read(mSpawnGeneration);
nif->read(mCode);
}
void NiParticleSystemController::read(NIFStream* nif) void NiParticleSystemController::read(NIFStream* nif)
{ {
Controller::read(nif); NiTimeController::read(nif);
velocity = nif->getFloat(); if (nif->getVersion() >= NIFStream::generateVersion(3, 3, 0, 13))
velocityRandom = nif->getFloat(); nif->read(mSpeed);
verticalDir = nif->getFloat(); nif->read(mSpeedVariation);
verticalAngle = nif->getFloat(); nif->read(mDeclination);
horizontalDir = nif->getFloat(); nif->read(mDeclinationVariation);
horizontalAngle = nif->getFloat(); nif->read(mPlanarAngle);
/*normal?*/ nif->getVector3(); nif->read(mPlanarAngleVariation);
color = nif->getVector4(); nif->read(mInitialNormal);
size = nif->getFloat(); nif->read(mInitialColor);
startTime = nif->getFloat(); nif->read(mInitialSize);
stopTime = nif->getFloat(); nif->read(mEmitStartTime);
nif->getChar(); nif->read(mEmitStopTime);
emitRate = nif->getFloat(); if (nif->getVersion() >= NIFStream::generateVersion(3, 3, 0, 13))
lifetime = nif->getFloat();
lifetimeRandom = nif->getFloat();
emitFlags = nif->getUShort();
offsetRandom = nif->getVector3();
emitter.read(nif);
/* Unknown Short, 0?
* Unknown Float, 1.0?
* Unknown Int, 1?
* Unknown Int, 0?
* Unknown Short, 0?
*/
nif->skip(16);
numParticles = nif->getUShort();
activeCount = nif->getUShort();
particles.resize(numParticles);
for (size_t i = 0; i < particles.size(); i++)
{ {
particles[i].velocity = nif->getVector3(); mResetParticleSystem = nif->get<uint8_t>() != 0;
nif->getVector3(); /* unknown */ nif->read(mBirthRate);
particles[i].lifetime = nif->getFloat();
particles[i].lifespan = nif->getFloat();
particles[i].timestamp = nif->getFloat();
nif->getUShort(); /* unknown */
particles[i].vertex = nif->getUShort();
} }
nif->read(mLifetime);
nif->getUInt(); /* -1? */ nif->read(mLifetimeVariation);
affectors.read(nif); if (nif->getVersion() >= NIFStream::generateVersion(3, 3, 0, 13))
colliders.read(nif); nif->read(mEmitFlags);
nif->getChar(); nif->read(mEmitterDimensions);
mEmitter.read(nif);
if (nif->getVersion() >= NIFStream::generateVersion(3, 3, 0, 13))
{
nif->read(mNumSpawnGenerations);
nif->read(mPercentageSpawned);
nif->read(mSpawnMultiplier);
nif->read(mSpawnSpeedChaos);
nif->read(mSpawnDirChaos);
mParticles.resize(nif->get<uint16_t>());
nif->read(mNumValid);
for (NiParticleInfo& particle : mParticles)
particle.read(nif);
nif->skip(4); // NiEmitterModifier link
}
mModifier.read(nif);
mCollider.read(nif);
if (nif->getVersion() >= NIFStream::generateVersion(3, 3, 0, 15))
nif->read(mStaticTargetBound);
} }
void NiParticleSystemController::post(Reader& nif) void NiParticleSystemController::post(Reader& nif)
{ {
Controller::post(nif); NiTimeController::post(nif);
emitter.post(nif);
affectors.post(nif); mEmitter.post(nif);
colliders.post(nif); mModifier.post(nif);
mCollider.post(nif);
} }
void NiMaterialColorController::read(NIFStream* nif) void NiMaterialColorController::read(NIFStream* nif)
{ {
NiPoint3InterpController::read(nif); NiPoint3InterpController::read(nif);
// Two bits that correspond to the controlled material color.
// 00: Ambient
// 01: Diffuse
// 10: Specular
// 11: Emissive
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0)) if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0))
mTargetColor = nif->getUShort() & 3; mTargetColor = static_cast<TargetColor>(nif->get<uint16_t>() & 3);
else else
mTargetColor = (flags >> 4) & 3; mTargetColor = static_cast<TargetColor>((mFlags >> 4) & 3);
if (nif->getVersion() <= NIFStream::generateVersion(10, 1, 0, 103)) if (nif->getVersion() <= NIFStream::generateVersion(10, 1, 0, 103))
mData.read(nif); mData.read(nif);
} }
@ -240,60 +245,70 @@ namespace Nif
void NiMaterialColorController::post(Reader& nif) void NiMaterialColorController::post(Reader& nif)
{ {
NiPoint3InterpController::post(nif); NiPoint3InterpController::post(nif);
mData.post(nif); mData.post(nif);
} }
void NiLookAtController::read(NIFStream* nif) void NiLookAtController::read(NIFStream* nif)
{ {
Controller::read(nif); NiTimeController::read(nif);
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0)) if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0))
lookAtFlags = nif->getUShort(); nif->read(mLookAtFlags);
target.read(nif); mLookAt.read(nif);
} }
void NiLookAtController::post(Reader& nif) void NiLookAtController::post(Reader& nif)
{ {
Controller::post(nif); NiTimeController::post(nif);
target.post(nif);
mLookAt.post(nif);
} }
void NiPathController::read(NIFStream* nif) void NiPathController::read(NIFStream* nif)
{ {
Controller::read(nif); NiTimeController::read(nif);
bankDir = nif->getInt(); if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0))
maxBankAngle = nif->getFloat(); nif->read(mPathFlags);
smoothing = nif->getFloat(); else
followAxis = nif->getShort(); mPathFlags = (mFlags >> 4);
posData.read(nif);
floatData.read(nif); nif->read(mBankDirection);
nif->read(mMaxBankAngle);
nif->read(mSmoothing);
nif->read(mFollowAxis);
mPathData.read(nif);
mPercentData.read(nif);
} }
void NiPathController::post(Reader& nif) void NiPathController::post(Reader& nif)
{ {
Controller::post(nif); NiTimeController::post(nif);
posData.post(nif); mPathData.post(nif);
floatData.post(nif); mPercentData.post(nif);
} }
void NiUVController::read(NIFStream* nif) void NiUVController::read(NIFStream* nif)
{ {
Controller::read(nif); NiTimeController::read(nif);
uvSet = nif->getUShort(); nif->read(mUvSet);
data.read(nif); mData.read(nif);
} }
void NiUVController::post(Reader& nif) void NiUVController::post(Reader& nif)
{ {
Controller::post(nif); NiTimeController::post(nif);
data.post(nif);
mData.post(nif);
} }
void NiKeyframeController::read(NIFStream* nif) void NiKeyframeController::read(NIFStream* nif)
{ {
NiSingleInterpController::read(nif); NiSingleInterpController::read(nif);
if (nif->getVersion() <= NIFStream::generateVersion(10, 1, 0, 103)) if (nif->getVersion() <= NIFStream::generateVersion(10, 1, 0, 103))
mData.read(nif); mData.read(nif);
} }
@ -301,29 +316,30 @@ namespace Nif
void NiKeyframeController::post(Reader& nif) void NiKeyframeController::post(Reader& nif)
{ {
NiSingleInterpController::post(nif); NiSingleInterpController::post(nif);
mData.post(nif); mData.post(nif);
} }
void NiMultiTargetTransformController::read(NIFStream* nif) void NiMultiTargetTransformController::read(NIFStream* nif)
{ {
NiInterpController::read(nif); NiInterpController::read(nif);
size_t numTargets = nif->getUShort();
std::vector<NiAVObjectPtr> targets; mExtraTargets.resize(nif->get<uint16_t>());
targets.resize(numTargets); for (NiAVObjectPtr& extraTarget : mExtraTargets)
for (size_t i = 0; i < targets.size(); i++) extraTarget.read(nif);
targets[i].read(nif);
mExtraTargets = targets;
} }
void NiMultiTargetTransformController::post(Reader& nif) void NiMultiTargetTransformController::post(Reader& nif)
{ {
NiInterpController::post(nif); NiInterpController::post(nif);
postRecordList(nif, mExtraTargets); postRecordList(nif, mExtraTargets);
} }
void NiAlphaController::read(NIFStream* nif) void NiAlphaController::read(NIFStream* nif)
{ {
NiFloatInterpController::read(nif); NiFloatInterpController::read(nif);
if (nif->getVersion() <= NIFStream::generateVersion(10, 1, 0, 103)) if (nif->getVersion() <= NIFStream::generateVersion(10, 1, 0, 103))
mData.read(nif); mData.read(nif);
} }
@ -331,12 +347,14 @@ namespace Nif
void NiAlphaController::post(Reader& nif) void NiAlphaController::post(Reader& nif)
{ {
NiFloatInterpController::post(nif); NiFloatInterpController::post(nif);
mData.post(nif); mData.post(nif);
} }
void NiRollController::read(NIFStream* nif) void NiRollController::read(NIFStream* nif)
{ {
NiSingleInterpController::read(nif); NiSingleInterpController::read(nif);
if (nif->getVersion() <= NIFStream::generateVersion(10, 1, 0, 103)) if (nif->getVersion() <= NIFStream::generateVersion(10, 1, 0, 103))
mData.read(nif); mData.read(nif);
} }
@ -344,49 +362,47 @@ namespace Nif
void NiRollController::post(Reader& nif) void NiRollController::post(Reader& nif)
{ {
NiSingleInterpController::post(nif); NiSingleInterpController::post(nif);
mData.post(nif); mData.post(nif);
} }
void NiGeomMorpherController::read(NIFStream* nif) void NiGeomMorpherController::read(NIFStream* nif)
{ {
NiInterpController::read(nif); NiInterpController::read(nif);
if (nif->getVersion() >= NIFFile::NIFVersion::VER_OB_OLD) if (nif->getVersion() >= NIFFile::NIFVersion::VER_OB_OLD)
mUpdateNormals = nif->getUShort() & 1; mUpdateNormals = nif->get<uint16_t>() & 1;
mData.read(nif); mData.read(nif);
if (nif->getVersion() >= NIFFile::NIFVersion::VER_MW)
if (nif->getVersion() < NIFFile::NIFVersion::VER_MW)
return;
mAlwaysActive = nif->get<uint8_t>() != 0;
if (nif->getVersion() <= NIFStream::generateVersion(10, 1, 0, 105))
return;
if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB)
{ {
mAlwaysActive = nif->getChar(); readRecordList(nif, mInterpolators);
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 106)) if (nif->getVersion() >= NIFStream::generateVersion(10, 2, 0, 0) && nif->getBethVersion() >= 10)
{ nif->skip(4 * nif->get<uint32_t>()); // Unknown
if (nif->getVersion() <= NIFFile::NIFVersion::VER_OB) return;
{ }
readRecordList(nif, mInterpolators);
if (nif->getVersion() >= NIFStream::generateVersion(10, 2, 0, 0) && nif->getBethVersion() > 9) mInterpolators.resize(nif->get<uint32_t>());
{ mWeights.resize(mInterpolators.size());
unsigned int numUnknown = nif->getUInt(); for (size_t i = 0; i < mInterpolators.size(); i++)
nif->skip(4 * numUnknown); {
} mInterpolators[i].read(nif);
} nif->read(mWeights[i]);
else
{
std::vector<NiInterpolatorPtr> interpolators;
size_t numInterps = nif->getUInt();
interpolators.resize(numInterps);
mWeights.resize(numInterps);
for (size_t i = 0; i < numInterps; i++)
{
interpolators[i].read(nif);
mWeights[i] = nif->getFloat();
}
mInterpolators = interpolators;
}
}
} }
} }
void NiGeomMorpherController::post(Reader& nif) void NiGeomMorpherController::post(Reader& nif)
{ {
NiInterpController::post(nif); NiInterpController::post(nif);
mData.post(nif); mData.post(nif);
postRecordList(nif, mInterpolators); postRecordList(nif, mInterpolators);
} }
@ -394,6 +410,7 @@ namespace Nif
void NiVisController::read(NIFStream* nif) void NiVisController::read(NIFStream* nif)
{ {
NiBoolInterpController::read(nif); NiBoolInterpController::read(nif);
if (nif->getVersion() <= NIFStream::generateVersion(10, 1, 0, 103)) if (nif->getVersion() <= NIFStream::generateVersion(10, 1, 0, 103))
mData.read(nif); mData.read(nif);
} }
@ -401,17 +418,19 @@ namespace Nif
void NiVisController::post(Reader& nif) void NiVisController::post(Reader& nif)
{ {
NiBoolInterpController::post(nif); NiBoolInterpController::post(nif);
mData.post(nif); mData.post(nif);
} }
void NiFlipController::read(NIFStream* nif) void NiFlipController::read(NIFStream* nif)
{ {
NiFloatInterpController::read(nif); NiFloatInterpController::read(nif);
mTexSlot = nif->getUInt();
mTexSlot = static_cast<NiTexturingProperty::TextureType>(nif->get<uint32_t>());
if (nif->getVersion() <= NIFStream::generateVersion(10, 1, 0, 103)) if (nif->getVersion() <= NIFStream::generateVersion(10, 1, 0, 103))
{ {
timeStart = nif->getFloat(); nif->read(mTimeStart);
mDelta = nif->getFloat(); nif->read(mDelta);
} }
readRecordList(nif, mSources); readRecordList(nif, mSources);
} }
@ -419,14 +438,16 @@ namespace Nif
void NiFlipController::post(Reader& nif) void NiFlipController::post(Reader& nif)
{ {
NiFloatInterpController::post(nif); NiFloatInterpController::post(nif);
postRecordList(nif, mSources); postRecordList(nif, mSources);
} }
void NiTextureTransformController::read(NIFStream* nif) void NiTextureTransformController::read(NIFStream* nif)
{ {
NiFloatInterpController::read(nif); NiFloatInterpController::read(nif);
mShaderMap = nif->getBoolean();
nif->read(mTexSlot); nif->read(mShaderMap);
mTexSlot = static_cast<NiTexturingProperty::TextureType>(nif->get<uint32_t>());
nif->read(mTransformMember); nif->read(mTransformMember);
if (nif->getVersion() <= NIFStream::generateVersion(10, 1, 0, 103)) if (nif->getVersion() <= NIFStream::generateVersion(10, 1, 0, 103))
mData.read(nif); mData.read(nif);
@ -435,176 +456,113 @@ namespace Nif
void NiTextureTransformController::post(Reader& nif) void NiTextureTransformController::post(Reader& nif)
{ {
NiFloatInterpController::post(nif); NiFloatInterpController::post(nif);
mData.post(nif); mData.post(nif);
} }
void bhkBlendController::read(NIFStream* nif) void bhkBlendController::read(NIFStream* nif)
{ {
Controller::read(nif); NiTimeController::read(nif);
nif->getUInt(); // Zero
uint32_t numKeys;
nif->read(numKeys);
// Is this possible?
if (numKeys != 0)
throw Nif::Exception(
"Unsupported keys in bhkBlendController " + std::to_string(recIndex), nif->getFile().getFilename());
} }
void BSEffectShaderPropertyFloatController::read(NIFStream* nif) void BSEffectShaderPropertyFloatController::read(NIFStream* nif)
{ {
NiFloatInterpController::read(nif); NiFloatInterpController::read(nif);
nif->read(mControlledVariable); nif->read(mControlledVariable);
} }
void BSEffectShaderPropertyColorController::read(NIFStream* nif) void BSEffectShaderPropertyColorController::read(NIFStream* nif)
{ {
NiPoint3InterpController::read(nif); NiPoint3InterpController::read(nif);
nif->read(mControlledColor); nif->read(mControlledColor);
} }
void NiControllerManager::read(NIFStream* nif) void NiControllerManager::read(NIFStream* nif)
{ {
Controller::read(nif); NiTimeController::read(nif);
mCumulative = nif->getBoolean();
nif->read(mCumulative);
readRecordList(nif, mSequences); readRecordList(nif, mSequences);
mObjectPalette.read(nif); mObjectPalette.read(nif);
} }
void NiControllerManager::post(Reader& nif) void NiControllerManager::post(Reader& nif)
{ {
Controller::post(nif); NiTimeController::post(nif);
postRecordList(nif, mSequences); postRecordList(nif, mSequences);
mObjectPalette.post(nif); mObjectPalette.post(nif);
} }
void NiPoint3Interpolator::read(NIFStream* nif)
{
defaultVal = nif->getVector3();
data.read(nif);
}
void NiPoint3Interpolator::post(Reader& nif)
{
data.post(nif);
}
void NiBoolInterpolator::read(NIFStream* nif)
{
defaultVal = nif->getBoolean();
data.read(nif);
}
void NiBoolInterpolator::post(Reader& nif)
{
data.post(nif);
}
void NiFloatInterpolator::read(NIFStream* nif)
{
defaultVal = nif->getFloat();
data.read(nif);
}
void NiFloatInterpolator::post(Reader& nif)
{
data.post(nif);
}
void NiTransformInterpolator::read(NIFStream* nif)
{
defaultPos = nif->getVector3();
defaultRot = nif->getQuaternion();
defaultScale = nif->getFloat();
if (nif->getVersion() <= NIFStream::generateVersion(10, 1, 0, 109))
{
if (!nif->getBoolean())
defaultPos = osg::Vec3f();
if (!nif->getBoolean())
defaultRot = osg::Quat();
if (!nif->getBoolean())
defaultScale = 1.f;
}
data.read(nif);
}
void NiTransformInterpolator::post(Reader& nif)
{
data.post(nif);
}
void NiColorInterpolator::read(NIFStream* nif)
{
defaultVal = nif->getVector4();
data.read(nif);
}
void NiColorInterpolator::post(Reader& nif)
{
data.post(nif);
}
void NiBlendInterpolator::read(NIFStream* nif) void NiBlendInterpolator::read(NIFStream* nif)
{ {
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 112)) if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 112))
mManagerControlled = nif->getChar() & 1;
size_t numInterps = 0;
if (nif->getVersion() <= NIFStream::generateVersion(10, 1, 0, 109))
{ {
numInterps = nif->getUShort(); nif->read(mFlags);
mArrayGrowBy = nif->getUShort(); mItems.resize(nif->get<uint8_t>());
} nif->read(mWeightThreshold);
else if (!(mFlags & Flag_ManagerControlled))
{
numInterps = nif->getChar();
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 112))
{ {
mWeightThreshold = nif->getFloat(); mInterpCount = nif->get<uint8_t>();
if (!mManagerControlled) mSingleIndex = nif->get<uint8_t>();
{ mHighPriority = nif->get<int8_t>();
mInterpCount = nif->getChar(); mNextHighPriority = nif->get<int8_t>();
mSingleIndex = nif->getChar(); nif->read(mSingleTime);
mHighPriority = nif->getChar(); nif->read(mHighWeightsSum);
mNextHighPriority = nif->getChar(); nif->read(mNextHighWeightsSum);
mSingleTime = nif->getFloat(); nif->read(mHighEaseSpinner);
mHighWeightsSum = nif->getFloat(); for (Item& item : mItems)
mNextHighWeightsSum = nif->getFloat(); item.read(nif);
mHighEaseSpinner = nif->getFloat();
}
} }
return;
} }
if (!mManagerControlled) if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 110))
{ {
mItems.resize(numInterps); mItems.resize(nif->get<uint8_t>());
for (Item& item : mItems) for (Item& item : mItems)
item.read(nif); item.read(nif);
if (nif->get<bool>())
mFlags |= Flag_ManagerControlled;
nif->read(mWeightThreshold);
if (nif->get<bool>())
mFlags |= Flag_OnlyUseHighestWeight;
mInterpCount = nif->get<uint8_t>();
mSingleIndex = nif->get<uint8_t>();
mSingleInterpolator.read(nif);
nif->read(mSingleTime);
mHighPriority = nif->get<int8_t>();
mNextHighPriority = nif->get<int8_t>();
return;
} }
if (nif->getVersion() <= NIFStream::generateVersion(10, 1, 0, 111)) mItems.resize(nif->get<uint16_t>());
nif->read(mArrayGrowBy);
for (Item& item : mItems)
item.read(nif);
if (nif->get<bool>())
mFlags |= Flag_ManagerControlled;
nif->read(mWeightThreshold);
if (nif->get<bool>())
mFlags |= Flag_OnlyUseHighestWeight;
nif->read(mInterpCount);
nif->read(mSingleIndex);
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 108))
{ {
mManagerControlled = nif->getBoolean(); mSingleInterpolator.read(nif);
mWeightThreshold = nif->getFloat(); nif->read(mSingleTime);
mOnlyUseHighestWeight = nif->getBoolean();
if (nif->getVersion() <= NIFStream::generateVersion(10, 1, 0, 109))
{
mInterpCount = nif->getUShort();
mSingleIndex = nif->getUShort();
}
else
{
mInterpCount = nif->getChar();
mSingleIndex = nif->getChar();
}
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 108))
{
mSingleInterpolator.read(nif);
mSingleTime = nif->getFloat();
}
if (nif->getVersion() <= NIFStream::generateVersion(10, 1, 0, 109))
{
mHighPriority = nif->getInt();
mNextHighPriority = nif->getInt();
}
else
{
mHighPriority = nif->getChar();
mNextHighPriority = nif->getChar();
}
} }
nif->read(mHighPriority);
nif->read(mNextHighPriority);
} }
void NiBlendInterpolator::post(Reader& nif) void NiBlendInterpolator::post(Reader& nif)
@ -617,13 +575,13 @@ namespace Nif
void NiBlendInterpolator::Item::read(NIFStream* nif) void NiBlendInterpolator::Item::read(NIFStream* nif)
{ {
mInterpolator.read(nif); mInterpolator.read(nif);
mWeight = nif->getFloat(); nif->read(mWeight);
mNormalizedWeight = nif->getFloat(); nif->read(mNormalizedWeight);
if (nif->getVersion() <= NIFStream::generateVersion(10, 1, 0, 109)) if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 110))
mPriority = nif->getInt(); mPriority = nif->get<int8_t>();
else else
mPriority = nif->getChar(); nif->read(mPriority);
mEaseSpinner = nif->getFloat(); nif->read(mEaseSpinner);
} }
void NiBlendInterpolator::Item::post(Reader& nif) void NiBlendInterpolator::Item::post(Reader& nif)
@ -631,38 +589,4 @@ namespace Nif
mInterpolator.post(nif); mInterpolator.post(nif);
} }
void NiBlendBoolInterpolator::read(NIFStream* nif)
{
NiBlendInterpolator::read(nif);
mValue = nif->getChar() != 0;
}
void NiBlendFloatInterpolator::read(NIFStream* nif)
{
NiBlendInterpolator::read(nif);
mValue = nif->getFloat();
}
void NiBlendPoint3Interpolator::read(NIFStream* nif)
{
NiBlendInterpolator::read(nif);
mValue = nif->getVector3();
}
void NiBlendTransformInterpolator::read(NIFStream* nif)
{
NiBlendInterpolator::read(nif);
if (nif->getVersion() <= NIFStream::generateVersion(10, 1, 0, 109))
{
mPosValue = nif->getVector3();
mRotValue = nif->getQuaternion();
mScaleValue = nif->getFloat();
if (!nif->getBoolean())
mPosValue = osg::Vec3f();
if (!nif->getBoolean())
mRotValue = osg::Quat();
if (!nif->getBoolean())
mScaleValue = 1.f;
}
}
} }

View file

@ -1,30 +1,8 @@
/*
OpenMW - The completely unofficial reimplementation of Morrowind
Copyright (C) 2008-2010 Nicolay Korslund
Email: < korslund@gmail.com >
WWW: https://openmw.org/
This file (controller.h) is part of the OpenMW package.
OpenMW is distributed as free software: you can redistribute it
and/or modify it under the terms of the GNU General Public License
version 3, as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
version 3 along with this program. If not, see
https://www.gnu.org/licenses/ .
*/
#ifndef OPENMW_COMPONENTS_NIF_CONTROLLER_HPP #ifndef OPENMW_COMPONENTS_NIF_CONTROLLER_HPP
#define OPENMW_COMPONENTS_NIF_CONTROLLER_HPP #define OPENMW_COMPONENTS_NIF_CONTROLLER_HPP
#include "base.hpp" #include "base.hpp"
#include "niftypes.hpp"
#include "property.hpp" #include "property.hpp"
namespace Nif namespace Nif
@ -34,16 +12,16 @@ namespace Nif
{ {
std::string mTargetName; std::string mTargetName;
NiInterpolatorPtr mInterpolator; NiInterpolatorPtr mInterpolator;
ControllerPtr mController; NiTimeControllerPtr mController;
NiBlendInterpolatorPtr mBlendInterpolator; NiBlendInterpolatorPtr mBlendInterpolator;
unsigned short mBlendIndex; uint16_t mBlendIndex;
unsigned char mPriority; uint8_t mPriority;
NiStringPalettePtr mStringPalette; NiStringPalettePtr mStringPalette;
size_t mNodeNameOffset; uint32_t mNodeNameOffset;
size_t mPropertyTypeOffset; uint32_t mPropertyTypeOffset;
size_t mControllerTypeOffset; uint32_t mControllerTypeOffset;
size_t mControllerIdOffset; uint32_t mControllerIdOffset;
size_t mInterpolatorIdOffset; uint32_t mInterpolatorIdOffset;
std::string mNodeName; std::string mNodeName;
std::string mPropertyType; std::string mPropertyType;
std::string mControllerType; std::string mControllerType;
@ -60,7 +38,7 @@ namespace Nif
std::string mName; std::string mName;
std::string mAccumRootName; std::string mAccumRootName;
ExtraPtr mTextKeys; ExtraPtr mTextKeys;
unsigned int mArrayGrowBy; uint32_t mArrayGrowBy;
std::vector<ControlledBlock> mControlledBlocks; std::vector<ControlledBlock> mControlledBlocks;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
@ -71,7 +49,7 @@ namespace Nif
struct NiControllerSequence : public NiSequence struct NiControllerSequence : public NiSequence
{ {
float mWeight{ 1.f }; float mWeight{ 1.f };
Controller::ExtrapolationMode mExtrapolationMode{ Controller::ExtrapolationMode::Constant }; NiTimeController::ExtrapolationMode mExtrapolationMode{ NiTimeController::ExtrapolationMode::Constant };
float mFrequency{ 1.f }; float mFrequency{ 1.f };
float mPhase{ 1.f }; float mPhase{ 1.f };
float mStartTime, mStopTime; float mStartTime, mStopTime;
@ -84,7 +62,7 @@ namespace Nif
}; };
// Base class for controllers that use NiInterpolators to animate objects. // Base class for controllers that use NiInterpolators to animate objects.
struct NiInterpController : public Controller struct NiInterpController : public NiTimeController
{ {
// Usually one of the flags. // Usually one of the flags.
bool mManagerControlled{ false }; bool mManagerControlled{ false };
@ -116,7 +94,20 @@ namespace Nif
{ {
}; };
struct NiParticleSystemController : public Controller struct NiParticleInfo
{
osg::Vec3f mVelocity;
osg::Vec3f mRotationAxis;
float mAge;
float mLifespan;
float mLastUpdate;
uint16_t mSpawnGeneration;
uint16_t mCode;
void read(NIFStream* nif);
};
struct NiParticleSystemController : public NiTimeController
{ {
enum BSPArrayController enum BSPArrayController
{ {
@ -124,104 +115,113 @@ namespace Nif
BSPArrayController_AtVertex = 0x10 BSPArrayController_AtVertex = 0x10
}; };
struct Particle
{
osg::Vec3f velocity;
float lifetime;
float lifespan;
float timestamp;
unsigned short vertex;
};
float velocity;
float velocityRandom;
float verticalDir; // 0=up, pi/2=horizontal, pi=down
float verticalAngle;
float horizontalDir;
float horizontalAngle;
osg::Vec4f color;
float size;
float startTime;
float stopTime;
float emitRate;
float lifetime;
float lifetimeRandom;
enum EmitFlags enum EmitFlags
{ {
EmitFlag_NoAutoAdjust = 0x1 // If this flag is set, we use the emitRate value. Otherwise, EmitFlag_NoAutoAdjust = 0x1 // If this flag is set, we use the emitRate value. Otherwise,
// we calculate an emit rate so that the maximum number of particles // we calculate an emit rate so that the maximum number of particles
// in the system (numParticles) is never exceeded. // in the system (numParticles) is never exceeded.
}; };
int emitFlags;
osg::Vec3f offsetRandom; float mSpeed;
float mSpeedVariation;
NiAVObjectPtr emitter; float mDeclination;
float mDeclinationVariation;
int numParticles; float mPlanarAngle;
int activeCount; float mPlanarAngleVariation;
std::vector<Particle> particles; osg::Vec3f mInitialNormal;
osg::Vec4f mInitialColor;
NiParticleModifierPtr affectors; float mInitialSize;
NiParticleModifierPtr colliders; float mEmitStartTime;
float mEmitStopTime;
bool mResetParticleSystem{ false };
float mBirthRate;
float mLifetime;
float mLifetimeVariation;
uint16_t mEmitFlags{ 0 };
osg::Vec3f mEmitterDimensions;
NiAVObjectPtr mEmitter;
uint16_t mNumSpawnGenerations;
float mPercentageSpawned;
uint16_t mSpawnMultiplier;
float mSpawnSpeedChaos;
float mSpawnDirChaos;
uint16_t mNumParticles;
uint16_t mNumValid;
std::vector<NiParticleInfo> mParticles;
NiParticleModifierPtr mModifier;
NiParticleModifierPtr mCollider;
uint8_t mStaticTargetBound;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
void post(Reader& nif) override; void post(Reader& nif) override;
bool noAutoAdjust() const { return emitFlags & EmitFlag_NoAutoAdjust; } bool noAutoAdjust() const { return mEmitFlags & EmitFlag_NoAutoAdjust; }
bool emitAtVertex() const { return flags & BSPArrayController_AtVertex; } bool emitAtVertex() const { return mFlags & BSPArrayController_AtVertex; }
}; };
using NiBSPArrayController = NiParticleSystemController; using NiBSPArrayController = NiParticleSystemController;
struct NiMaterialColorController : public NiPoint3InterpController struct NiMaterialColorController : public NiPoint3InterpController
{ {
NiPosDataPtr mData; enum class TargetColor
unsigned int mTargetColor;
void read(NIFStream* nif) override;
void post(Reader& nif) override;
};
struct NiPathController : public Controller
{
NiPosDataPtr posData;
NiFloatDataPtr floatData;
enum Flags
{ {
Flag_OpenCurve = 0x020, Ambient = 0,
Flag_AllowFlip = 0x040, Diffuse = 1,
Flag_Bank = 0x080, Specular = 2,
Flag_ConstVelocity = 0x100, Emissive = 3,
Flag_Follow = 0x200,
Flag_FlipFollowAxis = 0x400
}; };
int bankDir; NiPosDataPtr mData;
float maxBankAngle, smoothing; TargetColor mTargetColor;
short followAxis;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
void post(Reader& nif) override; void post(Reader& nif) override;
}; };
struct NiLookAtController : public Controller struct NiPathController : public NiTimeController
{ {
NiAVObjectPtr target; enum Flags
unsigned short lookAtFlags{ 0 }; {
Flag_CVDataNeedsUpdate = 0x01,
Flag_OpenCurve = 0x02,
Flag_AllowFlip = 0x04,
Flag_Bank = 0x08,
Flag_ConstVelocity = 0x10,
Flag_Follow = 0x20,
Flag_FlipFollowAxis = 0x40,
};
uint16_t mPathFlags;
int32_t mBankDirection;
float mMaxBankAngle;
float mSmoothing;
uint16_t mFollowAxis;
NiPosDataPtr mPathData;
NiFloatDataPtr mPercentData;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
void post(Reader& nif) override; void post(Reader& nif) override;
}; };
struct NiUVController : public Controller struct NiLookAtController : public NiTimeController
{ {
NiUVDataPtr data; enum Flags
unsigned int uvSet; {
Flag_Flip = 0x1,
Flag_LookYAxis = 0x2,
Flag_LookZAxis = 0x4,
};
uint16_t mLookAtFlags{ 0 };
NiAVObjectPtr mLookAt;
void read(NIFStream* nif) override;
void post(Reader& nif) override;
};
struct NiUVController : public NiTimeController
{
NiUVDataPtr mData;
uint16_t mUvSet;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
void post(Reader& nif) override; void post(Reader& nif) override;
@ -281,7 +281,7 @@ namespace Nif
struct NiFlipController : public NiFloatInterpController struct NiFlipController : public NiFloatInterpController
{ {
int mTexSlot; // NiTexturingProperty::TextureType NiTexturingProperty::TextureType mTexSlot;
float mDelta; // Time between two flips. delta = (start_time - stop_time) / num_sources float mDelta; // Time between two flips. delta = (start_time - stop_time) / num_sources
NiSourceTextureList mSources; NiSourceTextureList mSources;
@ -292,110 +292,95 @@ namespace Nif
struct NiTextureTransformController : public NiFloatInterpController struct NiTextureTransformController : public NiFloatInterpController
{ {
bool mShaderMap; bool mShaderMap;
int mTexSlot; // NiTexturingProperty::TextureType NiTexturingProperty::TextureType mTexSlot;
unsigned int mTransformMember; uint32_t mTransformMember;
NiFloatDataPtr mData; NiFloatDataPtr mData;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
void post(Reader& nif) override; void post(Reader& nif) override;
}; };
struct bhkBlendController : public Controller struct bhkBlendController : public NiTimeController
{ {
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
}; };
struct BSEffectShaderPropertyFloatController : public NiFloatInterpController struct BSEffectShaderPropertyFloatController : public NiFloatInterpController
{ {
unsigned int mControlledVariable; uint32_t mControlledVariable;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
}; };
struct BSEffectShaderPropertyColorController : public NiPoint3InterpController struct BSEffectShaderPropertyColorController : public NiPoint3InterpController
{ {
unsigned int mControlledColor; uint32_t mControlledColor;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
}; };
struct NiControllerManager : public Controller struct NiControllerManager : public NiTimeController
{ {
bool mCumulative; bool mCumulative;
NiControllerSequenceList mSequences; NiControllerSequenceList mSequences;
NiDefaultAVObjectPalettePtr mObjectPalette; NiDefaultAVObjectPalettePtr mObjectPalette;
void read(NIFStream* nif) override;
void post(Reader& nif) override;
};
struct NiInterpolator : public Record
{
};
struct NiPoint3Interpolator : public NiInterpolator
{
osg::Vec3f defaultVal;
NiPosDataPtr data;
void read(NIFStream* nif) override;
void post(Reader& nif) override;
};
struct NiBoolInterpolator : public NiInterpolator
{
char defaultVal;
NiBoolDataPtr data;
void read(NIFStream* nif) override;
void post(Reader& nif) override;
};
struct NiFloatInterpolator : public NiInterpolator
{
float defaultVal;
NiFloatDataPtr data;
void read(NIFStream* nif) override;
void post(Reader& nif) override;
};
struct NiTransformInterpolator : public NiInterpolator
{
osg::Vec3f defaultPos;
osg::Quat defaultRot;
float defaultScale;
NiKeyframeDataPtr data;
void read(NIFStream* nif) override;
void post(Reader& nif) override;
};
struct NiColorInterpolator : public NiInterpolator
{
osg::Vec4f defaultVal;
NiColorDataPtr data;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
void post(Reader& nif) override; void post(Reader& nif) override;
}; };
// Abstract
struct NiInterpolator : public Record
{
};
template <class T, class DataPtr>
struct TypedNiInterpolator : public NiInterpolator
{
T mDefaultValue;
DataPtr mData;
void read(NIFStream* nif) override
{
nif->read(mDefaultValue);
mData.read(nif);
}
void post(Reader& nif) override { mData.post(nif); }
};
using NiPoint3Interpolator = TypedNiInterpolator<osg::Vec3f, NiPosDataPtr>;
using NiBoolInterpolator = TypedNiInterpolator<bool, NiBoolDataPtr>;
using NiFloatInterpolator = TypedNiInterpolator<float, NiFloatDataPtr>;
using NiTransformInterpolator = TypedNiInterpolator<NiQuatTransform, NiKeyframeDataPtr>;
using NiColorInterpolator = TypedNiInterpolator<osg::Vec4f, NiColorDataPtr>;
// Abstract // Abstract
struct NiBlendInterpolator : public NiInterpolator struct NiBlendInterpolator : public NiInterpolator
{ {
enum Flags
{
Flag_ManagerControlled = 0x1,
Flag_OnlyUseHighestWeight = 0x2,
};
struct Item struct Item
{ {
NiInterpolatorPtr mInterpolator; NiInterpolatorPtr mInterpolator;
float mWeight, mNormalizedWeight; float mWeight, mNormalizedWeight;
int mPriority; int32_t mPriority;
float mEaseSpinner; float mEaseSpinner;
void read(NIFStream* nif); void read(NIFStream* nif);
void post(Reader& nif); void post(Reader& nif);
}; };
bool mManagerControlled{ false }; uint8_t mFlags{ 0 };
bool mOnlyUseHighestWeight{ false }; uint16_t mArrayGrowBy{ 0 };
unsigned short mArrayGrowBy{ 0 };
float mWeightThreshold; float mWeightThreshold;
unsigned short mInterpCount; uint16_t mInterpCount;
unsigned short mSingleIndex; uint16_t mSingleIndex;
int mHighPriority, mNextHighPriority; int32_t mHighPriority, mNextHighPriority;
float mSingleTime; float mSingleTime;
float mHighWeightsSum, mNextHighWeightsSum; float mHighWeightsSum, mNextHighWeightsSum;
float mHighEaseSpinner; float mHighEaseSpinner;
@ -406,31 +391,37 @@ namespace Nif
void post(Reader& nif) override; void post(Reader& nif) override;
}; };
struct NiBlendBoolInterpolator : public NiBlendInterpolator template <typename T>
struct TypedNiBlendInterpolator : public NiBlendInterpolator
{ {
char mValue; T mValue;
void read(NIFStream* nif) override;
void read(NIFStream* nif) override
{
NiBlendInterpolator::read(nif);
nif->read(mValue);
}
}; };
struct NiBlendFloatInterpolator : public NiBlendInterpolator template <>
struct TypedNiBlendInterpolator<NiQuatTransform> : public NiBlendInterpolator
{ {
float mValue; NiQuatTransform mValue;
void read(NIFStream* nif) override;
void read(NIFStream* nif) override
{
NiBlendInterpolator::read(nif);
if (nif->getVersion() <= NIFStream::generateVersion(10, 1, 0, 109))
nif->read(mValue);
}
}; };
struct NiBlendPoint3Interpolator : public NiBlendInterpolator using NiBlendBoolInterpolator = TypedNiBlendInterpolator<uint8_t>;
{ using NiBlendFloatInterpolator = TypedNiBlendInterpolator<float>;
osg::Vec3f mValue; using NiBlendPoint3Interpolator = TypedNiBlendInterpolator<osg::Vec3f>;
void read(NIFStream* nif) override; using NiBlendTransformInterpolator = TypedNiBlendInterpolator<NiQuatTransform>;
};
struct NiBlendTransformInterpolator : public NiBlendInterpolator }
{
osg::Vec3f mPosValue;
osg::Quat mRotValue;
float mScaleValue;
void read(NIFStream* nif) override;
};
} // Namespace
#endif #endif

View file

@ -528,7 +528,7 @@ namespace Nif
void NiBoolData::read(NIFStream* nif) void NiBoolData::read(NIFStream* nif)
{ {
mKeyList = std::make_shared<ByteKeyMap>(); mKeyList = std::make_shared<BoolKeyMap>();
mKeyList->read(nif); mKeyList->read(nif);
} }

View file

@ -219,7 +219,7 @@ namespace Nif
struct NiVisData : public Record struct NiVisData : public Record
{ {
// TODO: investigate possible use of ByteKeyMap // TODO: investigate possible use of BoolKeyMap
std::shared_ptr<std::map<float, bool>> mKeys; std::shared_ptr<std::map<float, bool>> mKeys;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
@ -357,7 +357,8 @@ namespace Nif
struct NiBoolData : public Record struct NiBoolData : public Record
{ {
ByteKeyMapPtr mKeyList; BoolKeyMapPtr mKeyList;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
}; };

View file

@ -152,13 +152,13 @@ namespace Nif
using Vector3KeyMap = KeyMapT<osg::Vec3f, &NIFStream::get<osg::Vec3f>>; using Vector3KeyMap = KeyMapT<osg::Vec3f, &NIFStream::get<osg::Vec3f>>;
using Vector4KeyMap = KeyMapT<osg::Vec4f, &NIFStream::get<osg::Vec4f>>; using Vector4KeyMap = KeyMapT<osg::Vec4f, &NIFStream::get<osg::Vec4f>>;
using QuaternionKeyMap = KeyMapT<osg::Quat, &NIFStream::get<osg::Quat>>; using QuaternionKeyMap = KeyMapT<osg::Quat, &NIFStream::get<osg::Quat>>;
using ByteKeyMap = KeyMapT<char, &NIFStream::get<char>>; using BoolKeyMap = KeyMapT<bool, &NIFStream::get<bool>>;
using FloatKeyMapPtr = std::shared_ptr<FloatKeyMap>; using FloatKeyMapPtr = std::shared_ptr<FloatKeyMap>;
using Vector3KeyMapPtr = std::shared_ptr<Vector3KeyMap>; using Vector3KeyMapPtr = std::shared_ptr<Vector3KeyMap>;
using Vector4KeyMapPtr = std::shared_ptr<Vector4KeyMap>; using Vector4KeyMapPtr = std::shared_ptr<Vector4KeyMap>;
using QuaternionKeyMapPtr = std::shared_ptr<QuaternionKeyMap>; using QuaternionKeyMapPtr = std::shared_ptr<QuaternionKeyMap>;
using ByteKeyMapPtr = std::shared_ptr<ByteKeyMap>; using BoolKeyMapPtr = std::shared_ptr<BoolKeyMap>;
} // Namespace } // Namespace
#endif //#ifndef OPENMW_COMPONENTS_NIF_NIFKEY_HPP #endif //#ifndef OPENMW_COMPONENTS_NIF_NIFKEY_HPP

View file

@ -137,6 +137,22 @@ namespace Nif
read(transform.mScale); read(transform.mScale);
} }
template <>
void NIFStream::read<NiQuatTransform>(NiQuatTransform& transform)
{
read(transform.mTranslation);
read(transform.mRotation);
read(transform.mScale);
if (getVersion() >= generateVersion(10, 1, 0, 110))
return;
if (!get<bool>())
transform.mTranslation = osg::Vec3f();
if (!get<bool>())
transform.mRotation = osg::Quat();
if (!get<bool>())
transform.mScale = 1.f;
}
template <> template <>
void NIFStream::read<bool>(bool& data) void NIFStream::read<bool>(bool& data)
{ {
@ -197,6 +213,12 @@ namespace Nif
readRange(*this, dest, size); readRange(*this, dest, size);
} }
template <>
void NIFStream::read<NiQuatTransform>(NiQuatTransform* dest, size_t size)
{
readRange(*this, dest, size);
}
template <> template <>
void NIFStream::read<bool>(bool* dest, size_t size) void NIFStream::read<bool>(bool* dest, size_t size)
{ {

View file

@ -149,7 +149,6 @@ namespace Nif
/// DEPRECATED: Use read() or get() /// DEPRECATED: Use read() or get()
char getChar() { return get<char>(); } char getChar() { return get<char>(); }
short getShort() { return get<short>(); }
unsigned short getUShort() { return get<unsigned short>(); } unsigned short getUShort() { return get<unsigned short>(); }
int getInt() { return get<int>(); } int getInt() { return get<int>(); }
unsigned int getUInt() { return get<unsigned int>(); } unsigned int getUInt() { return get<unsigned int>(); }
@ -157,10 +156,6 @@ namespace Nif
osg::Vec2f getVector2() { return get<osg::Vec2f>(); } osg::Vec2f getVector2() { return get<osg::Vec2f>(); }
osg::Vec3f getVector3() { return get<osg::Vec3f>(); } osg::Vec3f getVector3() { return get<osg::Vec3f>(); }
osg::Vec4f getVector4() { return get<osg::Vec4f>(); } osg::Vec4f getVector4() { return get<osg::Vec4f>(); }
Matrix3 getMatrix3() { return get<Matrix3>(); }
osg::Quat getQuaternion() { return get<osg::Quat>(); }
bool getBoolean() { return get<bool>(); }
std::string getString() { return get<std::string>(); }
}; };
template <> template <>
@ -178,6 +173,8 @@ namespace Nif
template <> template <>
void NIFStream::read<NiTransform>(NiTransform& transform); void NIFStream::read<NiTransform>(NiTransform& transform);
template <> template <>
void NIFStream::read<NiQuatTransform>(NiQuatTransform& transform);
template <>
void NIFStream::read<bool>(bool& data); void NIFStream::read<bool>(bool& data);
template <> template <>
void NIFStream::read<std::string>(std::string& str); void NIFStream::read<std::string>(std::string& str);
@ -197,6 +194,8 @@ namespace Nif
template <> template <>
void NIFStream::read<NiTransform>(NiTransform* dest, size_t size); void NIFStream::read<NiTransform>(NiTransform* dest, size_t size);
template <> template <>
void NIFStream::read<NiQuatTransform>(NiQuatTransform* dest, size_t size);
template <>
void NIFStream::read<bool>(bool* dest, size_t size); void NIFStream::read<bool>(bool* dest, size_t size);
template <> template <>
void NIFStream::read<std::string>(std::string* dest, size_t size); void NIFStream::read<std::string>(std::string* dest, size_t size);

View file

@ -25,6 +25,7 @@
#define OPENMW_COMPONENTS_NIF_NIFTYPES_HPP #define OPENMW_COMPONENTS_NIF_NIFTYPES_HPP
#include <osg/Matrixf> #include <osg/Matrixf>
#include <osg/Quat>
#include <osg/Vec3f> #include <osg/Vec3f>
// Common types used in NIF files // Common types used in NIF files
@ -80,5 +81,30 @@ namespace Nif
} }
}; };
struct NiQuatTransform
{
osg::Vec3f mTranslation;
osg::Quat mRotation;
float mScale;
osg::Matrixf toMatrix() const
{
osg::Matrixf transform(mRotation);
transform.setTrans(mTranslation);
for (int i = 0; i < 3; i++)
transform(i, i) *= mScale;
return transform;
}
bool isIdentity() const { return mTranslation == osg::Vec3f() && mRotation == osg::Quat() && mScale == 1.f; }
static const NiQuatTransform& getIdentity()
{
static const NiQuatTransform identity = { osg::Vec3f(), osg::Quat(), 1.f };
return identity;
}
};
} // Namespace } // Namespace
#endif #endif

View file

@ -25,7 +25,7 @@ namespace Nif
case BOX_BV: case BOX_BV:
{ {
box.center = nif->getVector3(); box.center = nif->getVector3();
box.axes = nif->getMatrix3(); nif->read(box.axes);
box.extents = nif->getVector3(); box.extents = nif->getVector3();
break; break;
} }
@ -153,8 +153,8 @@ namespace Nif
if (nif->getVersion() < NIFStream::generateVersion(10, 0, 1, 0)) if (nif->getVersion() < NIFStream::generateVersion(10, 0, 1, 0))
return; return;
unsigned int num = 0; unsigned int num = 0;
if (nif->getVersion() <= NIFStream::generateVersion(20, 1, 0, 3)) if (nif->getVersion() <= NIFStream::generateVersion(20, 1, 0, 3) && nif->get<bool>())
num = nif->getBoolean(); // Has Shader num = 1;
else if (nif->getVersion() >= NIFStream::generateVersion(20, 2, 0, 5)) else if (nif->getVersion() >= NIFStream::generateVersion(20, 2, 0, 5))
num = nif->getUInt(); num = nif->getUInt();
@ -163,7 +163,7 @@ namespace Nif
if (nif->getVersion() >= NIFStream::generateVersion(20, 2, 0, 5)) if (nif->getVersion() >= NIFStream::generateVersion(20, 2, 0, 5))
active = nif->getUInt(); active = nif->getUInt();
if (nif->getVersion() >= NIFFile::NIFVersion::VER_BGS) if (nif->getVersion() >= NIFFile::NIFVersion::VER_BGS)
needsUpdate = nif->getBoolean(); nif->read(needsUpdate);
} }
void NiGeometry::read(NIFStream* nif) void NiGeometry::read(NIFStream* nif)
@ -210,7 +210,7 @@ namespace Nif
nearDist = nif->getFloat(); nearDist = nif->getFloat();
farDist = nif->getFloat(); farDist = nif->getFloat();
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0)) if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0))
orthographic = nif->getBoolean(); nif->read(orthographic);
vleft = nif->getFloat(); vleft = nif->getFloat();
vright = nif->getFloat(); vright = nif->getFloat();
vtop = nif->getFloat(); vtop = nif->getFloat();

View file

@ -9,7 +9,7 @@ namespace Nif
struct NiParticleModifier : public Record struct NiParticleModifier : public Record
{ {
NiParticleModifierPtr mNext; NiParticleModifierPtr mNext;
ControllerPtr mController; NiTimeControllerPtr mController;
void read(NIFStream* nif) override; void read(NIFStream* nif) override;
void post(Reader& nif) override; void post(Reader& nif) override;

View file

@ -8,7 +8,7 @@ namespace Nif
void NiTexturingProperty::Texture::read(NIFStream* nif) void NiTexturingProperty::Texture::read(NIFStream* nif)
{ {
inUse = nif->getBoolean(); nif->read(inUse);
if (!inUse) if (!inUse)
return; return;
@ -36,7 +36,7 @@ namespace Nif
nif->skip(2); // Unknown short nif->skip(2); // Unknown short
else if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0)) else if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0))
{ {
if (nif->getBoolean()) // Has texture transform if (nif->get<bool>()) // Has texture transform
{ {
nif->getVector2(); // UV translation nif->getVector2(); // UV translation
nif->getVector2(); // UV scale nif->getVector2(); // UV scale

View file

@ -79,7 +79,7 @@ namespace Nif
* 5 - Bump map texture * 5 - Bump map texture
* 6 - Decal texture * 6 - Decal texture
*/ */
enum TextureType enum TextureType : uint32_t
{ {
BaseTexture = 0, BaseTexture = 0,
DarkTexture = 1, DarkTexture = 1,

View file

@ -116,7 +116,7 @@ namespace Nif
struct NiUVData; struct NiUVData;
struct NiPosData; struct NiPosData;
struct NiVisData; struct NiVisData;
struct Controller; struct NiTimeController;
struct NiObjectNET; struct NiObjectNET;
struct NiSkinData; struct NiSkinData;
struct NiFloatData; struct NiFloatData;
@ -157,7 +157,7 @@ namespace Nif
using NiUVDataPtr = RecordPtrT<NiUVData>; using NiUVDataPtr = RecordPtrT<NiUVData>;
using NiPosDataPtr = RecordPtrT<NiPosData>; using NiPosDataPtr = RecordPtrT<NiPosData>;
using NiVisDataPtr = RecordPtrT<NiVisData>; using NiVisDataPtr = RecordPtrT<NiVisData>;
using ControllerPtr = RecordPtrT<Controller>; using NiTimeControllerPtr = RecordPtrT<NiTimeController>;
using NiObjectNETPtr = RecordPtrT<NiObjectNET>; using NiObjectNETPtr = RecordPtrT<NiObjectNET>;
using NiSkinDataPtr = RecordPtrT<NiSkinData>; using NiSkinDataPtr = RecordPtrT<NiSkinData>;
using NiMorphDataPtr = RecordPtrT<NiMorphData>; using NiMorphDataPtr = RecordPtrT<NiMorphData>;

View file

@ -255,7 +255,7 @@ namespace NifBullet
if (node.recType == Nif::RC_NiCollisionSwitch && !node.collisionActive()) if (node.recType == Nif::RC_NiCollisionSwitch && !node.collisionActive())
return; return;
for (Nif::ControllerPtr ctrl = node.mController; !ctrl.empty(); ctrl = ctrl->next) for (Nif::NiTimeControllerPtr ctrl = node.mController; !ctrl.empty(); ctrl = ctrl->mNext)
{ {
if (args.mAnimated) if (args.mAnimated)
break; break;

View file

@ -15,11 +15,11 @@
namespace NifOsg namespace NifOsg
{ {
ControllerFunction::ControllerFunction(const Nif::Controller* ctrl) ControllerFunction::ControllerFunction(const Nif::NiTimeController* ctrl)
: mFrequency(ctrl->frequency) : mFrequency(ctrl->mFrequency)
, mPhase(ctrl->phase) , mPhase(ctrl->mPhase)
, mStartTime(ctrl->timeStart) , mStartTime(ctrl->mTimeStart)
, mStopTime(ctrl->timeStop) , mStopTime(ctrl->mTimeStop)
, mExtrapolationMode(ctrl->extrapolationMode()) , mExtrapolationMode(ctrl->extrapolationMode())
{ {
} }
@ -31,7 +31,7 @@ namespace NifOsg
return time; return time;
switch (mExtrapolationMode) switch (mExtrapolationMode)
{ {
case Nif::Controller::ExtrapolationMode::Cycle: case Nif::NiTimeController::ExtrapolationMode::Cycle:
{ {
float delta = mStopTime - mStartTime; float delta = mStopTime - mStartTime;
if (delta <= 0) if (delta <= 0)
@ -40,7 +40,7 @@ namespace NifOsg
float remainder = (cycles - std::floor(cycles)) * delta; float remainder = (cycles - std::floor(cycles)) * delta;
return mStartTime + remainder; return mStartTime + remainder;
} }
case Nif::Controller::ExtrapolationMode::Reverse: case Nif::NiTimeController::ExtrapolationMode::Reverse:
{ {
float delta = mStopTime - mStartTime; float delta = mStopTime - mStartTime;
if (delta <= 0) if (delta <= 0)
@ -55,7 +55,7 @@ namespace NifOsg
return mStopTime - remainder; return mStopTime - remainder;
} }
case Nif::Controller::ExtrapolationMode::Constant: case Nif::NiTimeController::ExtrapolationMode::Constant:
default: default:
return std::clamp(time, mStartTime, mStopTime); return std::clamp(time, mStartTime, mStopTime);
} }
@ -90,21 +90,23 @@ namespace NifOsg
{ {
const Nif::NiTransformInterpolator* interp const Nif::NiTransformInterpolator* interp
= static_cast<const Nif::NiTransformInterpolator*>(keyctrl->mInterpolator.getPtr()); = static_cast<const Nif::NiTransformInterpolator*>(keyctrl->mInterpolator.getPtr());
if (!interp->data.empty()) const Nif::NiQuatTransform& defaultTransform = interp->mDefaultValue;
if (!interp->mData.empty())
{ {
mRotations = QuaternionInterpolator(interp->data->mRotations, interp->defaultRot); mRotations = QuaternionInterpolator(interp->mData->mRotations, defaultTransform.mRotation);
mXRotations = FloatInterpolator(interp->data->mXRotations); mXRotations = FloatInterpolator(interp->mData->mXRotations);
mYRotations = FloatInterpolator(interp->data->mYRotations); mYRotations = FloatInterpolator(interp->mData->mYRotations);
mZRotations = FloatInterpolator(interp->data->mZRotations); mZRotations = FloatInterpolator(interp->mData->mZRotations);
mTranslations = Vec3Interpolator(interp->data->mTranslations, interp->defaultPos); mTranslations = Vec3Interpolator(interp->mData->mTranslations, defaultTransform.mTranslation);
mScales = FloatInterpolator(interp->data->mScales, interp->defaultScale); mScales = FloatInterpolator(interp->mData->mScales, defaultTransform.mScale);
mAxisOrder = interp->data->mAxisOrder;
mAxisOrder = interp->mData->mAxisOrder;
} }
else else
{ {
mRotations = QuaternionInterpolator(Nif::QuaternionKeyMapPtr(), interp->defaultRot); mRotations = QuaternionInterpolator(Nif::QuaternionKeyMapPtr(), defaultTransform.mRotation);
mTranslations = Vec3Interpolator(Nif::Vector3KeyMapPtr(), interp->defaultPos); mTranslations = Vec3Interpolator(Nif::Vector3KeyMapPtr(), defaultTransform.mTranslation);
mScales = FloatInterpolator(Nif::FloatKeyMapPtr(), interp->defaultScale); mScales = FloatInterpolator(Nif::FloatKeyMapPtr(), defaultTransform.mScale);
} }
} }
} }
@ -117,6 +119,7 @@ namespace NifOsg
mZRotations = FloatInterpolator(keydata->mZRotations); mZRotations = FloatInterpolator(keydata->mZRotations);
mTranslations = Vec3Interpolator(keydata->mTranslations); mTranslations = Vec3Interpolator(keydata->mTranslations);
mScales = FloatInterpolator(keydata->mScales, 1.f); mScales = FloatInterpolator(keydata->mScales, 1.f);
mAxisOrder = keydata->mAxisOrder; mAxisOrder = keydata->mAxisOrder;
} }
} }
@ -177,11 +180,11 @@ namespace NifOsg
else else
node->setRotation(node->mRotationScale); node->setRotation(node->mRotationScale);
if (!mScales.empty())
node->setScale(mScales.interpKey(time));
if (!mTranslations.empty()) if (!mTranslations.empty())
node->setTranslation(mTranslations.interpKey(time)); node->setTranslation(mTranslations.interpKey(time));
if (!mScales.empty())
node->setScale(mScales.interpKey(time));
} }
traverse(node, nv); traverse(node, nv);
@ -251,9 +254,7 @@ namespace NifOsg
} }
} }
UVController::UVController() {} UVController::UVController(const Nif::NiUVData* data, const std::set<unsigned int>& textureUnits)
UVController::UVController(const Nif::NiUVData* data, const std::set<int>& textureUnits)
: mUTrans(data->mKeyList[0], 0.f) : mUTrans(data->mKeyList[0], 0.f)
, mVTrans(data->mKeyList[1], 0.f) , mVTrans(data->mKeyList[1], 0.f)
, mUScale(data->mKeyList[2], 1.f) , mUScale(data->mKeyList[2], 1.f)
@ -277,8 +278,8 @@ namespace NifOsg
void UVController::setDefaults(osg::StateSet* stateset) void UVController::setDefaults(osg::StateSet* stateset)
{ {
osg::ref_ptr<osg::TexMat> texMat(new osg::TexMat); osg::ref_ptr<osg::TexMat> texMat(new osg::TexMat);
for (std::set<int>::const_iterator it = mTextureUnits.begin(); it != mTextureUnits.end(); ++it) for (unsigned int unit : mTextureUnits)
stateset->setTextureAttributeAndModes(*it, texMat, osg::StateAttribute::ON); stateset->setTextureAttributeAndModes(unit, texMat, osg::StateAttribute::ON);
} }
void UVController::apply(osg::StateSet* stateset, osg::NodeVisitor* nv) void UVController::apply(osg::StateSet* stateset, osg::NodeVisitor* nv)
@ -314,9 +315,10 @@ namespace NifOsg
{ {
if (!ctrl->mInterpolator.empty()) if (!ctrl->mInterpolator.empty())
{ {
if (ctrl->mInterpolator->recType == Nif::RC_NiBoolInterpolator) if (ctrl->mInterpolator->recType != Nif::RC_NiBoolInterpolator)
mInterpolator return;
= ByteInterpolator(static_cast<const Nif::NiBoolInterpolator*>(ctrl->mInterpolator.getPtr()));
mInterpolator = { static_cast<const Nif::NiBoolInterpolator*>(ctrl->mInterpolator.getPtr()) };
} }
else if (!ctrl->mData.empty()) else if (!ctrl->mData.empty())
mData = ctrl->mData->mKeys; mData = ctrl->mData->mKeys;
@ -444,7 +446,7 @@ namespace NifOsg
MaterialColorController::MaterialColorController( MaterialColorController::MaterialColorController(
const Nif::NiMaterialColorController* ctrl, const osg::Material* baseMaterial) const Nif::NiMaterialColorController* ctrl, const osg::Material* baseMaterial)
: mTargetColor(static_cast<MaterialColorController::TargetColor>(ctrl->mTargetColor)) : mTargetColor(ctrl->mTargetColor)
, mBaseMaterial(baseMaterial) , mBaseMaterial(baseMaterial)
{ {
if (!ctrl->mInterpolator.empty()) if (!ctrl->mInterpolator.empty())
@ -477,30 +479,31 @@ namespace NifOsg
{ {
osg::Vec3f value = mData.interpKey(getInputValue(nv)); osg::Vec3f value = mData.interpKey(getInputValue(nv));
osg::Material* mat = static_cast<osg::Material*>(stateset->getAttribute(osg::StateAttribute::MATERIAL)); osg::Material* mat = static_cast<osg::Material*>(stateset->getAttribute(osg::StateAttribute::MATERIAL));
using TargetColor = Nif::NiMaterialColorController::TargetColor;
switch (mTargetColor) switch (mTargetColor)
{ {
case Diffuse: case TargetColor::Diffuse:
{ {
osg::Vec4f diffuse = mat->getDiffuse(osg::Material::FRONT_AND_BACK); osg::Vec4f diffuse = mat->getDiffuse(osg::Material::FRONT_AND_BACK);
diffuse.set(value.x(), value.y(), value.z(), diffuse.a()); diffuse.set(value.x(), value.y(), value.z(), diffuse.a());
mat->setDiffuse(osg::Material::FRONT_AND_BACK, diffuse); mat->setDiffuse(osg::Material::FRONT_AND_BACK, diffuse);
break; break;
} }
case Specular: case TargetColor::Specular:
{ {
osg::Vec4f specular = mat->getSpecular(osg::Material::FRONT_AND_BACK); osg::Vec4f specular = mat->getSpecular(osg::Material::FRONT_AND_BACK);
specular.set(value.x(), value.y(), value.z(), specular.a()); specular.set(value.x(), value.y(), value.z(), specular.a());
mat->setSpecular(osg::Material::FRONT_AND_BACK, specular); mat->setSpecular(osg::Material::FRONT_AND_BACK, specular);
break; break;
} }
case Emissive: case TargetColor::Emissive:
{ {
osg::Vec4f emissive = mat->getEmission(osg::Material::FRONT_AND_BACK); osg::Vec4f emissive = mat->getEmission(osg::Material::FRONT_AND_BACK);
emissive.set(value.x(), value.y(), value.z(), emissive.a()); emissive.set(value.x(), value.y(), value.z(), emissive.a());
mat->setEmission(osg::Material::FRONT_AND_BACK, emissive); mat->setEmission(osg::Material::FRONT_AND_BACK, emissive);
break; break;
} }
case Ambient: case TargetColor::Ambient:
default: default:
{ {
osg::Vec4f ambient = mat->getAmbient(osg::Material::FRONT_AND_BACK); osg::Vec4f ambient = mat->getAmbient(osg::Material::FRONT_AND_BACK);
@ -552,14 +555,8 @@ namespace NifOsg
} }
ParticleSystemController::ParticleSystemController(const Nif::NiParticleSystemController* ctrl) ParticleSystemController::ParticleSystemController(const Nif::NiParticleSystemController* ctrl)
: mEmitStart(ctrl->startTime) : mEmitStart(ctrl->mEmitStartTime)
, mEmitStop(ctrl->stopTime) , mEmitStop(ctrl->mEmitStopTime)
{
}
ParticleSystemController::ParticleSystemController()
: mEmitStart(0.f)
, mEmitStop(0.f)
{ {
} }
@ -594,9 +591,9 @@ namespace NifOsg
} }
PathController::PathController(const Nif::NiPathController* ctrl) PathController::PathController(const Nif::NiPathController* ctrl)
: mPath(ctrl->posData->mKeyList, osg::Vec3f()) : mPath(ctrl->mPathData->mKeyList, osg::Vec3f())
, mPercent(ctrl->floatData->mKeyList, 1.f) , mPercent(ctrl->mPercentData->mKeyList, 1.f)
, mFlags(ctrl->flags) , mFlags(ctrl->mPathFlags)
{ {
} }

View file

@ -63,18 +63,17 @@ namespace NifOsg
ValueInterpolator() = default; ValueInterpolator() = default;
template <class T, template <class T,
typename typename = std::enable_if_t<
= std::enable_if_t<std::conjunction_v<std::disjunction<std::is_same<ValueT, float>, std::conjunction_v<std::disjunction<std::is_same<ValueT, float>, std::is_same<ValueT, osg::Vec3f>,
std::is_same<ValueT, osg::Vec3f>, std::is_same<ValueT, bool>, std::is_same<ValueT, bool>, std::is_same<ValueT, osg::Vec4f>>,
std::is_same<ValueT, osg::Vec4f>, std::is_same<ValueT, char>>, std::is_same<decltype(T::mDefaultValue), ValueT>>,
std::is_same<decltype(T::defaultVal), ValueT>>,
T>> T>>
ValueInterpolator(const T* interpolator) ValueInterpolator(const T* interpolator)
: mDefaultVal(interpolator->defaultVal) : mDefaultVal(interpolator->mDefaultValue)
{ {
if (interpolator->data.empty()) if (interpolator->mData.empty())
return; return;
mKeys = interpolator->data->mKeyList; mKeys = interpolator->mData->mKeyList;
if (mKeys) if (mKeys)
{ {
mLastLowKey = mKeys->mKeys.end(); mLastLowKey = mKeys->mKeys.end();
@ -182,7 +181,7 @@ namespace NifOsg
using FloatInterpolator = ValueInterpolator<Nif::FloatKeyMap>; using FloatInterpolator = ValueInterpolator<Nif::FloatKeyMap>;
using Vec3Interpolator = ValueInterpolator<Nif::Vector3KeyMap>; using Vec3Interpolator = ValueInterpolator<Nif::Vector3KeyMap>;
using Vec4Interpolator = ValueInterpolator<Nif::Vector4KeyMap>; using Vec4Interpolator = ValueInterpolator<Nif::Vector4KeyMap>;
using ByteInterpolator = ValueInterpolator<Nif::ByteKeyMap>; using BoolInterpolator = ValueInterpolator<Nif::BoolKeyMap>;
class ControllerFunction : public SceneUtil::ControllerFunction class ControllerFunction : public SceneUtil::ControllerFunction
{ {
@ -191,10 +190,10 @@ namespace NifOsg
float mPhase; float mPhase;
float mStartTime; float mStartTime;
float mStopTime; float mStopTime;
Nif::Controller::ExtrapolationMode mExtrapolationMode; Nif::NiTimeController::ExtrapolationMode mExtrapolationMode;
public: public:
ControllerFunction(const Nif::Controller* ctrl); ControllerFunction(const Nif::NiTimeController* ctrl);
float calculate(float value) const override; float calculate(float value) const override;
@ -262,9 +261,9 @@ namespace NifOsg
class UVController : public SceneUtil::StateSetUpdater, public SceneUtil::Controller class UVController : public SceneUtil::StateSetUpdater, public SceneUtil::Controller
{ {
public: public:
UVController(); UVController() = default;
UVController(const UVController&, const osg::CopyOp&); UVController(const UVController&, const osg::CopyOp&);
UVController(const Nif::NiUVData* data, const std::set<int>& textureUnits); UVController(const Nif::NiUVData* data, const std::set<unsigned int>& textureUnits);
META_Object(NifOsg, UVController) META_Object(NifOsg, UVController)
@ -276,14 +275,14 @@ namespace NifOsg
FloatInterpolator mVTrans; FloatInterpolator mVTrans;
FloatInterpolator mUScale; FloatInterpolator mUScale;
FloatInterpolator mVScale; FloatInterpolator mVScale;
std::set<int> mTextureUnits; std::set<unsigned int> mTextureUnits;
}; };
class VisController : public SceneUtil::NodeCallback<VisController>, public SceneUtil::Controller class VisController : public SceneUtil::NodeCallback<VisController>, public SceneUtil::Controller
{ {
private: private:
std::shared_ptr<std::map<float, bool>> mData; std::shared_ptr<std::map<float, bool>> mData;
ByteInterpolator mInterpolator; BoolInterpolator mInterpolator;
unsigned int mMask{ 0u }; unsigned int mMask{ 0u };
bool calculate(float time) const; bool calculate(float time) const;
@ -336,13 +335,6 @@ namespace NifOsg
class MaterialColorController : public SceneUtil::StateSetUpdater, public SceneUtil::Controller class MaterialColorController : public SceneUtil::StateSetUpdater, public SceneUtil::Controller
{ {
public: public:
enum TargetColor
{
Ambient = 0,
Diffuse = 1,
Specular = 2,
Emissive = 3
};
MaterialColorController(const Nif::NiMaterialColorController* ctrl, const osg::Material* baseMaterial); MaterialColorController(const Nif::NiMaterialColorController* ctrl, const osg::Material* baseMaterial);
MaterialColorController(); MaterialColorController();
MaterialColorController(const MaterialColorController& copy, const osg::CopyOp& copyop); MaterialColorController(const MaterialColorController& copy, const osg::CopyOp& copyop);
@ -355,7 +347,7 @@ namespace NifOsg
private: private:
Vec3Interpolator mData; Vec3Interpolator mData;
TargetColor mTargetColor = Ambient; Nif::NiMaterialColorController::TargetColor mTargetColor;
osg::ref_ptr<const osg::Material> mBaseMaterial; osg::ref_ptr<const osg::Material> mBaseMaterial;
}; };
@ -386,7 +378,7 @@ namespace NifOsg
{ {
public: public:
ParticleSystemController(const Nif::NiParticleSystemController* ctrl); ParticleSystemController(const Nif::NiParticleSystemController* ctrl);
ParticleSystemController(); ParticleSystemController() = default;
ParticleSystemController(const ParticleSystemController& copy, const osg::CopyOp& copyop); ParticleSystemController(const ParticleSystemController& copy, const osg::CopyOp& copyop);
META_Object(NifOsg, ParticleSystemController) META_Object(NifOsg, ParticleSystemController)
@ -394,8 +386,8 @@ namespace NifOsg
void operator()(osgParticle::ParticleProcessor* node, osg::NodeVisitor* nv); void operator()(osgParticle::ParticleProcessor* node, osg::NodeVisitor* nv);
private: private:
float mEmitStart; float mEmitStart{ 0.f };
float mEmitStop; float mEmitStop{ 0.f };
}; };
class PathController : public SceneUtil::NodeCallback<PathController, NifOsg::MatrixTransform*>, class PathController : public SceneUtil::NodeCallback<PathController, NifOsg::MatrixTransform*>,

View file

@ -293,8 +293,8 @@ namespace NifOsg
auto textKeyExtraData = static_cast<const Nif::NiTextKeyExtraData*>(extraList[0].getPtr()); auto textKeyExtraData = static_cast<const Nif::NiTextKeyExtraData*>(extraList[0].getPtr());
extractTextKeys(textKeyExtraData, target.mTextKeys); extractTextKeys(textKeyExtraData, target.mTextKeys);
Nif::ControllerPtr ctrl = seq->mController; Nif::NiTimeControllerPtr ctrl = seq->mController;
for (size_t i = 1; i < extraList.size() && !ctrl.empty(); i++, (ctrl = ctrl->next)) for (size_t i = 1; i < extraList.size() && !ctrl.empty(); i++, (ctrl = ctrl->mNext))
{ {
Nif::ExtraPtr extra = extraList[i]; Nif::ExtraPtr extra = extraList[i];
if (extra->recType != Nif::RC_NiStringExtraData || ctrl->recType != Nif::RC_NiKeyframeController) if (extra->recType != Nif::RC_NiStringExtraData || ctrl->recType != Nif::RC_NiKeyframeController)
@ -449,7 +449,7 @@ namespace NifOsg
animflags, hasStencilProperty); animflags, hasStencilProperty);
} }
static void setupController(const Nif::Controller* ctrl, SceneUtil::Controller* toSetup, int animflags) static void setupController(const Nif::NiTimeController* ctrl, SceneUtil::Controller* toSetup, int animflags)
{ {
bool autoPlay = animflags & Nif::NiNode::AnimFlag_AutoPlay; bool autoPlay = animflags & Nif::NiNode::AnimFlag_AutoPlay;
if (autoPlay) if (autoPlay)
@ -725,7 +725,7 @@ namespace NifOsg
if (nifNode->isHidden()) if (nifNode->isHidden())
{ {
bool hasVisController = false; bool hasVisController = false;
for (Nif::ControllerPtr ctrl = nifNode->mController; !ctrl.empty(); ctrl = ctrl->next) for (Nif::NiTimeControllerPtr ctrl = nifNode->mController; !ctrl.empty(); ctrl = ctrl->mNext)
{ {
hasVisController |= (ctrl->recType == Nif::RC_NiVisController); hasVisController |= (ctrl->recType == Nif::RC_NiVisController);
if (hasVisController) if (hasVisController)
@ -858,25 +858,24 @@ namespace NifOsg
SceneUtil::CompositeStateSetUpdater* composite, const std::vector<unsigned int>& boundTextures, SceneUtil::CompositeStateSetUpdater* composite, const std::vector<unsigned int>& boundTextures,
int animflags) int animflags)
{ {
for (Nif::ControllerPtr ctrl = nifNode->mController; !ctrl.empty(); ctrl = ctrl->next) for (Nif::NiTimeControllerPtr ctrl = nifNode->mController; !ctrl.empty(); ctrl = ctrl->mNext)
{ {
if (!ctrl->isActive()) if (!ctrl->isActive())
continue; continue;
if (ctrl->recType == Nif::RC_NiUVController) if (ctrl->recType == Nif::RC_NiUVController)
{ {
const Nif::NiUVController* niuvctrl = static_cast<const Nif::NiUVController*>(ctrl.getPtr()); const Nif::NiUVController* niuvctrl = static_cast<const Nif::NiUVController*>(ctrl.getPtr());
if (niuvctrl->data.empty()) if (niuvctrl->mData.empty())
continue; continue;
const unsigned int uvSet = niuvctrl->uvSet; std::set<unsigned int> texUnits;
std::set<int> texUnits; // UVController should only work for textures which use the given UV Set.
// UVController should work only for textures which use a given UV Set, usually 0.
for (unsigned int i = 0; i < boundTextures.size(); ++i) for (unsigned int i = 0; i < boundTextures.size(); ++i)
{ {
if (boundTextures[i] == uvSet) if (boundTextures[i] == niuvctrl->mUvSet)
texUnits.insert(i); texUnits.insert(i);
} }
osg::ref_ptr<UVController> uvctrl = new UVController(niuvctrl->data.getPtr(), texUnits); osg::ref_ptr<UVController> uvctrl = new UVController(niuvctrl->mData.getPtr(), texUnits);
setupController(niuvctrl, uvctrl, animflags); setupController(niuvctrl, uvctrl, animflags);
composite->addController(uvctrl); composite->addController(uvctrl);
} }
@ -885,7 +884,7 @@ namespace NifOsg
void handleNodeControllers(const Nif::NiAVObject* nifNode, osg::Node* node, int animflags, bool& isAnimated) void handleNodeControllers(const Nif::NiAVObject* nifNode, osg::Node* node, int animflags, bool& isAnimated)
{ {
for (Nif::ControllerPtr ctrl = nifNode->mController; !ctrl.empty(); ctrl = ctrl->next) for (Nif::NiTimeControllerPtr ctrl = nifNode->mController; !ctrl.empty(); ctrl = ctrl->mNext)
{ {
if (!ctrl->isActive()) if (!ctrl->isActive())
continue; continue;
@ -908,7 +907,7 @@ namespace NifOsg
else if (ctrl->recType == Nif::RC_NiPathController) else if (ctrl->recType == Nif::RC_NiPathController)
{ {
const Nif::NiPathController* path = static_cast<const Nif::NiPathController*>(ctrl.getPtr()); const Nif::NiPathController* path = static_cast<const Nif::NiPathController*>(ctrl.getPtr());
if (path->posData.empty() || path->floatData.empty()) if (path->mPathData.empty() || path->mPercentData.empty())
continue; continue;
osg::ref_ptr<PathController> callback(new PathController(path)); osg::ref_ptr<PathController> callback(new PathController(path));
setupController(path, callback, animflags); setupController(path, callback, animflags);
@ -963,7 +962,7 @@ namespace NifOsg
void handleMaterialControllers(const Nif::Property* materialProperty, void handleMaterialControllers(const Nif::Property* materialProperty,
SceneUtil::CompositeStateSetUpdater* composite, int animflags, const osg::Material* baseMaterial) SceneUtil::CompositeStateSetUpdater* composite, int animflags, const osg::Material* baseMaterial)
{ {
for (Nif::ControllerPtr ctrl = materialProperty->mController; !ctrl.empty(); ctrl = ctrl->next) for (Nif::NiTimeControllerPtr ctrl = materialProperty->mController; !ctrl.empty(); ctrl = ctrl->mNext)
{ {
if (!ctrl->isActive()) if (!ctrl->isActive())
continue; continue;
@ -988,18 +987,16 @@ namespace NifOsg
{ {
const Nif::NiMaterialColorController* matctrl const Nif::NiMaterialColorController* matctrl
= static_cast<const Nif::NiMaterialColorController*>(ctrl.getPtr()); = static_cast<const Nif::NiMaterialColorController*>(ctrl.getPtr());
if (matctrl->mData.empty() && matctrl->mInterpolator.empty()) Nif::NiInterpolatorPtr interp = matctrl->mInterpolator;
if (matctrl->mData.empty() && interp.empty())
continue; continue;
auto targetColor = static_cast<MaterialColorController::TargetColor>(matctrl->mTargetColor);
if (mVersion <= Nif::NIFFile::VER_MW if (mVersion <= Nif::NIFFile::VER_MW
&& targetColor == MaterialColorController::TargetColor::Specular) && matctrl->mTargetColor == Nif::NiMaterialColorController::TargetColor::Specular)
continue; continue;
if (!matctrl->mInterpolator.empty() if (!interp.empty() && interp->recType != Nif::RC_NiPoint3Interpolator)
&& matctrl->mInterpolator->recType != Nif::RC_NiPoint3Interpolator)
{ {
Log(Debug::Error) Log(Debug::Error) << "Unsupported interpolator type for NiMaterialColorController "
<< "Unsupported interpolator type for NiMaterialColorController " << matctrl->recIndex << matctrl->recIndex << " in " << mFilename << ": " << interp->recName;
<< " in " << mFilename << ": " << matctrl->mInterpolator->recName;
continue; continue;
} }
osg::ref_ptr<MaterialColorController> osgctrl = new MaterialColorController(matctrl, baseMaterial); osg::ref_ptr<MaterialColorController> osgctrl = new MaterialColorController(matctrl, baseMaterial);
@ -1014,7 +1011,7 @@ namespace NifOsg
void handleTextureControllers(const Nif::Property* texProperty, SceneUtil::CompositeStateSetUpdater* composite, void handleTextureControllers(const Nif::Property* texProperty, SceneUtil::CompositeStateSetUpdater* composite,
Resource::ImageManager* imageManager, osg::StateSet* stateset, int animflags) Resource::ImageManager* imageManager, osg::StateSet* stateset, int animflags)
{ {
for (Nif::ControllerPtr ctrl = texProperty->mController; !ctrl.empty(); ctrl = ctrl->next) for (Nif::NiTimeControllerPtr ctrl = texProperty->mController; !ctrl.empty(); ctrl = ctrl->mNext)
{ {
if (!ctrl->isActive()) if (!ctrl->isActive())
continue; continue;
@ -1063,7 +1060,7 @@ namespace NifOsg
} }
} }
void handleParticlePrograms(Nif::NiParticleModifierPtr affectors, Nif::NiParticleModifierPtr colliders, void handleParticlePrograms(Nif::NiParticleModifierPtr modifier, Nif::NiParticleModifierPtr collider,
osg::Group* attachTo, osgParticle::ParticleSystem* partsys, osg::Group* attachTo, osgParticle::ParticleSystem* partsys,
osgParticle::ParticleProcessor::ReferenceFrame rf) osgParticle::ParticleProcessor::ReferenceFrame rf)
{ {
@ -1071,50 +1068,50 @@ namespace NifOsg
attachTo->addChild(program); attachTo->addChild(program);
program->setParticleSystem(partsys); program->setParticleSystem(partsys);
program->setReferenceFrame(rf); program->setReferenceFrame(rf);
for (; !affectors.empty(); affectors = affectors->mNext) for (; !modifier.empty(); modifier = modifier->mNext)
{ {
if (affectors->recType == Nif::RC_NiParticleGrowFade) if (modifier->recType == Nif::RC_NiParticleGrowFade)
{ {
const Nif::NiParticleGrowFade* gf = static_cast<const Nif::NiParticleGrowFade*>(affectors.getPtr()); const Nif::NiParticleGrowFade* gf = static_cast<const Nif::NiParticleGrowFade*>(modifier.getPtr());
program->addOperator(new GrowFadeAffector(gf->mGrowTime, gf->mFadeTime)); program->addOperator(new GrowFadeAffector(gf->mGrowTime, gf->mFadeTime));
} }
else if (affectors->recType == Nif::RC_NiGravity) else if (modifier->recType == Nif::RC_NiGravity)
{ {
const Nif::NiGravity* gr = static_cast<const Nif::NiGravity*>(affectors.getPtr()); const Nif::NiGravity* gr = static_cast<const Nif::NiGravity*>(modifier.getPtr());
program->addOperator(new GravityAffector(gr)); program->addOperator(new GravityAffector(gr));
} }
else if (affectors->recType == Nif::RC_NiParticleColorModifier) else if (modifier->recType == Nif::RC_NiParticleColorModifier)
{ {
const Nif::NiParticleColorModifier* cl const Nif::NiParticleColorModifier* cl
= static_cast<const Nif::NiParticleColorModifier*>(affectors.getPtr()); = static_cast<const Nif::NiParticleColorModifier*>(modifier.getPtr());
if (cl->mData.empty()) if (cl->mData.empty())
continue; continue;
const Nif::NiColorData* clrdata = cl->mData.getPtr(); const Nif::NiColorData* clrdata = cl->mData.getPtr();
program->addOperator(new ParticleColorAffector(clrdata)); program->addOperator(new ParticleColorAffector(clrdata));
} }
else if (affectors->recType == Nif::RC_NiParticleRotation) else if (modifier->recType == Nif::RC_NiParticleRotation)
{ {
// unused // unused
} }
else else
Log(Debug::Info) << "Unhandled particle modifier " << affectors->recName << " in " << mFilename; Log(Debug::Info) << "Unhandled particle modifier " << modifier->recName << " in " << mFilename;
} }
for (; !colliders.empty(); colliders = colliders->mNext) for (; !collider.empty(); collider = collider->mNext)
{ {
if (colliders->recType == Nif::RC_NiPlanarCollider) if (collider->recType == Nif::RC_NiPlanarCollider)
{ {
const Nif::NiPlanarCollider* planarcollider const Nif::NiPlanarCollider* planarcollider
= static_cast<const Nif::NiPlanarCollider*>(colliders.getPtr()); = static_cast<const Nif::NiPlanarCollider*>(collider.getPtr());
program->addOperator(new PlanarCollider(planarcollider)); program->addOperator(new PlanarCollider(planarcollider));
} }
else if (colliders->recType == Nif::RC_NiSphericalCollider) else if (collider->recType == Nif::RC_NiSphericalCollider)
{ {
const Nif::NiSphericalCollider* sphericalcollider const Nif::NiSphericalCollider* sphericalcollider
= static_cast<const Nif::NiSphericalCollider*>(colliders.getPtr()); = static_cast<const Nif::NiSphericalCollider*>(collider.getPtr());
program->addOperator(new SphericalCollider(sphericalcollider)); program->addOperator(new SphericalCollider(sphericalcollider));
} }
else else
Log(Debug::Info) << "Unhandled particle collider " << colliders->recName << " in " << mFilename; Log(Debug::Info) << "Unhandled particle collider " << collider->recName << " in " << mFilename;
} }
} }
@ -1126,7 +1123,7 @@ namespace NifOsg
auto particleNode = static_cast<const Nif::NiParticles*>(nifNode); auto particleNode = static_cast<const Nif::NiParticles*>(nifNode);
if (particleNode->data.empty() || particleNode->data->recType != Nif::RC_NiParticlesData) if (particleNode->data.empty() || particleNode->data->recType != Nif::RC_NiParticlesData)
{ {
partsys->setQuota(partctrl->numParticles); partsys->setQuota(partctrl->mParticles.size());
return; return;
} }
@ -1136,35 +1133,35 @@ namespace NifOsg
osg::BoundingBox box; osg::BoundingBox box;
int i = 0; int i = 0;
for (const auto& particle : partctrl->particles) for (const auto& particle : partctrl->mParticles)
{ {
if (i++ >= particledata->mActiveCount) if (i++ >= particledata->mActiveCount)
break; break;
if (particle.lifespan <= 0) if (particle.mLifespan <= 0)
continue; continue;
if (particle.vertex >= particledata->mVertices.size()) if (particle.mCode >= particledata->mVertices.size())
continue; continue;
ParticleAgeSetter particletemplate(std::max(0.f, particle.lifetime)); ParticleAgeSetter particletemplate(std::max(0.f, particle.mAge));
osgParticle::Particle* created = partsys->createParticle(&particletemplate); osgParticle::Particle* created = partsys->createParticle(&particletemplate);
created->setLifeTime(particle.lifespan); created->setLifeTime(particle.mLifespan);
// Note this position and velocity is not correct for a particle system with absolute reference frame, // Note this position and velocity is not correct for a particle system with absolute reference frame,
// which can not be done in this loader since we are not attached to the scene yet. Will be fixed up // which can not be done in this loader since we are not attached to the scene yet. Will be fixed up
// post-load in the SceneManager. // post-load in the SceneManager.
created->setVelocity(particle.velocity); created->setVelocity(particle.mVelocity);
const osg::Vec3f& position = particledata->mVertices[particle.vertex]; const osg::Vec3f& position = particledata->mVertices[particle.mCode];
created->setPosition(position); created->setPosition(position);
created->setColorRange(osgParticle::rangev4(partctrl->color, partctrl->color)); created->setColorRange(osgParticle::rangev4(partctrl->mInitialColor, partctrl->mInitialColor));
created->setAlphaRange(osgParticle::rangef(1.f, 1.f)); created->setAlphaRange(osgParticle::rangef(1.f, 1.f));
float size = partctrl->size; float size = partctrl->mInitialSize;
if (particle.vertex < particledata->mSizes.size()) if (particle.mCode < particledata->mSizes.size())
size *= particledata->mSizes[particle.vertex]; size *= particledata->mSizes[particle.mCode];
created->setSizeRange(osgParticle::rangef(size, size)); created->setSizeRange(osgParticle::rangef(size, size));
box.expandBy(osg::BoundingSphere(position, size)); box.expandBy(osg::BoundingSphere(position, size));
@ -1181,39 +1178,39 @@ namespace NifOsg
std::vector<int> targets; std::vector<int> targets;
if (partctrl->recType == Nif::RC_NiBSPArrayController && !partctrl->emitAtVertex()) if (partctrl->recType == Nif::RC_NiBSPArrayController && !partctrl->emitAtVertex())
{ {
getAllNiNodes(partctrl->emitter.getPtr(), targets); getAllNiNodes(partctrl->mEmitter.getPtr(), targets);
} }
osg::ref_ptr<Emitter> emitter = new Emitter(targets); osg::ref_ptr<Emitter> emitter = new Emitter(targets);
osgParticle::ConstantRateCounter* counter = new osgParticle::ConstantRateCounter; osgParticle::ConstantRateCounter* counter = new osgParticle::ConstantRateCounter;
if (partctrl->noAutoAdjust()) if (partctrl->noAutoAdjust())
counter->setNumberOfParticlesPerSecondToCreate(partctrl->emitRate); counter->setNumberOfParticlesPerSecondToCreate(partctrl->mBirthRate);
else if (partctrl->lifetime == 0 && partctrl->lifetimeRandom == 0) else if (partctrl->mLifetime == 0 && partctrl->mLifetimeVariation == 0)
counter->setNumberOfParticlesPerSecondToCreate(0); counter->setNumberOfParticlesPerSecondToCreate(0);
else else
counter->setNumberOfParticlesPerSecondToCreate( counter->setNumberOfParticlesPerSecondToCreate(
partctrl->numParticles / (partctrl->lifetime + partctrl->lifetimeRandom / 2)); partctrl->mParticles.size() / (partctrl->mLifetime + partctrl->mLifetimeVariation / 2));
emitter->setCounter(counter); emitter->setCounter(counter);
ParticleShooter* shooter = new ParticleShooter(partctrl->velocity - partctrl->velocityRandom * 0.5f, ParticleShooter* shooter = new ParticleShooter(partctrl->mSpeed - partctrl->mSpeedVariation * 0.5f,
partctrl->velocity + partctrl->velocityRandom * 0.5f, partctrl->horizontalDir, partctrl->mSpeed + partctrl->mSpeedVariation * 0.5f, partctrl->mPlanarAngle,
partctrl->horizontalAngle, partctrl->verticalDir, partctrl->verticalAngle, partctrl->lifetime, partctrl->mPlanarAngleVariation, partctrl->mDeclination, partctrl->mDeclinationVariation,
partctrl->lifetimeRandom); partctrl->mLifetime, partctrl->mLifetimeVariation);
emitter->setShooter(shooter); emitter->setShooter(shooter);
emitter->setFlags(partctrl->flags); emitter->setFlags(partctrl->mFlags);
if (partctrl->recType == Nif::RC_NiBSPArrayController && partctrl->emitAtVertex()) if (partctrl->recType == Nif::RC_NiBSPArrayController && partctrl->emitAtVertex())
{ {
emitter->setGeometryEmitterTarget(partctrl->emitter->recIndex); emitter->setGeometryEmitterTarget(partctrl->mEmitter->recIndex);
} }
else else
{ {
osgParticle::BoxPlacer* placer = new osgParticle::BoxPlacer; osgParticle::BoxPlacer* placer = new osgParticle::BoxPlacer;
placer->setXRange(-partctrl->offsetRandom.x() / 2.f, partctrl->offsetRandom.x() / 2.f); placer->setXRange(-partctrl->mEmitterDimensions.x() / 2.f, partctrl->mEmitterDimensions.x() / 2.f);
placer->setYRange(-partctrl->offsetRandom.y() / 2.f, partctrl->offsetRandom.y() / 2.f); placer->setYRange(-partctrl->mEmitterDimensions.y() / 2.f, partctrl->mEmitterDimensions.y() / 2.f);
placer->setZRange(-partctrl->offsetRandom.z() / 2.f, partctrl->offsetRandom.z() / 2.f); placer->setZRange(-partctrl->mEmitterDimensions.z() / 2.f, partctrl->mEmitterDimensions.z() / 2.f);
emitter->setPlacer(placer); emitter->setPlacer(placer);
} }
@ -1254,7 +1251,7 @@ namespace NifOsg
partsys->setSortMode(osgParticle::ParticleSystem::SORT_BACK_TO_FRONT); partsys->setSortMode(osgParticle::ParticleSystem::SORT_BACK_TO_FRONT);
const Nif::NiParticleSystemController* partctrl = nullptr; const Nif::NiParticleSystemController* partctrl = nullptr;
for (Nif::ControllerPtr ctrl = nifNode->mController; !ctrl.empty(); ctrl = ctrl->next) for (Nif::NiTimeControllerPtr ctrl = nifNode->mController; !ctrl.empty(); ctrl = ctrl->mNext)
{ {
if (!ctrl->isActive()) if (!ctrl->isActive())
continue; continue;
@ -1282,11 +1279,13 @@ namespace NifOsg
handleParticleInitialState(nifNode, partsys, partctrl); handleParticleInitialState(nifNode, partsys, partctrl);
partsys->getDefaultParticleTemplate().setSizeRange(osgParticle::rangef(partctrl->size, partctrl->size)); partsys->getDefaultParticleTemplate().setSizeRange(
partsys->getDefaultParticleTemplate().setColorRange(osgParticle::rangev4(partctrl->color, partctrl->color)); osgParticle::rangef(partctrl->mInitialSize, partctrl->mInitialSize));
partsys->getDefaultParticleTemplate().setColorRange(
osgParticle::rangev4(partctrl->mInitialColor, partctrl->mInitialColor));
partsys->getDefaultParticleTemplate().setAlphaRange(osgParticle::rangef(1.f, 1.f)); partsys->getDefaultParticleTemplate().setAlphaRange(osgParticle::rangef(1.f, 1.f));
if (!partctrl->emitter.empty()) if (!partctrl->mEmitter.empty())
{ {
osg::ref_ptr<Emitter> emitter = handleParticleEmitter(partctrl); osg::ref_ptr<Emitter> emitter = handleParticleEmitter(partctrl);
emitter->setParticleSystem(partsys); emitter->setParticleSystem(partsys);
@ -1295,7 +1294,7 @@ namespace NifOsg
// The emitter node may not actually be handled yet, so let's delay attaching the emitter to a later // The emitter node may not actually be handled yet, so let's delay attaching the emitter to a later
// moment. If the emitter node is placed later than the particle node, it'll have a single frame delay // moment. If the emitter node is placed later than the particle node, it'll have a single frame delay
// in particle processing. But that shouldn't be a game-breaking issue. // in particle processing. But that shouldn't be a game-breaking issue.
mEmitterQueue.emplace_back(partctrl->emitter->recIndex, emitter); mEmitterQueue.emplace_back(partctrl->mEmitter->recIndex, emitter);
osg::ref_ptr<ParticleSystemController> callback(new ParticleSystemController(partctrl)); osg::ref_ptr<ParticleSystemController> callback(new ParticleSystemController(partctrl));
setupController(partctrl, callback, animflags); setupController(partctrl, callback, animflags);
@ -1312,16 +1311,16 @@ namespace NifOsg
partsys->update(0.0, nv); partsys->update(0.0, nv);
} }
// affectors should be attached *after* the emitter in the scene graph for correct update order // modifiers should be attached *after* the emitter in the scene graph for correct update order
// attach to same node as the ParticleSystem, we need osgParticle Operators to get the correct // attach to same node as the ParticleSystem, we need osgParticle Operators to get the correct
// localToWorldMatrix for transforming to particle space // localToWorldMatrix for transforming to particle space
handleParticlePrograms(partctrl->affectors, partctrl->colliders, parentNode, partsys.get(), rf); handleParticlePrograms(partctrl->mModifier, partctrl->mCollider, parentNode, partsys.get(), rf);
std::vector<const Nif::Property*> drawableProps; std::vector<const Nif::Property*> drawableProps;
collectDrawableProperties(nifNode, parent, drawableProps); collectDrawableProperties(nifNode, parent, drawableProps);
applyDrawableProperties(parentNode, drawableProps, composite, true, animflags); applyDrawableProperties(parentNode, drawableProps, composite, true, animflags);
// particle system updater (after the emitters and affectors in the scene graph) // particle system updater (after the emitters and modifiers in the scene graph)
// I think for correct culling needs to be *before* the ParticleSystem, though osg examples do it the other // I think for correct culling needs to be *before* the ParticleSystem, though osg examples do it the other
// way // way
osg::ref_ptr<osgParticle::ParticleSystemUpdater> updater = new osgParticle::ParticleSystemUpdater; osg::ref_ptr<osgParticle::ParticleSystemUpdater> updater = new osgParticle::ParticleSystemUpdater;
@ -1485,7 +1484,7 @@ namespace NifOsg
if (geom->empty()) if (geom->empty())
return; return;
osg::ref_ptr<osg::Drawable> drawable; osg::ref_ptr<osg::Drawable> drawable;
for (Nif::ControllerPtr ctrl = nifNode->mController; !ctrl.empty(); ctrl = ctrl->next) for (Nif::NiTimeControllerPtr ctrl = nifNode->mController; !ctrl.empty(); ctrl = ctrl->mNext)
{ {
if (!ctrl->isActive()) if (!ctrl->isActive())
continue; continue;