feat(render): implement Campaign AR and terrain fidelity
This commit is contained in:
parent
99cf26e00c
commit
7a5f96ede5
368 changed files with 50611 additions and 950 deletions
|
|
@ -1,6 +1,7 @@
|
|||
using System.Numerics;
|
||||
using AcDream.App.Rendering.Scene;
|
||||
using AcDream.App.Rendering.Scene.Arch;
|
||||
using AcDream.Core.World;
|
||||
|
||||
namespace AcDream.App.Tests.Rendering;
|
||||
|
||||
|
|
@ -661,6 +662,429 @@ public sealed class ArchRenderSceneTests
|
|||
scene.OpenQuery().IndexRevision > registeredRevision);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DirectionalShadowTopologyRevision_IgnoresDynamicPoseButTracksEligibilityGeometryAppearanceAndCasterIdentity()
|
||||
{
|
||||
RenderSceneGeneration generation = Generation(19);
|
||||
using var scene = new ArchRenderScene(generation);
|
||||
Matrix4x4 firstPart = Matrix4x4.CreateTranslation(1f, 2f, 3f);
|
||||
RenderProjectionRecord original = Record(
|
||||
19,
|
||||
1,
|
||||
RenderProjectionClass.LiveDynamicRoot) with
|
||||
{
|
||||
Source = new RenderSourceMetadata(
|
||||
LocalEntityId: 19,
|
||||
ServerGuid: 19,
|
||||
SourceId: 19,
|
||||
ParentCellId: 0,
|
||||
EffectCellId: 0,
|
||||
BuildingShellAnchorCellId: 0,
|
||||
TransformFingerprint: new RenderSceneHash128(1, 1),
|
||||
GeometryFingerprint: new RenderSceneHash128(2, 2),
|
||||
AppearanceFingerprint: new RenderSceneHash128(3, 3),
|
||||
DirectionalShadowTopologyFingerprint:
|
||||
new RenderSceneHash128(4, 4)),
|
||||
EntityPayload = new RenderEntityPayload(
|
||||
[new MeshRef(0x01000001, firstPart)],
|
||||
PaletteOverride: null,
|
||||
IsBuildingShell: false),
|
||||
};
|
||||
scene.Apply([RenderProjectionDelta.Register(generation, 1, original)]);
|
||||
ulong registered =
|
||||
scene.OpenQuery().DirectionalShadowTopologyRevision;
|
||||
|
||||
Matrix4x4 movedRoot = Matrix4x4.CreateTranslation(20f, 21f, 22f);
|
||||
Matrix4x4 movedPart = Matrix4x4.CreateTranslation(23f, 24f, 25f);
|
||||
RenderProjectionRecord poseOnly = original with
|
||||
{
|
||||
Transform = new RenderTransform(movedRoot),
|
||||
MeshSet = original.MeshSet with
|
||||
{
|
||||
Handle = Asset(999),
|
||||
},
|
||||
Source = original.Source with
|
||||
{
|
||||
TransformFingerprint = new RenderSceneHash128(10, 10),
|
||||
GeometryFingerprint = new RenderSceneHash128(20, 20),
|
||||
},
|
||||
EntityPayload = original.EntityPayload with
|
||||
{
|
||||
MeshRefs = [new MeshRef(0x01000001, movedPart)],
|
||||
},
|
||||
};
|
||||
scene.Apply(
|
||||
[
|
||||
RenderProjectionDelta.Update(
|
||||
RenderProjectionDeltaKind.UpdateTransform,
|
||||
generation,
|
||||
2,
|
||||
poseOnly),
|
||||
RenderProjectionDelta.Update(
|
||||
RenderProjectionDeltaKind.UpdateAppearance,
|
||||
generation,
|
||||
3,
|
||||
poseOnly),
|
||||
]);
|
||||
Assert.Equal(
|
||||
registered,
|
||||
scene.OpenQuery().DirectionalShadowTopologyRevision);
|
||||
|
||||
RenderProjectionRecord hidden = poseOnly with
|
||||
{
|
||||
Flags = poseOnly.Flags & ~RenderProjectionFlags.Draw,
|
||||
};
|
||||
scene.Apply(
|
||||
[
|
||||
RenderProjectionDelta.Update(
|
||||
RenderProjectionDeltaKind.UpdateFlags,
|
||||
generation,
|
||||
4,
|
||||
hidden),
|
||||
]);
|
||||
ulong eligibilityChanged =
|
||||
scene.OpenQuery().DirectionalShadowTopologyRevision;
|
||||
Assert.True(eligibilityChanged > registered);
|
||||
|
||||
RenderProjectionRecord geometryChanged = hidden with
|
||||
{
|
||||
Source = hidden.Source with
|
||||
{
|
||||
DirectionalShadowTopologyFingerprint =
|
||||
new RenderSceneHash128(5, 5),
|
||||
},
|
||||
EntityPayload = hidden.EntityPayload with
|
||||
{
|
||||
MeshRefs =
|
||||
[
|
||||
new MeshRef(0x01000002, movedPart)
|
||||
{
|
||||
SurfaceOverrides = new Dictionary<uint, uint>
|
||||
{
|
||||
[0x08000001] = 0x05000001,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
scene.Apply(
|
||||
[
|
||||
RenderProjectionDelta.Update(
|
||||
RenderProjectionDeltaKind.UpdateAppearance,
|
||||
generation,
|
||||
5,
|
||||
geometryChanged),
|
||||
]);
|
||||
ulong geometryRevision =
|
||||
scene.OpenQuery().DirectionalShadowTopologyRevision;
|
||||
Assert.True(geometryRevision > eligibilityChanged);
|
||||
|
||||
var palette = new PaletteOverride(
|
||||
0x04000001,
|
||||
[new PaletteOverride.SubPaletteRange(0x04000002, 1, 2)]);
|
||||
RenderProjectionRecord appearanceChanged = geometryChanged with
|
||||
{
|
||||
Material = geometryChanged.Material with { PaletteKey = 1234 },
|
||||
Source = geometryChanged.Source with
|
||||
{
|
||||
AppearanceFingerprint = new RenderSceneHash128(6, 6),
|
||||
},
|
||||
EntityPayload = geometryChanged.EntityPayload with
|
||||
{
|
||||
PaletteOverride = palette,
|
||||
},
|
||||
};
|
||||
scene.Apply(
|
||||
[
|
||||
RenderProjectionDelta.Update(
|
||||
RenderProjectionDeltaKind.UpdateAppearance,
|
||||
generation,
|
||||
6,
|
||||
appearanceChanged),
|
||||
]);
|
||||
ulong appearanceRevision =
|
||||
scene.OpenQuery().DirectionalShadowTopologyRevision;
|
||||
Assert.True(appearanceRevision > geometryRevision);
|
||||
|
||||
RenderProjectionRecord identityChanged = appearanceChanged with
|
||||
{
|
||||
EntityPayload = appearanceChanged.EntityPayload with
|
||||
{
|
||||
CasterIdentity = RenderCasterIdentityKind.RemotePlayer,
|
||||
},
|
||||
};
|
||||
scene.Apply(
|
||||
[
|
||||
RenderProjectionDelta.Update(
|
||||
RenderProjectionDeltaKind.UpdateAppearance,
|
||||
generation,
|
||||
7,
|
||||
identityChanged),
|
||||
]);
|
||||
Assert.True(
|
||||
scene.OpenQuery().DirectionalShadowTopologyRevision
|
||||
> appearanceRevision);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DirectionalShadowTopologyRevision_TracksRareStaticTransformChanges()
|
||||
{
|
||||
RenderSceneGeneration generation = Generation(20);
|
||||
using var scene = new ArchRenderScene(generation);
|
||||
RenderProjectionRecord original = Record(
|
||||
20,
|
||||
1,
|
||||
RenderProjectionClass.OutdoorStatic);
|
||||
scene.Apply([RenderProjectionDelta.Register(generation, 1, original)]);
|
||||
ulong registered =
|
||||
scene.OpenQuery().DirectionalShadowTopologyRevision;
|
||||
RenderProjectionRecord moved = original with
|
||||
{
|
||||
Transform = new RenderTransform(
|
||||
Matrix4x4.CreateTranslation(100f, 101f, 102f)),
|
||||
};
|
||||
|
||||
scene.Apply(
|
||||
[
|
||||
RenderProjectionDelta.Update(
|
||||
RenderProjectionDeltaKind.UpdateTransform,
|
||||
generation,
|
||||
2,
|
||||
moved),
|
||||
]);
|
||||
|
||||
Assert.True(
|
||||
scene.OpenQuery().DirectionalShadowTopologyRevision > registered);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DirectionalShadowTransformJournal_TracksRootPartAndDynamicSync_IndependentlyOfDirtyDrain()
|
||||
{
|
||||
RenderSceneGeneration generation = Generation(21);
|
||||
using var scene = new ArchRenderScene(generation);
|
||||
Matrix4x4 firstPart = Matrix4x4.CreateTranslation(1f, 2f, 3f);
|
||||
RenderProjectionRecord original = Record(
|
||||
21,
|
||||
1,
|
||||
RenderProjectionClass.LiveDynamicRoot) with
|
||||
{
|
||||
EntityPayload = new RenderEntityPayload(
|
||||
[new MeshRef(0x01000021, firstPart)],
|
||||
PaletteOverride: null,
|
||||
IsBuildingShell: false),
|
||||
};
|
||||
scene.Apply([RenderProjectionDelta.Register(generation, 1, original)]);
|
||||
RenderSceneQuery query = scene.OpenQuery();
|
||||
ulong initialRevision = query.DirectionalShadowTransformRevision;
|
||||
|
||||
Matrix4x4 movedRoot = Matrix4x4.CreateTranslation(
|
||||
BitConverter.Int32BitsToSingle(0x41234567),
|
||||
4f,
|
||||
5f);
|
||||
RenderProjectionRecord moved = original with
|
||||
{
|
||||
Transform = new RenderTransform(movedRoot),
|
||||
PreviousTransform = new PreviousRenderTransform(
|
||||
original.Transform.LocalToWorld),
|
||||
};
|
||||
scene.Apply(
|
||||
[
|
||||
RenderProjectionDelta.Update(
|
||||
RenderProjectionDeltaKind.UpdateTransform,
|
||||
generation,
|
||||
2,
|
||||
moved),
|
||||
]);
|
||||
Matrix4x4 movedPart = Matrix4x4.CreateTranslation(
|
||||
6f,
|
||||
BitConverter.Int32BitsToSingle(0x40ABCDEF),
|
||||
8f);
|
||||
RenderProjectionRecord posed = moved with
|
||||
{
|
||||
EntityPayload = moved.EntityPayload with
|
||||
{
|
||||
MeshRefs = [new MeshRef(0x01000021, movedPart)],
|
||||
},
|
||||
};
|
||||
scene.Apply(
|
||||
[
|
||||
RenderProjectionDelta.Update(
|
||||
RenderProjectionDeltaKind.UpdateAppearance,
|
||||
generation,
|
||||
3,
|
||||
posed),
|
||||
]);
|
||||
scene.ClearDirty();
|
||||
var changes = new DirectionalShadowTransformSnapshot[3];
|
||||
DirectionalShadowTransformChanges firstChanges =
|
||||
query.CopyDirectionalShadowTransformChanges(
|
||||
initialRevision,
|
||||
changes);
|
||||
|
||||
Assert.False(firstChanges.RequiresFullRefresh);
|
||||
Assert.Equal(2, firstChanges.Count);
|
||||
Assert.Equal(1, firstChanges.UpdateTransformCount);
|
||||
Assert.Equal(1, firstChanges.UpdateAppearanceCount);
|
||||
Assert.Equal(0, firstChanges.DynamicSynchronizationCount);
|
||||
Assert.Equal(2, firstChanges.LiveDynamicRootCount);
|
||||
Assert.Equal(original.Id, changes[0].Id);
|
||||
Assert.Equal(original.Id, changes[1].Id);
|
||||
|
||||
var synchronized = new DynamicProjectionUpdate(
|
||||
original.Id,
|
||||
original.OwnerIncarnation,
|
||||
new RenderTransform(Matrix4x4.CreateTranslation(9f, 10f, 11f)),
|
||||
posed.Bounds);
|
||||
scene.SynchronizeDynamicSources(
|
||||
new DynamicProjectionSyncInput(generation, [synchronized]));
|
||||
DirectionalShadowTransformChanges syncChanges =
|
||||
query.CopyDirectionalShadowTransformChanges(
|
||||
firstChanges.LatestRevision,
|
||||
changes);
|
||||
Assert.False(syncChanges.RequiresFullRefresh);
|
||||
Assert.Equal(1, syncChanges.Count);
|
||||
Assert.Equal(1, syncChanges.DynamicSynchronizationCount);
|
||||
Assert.Equal(1, syncChanges.LiveDynamicRootCount);
|
||||
Assert.Equal(original.Id, changes[0].Id);
|
||||
|
||||
RenderProjectionRecord flagsOnly = posed with
|
||||
{
|
||||
Flags = posed.Flags ^ RenderProjectionFlags.Selectable,
|
||||
};
|
||||
scene.Apply(
|
||||
[
|
||||
RenderProjectionDelta.Update(
|
||||
RenderProjectionDeltaKind.UpdateFlags,
|
||||
generation,
|
||||
4,
|
||||
flagsOnly),
|
||||
]);
|
||||
Assert.Equal(
|
||||
syncChanges.LatestRevision,
|
||||
query.DirectionalShadowTransformRevision);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DirectionalShadowTransformJournal_OverflowAndGenerationResetFailSafe()
|
||||
{
|
||||
RenderSceneGeneration generation = Generation(22);
|
||||
using var scene = new ArchRenderScene(generation);
|
||||
RenderProjectionRecord original = Record(
|
||||
22,
|
||||
1,
|
||||
RenderProjectionClass.LiveDynamicRoot);
|
||||
scene.Apply([RenderProjectionDelta.Register(generation, 1, original)]);
|
||||
RenderSceneQuery oldQuery = scene.OpenQuery();
|
||||
ulong initialRevision = oldQuery.DirectionalShadowTransformRevision;
|
||||
for (int index = 0;
|
||||
index < DirectionalShadowTransformChangeJournal.Capacity + 1;
|
||||
index++)
|
||||
{
|
||||
var update = new DynamicProjectionUpdate(
|
||||
original.Id,
|
||||
original.OwnerIncarnation,
|
||||
new RenderTransform(Matrix4x4.CreateTranslation(index + 1, 0f, 0f)),
|
||||
original.Bounds);
|
||||
scene.SynchronizeDynamicSources(
|
||||
new DynamicProjectionSyncInput(generation, [update]));
|
||||
}
|
||||
|
||||
var changes = new DirectionalShadowTransformSnapshot[
|
||||
DirectionalShadowTransformChangeJournal.Capacity];
|
||||
DirectionalShadowTransformChanges overflow =
|
||||
oldQuery.CopyDirectionalShadowTransformChanges(
|
||||
initialRevision,
|
||||
changes);
|
||||
Assert.True(overflow.RequiresFullRefresh);
|
||||
Assert.Equal(0, overflow.Count);
|
||||
|
||||
RenderSceneGeneration replacement = Generation(23);
|
||||
scene.Clear(replacement);
|
||||
Assert.Throws<InvalidOperationException>(
|
||||
() => _ = oldQuery.DirectionalShadowTransformRevision);
|
||||
Assert.Equal(1ul, scene.OpenQuery().DirectionalShadowTransformRevision);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DirectionalShadowTransformJournal_IgnoresIdenticalPoseAndBoundsOnlySynchronization()
|
||||
{
|
||||
RenderSceneGeneration generation = Generation(24);
|
||||
using var scene = new ArchRenderScene(generation);
|
||||
var meshes = new List<MeshRef>
|
||||
{
|
||||
new(0x01000024, Matrix4x4.CreateTranslation(1f, 2f, 3f)),
|
||||
};
|
||||
RenderProjectionRecord original = Record(
|
||||
24,
|
||||
1,
|
||||
RenderProjectionClass.LiveDynamicRoot) with
|
||||
{
|
||||
EntityPayload = new RenderEntityPayload(
|
||||
meshes,
|
||||
PaletteOverride: null,
|
||||
IsBuildingShell: false),
|
||||
};
|
||||
scene.Apply([RenderProjectionDelta.Register(generation, 1, original)]);
|
||||
RenderSceneQuery query = scene.OpenQuery();
|
||||
ulong initial = query.DirectionalShadowTransformRevision;
|
||||
|
||||
scene.Apply(
|
||||
[
|
||||
RenderProjectionDelta.Update(
|
||||
RenderProjectionDeltaKind.UpdateTransform,
|
||||
generation,
|
||||
2,
|
||||
original),
|
||||
RenderProjectionDelta.Update(
|
||||
RenderProjectionDeltaKind.UpdateAppearance,
|
||||
generation,
|
||||
3,
|
||||
original),
|
||||
]);
|
||||
var boundsOnly = new DynamicProjectionUpdate(
|
||||
original.Id,
|
||||
original.OwnerIncarnation,
|
||||
original.Transform,
|
||||
new RenderWorldBounds(Vector3.One, new Vector3(2f)));
|
||||
scene.SynchronizeDynamicSources(
|
||||
new DynamicProjectionSyncInput(generation, [boundsOnly]));
|
||||
Assert.Equal(initial, query.DirectionalShadowTransformRevision);
|
||||
|
||||
Matrix4x4 changedPart = Matrix4x4.CreateTranslation(4f, 5f, 6f);
|
||||
meshes[0] = new MeshRef(0x01000024, changedPart);
|
||||
scene.Apply(
|
||||
[
|
||||
RenderProjectionDelta.Update(
|
||||
RenderProjectionDeltaKind.UpdateAppearance,
|
||||
generation,
|
||||
4,
|
||||
original),
|
||||
]);
|
||||
var changes = new DirectionalShadowTransformSnapshot[1];
|
||||
DirectionalShadowTransformChanges changed =
|
||||
query.CopyDirectionalShadowTransformChanges(initial, changes);
|
||||
Assert.Equal(1, changed.Count);
|
||||
Assert.Equal(0, changed.UpdateTransformCount);
|
||||
Assert.Equal(1, changed.UpdateAppearanceCount);
|
||||
Assert.Equal(0, changed.DynamicSynchronizationCount);
|
||||
Assert.Equal(1, changed.LiveDynamicRootCount);
|
||||
Assert.Equal(original.Id, changes[0].Id);
|
||||
Assert.Equal(
|
||||
BitConverter.SingleToInt32Bits(changedPart.M41),
|
||||
BitConverter.SingleToInt32Bits(
|
||||
changes[0].EntityPayload.MeshRefs[0].PartTransform.M41));
|
||||
|
||||
scene.Apply(
|
||||
[
|
||||
RenderProjectionDelta.Update(
|
||||
RenderProjectionDeltaKind.UpdateAppearance,
|
||||
generation,
|
||||
5,
|
||||
original),
|
||||
]);
|
||||
Assert.Equal(changed.LatestRevision, query.DirectionalShadowTransformRevision);
|
||||
}
|
||||
|
||||
private static RenderProjectionRecord Record(
|
||||
ulong id,
|
||||
ulong incarnation,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue