refactor(content): share EnvCell geometry identity

This commit is contained in:
Erik 2026-07-24 13:32:03 +02:00
parent 05a22dee99
commit 86fadf8661
3 changed files with 73 additions and 8 deletions

View file

@ -1,5 +1,6 @@
using System.Collections.Immutable;
using System.Numerics;
using AcDream.Core.Rendering.Wb;
using DatReaderWriter.DBObjs;
using DatReaderWriter.Enums;
using DatReaderWriter.Types;
@ -125,14 +126,7 @@ public sealed class EnvCellLandblockBuildBuilder
uint environmentId,
ushort cellStructure,
IReadOnlyList<ushort> surfaces)
{
var hash = 17L;
hash = hash * 31 + (int)environmentId;
hash = hash * 31 + cellStructure;
foreach (var surface in surfaces)
hash = hash * 31 + surface;
return (ulong)hash | 0x2_0000_0000UL;
}
=> EnvCellGeometryIdentity.Compute(environmentId, cellStructure, surfaces);
private static WbBoundingBox ComputeLocalBounds(CellStruct cellStruct)
{

View file

@ -0,0 +1,29 @@
using System.Collections.Generic;
namespace AcDream.Core.Rendering.Wb;
/// <summary>
/// Computes WorldBuilder's content identity for an EnvCell shell.
/// Equal environment, cell-structure, and ordered surface tuples share one
/// prepared geometry payload even when they occur under different cell IDs.
/// </summary>
public static class EnvCellGeometryIdentity
{
/// <summary>
/// Source: WorldBuilder <c>EnvCellRenderManager.GetEnvCellGeomId</c>,
/// lines 94-103. Bit 33 separates content identities from per-cell IDs,
/// whose synthetic geometry requests use bit 32.
/// </summary>
public static ulong Compute(
uint environmentId,
ushort cellStructure,
IReadOnlyList<ushort> surfaces)
{
long hash = 17;
hash = unchecked(hash * 31 + environmentId);
hash = unchecked(hash * 31 + cellStructure);
for (int i = 0; i < surfaces.Count; i++)
hash = unchecked(hash * 31 + surfaces[i]);
return (ulong)hash | 0x2_0000_0000UL;
}
}

View file

@ -0,0 +1,42 @@
using AcDream.Core.Rendering.Wb;
namespace AcDream.Core.Tests.Rendering.Wb;
public sealed class EnvCellGeometryIdentityTests
{
[Fact]
public void Compute_EmptySurfaces_MatchesExtractedWorldBuilderValue()
{
Assert.Equal(
0x2_0000_47D6UL,
EnvCellGeometryIdentity.Compute(0x42, 7, Array.Empty<ushort>()));
}
[Fact]
public void Compute_OrderedSurfaces_MatchesExtractedWorldBuilderValue()
{
Assert.Equal(
0x2_20A7_A46CUL,
EnvCellGeometryIdentity.Compute(0x42, 7, new ushort[] { 1, 2, 3 }));
}
[Fact]
public void Compute_SurfaceOrderIsPartOfIdentity()
{
var forward = EnvCellGeometryIdentity.Compute(1, 1, new ushort[] { 10, 20 });
var reverse = EnvCellGeometryIdentity.Compute(1, 1, new ushort[] { 20, 10 });
Assert.NotEqual(forward, reverse);
}
[Fact]
public void Compute_AlwaysUsesDeduplicatedGeometryNamespace()
{
var id = EnvCellGeometryIdentity.Compute(
uint.MaxValue,
ushort.MaxValue,
new ushort[] { ushort.MaxValue, ushort.MaxValue });
Assert.NotEqual(0UL, id & 0x2_0000_0000UL);
}
}