Skip to content

Commit

Permalink
Texture Animation (#27)
Browse files Browse the repository at this point in the history
- Added Support for extracting necessary information for texture animation
- Fixed printing statements in GPUVector and ShaderHandlerVK
  • Loading branch information
NixAJ committed Mar 27, 2024
1 parent 0f89f17 commit 9675801
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 84 deletions.
136 changes: 81 additions & 55 deletions Source/FileFormat/FileFormat/Novus/Model/ComplexModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,17 @@ namespace Model
output.write(reinterpret_cast<const char*>(&flags), sizeof(ComplexModel::Flags));
}

// Write Global Loops
{
u32 numElements = static_cast<u32>(globalLoops.size());
output.write(reinterpret_cast<const char*>(&numElements), sizeof(u32));

if (numElements > 0)
{
output.write(reinterpret_cast<const char*>(&globalLoops[0]), numElements * sizeof(u32));
}
}

// Write Sequences
{
u32 numElements = modelHeader.numSequences;
Expand Down Expand Up @@ -433,6 +444,12 @@ namespace Model
return false;
}

// Read Global Loops
{
if (!ReadVectorFromBuffer(buffer, out.globalLoops))
return false;
}

// Read Sequences
{
if (!buffer->GetVector(out.sequences, out.modelHeader.numSequences))
Expand Down Expand Up @@ -764,23 +781,29 @@ namespace Model
template <typename T>
void GetAnimationTrack(std::shared_ptr<Bytebuffer>& rootBuffer, const Layout& file, ComplexModel::AnimationData<T>& animationData, M2Track<T>& m2Track)
{
i16 globalLoopIndex = m2Track.globalSequence;
u32 numTracks = m2Track.values.size;

animationData.interpolationType = static_cast<ComplexModel::AnimationInterpolationType>(m2Track.interpolationType);
animationData.isGlobalSequence = m2Track.globalSequence != -1;
animationData.globalLoopIndex = globalLoopIndex;
animationData.tracks.reserve(numTracks);

// Handle Global Sequence
if (animationData.isGlobalSequence)
if (globalLoopIndex != -1)
{
u32 sequenceID = file.md21.sequences.size + m2Track.globalSequence;

if (sequenceID < file.md21.sequences.size)
if (globalLoopIndex < static_cast<i32>(file.md21.sequences.size))
{
M2Sequence* m2Sequence = file.md21.sequences.GetElement(rootBuffer, sequenceID);
M2Sequence* m2Sequence = file.md21.sequences.GetElement(rootBuffer, globalLoopIndex);

while (m2Sequence->flags.IsAlias)
{
if (m2Sequence->nextAliasID >= file.md21.sequences.size)
break;

m2Sequence = file.md21.sequences.GetElement(rootBuffer, m2Sequence->nextAliasID);
}

bool hasExternalAnimationData = !m2Sequence->flags.HasEmbeddedAnimationData;
if (hasExternalAnimationData || m2Sequence->flags.IsAlias)
if (hasExternalAnimationData && !m2Sequence->flags.IsAlias)
{
// TODO : Animation is stored externally
return;
Expand All @@ -794,16 +817,13 @@ namespace Model
{
ComplexModel::AnimationTrack<T>& track = animationData.tracks.emplace_back();

track.sequenceID = sequenceID;

track.timestamps.resize(m2Timestamps->size);
memcpy(track.timestamps.data(), m2Timestamps->Get(rootBuffer), sizeof(u32) * m2Timestamps->size);

track.values.resize(m2Values->size);
memcpy(track.values.data(), m2Values->Get(rootBuffer), sizeof(T) * m2Values->size);
}
}
// Read All Sequence Tracks
else
{
for (u32 i = 0; i < numTracks; i++)
Expand All @@ -812,23 +832,29 @@ namespace Model
{
M2Sequence* m2Sequence = file.md21.sequences.GetElement(rootBuffer, i);

while (m2Sequence->flags.IsAlias)
{
if (m2Sequence->nextAliasID >= file.md21.sequences.size)
break;

m2Sequence = file.md21.sequences.GetElement(rootBuffer, m2Sequence->nextAliasID);
}

bool hasExternalAnimationData = !m2Sequence->flags.HasEmbeddedAnimationData;
if (hasExternalAnimationData || m2Sequence->flags.IsAlias)
if (hasExternalAnimationData && !m2Sequence->flags.IsAlias)
{
// TODO : Animation is stored externally
continue;
}
}

ComplexModel::AnimationTrack<T>& track = animationData.tracks.emplace_back();

M2Array<u32>* m2Timestamps = m2Track.timestamps.GetElement(rootBuffer, i);
M2Array<T>* m2Values = m2Track.values.GetElement(rootBuffer, i);

if (m2Timestamps->size > 0 && m2Values->size > 0)
{
ComplexModel::AnimationTrack<T>& track = animationData.tracks.emplace_back();

track.sequenceID = i;

track.timestamps.resize(m2Timestamps->size);
memcpy(track.timestamps.data(), m2Timestamps->Get(rootBuffer), sizeof(u32) * m2Timestamps->size);

Expand All @@ -843,16 +869,14 @@ namespace Model
u32 numTracks = static_cast<u32>(src.tracks.size());

dest.interpolationType = src.interpolationType;
dest.isGlobalSequence = src.isGlobalSequence;
dest.globalLoopIndex = src.globalLoopIndex;
dest.tracks.resize(numTracks);

for (u32 i = 0; i < numTracks; i++)
{
ComplexModel::AnimationTrack<M2CompressedQuaternion>& srcTrack = src.tracks[i];
ComplexModel::AnimationTrack<quat>& destTrack = dest.tracks[i];

destTrack.sequenceID = srcTrack.sequenceID;

// Copy Timestamps
{
u32 numTimestamps = static_cast<u32>(srcTrack.values.size());
Expand Down Expand Up @@ -880,12 +904,22 @@ namespace Model
out.flags = *reinterpret_cast<ComplexModel::Flags*>(&layout.md21.flags);
out.flags.IsConvertedMapObject = false;

u32 numGlobalSequences = layout.md21.loopingSequenceTimestamps.size;
u32 numSequences = layout.md21.sequences.size;

// Read Global Loops
{
out.globalLoops.resize(layout.md21.globalLoops.size);

for (u32 i = 0; i < layout.md21.globalLoops.size; i++)
{
u32* m2GlobalLoop = layout.md21.globalLoops.GetElement(rootBuffer, i);
out.globalLoops[i] = *m2GlobalLoop;
}
}

// Read Sequences
{
out.sequences.resize(numSequences + numGlobalSequences);
out.sequences.resize(numSequences);

for (u32 i = 0; i < layout.md21.sequences.size; i++)
{
Expand All @@ -898,7 +932,6 @@ namespace Model
sequence.duration = m2Sequence->duration;
sequence.moveSpeed = m2Sequence->moveSpeed;

sequence.flags.isAlwaysPlaying = false;
sequence.flags.isAlias = m2Sequence->flags.IsAlias;
sequence.flags.blendTransition = m2Sequence->flags.BlendTransition;
sequence.flags.blendTransitionIfActive = m2Sequence->flags.SetBlendTransitionOnLoad;
Expand All @@ -920,38 +953,6 @@ namespace Model
}
}

// Add Global Sequences (Important GlobalSequences are added at the end, otherwise we have to patch nextVariationId && nextAliasId
{
for (u32 i = 0; i < numGlobalSequences; i++)
{
ComplexModel::AnimationSequence& sequence = out.sequences[numSequences + i];
sequence.id = -1;
sequence.subID = -1;

sequence.duration = *layout.md21.loopingSequenceTimestamps.GetElement(rootBuffer, i);
sequence.moveSpeed = 0;

sequence.flags.isAlwaysPlaying = true;
sequence.flags.isAlias = false;
sequence.flags.blendTransition = false;

sequence.frequency = 0;
sequence.repetitionRange = uvec2(0, 0);
sequence.blendTimeStart = 0;
sequence.blendTimeEnd = 0;

vec3 aabbMin = CoordinateSpaces::ModelPosToNovus(layout.md21.cullingAABBBounds.aabb.min);
vec3 aabbMax = CoordinateSpaces::ModelPosToNovus(layout.md21.cullingAABBBounds.aabb.max);

sequence.aabbCenter = (aabbMin + aabbMax) * 0.5f;
sequence.aabbExtents = aabbMax - sequence.aabbCenter;
sequence.radius = 0;

sequence.nextVariationID = -1;
sequence.nextAliasID = -1;
}
}

// Read Bones
{
u32 numBones = layout.md21.bones.size;
Expand Down Expand Up @@ -1086,7 +1087,32 @@ namespace Model
memcpy(out.materials.data(), &rootBuffer->GetDataPointer()[materialsOffset], numMaterials * sizeof(ComplexModel::Material));
}

// TODO : Texture Transforms
// Read Texture Transforms
{
u32 numTextureTransforms = layout.md21.textureTransforms.size;

out.textureTransforms.resize(numTextureTransforms);
for (u32 i = 0; i < numTextureTransforms; i++)
{
ComplexModel::TextureTransform& textureTransform = out.textureTransforms[i];
M2TextureTransform* m2TextureTransform = layout.md21.textureTransforms.GetElement(rootBuffer, i);

// Read Translation Track
{
GetAnimationTrack(rootBuffer, layout, textureTransform.translation, m2TextureTransform->translation);
}

// Read Rotation Track
{
GetAnimationTrack(rootBuffer, layout, textureTransform.rotation, m2TextureTransform->rotation);
}

// Read Scale Track
{
GetAnimationTrack(rootBuffer, layout, textureTransform.scale, m2TextureTransform->scale);
}
}
}

// Read Texture Lookup Tables
{
Expand Down
26 changes: 9 additions & 17 deletions Source/FileFormat/FileFormat/Novus/Model/ComplexModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ namespace Model
struct ComplexModel
{
public:
static const u32 CURRENT_VERSION = 8;
static const u32 CURRENT_VERSION = 9;

struct Flags
{
Expand Down Expand Up @@ -54,7 +54,6 @@ namespace Model
struct AnimationTrack
{
public:
u32 sequenceID = 0;
std::vector<u32> timestamps = { };
std::vector<T> values = { };

Expand Down Expand Up @@ -87,11 +86,9 @@ namespace Model
}
}
}
AnimationTrack(AnimationTrack<T>&& other) : sequenceID(other.sequenceID), timestamps(std::move(other.timestamps)), values(std::move(other.values)) { }
AnimationTrack(AnimationTrack<T>&& other) : timestamps(std::move(other.timestamps)), values(std::move(other.values)) { }
AnimationTrack<T>& operator=(const AnimationTrack<T>& other)
{
sequenceID = other.sequenceID;

size_t numTimestamps = other.timestamps.size();
if (numTimestamps)
{
Expand Down Expand Up @@ -134,7 +131,7 @@ namespace Model
{
public:
AnimationInterpolationType interpolationType = AnimationInterpolationType::NONE;
bool isGlobalSequence = false;
i16 globalLoopIndex = -1;

std::vector<AnimationTrack<T>> tracks;

Expand All @@ -154,11 +151,11 @@ namespace Model
tracks[i] = other.tracks[i];
}
}
AnimationData(AnimationData<T>&& other) : interpolationType(other.interpolationType), isGlobalSequence(other.isGlobalSequence), tracks(std::move(other.tracks)) { }
AnimationData(AnimationData<T>&& other) : interpolationType(other.interpolationType), globalLoopIndex(other.globalLoopIndex), tracks(std::move(other.tracks)) { }
AnimationData<T>& operator=(const AnimationData<T>& other)
{
interpolationType = other.interpolationType;
isGlobalSequence = other.isGlobalSequence;
globalLoopIndex = other.globalLoopIndex;

size_t numTracks = other.tracks.size();
tracks.resize(numTracks);
Expand All @@ -174,7 +171,7 @@ namespace Model
void Serialize(std::ofstream& stream) const
{
stream.write(reinterpret_cast<char const*>(&interpolationType), sizeof(AnimationInterpolationType));
stream.write(reinterpret_cast<char const*>(&isGlobalSequence), sizeof(bool));
stream.write(reinterpret_cast<char const*>(&globalLoopIndex), sizeof(i16));

u32 numTracks = static_cast<u32>(tracks.size());
{
Expand All @@ -184,8 +181,6 @@ namespace Model
{
const AnimationTrack<T>& track = tracks[i];

stream.write(reinterpret_cast<char const*>(&track.sequenceID), sizeof(u32));

u32 numTimestamps = static_cast<u32>(track.timestamps.size());
{
stream.write(reinterpret_cast<char const*>(&numTimestamps), sizeof(u32));
Expand All @@ -205,7 +200,7 @@ namespace Model
if (!buffer->Get(interpolationType))
return false;

if (!buffer->Get(isGlobalSequence))
if (!buffer->Get(globalLoopIndex))
return false;

u32 numTracks = 0;
Expand All @@ -218,9 +213,6 @@ namespace Model
{
AnimationTrack<T>& track = tracks[i];

if (!buffer->GetU32(track.sequenceID))
return false;

u32 numTimestamps = 0;
if (!buffer->GetU32(numTimestamps))
return false;
Expand Down Expand Up @@ -418,7 +410,6 @@ namespace Model
public:
struct Flags
{
u32 isAlwaysPlaying : 1;
u32 isAlias : 1;
u32 blendTransition : 1; // (This applies if set on either side of the transition) If set we lerp between the end -> start states, but only if end != start (Compare Bone Values)
u32 blendTransitionIfActive : 1;
Expand Down Expand Up @@ -557,6 +548,7 @@ namespace Model

Flags flags = { };

std::vector<u32> globalLoops;
std::vector<AnimationSequence> sequences;
std::vector<Bone> bones;
robin_hood::unordered_map<u16, i16> keyBoneIDToBoneIndex;
Expand Down Expand Up @@ -600,4 +592,4 @@ namespace Model
static i8 GetVertexShaderID(i16 shaderID, u16 textureCount);
static i8 GetPixelShaderID(i16 shaderID, u16 textureCount);
};
}
}
2 changes: 1 addition & 1 deletion Source/FileFormat/FileFormat/Warcraft/M2/M2.h
Original file line number Diff line number Diff line change
Expand Up @@ -539,7 +539,7 @@ namespace M2
M2Array<char> uniqueName = { };
Flags flags = { };

M2Array<u32> loopingSequenceTimestamps = { };
M2Array<u32> globalLoops = { };
M2Array<M2Sequence> sequences = { };
M2Array<u16> sequenceIDToAnimationID = { };
M2Array<M2CompBone> bones = { };
Expand Down
Loading

0 comments on commit 9675801

Please sign in to comment.