feat(physics): S2 chunk 1b - callers supply the visual part array; installed-DAT comparator
Every production registration now hands ShadowObjectRegistry the object's whole visual part array beside its collision dispatch: static publication (App LandblockPhysicsPublisher and the headless Content twin) through ShadowShapeBuilder.FromStaticRenderParts, live entities (Runtime LiveEntityCollisionBuilder) through the new FromSetupRenderParts, which walks every Setup part with the same physics-sphere-else-drawing-sphere and part-box rule from the PhysicsDataCache Runtime already reaches. Nothing consumes the retail products yet; the App hermetic lane still passes 6,758/6,758. The Lane=InstalledDat comparator registers five real fixtures through the real publication inputs and prints retail CELLARRAY, collision cells, and the old render cells side by side: Facility Hub stair Setup 0x02000623 (7 cells incl. 0x8A02015F/015E), cathedral ramp 0x020009A2 (3 cells, the genuine multi-part case), the #334 Neftet formation (25 cells), and a landblock-edge crosser (6 cells, 2 in the neighbor block). All three answers agree for BSP-bearing objects, as the shared primitive predicts; the divergence chunk 3 expects appears only for decorative non-BSP parts. Core Physics 2,202/2,202; Runtime 1,884/1,884; App hermetic 6,758/6,758; comparator + stair pin 5/5; solution Release build 0 warnings / 0 errors. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
059b8883ab
commit
5a2792d689
7 changed files with 908 additions and 11 deletions
|
|
@ -429,6 +429,110 @@ public static class ShadowShapeBuilder
|
|||
return parts;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves EVERY part of a live entity's <see cref="Setup"/> into the
|
||||
/// paired sphere + vertex-box geometry used by retail's render-shadow
|
||||
/// cell registration — the live-entity analog of
|
||||
/// <see cref="FromStaticRenderParts"/>. Unlike <see cref="FromSetup"/>'s
|
||||
/// step 3 (which emits a BSP shape only for a part whose GfxObj carries a
|
||||
/// physics BSP), this walks every Setup part unconditionally:
|
||||
/// <c>CEnvCell::init_static_objects</c>/<c>CPartArray::AddPartsShadow</c>
|
||||
/// registers every visual part in each crossed cell even when that part
|
||||
/// cannot collide, and a live Setup is no exception.
|
||||
///
|
||||
/// <para>The returned values are geometry carriers only. Callers must not
|
||||
/// publish them to the collision registry.</para>
|
||||
/// </summary>
|
||||
/// <param name="setup">The live entity's Setup.</param>
|
||||
/// <param name="entScale">The entity's spawn scale, applied uniformly —
|
||||
/// same composition as <see cref="FromSetup"/>.</param>
|
||||
/// <param name="effectivePartGfxObjIds">Current part identities after
|
||||
/// retail <c>AnimPartChanged</c> processing (see <see cref="FromSetup"/>).
|
||||
/// Null or short lists fall back to the Setup identity.</param>
|
||||
/// <param name="partPoseOverride">Pose priority 1: the motion-table
|
||||
/// default-state pose (#175). Falls back to the Setup's own placement
|
||||
/// frame, then identity — the same priority <see cref="FromSetup"/>'s
|
||||
/// step 3 uses.</param>
|
||||
/// <param name="getGfxObj">Resolves a GfxObj id to its cached physics
|
||||
/// (BSP + bounding sphere + visual bounds). Production:
|
||||
/// <c>id => physicsData.GetGfxObj(id)</c> — the SAME resolver shape
|
||||
/// <see cref="FromStaticRenderParts"/> takes, so a static and a live
|
||||
/// Setup sharing a GfxObj id can never disagree about its render
|
||||
/// geometry.</param>
|
||||
/// <param name="getVisualBounds">Fallback drawing-sphere/AABB source for
|
||||
/// a part whose GfxObj carries no physics BSP. Production:
|
||||
/// <c>id => physicsData.GetVisualBounds(id)</c>.</param>
|
||||
public static List<ShadowShape> FromSetupRenderParts(
|
||||
Setup setup,
|
||||
float entScale,
|
||||
IReadOnlyList<uint>? effectivePartGfxObjIds,
|
||||
IReadOnlyList<Frame>? partPoseOverride,
|
||||
Func<uint, GfxObjPhysics?> getGfxObj,
|
||||
Func<uint, GfxObjVisualBounds?> getVisualBounds)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(setup);
|
||||
ArgumentNullException.ThrowIfNull(getGfxObj);
|
||||
ArgumentNullException.ThrowIfNull(getVisualBounds);
|
||||
|
||||
var parts = new List<ShadowShape>(setup.Parts.Count);
|
||||
AnimationFrame? placementFrame = ResolvePlacementFrame(setup);
|
||||
for (int i = 0; i < setup.Parts.Count; i++)
|
||||
{
|
||||
uint gfxId = EffectivePartGfxObjId(setup, effectivePartGfxObjIds, i);
|
||||
|
||||
GfxObjPhysics? physics = getGfxObj(gfxId);
|
||||
bool partHasPhysicsBsp = physics?.FlatPhysicsBsp is { RootIndex: >= 0 }
|
||||
|| physics?.BSP?.Root is not null;
|
||||
|
||||
FlatGfxObjVisualBounds? flatBounds = physics?.VisualBounds;
|
||||
if (flatBounds is null && getVisualBounds(gfxId) is { } visual)
|
||||
{
|
||||
flatBounds = new FlatGfxObjVisualBounds(
|
||||
visual.Min,
|
||||
visual.Max,
|
||||
visual.Center,
|
||||
visual.Radius,
|
||||
visual.HalfExtents);
|
||||
}
|
||||
if (flatBounds is not { } bounds)
|
||||
continue;
|
||||
|
||||
Frame partFrame;
|
||||
if (partPoseOverride is not null && i < partPoseOverride.Count)
|
||||
partFrame = partPoseOverride[i];
|
||||
else if (placementFrame is not null && i < placementFrame.Frames.Count)
|
||||
partFrame = placementFrame.Frames[i];
|
||||
else
|
||||
partFrame = new Frame { Origin = Vector3.Zero, Orientation = Quaternion.Identity };
|
||||
|
||||
FlatCollisionSphere sphere;
|
||||
if (partHasPhysicsBsp)
|
||||
{
|
||||
FlatPhysicsBsp? flat = physics!.FlatPhysicsBsp;
|
||||
sphere = flat is { RootIndex: >= 0 }
|
||||
? flat.Nodes[flat.RootIndex].BoundingSphere
|
||||
: new FlatCollisionSphere(
|
||||
physics.BoundingSphere?.Origin ?? bounds.Center,
|
||||
physics.BoundingSphere?.Radius ?? bounds.Radius);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Retail falls back from physics_sphere to the GfxObj drawing
|
||||
// sphere — same fallback as FromStaticRenderParts.
|
||||
sphere = new FlatCollisionSphere(bounds.Center, bounds.Radius);
|
||||
}
|
||||
|
||||
parts.Add(ShadowShape.Bsp(
|
||||
gfxId,
|
||||
new Vector3(partFrame.Origin.X, partFrame.Origin.Y, partFrame.Origin.Z) * entScale,
|
||||
partFrame.Orientation,
|
||||
entScale,
|
||||
ShadowPartGeometry.Create(sphere, bounds)));
|
||||
}
|
||||
|
||||
return parts;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The collision identity of part <paramref name="index"/>: the installed
|
||||
/// <c>AnimPartChanged</c> replacement when one was supplied, else the
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue