using System; using AcDream.Core.Meshing; using DatReaderWriter.DBObjs; namespace AcDream.App.Rendering; /// /// Retail particle geometry selection from CPhysicsPart::Always2D /// (named retail 0x0050D8A0). A GfxObj with no degrade entry, or whose first /// degrade has mode 1, is drawn as its authored 3D mesh. Every other first /// degrade mode uses the camera-facing 2D presentation. /// public static class RetailParticleGeometryClassifier { public static RetailParticleGeometryKind Classify(uint? firstDegradeMode) => firstDegradeMode is uint mode && mode != 1u ? RetailParticleGeometryKind.Billboard : RetailParticleGeometryKind.FullMesh; } public enum RetailParticleGeometryKind { FullMesh, Billboard, } /// /// Resolves authored particle-surface blending without allowing one malformed /// Surface entry to abort the render frame. The fallback remains the batch's /// already-decoded additive bit; it never fabricates geometry or a material. /// internal static class RetailParticleBlendResolver { public static TranslucencyKind Resolve( uint surfaceId, bool batchIsAdditive, Func? loadSurface, Action? diagnostic = null) { TranslucencyKind fallback = batchIsAdditive ? TranslucencyKind.Additive : TranslucencyKind.AlphaBlend; if (surfaceId == 0 || loadSurface is null) return fallback; try { Surface? surface = loadSurface(surfaceId); return surface is null ? fallback : TranslucencyKindExtensions.FromSurfaceType(surface.Type); } catch (Exception ex) { diagnostic?.Invoke( $"[particle-material] Failed to decode Surface 0x{surfaceId:X8}: {ex.Message}"); return fallback; } } }