using DatReaderWriter.Enums;
namespace AcDream.Core.Meshing;
///
/// Retail's textured-vs-untextured surface classification, ported from
/// D3DPolyRender::DrawMesh @0x0059d4a0 (named-retail decomp,
/// docs/research/named-retail/acclient_2013_pseudo_c.txt ~line 426048): a
/// surface subset is TEXTURED — and therefore never subject to either of
/// retail's "skip untextured" gates (see
/// ) — when
/// (surface->type & 6) != 0, i.e. when
/// (BASE1_IMAGE, 0x2) or
/// (BASE1_CLIPMAP, 0x4) is set
/// (docs/research/named-retail/acclient.h:5822-5824). Every other surface —
/// including and any surface whose type
/// carries neither bit — is UNTEXTURED: a flat-colour ("solid") subset filled
/// from Surface.ColorValue rather than a decoded texture.
///
///
/// #426 (2026-08-23, the Holtburg windmill axle 0x010010CE): the polygon-side
/// StipplingType.NoPos flag ("this side has no texture coordinates",
/// acclient.h:7386) is NOT the same fact as "this surface is untextured" —
/// every solid-colour polygon carries NoPos (it has no UVs to carry), but
/// NoPos says nothing about whether the surface itself is textured. The old
/// extraction conflated the two (isSolid = NoPos || Base1Solid) and,
/// worse, used NoPos to decide whether to emit the polygon's positive side AT
/// ALL — dropping every solid-colour polygon on every object. This type is
/// the ONE place that answers "is this surface textured", built from the
/// Surface's own Type flags so extraction (isSolid / TextureKey.IsSolid) and
/// draw-time skip policy agree by construction.
///
public static class RetailUntexturedSurfacePolicy
{
public static bool IsUntextured(SurfaceType type) =>
(type & (SurfaceType.Base1Image | SurfaceType.Base1ClipMap)) == 0;
}
///
/// Retail's draw-time policy for an UNTEXTURED (solid-colour) mesh subset on
/// an ordinary , ported from the
/// same D3DPolyRender::DrawMesh untextured branch: the subset draws
/// unless skipNoTexture != 0 &&
/// RenderDeviceD3D::ObjBuildingOrBuildingPart != 0. skipNoTexture
/// @0x00820e30 is a global initialised to 1 and never cleared, so in
/// practice the gate reduces to
/// RenderDeviceD3D::ObjBuildingOrBuildingPart == 0.
/// RenderDeviceD3D::DrawBuilding (@0x0059f2a0) sets that flag around
/// the building-shell draw, so a building shell's own untextured subsets are
/// the ONE case where an ordinary WorldEntity skips them — statics, scenery,
/// creatures, and items (DrawMeshInternal @0x0059f360 →
/// DrawMesh(gfxobj, mesh, arg4: 0)) always draw their untextured
/// subsets.
///
///
/// EnvCell interiors are retail's OTHER "skip untextured" case
/// (RenderDeviceD3D::DrawEnvCell @0x0059f170 calls
/// DrawMesh(..., arg4: 1)), but EnvCell/CellStruct geometry never
/// reaches this predicate — it draws through EnvCellRenderer /
/// MeshExtractor.PrepareCellStructMeshData, which keeps its own
/// NoPos-based approximation of the same rule (see
/// docs/architecture/retail-divergence-register.md AP-234 and
/// CellMesh.cs's matching gate).
///
/// ONE shared predicate for WbDrawDispatcher's classic classifier
/// (ClassifyBatches), packed classifier (ClassifyPackedBatches),
/// and the directional-shadow caster walk (AddDirectionalShadowBatches)
/// — Campaign VM VM6 showed that two hand-maintained classifiers computing
/// the "same" fact independently drift apart.
///
///
public static class RetailUntexturedSubsetPolicy
{
public static bool Draws(bool isBuildingShell, bool isUntextured) =>
!isUntextured || !isBuildingShell;
}