279 lines
9 KiB
C#
279 lines
9 KiB
C#
using System.Numerics;
|
|
using AcDream.App.Rendering.Scene;
|
|
using AcDream.App.Rendering.Walk;
|
|
|
|
namespace AcDream.App.Tests.Rendering.Walk;
|
|
|
|
public sealed class WalkProductionWorldDataTests
|
|
{
|
|
[Fact]
|
|
public void BuildingShellBucketCellId_PortalLessBuildingUsesLandscapePositionCell()
|
|
{
|
|
const uint positionCellId = 0xF4180011u;
|
|
RenderProjectionRecord record = Record(
|
|
id: 0xCF418060u,
|
|
position: new Vector3(53.57f, 13.874f, 160f)) with
|
|
{
|
|
Source = new RenderSourceMetadata() with
|
|
{
|
|
LocalEntityId = 0xCF418060u,
|
|
SourceId = 0x01001FD3u,
|
|
EffectCellId = positionCellId,
|
|
BuildingShellAnchorCellId = 0,
|
|
},
|
|
EntityPayload = new RenderEntityPayload() with
|
|
{
|
|
IsBuildingShell = true,
|
|
},
|
|
};
|
|
var building = new WalkBuilding
|
|
{
|
|
PositionCellId = positionCellId,
|
|
Portals = [],
|
|
};
|
|
|
|
Assert.Equal(
|
|
positionCellId,
|
|
WalkProductionWorldData.BuildingShellBucketCellId(in record));
|
|
Assert.Equal(
|
|
positionCellId,
|
|
WalkProductionWorldData.BuildingShellBucketCellId(building));
|
|
}
|
|
|
|
[Fact]
|
|
public void BuildingShellBucketCellId_PortalBearingBuildingKeepsInteriorAnchor()
|
|
{
|
|
const uint anchorCellId = 0xF4180112u;
|
|
RenderProjectionRecord record = Record(
|
|
id: 0xCF41805Fu,
|
|
position: new Vector3(36f, 13.8349f, 160f)) with
|
|
{
|
|
Source = new RenderSourceMetadata() with
|
|
{
|
|
LocalEntityId = 0xCF41805Fu,
|
|
SourceId = 0x01001FB7u,
|
|
EffectCellId = 0xF4180009u,
|
|
BuildingShellAnchorCellId = anchorCellId,
|
|
},
|
|
EntityPayload = new RenderEntityPayload() with
|
|
{
|
|
IsBuildingShell = true,
|
|
},
|
|
};
|
|
var building = new WalkBuilding
|
|
{
|
|
PositionCellId = 0xF4180009u,
|
|
Portals =
|
|
[
|
|
new WalkBldPortal { OtherCellId = anchorCellId },
|
|
],
|
|
};
|
|
|
|
Assert.Equal(
|
|
anchorCellId,
|
|
WalkProductionWorldData.BuildingShellBucketCellId(in record));
|
|
Assert.Equal(
|
|
anchorCellId,
|
|
WalkProductionWorldData.BuildingShellBucketCellId(building));
|
|
}
|
|
|
|
[Fact]
|
|
public void BucketOutdoorRecord_UsesEveryOutdoorPhysicsShadowCellAcrossLandblockEdge()
|
|
{
|
|
RenderProjectionRecord record = Record(
|
|
id: 0x1234u,
|
|
position: new Vector3(191f, 191f, 0f));
|
|
var buckets = new Dictionary<uint, List<RenderProjectionRecord>>();
|
|
|
|
WalkProductionWorldData.BucketOutdoorRecord(
|
|
in record,
|
|
[0xF07F0040u, 0xF0800001u, 0xF4180101u],
|
|
buckets,
|
|
renderCenterLbX: 0xF0,
|
|
renderCenterLbY: 0x7F);
|
|
|
|
Assert.Equal([0xF07F0040u, 0xF0800001u], buckets.Keys.Order());
|
|
Assert.All(buckets.Values, bucket => Assert.Equal(record, Assert.Single(bucket)));
|
|
}
|
|
|
|
[Fact]
|
|
public void BucketOutdoorRecord_UnregisteredEffectFallsBackToRootPositionCell()
|
|
{
|
|
RenderProjectionRecord record = Record(
|
|
id: 0x5678u,
|
|
position: new Vector3(193f, 25f, 0f));
|
|
var buckets = new Dictionary<uint, List<RenderProjectionRecord>>();
|
|
|
|
WalkProductionWorldData.BucketOutdoorRecord(
|
|
in record,
|
|
Array.Empty<uint>(),
|
|
buckets,
|
|
renderCenterLbX: 0xEF,
|
|
renderCenterLbY: 0x7F);
|
|
|
|
Assert.Equal([0xF07F0002u], buckets.Keys);
|
|
Assert.Equal(record, Assert.Single(buckets[0xF07F0002u]));
|
|
}
|
|
|
|
[Fact]
|
|
public void BucketIndoorRecord_InstallsCrossCellPartInBothInteriorCells()
|
|
{
|
|
RenderProjectionRecord record = Record(
|
|
id: 0x48A02035u,
|
|
position: new Vector3(55.25f, -46.47f, -3f)) with
|
|
{
|
|
Source = new RenderSourceMetadata() with
|
|
{
|
|
LocalEntityId = 0x48A02035u,
|
|
ParentCellId = 0x8A02015Fu,
|
|
},
|
|
};
|
|
var indoor = new Dictionary<uint, List<RenderProjectionRecord>>();
|
|
var outdoor = new Dictionary<uint, List<RenderProjectionRecord>>();
|
|
|
|
WalkProductionWorldData.BucketIndoorRecord(
|
|
in record,
|
|
[0x8A02015Fu, 0x8A02015Eu],
|
|
indoor,
|
|
outdoor);
|
|
|
|
Assert.Equal([0x8A02015Eu, 0x8A02015Fu], indoor.Keys.Order());
|
|
Assert.All(indoor.Values, bucket => Assert.Equal(record, Assert.Single(bucket)));
|
|
Assert.Empty(outdoor);
|
|
}
|
|
|
|
[Fact]
|
|
public void BucketIndoorRecord_CanCrossAnExitIntoLandscapeCell()
|
|
{
|
|
RenderProjectionRecord record = Record(
|
|
id: 0x48A02035u,
|
|
position: Vector3.Zero) with
|
|
{
|
|
Source = new RenderSourceMetadata() with
|
|
{
|
|
LocalEntityId = 0x48A02035u,
|
|
ParentCellId = 0x8A02015Fu,
|
|
},
|
|
};
|
|
var indoor = new Dictionary<uint, List<RenderProjectionRecord>>();
|
|
var outdoor = new Dictionary<uint, List<RenderProjectionRecord>>();
|
|
|
|
WalkProductionWorldData.BucketIndoorRecord(
|
|
in record,
|
|
[0x8A02015Fu, 0x8A020021u],
|
|
indoor,
|
|
outdoor);
|
|
|
|
Assert.Equal(record, Assert.Single(indoor[0x8A02015Fu]));
|
|
Assert.Equal(record, Assert.Single(outdoor[0x8A020021u]));
|
|
}
|
|
|
|
[Fact]
|
|
public void BucketDynamicRecord_InstallsMultipartPlayerInEveryCrossedInteriorCell()
|
|
{
|
|
RenderProjectionRecord record = Record(
|
|
id: 0x000F4243u,
|
|
position: new Vector3(58.81f, -49.42f, -0.85f)) with
|
|
{
|
|
Source = new RenderSourceMetadata() with
|
|
{
|
|
LocalEntityId = 0x000F4243u,
|
|
ParentCellId = 0x8A02015Eu,
|
|
},
|
|
};
|
|
var indoor = new Dictionary<uint, List<RenderProjectionRecord>>();
|
|
var outdoor = new Dictionary<uint, List<RenderProjectionRecord>>();
|
|
|
|
WalkProductionWorldData.BucketDynamicRecord(
|
|
in record,
|
|
[0x8A02015Eu, 0x8A02015Fu, 0x8A0201C1u],
|
|
indoor,
|
|
outdoor,
|
|
renderCenterLbX: 0x8A,
|
|
renderCenterLbY: 0x02);
|
|
|
|
Assert.Equal(
|
|
[0x8A02015Eu, 0x8A02015Fu, 0x8A0201C1u],
|
|
indoor.Keys.Order());
|
|
Assert.All(
|
|
indoor.Values,
|
|
bucket => Assert.Equal(record, Assert.Single(bucket)));
|
|
Assert.Empty(outdoor);
|
|
}
|
|
|
|
[Fact]
|
|
public void BucketDynamicRecord_UnregisteredInteriorEffectFallsBackToParentCell()
|
|
{
|
|
RenderProjectionRecord record = Record(
|
|
id: 0x00001234u,
|
|
position: Vector3.Zero) with
|
|
{
|
|
Source = new RenderSourceMetadata() with
|
|
{
|
|
LocalEntityId = 0x00001234u,
|
|
ParentCellId = 0x8A02015Fu,
|
|
},
|
|
};
|
|
var indoor = new Dictionary<uint, List<RenderProjectionRecord>>();
|
|
var outdoor = new Dictionary<uint, List<RenderProjectionRecord>>();
|
|
|
|
WalkProductionWorldData.BucketDynamicRecord(
|
|
in record,
|
|
Array.Empty<uint>(),
|
|
indoor,
|
|
outdoor,
|
|
renderCenterLbX: 0x8A,
|
|
renderCenterLbY: 0x02);
|
|
|
|
Assert.Equal(record, Assert.Single(indoor[0x8A02015Fu]));
|
|
Assert.Empty(outdoor);
|
|
}
|
|
|
|
[Fact]
|
|
public void ResolveDynamicRenderCells_EquippedDescendantInheritsRootCellArray()
|
|
{
|
|
RenderProjectionRecord wand = Record(
|
|
id: 0x800045EEu,
|
|
position: Vector3.Zero) with
|
|
{
|
|
Source = new RenderSourceMetadata() with
|
|
{
|
|
LocalEntityId = 0x800045EEu,
|
|
ParentCellId = 0xF4180104u,
|
|
},
|
|
EntityPayload = new RenderEntityPayload() with
|
|
{
|
|
CasterIdentity = RenderCasterIdentityKind.EquippedChild,
|
|
},
|
|
};
|
|
var owners = new Dictionary<uint, IReadOnlyList<uint>>
|
|
{
|
|
[0x5000000Au] = [0xF4180106u, 0xF4180104u],
|
|
};
|
|
var parents = new Dictionary<uint, uint>
|
|
{
|
|
[0x800045EEu] = 0x80000461u,
|
|
[0x80000461u] = 0x5000000Au,
|
|
};
|
|
|
|
IReadOnlyList<uint> cells =
|
|
WalkProductionWorldData.ResolveDynamicRenderCells(
|
|
in wand,
|
|
id => owners.TryGetValue(id, out IReadOnlyList<uint>? value)
|
|
? value
|
|
: Array.Empty<uint>(),
|
|
id => parents.TryGetValue(id, out uint value)
|
|
? value
|
|
: null);
|
|
|
|
Assert.Equal([0xF4180106u, 0xF4180104u], cells);
|
|
}
|
|
|
|
private static RenderProjectionRecord Record(uint id, Vector3 position) =>
|
|
new RenderProjectionRecord() with
|
|
{
|
|
Id = RenderProjectionId.FromRaw(id),
|
|
Transform = new RenderTransform(Matrix4x4.CreateTranslation(position)),
|
|
Source = new RenderSourceMetadata() with { LocalEntityId = id },
|
|
};
|
|
}
|