mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-07-31 09:49:53 -04:00
fix(rendering): hide sky birds until close enough for animation
Bird and bat doodads appeared frozen mid-flap in the sky because bone computation stops beyond 150 units (LOD3 distance) but the model was still rendered in its bind pose. Now classified as sky birds and their render distance is capped to the bone-update range, fading in smoothly as the player approaches.
This commit is contained in:
@@ -51,6 +51,7 @@ struct M2ClassificationResult {
|
||||
bool isWaterfall = false; ///< Waterfall model (ambient sound + splash particles)
|
||||
bool isBrazierOrFire = false; ///< Brazier / campfire / bonfire model
|
||||
bool isTorch = false; ///< Wall-mounted or standing torch
|
||||
bool isSkyBird = false; ///< Flying bird/bat doodad (hide until animation range)
|
||||
|
||||
// --- Ambient emitter type (for sound system) ---
|
||||
AmbientEmitterType ambientEmitterType = AmbientEmitterType::None;
|
||||
|
||||
@@ -130,6 +130,7 @@ struct M2ModelGPU {
|
||||
bool isLanternLike = false; // Model name matches lantern/lamp/light (precomputed)
|
||||
bool isKoboldFlame = false; // Model name matches kobold+(candle/torch/mine) (precomputed)
|
||||
bool isLavaModel = false; // Model name contains lava/molten/magma (UV scroll fallback)
|
||||
bool isSkyBird = false; // Flying bird/bat doodad — hide until animation range
|
||||
bool hasTextureAnimation = false; // True if any batch has UV animation
|
||||
bool hasTransparentBatches = false; // True if any batch uses alpha-blend or additive (blendMode >= 2)
|
||||
uint8_t availableLODs = 0; // Bitmask: bit N set if any batch has submeshLevel==N
|
||||
@@ -215,6 +216,7 @@ struct M2Instance {
|
||||
bool cachedIsGroundDetail = false;
|
||||
bool cachedIsInvisibleTrap = false;
|
||||
bool cachedIsInstancePortal = false;
|
||||
bool cachedIsSkyBird = false;
|
||||
bool cachedIsValid = false;
|
||||
bool skipCollision = false; // WMO interior doodads — skip player wall collision
|
||||
float cachedBoundRadius = 0.0f;
|
||||
|
||||
@@ -200,6 +200,18 @@ M2ClassificationResult classifyM2Model(
|
||||
});
|
||||
const bool ambientCreature = hasAny(n, kAmbientTokens);
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Sky birds / bats: animated flying doodads that look frozen beyond bone range
|
||||
// ---------------------------------------------------------------
|
||||
static constexpr auto kSkyBirdTokens = std::to_array<std::string_view>({
|
||||
"albatross", "carrionbird", "crane", "crow",
|
||||
"eagle", "gull", "hawk", "osprey",
|
||||
"owl", "parrot", "pelican",
|
||||
"raven", "seagull", "vulture",
|
||||
});
|
||||
r.isSkyBird = hasAny(n, kSkyBirdTokens) || has(n, "\\bird")
|
||||
|| has(n, "\\bat\\") || has(n, "\\bat.");
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Animation / foliage rendering flags
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
@@ -1245,6 +1245,7 @@ bool M2Renderer::loadModel(const pipeline::M2Model& model, uint32_t modelId) {
|
||||
gpuModel.isWaterfall = cls.isWaterfall;
|
||||
gpuModel.isBrazierOrFire = cls.isBrazierOrFire;
|
||||
gpuModel.isTorch = cls.isTorch;
|
||||
gpuModel.isSkyBird = cls.isSkyBird;
|
||||
gpuModel.ambientEmitterType = cls.ambientEmitterType;
|
||||
gpuModel.boundMin = tightMin;
|
||||
gpuModel.boundMax = tightMax;
|
||||
|
||||
@@ -90,6 +90,7 @@ uint32_t M2Renderer::createInstance(uint32_t modelId, const glm::vec3& position,
|
||||
instance.cachedIsGroundDetail = mdlRef.isGroundDetail;
|
||||
instance.cachedIsInvisibleTrap = mdlRef.isInvisibleTrap;
|
||||
instance.cachedIsInstancePortal = mdlRef.isInstancePortal;
|
||||
instance.cachedIsSkyBird = mdlRef.isSkyBird;
|
||||
instance.cachedIsValid = mdlRef.isValid();
|
||||
instance.cachedModel = &mdlRef;
|
||||
instance.recomputeCachedCullFactors();
|
||||
@@ -212,6 +213,7 @@ uint32_t M2Renderer::createInstanceWithMatrix(uint32_t modelId, const glm::mat4&
|
||||
instance.cachedBoundRadius = mdl2.boundRadius;
|
||||
instance.cachedIsGroundDetail = mdl2.isGroundDetail;
|
||||
instance.cachedIsInvisibleTrap = mdl2.isInvisibleTrap;
|
||||
instance.cachedIsSkyBird = mdl2.isSkyBird;
|
||||
instance.cachedIsValid = mdl2.isValid();
|
||||
instance.cachedModel = &mdl2;
|
||||
instance.recomputeCachedCullFactors();
|
||||
@@ -870,6 +872,16 @@ void M2Renderer::render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, const
|
||||
if (paddedRadius > 0.0f && !frustum.intersectsSphere(instance.position, paddedRadius)) continue;
|
||||
}
|
||||
|
||||
// Animated sky birds: cap render distance to bone-update range so they
|
||||
// fade in only when close enough for animation, hiding frozen poses.
|
||||
if (instance.cachedIsSkyBird && instance.cachedHasAnimation && !instance.cachedDisableAnimation) {
|
||||
constexpr float kBirdMaxDistSq = rendering::M2_LOD3_DISTANCE * rendering::M2_LOD3_DISTANCE;
|
||||
if (effectiveMaxDistSq > kBirdMaxDistSq) {
|
||||
effectiveMaxDistSq = kBirdMaxDistSq;
|
||||
if (distSq > effectiveMaxDistSq) continue;
|
||||
}
|
||||
}
|
||||
|
||||
sortedVisible_.push_back({i, instance.modelId, distSq, effectiveMaxDistSq});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user