Files
WoWee/include/rendering/m2_model_classifier.hpp
Kelsi a36cc3553c 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.
2026-07-11 11:40:49 -07:00

126 lines
6.0 KiB
C++

#pragma once
#include <glm/glm.hpp>
#include <string>
#include <cstddef>
namespace wowee {
namespace rendering {
/// Ambient sound emitter type for doodad models (fire, water, etc.).
enum class AmbientEmitterType : uint8_t {
None = 0,
FireplaceSmall = 1, ///< Small fire / campfire
FireplaceLarge = 2, ///< Large brazier / bonfire
Torch = 3, ///< Wall torch / standing torch
Fountain = 4, ///< Fountain water loop
Waterfall = 5, ///< Waterfall ambient
Forge = 6, ///< Forge / anvil fire
};
/**
* Output of classifyM2Model(): all name/geometry-based flags for an M2 model.
* Pure data — no Vulkan, GPU, or asset-manager dependencies.
*/
struct M2ClassificationResult {
// --- Collision shape selectors ---
bool collisionNoBlock = false; ///< Foliage/soft-trees/rugs: no blocking
bool collisionBridge = false; ///< Walk-on-top bridge/plank/walkway
bool collisionPlanter = false; ///< Low stepped planter/curb
bool collisionSteppedFountain = false; ///< Stepped fountain base
bool collisionSteppedLowPlatform = false; ///< Low stepped platform (curb/planter/bridge)
bool collisionStatue = false; ///< Statue/monument/sculpture
bool collisionSmallSolidProp = false; ///< Blockable solid prop (crate/chest/barrel)
bool collisionNarrowVerticalProp = false; ///< Narrow tall prop (lamp/post/pole)
bool collisionTreeTrunk = false; ///< Tree trunk cylinder
// --- Rendering / effect classification ---
bool isFoliageLike = false; ///< Foliage or tree (wind sway, disabled animation)
bool isSmallFoliage = false; ///< Small bush/grass/plant (skip during taxi/flight)
bool isSpellEffect = false; ///< Spell effect / particle-dominated visual
bool isLavaModel = false; ///< Lava surface (UV scroll animation)
bool isInstancePortal = false; ///< Instance portal (additive, spin, no collision)
bool isWaterVegetation = false; ///< Aquatic vegetation (cattails, kelp, reeds, etc.)
bool isFireflyEffect = false; ///< Ambient creature (exempt from particle dampeners)
bool isElvenLike = false; ///< Night elf / Blood elf themed model
bool isLanternLike = false; ///< Lantern/lamp/light model
bool isKoboldFlame = false; ///< Kobold candle/torch model
bool isGroundDetail = false; ///< Ground-clutter detail doodad (always non-blocking)
bool isInvisibleTrap = false; ///< Event-object invisible trap (no render, no collision)
bool isSmoke = false; ///< Smoke model (UV scroll animation)
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;
// --- Animation flags ---
bool disableAnimation = false; ///< Keep visually stable (foliage, chest lids, etc.)
bool shadowWindFoliage = false; ///< Apply wind sway in shadow pass for foliage/trees
};
/**
* Classify an M2 model by name and geometry.
*
* Pure function — no Vulkan, VkContext, or AssetManager dependencies.
* All results are derived solely from the model name string and tight vertex bounds.
*
* @param name Full model path/name from the M2 header (any case)
* @param boundsMin Per-vertex tight bounding-box minimum
* @param boundsMax Per-vertex tight bounding-box maximum
* @param vertexCount Number of mesh vertices
* @param emitterCount Number of particle emitters
*/
M2ClassificationResult classifyM2Model(
const std::string& name,
const glm::vec3& boundsMin,
const glm::vec3& boundsMax,
std::size_t vertexCount,
std::size_t emitterCount);
// ---------------------------------------------------------------------------
// Batch texture classification
// ---------------------------------------------------------------------------
/**
* Per-batch texture key classification — glow / tint token flags.
* Input must be a lowercased, backslash-normalised texture path (as stored in
* M2Renderer's textureKeysLower vector). Pure data — no Vulkan dependencies.
*/
struct M2BatchTexClassification {
bool exactLanternGlowTex = false; ///< One of the known exact lantern-glow texture paths
bool hasGlowToken = false; ///< glow / flare / halo / light
bool hasFlameToken = false; ///< flame / fire / flamelick / ember
bool hasGlowCardToken = false; ///< glow / flamelick / lensflare / t_vfx / lightbeam / glowball / genericglow
bool likelyFlame = false; ///< fire / flame / torch
bool lanternFamily = false; ///< lantern / lamp / elf / silvermoon / quel / thalas
int glowTint = 0; ///< 0 = neutral, 1 = cool (blue/arcane), 2 = warm (red/scarlet)
};
/**
* Classify a batch texture by its lowercased path for glow/tint hinting.
*
* Pure function — no Vulkan, VkContext, or AssetManager dependencies.
*
* @param lowerTexKey Lowercased, backslash-normalised texture path (may be empty)
*/
M2BatchTexClassification classifyBatchTexture(const std::string& lowerTexKey);
// ---------------------------------------------------------------------------
// Lightweight ambient emitter classification (name-only, no geometry needed)
// ---------------------------------------------------------------------------
/**
* Classify an M2 model path for ambient sound emitter type.
* Faster than the full classifyM2Model() when only the emitter type is needed.
*
* @param lowerName Lowercased model path/name
* @return AmbientEmitterType::None if the model is not an ambient emitter source
*/
AmbientEmitterType classifyAmbientEmitter(const std::string& lowerName);
} // namespace rendering
} // namespace wowee