checkpoint: preserve cathedral look-in investigation state
This commit is contained in:
parent
c287db8651
commit
572de1ec30
11 changed files with 757 additions and 197 deletions
|
|
@ -6,6 +6,7 @@ using AcDream.Core.Lighting;
|
|||
using AcDream.Core.Meshing;
|
||||
using AcDream.Core.Selection;
|
||||
using AcDream.Core.World;
|
||||
using AcDream.App.Rendering.Walk;
|
||||
using DatReaderWriter.Enums;
|
||||
|
||||
namespace AcDream.App.Rendering.Wb;
|
||||
|
|
@ -40,6 +41,8 @@ public sealed unsafe partial class WbDrawDispatcher
|
|||
private RenderSceneGeneration _packedProductionGeneration;
|
||||
private ulong _packedProductionFrameSequence;
|
||||
private int _packedProductionNextRange;
|
||||
private readonly Dictionary<(int RouteIndex, uint LocalEntityId, int PartIndex, uint GfxObjId), string>
|
||||
_probeLookInPartStates = [];
|
||||
|
||||
internal IReadOnlyList<CurrentRenderDispatcherSubmission>
|
||||
PackedDispatcherSubmissions => _packedSubmissions;
|
||||
|
|
@ -295,7 +298,12 @@ public sealed unsafe partial class WbDrawDispatcher
|
|||
source.MeshPartOffset,
|
||||
source.MeshPartCount),
|
||||
ref anyVao,
|
||||
publishSelection);
|
||||
publishSelection,
|
||||
range.Route is RenderFrameCandidateRoute.LookInObject
|
||||
? view.WalkLookInViews
|
||||
: null,
|
||||
range.RouteIndex,
|
||||
range.CellId);
|
||||
}
|
||||
|
||||
return new PackedRangeClassification(
|
||||
|
|
@ -334,7 +342,10 @@ public sealed unsafe partial class WbDrawDispatcher
|
|||
in RenderInstanceCandidate entity,
|
||||
ReadOnlySpan<RenderFrameMeshPart> meshParts,
|
||||
ref uint anyVao,
|
||||
bool publishSelection)
|
||||
bool publishSelection,
|
||||
IWalkLookInViewSource? lookInViews,
|
||||
int lookInRouteIndex,
|
||||
uint lookInCellId)
|
||||
{
|
||||
(uint slot, bool culled) = ResolveSlotForFrame(
|
||||
_clipRoutingActive,
|
||||
|
|
@ -361,7 +372,8 @@ public sealed unsafe partial class WbDrawDispatcher
|
|||
: new Vector2(0f, 1f);
|
||||
|
||||
PackedProjectionClassificationEntry? cacheEntry = null;
|
||||
if (!entity.Animated)
|
||||
bool lookInConeActive = lookInViews is not null;
|
||||
if (!entity.Animated && !lookInConeActive)
|
||||
{
|
||||
PackedClassificationIdentity identity =
|
||||
PackedClassificationIdentity.From(in projection);
|
||||
|
|
@ -394,7 +406,7 @@ public sealed unsafe partial class WbDrawDispatcher
|
|||
entity.ProjectionId,
|
||||
in identity);
|
||||
}
|
||||
else
|
||||
else if (entity.Animated)
|
||||
{
|
||||
_packedClassificationCache.RecordAnimatedClassification();
|
||||
}
|
||||
|
|
@ -485,6 +497,21 @@ public sealed unsafe partial class WbDrawDispatcher
|
|||
partTransform * meshRef.PartTransform;
|
||||
Matrix4x4 model =
|
||||
restPose * entity.RootWorld;
|
||||
int selectionPartIndex = unchecked(
|
||||
(partIndex << 16)
|
||||
| (setupPartIndex & 0xFFFF));
|
||||
if (!PartVisibleInLookInTurn(
|
||||
lookInViews,
|
||||
lookInRouteIndex,
|
||||
lookInCellId,
|
||||
in entity,
|
||||
selectionPartIndex,
|
||||
(uint)gfxObjId,
|
||||
partData,
|
||||
model))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!ClassifyPackedBatches(
|
||||
partData,
|
||||
restPose,
|
||||
|
|
@ -502,9 +529,6 @@ public sealed unsafe partial class WbDrawDispatcher
|
|||
{
|
||||
reusableAcrossFrames = false;
|
||||
}
|
||||
int selectionPartIndex = unchecked(
|
||||
(partIndex << 16)
|
||||
| (setupPartIndex & 0xFFFF));
|
||||
cacheEntry?.SelectionParts.Add(
|
||||
new PackedClassifiedSelectionPart(
|
||||
selectionPartIndex,
|
||||
|
|
@ -538,6 +562,18 @@ public sealed unsafe partial class WbDrawDispatcher
|
|||
|
||||
Matrix4x4 restPose = meshRef.PartTransform;
|
||||
Matrix4x4 model = restPose * entity.RootWorld;
|
||||
if (!PartVisibleInLookInTurn(
|
||||
lookInViews,
|
||||
lookInRouteIndex,
|
||||
lookInCellId,
|
||||
in entity,
|
||||
partIndex,
|
||||
meshRef.GfxObjId,
|
||||
renderData,
|
||||
model))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!ClassifyPackedBatches(
|
||||
renderData,
|
||||
restPose,
|
||||
|
|
@ -576,6 +612,97 @@ public sealed unsafe partial class WbDrawDispatcher
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retail RenderDeviceD3D::DrawMesh @0x005A0860 admits each CGfxObj
|
||||
/// independently by transforming its drawing sphere into the active
|
||||
/// portal view. A whole-entity sphere is not equivalent for multipart
|
||||
/// creatures: the union can be roughly ten metres wide and intersect an
|
||||
/// aperture while every actual body/equipment part is behind its wall.
|
||||
/// </summary>
|
||||
private bool PartVisibleInLookInTurn(
|
||||
IWalkLookInViewSource? lookInViews,
|
||||
int routeIndex,
|
||||
uint cellId,
|
||||
in RenderInstanceCandidate entity,
|
||||
int partIndex,
|
||||
uint gfxObjId,
|
||||
ObjectRenderData renderData,
|
||||
Matrix4x4 localToWorld)
|
||||
{
|
||||
if (lookInViews is null)
|
||||
return true;
|
||||
|
||||
// ObjectRenderData.SelectionSphere is retained per GfxObj by the
|
||||
// prepared mesh payload. It is never the entity-union sphere and is
|
||||
// therefore the correct granularity for DrawMesh admission. The
|
||||
// current package stores a conservative vertex-derived sphere here;
|
||||
// this preserves whole-mesh drawing while avoiding the invalid
|
||||
// aggregate-character admission that caused the cathedral bleed.
|
||||
if (renderData.SelectionSphere is not { Radius: > 0f } sphere)
|
||||
return true;
|
||||
|
||||
bool visible = LookInDrawingSphereVisible(
|
||||
lookInViews,
|
||||
routeIndex,
|
||||
sphere,
|
||||
localToWorld,
|
||||
out Vector3 center,
|
||||
out float radius);
|
||||
|
||||
if (AcDream.Core.Rendering.RenderingDiagnostics.ProbeWalkRootEnabled
|
||||
&& cellId == 0xF4180112u)
|
||||
{
|
||||
var key = (routeIndex, entity.LocalEntityId, partIndex, gfxObjId);
|
||||
string state =
|
||||
$"visible={(visible ? 1 : 0)} "
|
||||
+ $"center=({center.X:F2},{center.Y:F2},{center.Z:F2}) "
|
||||
+ $"r={radius:F2}";
|
||||
if (!_probeLookInPartStates.TryGetValue(key, out string? previous)
|
||||
|| previous != state)
|
||||
{
|
||||
_probeLookInPartStates[key] = state;
|
||||
Console.WriteLine(
|
||||
$"[lookin-part] route={routeIndex} cell=0x{cellId:X8} "
|
||||
+ $"id={entity.LocalEntityId:x} part={partIndex} "
|
||||
+ $"gfx=0x{gfxObjId:X8} {state}");
|
||||
}
|
||||
}
|
||||
|
||||
return visible;
|
||||
}
|
||||
|
||||
internal static bool LookInDrawingSphereVisible(
|
||||
IWalkLookInViewSource lookInViews,
|
||||
int routeIndex,
|
||||
DatReaderWriter.Types.Sphere sphere,
|
||||
Matrix4x4 localToWorld,
|
||||
out Vector3 center,
|
||||
out float radius)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(lookInViews);
|
||||
ArgumentNullException.ThrowIfNull(sphere);
|
||||
|
||||
center = Vector3.Transform(sphere.Origin, localToWorld);
|
||||
float scaleX = new Vector3(
|
||||
localToWorld.M11,
|
||||
localToWorld.M12,
|
||||
localToWorld.M13).Length();
|
||||
float scaleY = new Vector3(
|
||||
localToWorld.M21,
|
||||
localToWorld.M22,
|
||||
localToWorld.M23).Length();
|
||||
float scaleZ = new Vector3(
|
||||
localToWorld.M31,
|
||||
localToWorld.M32,
|
||||
localToWorld.M33).Length();
|
||||
radius = sphere.Radius
|
||||
* MathF.Max(scaleX, MathF.Max(scaleY, scaleZ));
|
||||
return lookInViews.SphereVisibleInLookInTurn(
|
||||
routeIndex,
|
||||
in center,
|
||||
radius);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mirrors the production dispatcher's no-VAO early return. That return
|
||||
/// records an empty submission with transparent deferral disabled even
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue