using AcDream.Core.Meshing;
namespace AcDream.App.Rendering.Wb;
///
/// Campaign VM VM6: the two BatchData.flags bits that mark a world
/// draw batch as swaying foliage — bit 0 remains #226's built-mesh marker
/// (see mesh_detail.frag's vBatchFlags & 1u check) and is
/// never touched here. mesh_atmospheric.vert and the four
/// directional_shadow_world_*.vert caster shaders read these bits
/// through the shared foliage_wind.glsl include
/// (acdreamFoliageDisplace's batchFlags & 0x6u gate); the
/// retail mesh_modern pipelines never read them, so setting them does
/// not change pack-off output.
///
internal static class FoliageWindClassification
{
///
/// Bit 1: the subset is an alpha-cutout/ClipMap material AND its owning
/// entity is procedural scenery (see ). Leaves,
/// fronds, bushes, grass tufts.
///
internal const uint CutoutFoliageFlag = 0x2u;
///
/// Bit 2: the subset is opaque AND its owning entity is procedural
/// scenery AND that same entity owns at least one cutout subset — a
/// tree trunk or branch, not a rock (rocks have no cutout subset).
///
internal const uint TrunkFlag = 0x4u;
private const uint SceneryEntityIdBit = 0x8000_0000u;
///
/// Pure classification: no allocation, no scan.
/// is expected to be computed once per GfxObj/Setup mesh and cached with
/// the mesh record (),
/// not recomputed per call.
///
internal static uint Classify(
uint entityId,
bool isExcluded,
TranslucencyKind translucency,
bool meshHasCutoutSubset)
{
if (!IsProceduralScenery(entityId) || isExcluded)
return 0u;
if (translucency == TranslucencyKind.ClipMap)
return CutoutFoliageFlag;
if (translucency == TranslucencyKind.Opaque && meshHasCutoutSubset)
return TrunkFlag;
return 0u;
}
///
/// Bit 31 of the entity id — see ProceduralSceneryIdAllocator's
/// 0x8XXYYIII namespace. No other consumer decodes more than this
/// one bit.
///
internal static bool IsProceduralScenery(uint entityId) =>
(entityId & SceneryEntityIdBit) != 0u;
}