fix(render): clip walk content to authored portal views

This commit is contained in:
Erik 2026-08-31 06:15:12 +02:00
parent 90eba0ec3c
commit 11e68aad82
10 changed files with 408 additions and 78 deletions

View file

@ -274,23 +274,15 @@ public sealed partial class WbDrawDispatcher
Matrix4x4 model = restPose * entity.RootWorld;
int selectionPartIndex = unchecked((partIndex << 16) | (setupPartIndex & 0xFFFF));
if (!PartVisibleInLookInTurn(
lookInViews,
lookInRouteIndex,
lookInCellId,
in entity,
selectionPartIndex,
(uint)gfxObjId,
partData,
model))
{
IReadOnlyList<uint>? partClipSlots = ResolvePartClipSlots(
lookInViews, lookInRouteIndex, partData, model);
if (partClipSlots is { Count: 0 })
continue;
}
EmitClassifiedBatches(
partData, model, in entity, meshRef, paletteIdentity,
entityHasCutoutSubset, slot, lights, indoor, selectionLighting,
detailCategory, opacity, batches);
detailCategory, opacity, partClipSlots, batches);
selectionParts.Add(new WalkClassifiedSelectionPart(
entity.ServerGuid, entity.LocalEntityId, selectionPartIndex,
(uint)gfxObjId, model));
@ -308,22 +300,14 @@ public sealed partial class WbDrawDispatcher
continue;
Matrix4x4 model = meshRef.PartTransform * entity.RootWorld;
if (!PartVisibleInLookInTurn(
lookInViews,
lookInRouteIndex,
lookInCellId,
in entity,
partIndex,
(uint)meshRef.GfxObjId,
renderData,
model))
{
IReadOnlyList<uint>? partClipSlots = ResolvePartClipSlots(
lookInViews, lookInRouteIndex, renderData, model);
if (partClipSlots is { Count: 0 })
continue;
}
EmitClassifiedBatches(
renderData, model, in entity, meshRef, paletteIdentity,
entityHasCutoutSubsetOverride: null, slot, lights, indoor,
selectionLighting, detailCategory, opacity, batches);
selectionLighting, detailCategory, opacity, partClipSlots, batches);
selectionParts.Add(new WalkClassifiedSelectionPart(
entity.ServerGuid, entity.LocalEntityId, partIndex,
(uint)meshRef.GfxObjId, model));
@ -379,28 +363,29 @@ public sealed partial class WbDrawDispatcher
lights = InstanceLightSet.From(selected);
}
private static bool PartVisibleInLookInTurn(
private static IReadOnlyList<uint>? ResolvePartClipSlots(
IWalkLookInViewSource? lookInViews,
int routeIndex,
uint cellId,
in RenderInstanceCandidate entity,
int partIndex,
uint gfxObjId,
ObjectRenderData renderData,
Matrix4x4 localToWorld)
{
if (lookInViews is null)
return true;
return null;
if (renderData.SelectionSphere is not { Radius: > 0f } sphere)
return true;
{
return lookInViews.VisibleClipSlotsInLookInTurn(
routeIndex,
Vector3.Zero,
radius: 0f,
testSphere: false);
}
return LookInDrawingSphereVisible(
lookInViews,
TransformDrawingSphere(sphere, localToWorld, out Vector3 center, out float radius);
return lookInViews.VisibleClipSlotsInLookInTurn(
routeIndex,
sphere,
localToWorld,
out _,
out _);
in center,
radius,
testSphere: true);
}
internal static bool LookInDrawingSphereVisible(
@ -414,6 +399,19 @@ public sealed partial class WbDrawDispatcher
ArgumentNullException.ThrowIfNull(lookInViews);
ArgumentNullException.ThrowIfNull(sphere);
TransformDrawingSphere(sphere, localToWorld, out center, out radius);
return lookInViews.SphereVisibleInLookInTurn(
routeIndex,
in center,
radius);
}
private static void TransformDrawingSphere(
DatReaderWriter.Types.Sphere sphere,
Matrix4x4 localToWorld,
out Vector3 center,
out float radius)
{
center = Vector3.Transform(sphere.Origin, localToWorld);
float scaleX = new Vector3(
localToWorld.M11,
@ -429,10 +427,6 @@ public sealed partial class WbDrawDispatcher
localToWorld.M33).Length();
radius = sphere.Radius
* MathF.Max(scaleX, MathF.Max(scaleY, scaleZ));
return lookInViews.SphereVisibleInLookInTurn(
routeIndex,
in center,
radius);
}
/// <summary>
@ -454,6 +448,7 @@ public sealed partial class WbDrawDispatcher
Vector2 selectionLighting,
uint detailCategory,
float opacity,
IReadOnlyList<uint>? clipSlots,
List<WalkClassifiedBatch> sink)
{
bool entityHasCutoutSubset = entityHasCutoutSubsetOverride ?? renderData.HasCutoutSubset;
@ -466,10 +461,23 @@ public sealed partial class WbDrawDispatcher
if (!survives)
continue;
sink.Add(new WalkClassifiedBatch(
key, model, slot, lights, indoor ? 1u : 0u, Alpha: opacity,
selectionLighting, detailCategory, IsOpaque: IsOpaque(key.Translucency),
LocalSortCenter: renderData.SortCenter));
if (clipSlots is null)
{
sink.Add(new WalkClassifiedBatch(
key, model, slot, lights, indoor ? 1u : 0u, Alpha: opacity,
selectionLighting, detailCategory, IsOpaque: IsOpaque(key.Translucency),
LocalSortCenter: renderData.SortCenter));
continue;
}
for (int clipIndex = 0; clipIndex < clipSlots.Count; clipIndex++)
{
sink.Add(new WalkClassifiedBatch(
key, model, clipSlots[clipIndex], lights, indoor ? 1u : 0u,
Alpha: opacity, selectionLighting, detailCategory,
IsOpaque: IsOpaque(key.Translucency),
LocalSortCenter: renderData.SortCenter));
}
}
}