mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-25 00:20:16 +00:00
Implement M2 texture animation (UV scrolling) for fountain water
Parse M2TextureTransform entries and texture transform lookups from the M2 binary, then apply per-batch UV offsets in the vertex shader using the existing animation time base and global sequence durations.
This commit is contained in:
parent
4bb2828b21
commit
ad04da31c3
4 changed files with 83 additions and 1 deletions
|
|
@ -210,6 +210,13 @@ struct CompressedQuat {
|
|||
int16_t x, y, z, w;
|
||||
};
|
||||
|
||||
// M2 texture transform (on-disk, 3 × M2TrackDisk = 60 bytes)
|
||||
struct M2TextureTransformDisk {
|
||||
M2TrackDisk translation; // 20
|
||||
M2TrackDisk rotation; // 20
|
||||
M2TrackDisk scaling; // 20
|
||||
};
|
||||
|
||||
// M2 attachment point (on-disk)
|
||||
struct M2AttachmentDisk {
|
||||
uint32_t id;
|
||||
|
|
@ -511,6 +518,35 @@ M2Model M2Loader::load(const std::vector<uint8_t>& m2Data) {
|
|||
model.textureLookup = readArray<uint16_t>(m2Data, header.ofsTexLookup, header.nTexLookup);
|
||||
}
|
||||
|
||||
// Read texture transforms (UV animation data)
|
||||
if (header.nUVAnimation > 0 && header.ofsUVAnimation > 0) {
|
||||
// Build per-sequence flags for skipping external .anim data
|
||||
std::vector<uint32_t> seqFlags;
|
||||
seqFlags.reserve(model.sequences.size());
|
||||
for (const auto& seq : model.sequences) {
|
||||
seqFlags.push_back(seq.flags);
|
||||
}
|
||||
|
||||
model.textureTransforms.reserve(header.nUVAnimation);
|
||||
for (uint32_t i = 0; i < header.nUVAnimation; i++) {
|
||||
uint32_t ofs = header.ofsUVAnimation + i * sizeof(M2TextureTransformDisk);
|
||||
if (ofs + sizeof(M2TextureTransformDisk) > m2Data.size()) break;
|
||||
|
||||
M2TextureTransformDisk dt = readValue<M2TextureTransformDisk>(m2Data, ofs);
|
||||
M2TextureTransform tt;
|
||||
parseAnimTrack(m2Data, dt.translation, tt.translation, TrackType::VEC3, seqFlags);
|
||||
parseAnimTrack(m2Data, dt.rotation, tt.rotation, TrackType::QUAT_COMPRESSED, seqFlags);
|
||||
parseAnimTrack(m2Data, dt.scaling, tt.scale, TrackType::VEC3, seqFlags);
|
||||
model.textureTransforms.push_back(std::move(tt));
|
||||
}
|
||||
core::Logger::getInstance().debug(" Texture transforms: ", model.textureTransforms.size());
|
||||
}
|
||||
|
||||
// Read texture transform lookup (nTransLookup)
|
||||
if (header.nTransLookup > 0 && header.ofsTransLookup > 0) {
|
||||
model.textureTransformLookup = readArray<uint16_t>(m2Data, header.ofsTransLookup, header.nTransLookup);
|
||||
}
|
||||
|
||||
// Read attachment points
|
||||
if (header.nAttachments > 0 && header.ofsAttachments > 0) {
|
||||
auto diskAttachments = readArray<M2AttachmentDisk>(m2Data, header.ofsAttachments, header.nAttachments);
|
||||
|
|
|
|||
|
|
@ -219,6 +219,7 @@ bool M2Renderer::initialize(pipeline::AssetManager* assets) {
|
|||
uniform mat4 uProjection;
|
||||
uniform bool uUseBones;
|
||||
uniform mat4 uBones[128];
|
||||
uniform vec2 uUVOffset;
|
||||
out vec3 FragPos;
|
||||
out vec3 Normal;
|
||||
out vec2 TexCoord;
|
||||
|
|
@ -240,7 +241,7 @@ bool M2Renderer::initialize(pipeline::AssetManager* assets) {
|
|||
vec4 worldPos = uModel * vec4(pos, 1.0);
|
||||
FragPos = worldPos.xyz;
|
||||
Normal = mat3(uModel) * norm;
|
||||
TexCoord = aTexCoord;
|
||||
TexCoord = aTexCoord + uUVOffset;
|
||||
|
||||
gl_Position = uProjection * uView * worldPos;
|
||||
}
|
||||
|
|
@ -710,6 +711,11 @@ bool M2Renderer::loadModel(const pipeline::M2Model& model, uint32_t modelId) {
|
|||
}
|
||||
}
|
||||
|
||||
// Copy texture transform data for UV animation
|
||||
gpuModel.textureTransforms = model.textureTransforms;
|
||||
gpuModel.textureTransformLookup = model.textureTransformLookup;
|
||||
gpuModel.hasTextureAnimation = false;
|
||||
|
||||
// Build per-batch GPU entries
|
||||
if (!model.batches.empty()) {
|
||||
for (const auto& batch : model.batches) {
|
||||
|
|
@ -717,6 +723,12 @@ bool M2Renderer::loadModel(const pipeline::M2Model& model, uint32_t modelId) {
|
|||
bgpu.indexStart = batch.indexStart;
|
||||
bgpu.indexCount = batch.indexCount;
|
||||
|
||||
// Store texture animation index from batch
|
||||
bgpu.textureAnimIndex = batch.textureAnimIndex;
|
||||
if (bgpu.textureAnimIndex != 0xFFFF) {
|
||||
gpuModel.hasTextureAnimation = true;
|
||||
}
|
||||
|
||||
// Resolve texture: batch.textureIndex → textureLookup → allTextures
|
||||
GLuint tex = whiteTexture;
|
||||
if (batch.textureIndex < model.textureLookup.size()) {
|
||||
|
|
@ -1224,6 +1236,23 @@ void M2Renderer::render(const Camera& camera, const glm::mat4& view, const glm::
|
|||
for (const auto& batch : model.batches) {
|
||||
if (batch.indexCount == 0) continue;
|
||||
|
||||
// Compute UV offset for texture animation
|
||||
glm::vec2 uvOffset(0.0f, 0.0f);
|
||||
if (batch.textureAnimIndex != 0xFFFF && model.hasTextureAnimation) {
|
||||
uint16_t lookupIdx = batch.textureAnimIndex;
|
||||
if (lookupIdx < model.textureTransformLookup.size()) {
|
||||
uint16_t transformIdx = model.textureTransformLookup[lookupIdx];
|
||||
if (transformIdx < model.textureTransforms.size()) {
|
||||
const auto& tt = model.textureTransforms[transformIdx];
|
||||
glm::vec3 trans = interpVec3(tt.translation,
|
||||
instance.currentSequenceIndex, instance.animTime,
|
||||
glm::vec3(0.0f), model.globalSequenceDurations);
|
||||
uvOffset = glm::vec2(trans.x, trans.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
shader->setUniform("uUVOffset", uvOffset);
|
||||
|
||||
bool hasTexture = (batch.texture != 0);
|
||||
shader->setUniform("uHasTexture", hasTexture);
|
||||
shader->setUniform("uAlphaTest", batch.hasAlpha);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue