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; }