feat(rendering): complete current-path render referee
Extend the non-drawing oracle through ordered PView routes, dispatcher visibility and final instance payloads, and accepted retail selection parts. Lifecycle artifacts can now referee the later shadow scene without influencing production visibility or draw decisions.
This commit is contained in:
parent
dbaea19269
commit
f9b68f8f2a
11 changed files with 964 additions and 41 deletions
|
|
@ -1,4 +1,6 @@
|
|||
using System.Numerics;
|
||||
using AcDream.App.Rendering.Wb;
|
||||
using AcDream.Core.Selection;
|
||||
using AcDream.Core.World;
|
||||
|
||||
namespace AcDream.App.Rendering.Scene;
|
||||
|
|
@ -25,6 +27,48 @@ internal readonly record struct CurrentRenderProjectionFingerprint(
|
|||
RenderSceneHash128 Geometry,
|
||||
RenderSceneHash128 Appearance);
|
||||
|
||||
internal enum CurrentRenderPViewRoute : byte
|
||||
{
|
||||
LandscapeOutdoorStatic,
|
||||
LandscapeOutsideDynamic,
|
||||
LookInObject,
|
||||
CellStatic,
|
||||
DynamicLast,
|
||||
}
|
||||
|
||||
internal readonly record struct CurrentRenderPViewCandidateFingerprint(
|
||||
int Sequence,
|
||||
CurrentRenderPViewRoute Route,
|
||||
int RouteIndex,
|
||||
uint CellId,
|
||||
CurrentRenderProjectionFingerprint Projection);
|
||||
|
||||
internal readonly record struct CurrentRenderDispatcherFingerprint(
|
||||
int Sequence,
|
||||
int DrawSequence,
|
||||
WbDrawDispatcher.EntitySet Set,
|
||||
int MeshRefIndex,
|
||||
uint TupleLandblockId,
|
||||
uint CacheLandblockId,
|
||||
CurrentRenderProjectionFingerprint Projection);
|
||||
|
||||
internal readonly record struct CurrentRenderDispatcherSubmission(
|
||||
int VisibleInstanceCount,
|
||||
int ImmediateInstanceCount,
|
||||
int OpaqueGroupCount,
|
||||
int TransparentGroupCount,
|
||||
bool TransparentDeferred,
|
||||
RenderSceneHash128 Digest);
|
||||
|
||||
internal readonly record struct CurrentRenderSelectionFingerprint(
|
||||
int Sequence,
|
||||
uint ServerGuid,
|
||||
uint LocalEntityId,
|
||||
int PartIndex,
|
||||
uint GfxObjId,
|
||||
Matrix4x4 LocalToWorld,
|
||||
RenderSceneHash128 Geometry);
|
||||
|
||||
internal readonly record struct CurrentRenderSceneOracleSnapshot(
|
||||
bool Enabled,
|
||||
ulong CompletedFrameSequence,
|
||||
|
|
@ -34,7 +78,24 @@ internal readonly record struct CurrentRenderSceneOracleSnapshot(
|
|||
int CellStaticCount,
|
||||
int DynamicCount,
|
||||
int CellBucketCount,
|
||||
RenderSceneHash128 Digest)
|
||||
RenderSceneHash128 Digest,
|
||||
ulong CompletedPViewFrameSequence,
|
||||
int AbortedPViewFrames,
|
||||
int PViewCandidateCount,
|
||||
RenderSceneHash128 PViewDigest,
|
||||
ulong DispatcherFrameSequence,
|
||||
int AbortedDispatcherFrames,
|
||||
int DispatcherDrawCount,
|
||||
int DispatcherEntityCount,
|
||||
int DispatcherMeshRefCount,
|
||||
int DispatcherInstanceCount,
|
||||
int DispatcherOpaqueGroupCount,
|
||||
int DispatcherTransparentGroupCount,
|
||||
RenderSceneHash128 DispatcherDigest,
|
||||
ulong CompletedSelectionFrameSequence,
|
||||
int AbortedSelectionFrames,
|
||||
int SelectionPartCount,
|
||||
RenderSceneHash128 SelectionDigest)
|
||||
{
|
||||
public static CurrentRenderSceneOracleSnapshot Disabled { get; } = new(
|
||||
Enabled: false,
|
||||
|
|
@ -45,7 +106,24 @@ internal readonly record struct CurrentRenderSceneOracleSnapshot(
|
|||
CellStaticCount: 0,
|
||||
DynamicCount: 0,
|
||||
CellBucketCount: 0,
|
||||
Digest: RenderSceneHash128.Empty);
|
||||
Digest: RenderSceneHash128.Empty,
|
||||
CompletedPViewFrameSequence: 0,
|
||||
AbortedPViewFrames: 0,
|
||||
PViewCandidateCount: 0,
|
||||
PViewDigest: RenderSceneHash128.Empty,
|
||||
DispatcherFrameSequence: 0,
|
||||
AbortedDispatcherFrames: 0,
|
||||
DispatcherDrawCount: 0,
|
||||
DispatcherEntityCount: 0,
|
||||
DispatcherMeshRefCount: 0,
|
||||
DispatcherInstanceCount: 0,
|
||||
DispatcherOpaqueGroupCount: 0,
|
||||
DispatcherTransparentGroupCount: 0,
|
||||
DispatcherDigest: RenderSceneHash128.Empty,
|
||||
CompletedSelectionFrameSequence: 0,
|
||||
AbortedSelectionFrames: 0,
|
||||
SelectionPartCount: 0,
|
||||
SelectionDigest: RenderSceneHash128.Empty);
|
||||
}
|
||||
|
||||
internal interface ICurrentRenderSceneOracleSnapshotSource
|
||||
|
|
@ -53,6 +131,56 @@ internal interface ICurrentRenderSceneOracleSnapshotSource
|
|||
CurrentRenderSceneOracleSnapshot Snapshot { get; }
|
||||
}
|
||||
|
||||
internal interface ICurrentRenderPViewObserver
|
||||
{
|
||||
void BeginPViewFrame();
|
||||
|
||||
void ObservePViewBucket(
|
||||
CurrentRenderPViewRoute route,
|
||||
int routeIndex,
|
||||
uint cellId,
|
||||
IReadOnlyList<WorldEntity> entities);
|
||||
|
||||
void CompletePViewFrame();
|
||||
|
||||
void AbortPViewFrame();
|
||||
}
|
||||
|
||||
internal interface ICurrentRenderDispatcherObserver
|
||||
{
|
||||
void BeginDispatcherFrame();
|
||||
|
||||
void ObserveDispatcherDraw(
|
||||
WbDrawDispatcher.EntitySet set,
|
||||
int entitiesWalked,
|
||||
IReadOnlyList<(
|
||||
WorldEntity Entity,
|
||||
int MeshRefIndex,
|
||||
uint LandblockId)> accepted);
|
||||
|
||||
void ObserveDispatcherSubmission(
|
||||
in CurrentRenderDispatcherSubmission submission);
|
||||
|
||||
void AbortDispatcherFrame();
|
||||
}
|
||||
|
||||
internal interface ICurrentRenderSelectionObserver
|
||||
{
|
||||
void BeginSelectionFrame();
|
||||
|
||||
void ObserveSelectionPart(
|
||||
uint serverGuid,
|
||||
uint localEntityId,
|
||||
int partIndex,
|
||||
uint gfxObjId,
|
||||
Matrix4x4 localToWorld,
|
||||
RetailSelectionMesh mesh);
|
||||
|
||||
void CompleteSelectionFrame();
|
||||
|
||||
void AbortSelectionFrame();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deterministic fingerprint of the already-accepted
|
||||
/// <see cref="InteriorEntityPartition"/> output. This is the Slice-F referee:
|
||||
|
|
@ -65,21 +193,59 @@ internal interface ICurrentRenderSceneOracleSnapshotSource
|
|||
/// </summary>
|
||||
internal sealed class CurrentRenderSceneOracle :
|
||||
InteriorEntityPartition.IObserver,
|
||||
ICurrentRenderPViewObserver,
|
||||
ICurrentRenderDispatcherObserver,
|
||||
ICurrentRenderSelectionObserver,
|
||||
ICurrentRenderSceneOracleSnapshotSource
|
||||
{
|
||||
private readonly List<CurrentRenderProjectionFingerprint> _projections = [];
|
||||
private readonly Dictionary<WorldEntity, CurrentRenderProjectionFingerprint>
|
||||
_projectionByEntity =
|
||||
new(ReferenceEqualityComparer.Instance);
|
||||
private readonly List<CurrentRenderPViewCandidateFingerprint>
|
||||
_pviewCandidates = [];
|
||||
private readonly List<CurrentRenderDispatcherFingerprint>
|
||||
_dispatcherCandidates = [];
|
||||
private readonly Dictionary<WorldEntity, CurrentRenderProjectionFingerprint>
|
||||
_dispatcherProjectionByEntity =
|
||||
new(ReferenceEqualityComparer.Instance);
|
||||
private readonly List<CurrentRenderSelectionFingerprint>
|
||||
_selectionParts = [];
|
||||
private ulong _frameSequence;
|
||||
private int _abortedFrames;
|
||||
private int _outdoorStaticCount;
|
||||
private int _cellStaticCount;
|
||||
private int _dynamicCount;
|
||||
private bool _frameOpen;
|
||||
private ulong _pviewFrameSequence;
|
||||
private int _abortedPViewFrames;
|
||||
private bool _pviewFrameOpen;
|
||||
private StableRenderHash128 _pviewHash;
|
||||
private ulong _dispatcherFrameSequence;
|
||||
private int _abortedDispatcherFrames;
|
||||
private int _dispatcherDrawCount;
|
||||
private int _dispatcherEntityCount;
|
||||
private int _dispatcherInstanceCount;
|
||||
private int _dispatcherOpaqueGroupCount;
|
||||
private int _dispatcherTransparentGroupCount;
|
||||
private bool _dispatcherFrameOpen;
|
||||
private StableRenderHash128 _dispatcherHash;
|
||||
private ulong _selectionFrameSequence;
|
||||
private int _abortedSelectionFrames;
|
||||
private bool _selectionFrameOpen;
|
||||
private StableRenderHash128 _selectionHash;
|
||||
|
||||
public CurrentRenderSceneOracleSnapshot Snapshot { get; private set; } =
|
||||
CurrentRenderSceneOracleSnapshot.Disabled with { Enabled = true };
|
||||
|
||||
internal IReadOnlyList<CurrentRenderProjectionFingerprint> Projections =>
|
||||
_projections;
|
||||
internal IReadOnlyList<CurrentRenderPViewCandidateFingerprint>
|
||||
PViewCandidates => _pviewCandidates;
|
||||
internal IReadOnlyList<CurrentRenderDispatcherFingerprint>
|
||||
DispatcherCandidates => _dispatcherCandidates;
|
||||
internal IReadOnlyList<CurrentRenderSelectionFingerprint>
|
||||
SelectionParts => _selectionParts;
|
||||
|
||||
public void BeginFrame()
|
||||
{
|
||||
|
|
@ -91,6 +257,7 @@ internal sealed class CurrentRenderSceneOracle :
|
|||
|
||||
_frameOpen = true;
|
||||
_projections.Clear();
|
||||
_projectionByEntity.Clear();
|
||||
_outdoorStaticCount = 0;
|
||||
_cellStaticCount = 0;
|
||||
_dynamicCount = 0;
|
||||
|
|
@ -126,10 +293,16 @@ internal sealed class CurrentRenderSceneOracle :
|
|||
"Unknown current render projection class.");
|
||||
}
|
||||
|
||||
_projections.Add(CreateFingerprint(
|
||||
CurrentRenderProjectionFingerprint fingerprint = CreateFingerprint(
|
||||
landblockId,
|
||||
entity,
|
||||
projectionClass));
|
||||
projectionClass);
|
||||
_projections.Add(fingerprint);
|
||||
if (!_projectionByEntity.TryAdd(entity, fingerprint))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"The current render partition observed entity 0x{entity.Id:X8} more than once.");
|
||||
}
|
||||
}
|
||||
|
||||
public void Complete(InteriorEntityPartition.Result result)
|
||||
|
|
@ -170,7 +343,26 @@ internal sealed class CurrentRenderSceneOracle :
|
|||
CellStaticCount: _cellStaticCount,
|
||||
DynamicCount: _dynamicCount,
|
||||
CellBucketCount: result.ByCell.Count,
|
||||
Digest: aggregate.Finish());
|
||||
Digest: aggregate.Finish(),
|
||||
CompletedPViewFrameSequence: Snapshot.CompletedPViewFrameSequence,
|
||||
AbortedPViewFrames: _abortedPViewFrames,
|
||||
PViewCandidateCount: Snapshot.PViewCandidateCount,
|
||||
PViewDigest: Snapshot.PViewDigest,
|
||||
DispatcherFrameSequence: Snapshot.DispatcherFrameSequence,
|
||||
AbortedDispatcherFrames: _abortedDispatcherFrames,
|
||||
DispatcherDrawCount: Snapshot.DispatcherDrawCount,
|
||||
DispatcherEntityCount: Snapshot.DispatcherEntityCount,
|
||||
DispatcherMeshRefCount: Snapshot.DispatcherMeshRefCount,
|
||||
DispatcherInstanceCount: Snapshot.DispatcherInstanceCount,
|
||||
DispatcherOpaqueGroupCount: Snapshot.DispatcherOpaqueGroupCount,
|
||||
DispatcherTransparentGroupCount:
|
||||
Snapshot.DispatcherTransparentGroupCount,
|
||||
DispatcherDigest: Snapshot.DispatcherDigest,
|
||||
CompletedSelectionFrameSequence:
|
||||
Snapshot.CompletedSelectionFrameSequence,
|
||||
AbortedSelectionFrames: _abortedSelectionFrames,
|
||||
SelectionPartCount: Snapshot.SelectionPartCount,
|
||||
SelectionDigest: Snapshot.SelectionDigest);
|
||||
}
|
||||
|
||||
public void AbortFrame()
|
||||
|
|
@ -187,6 +379,328 @@ internal sealed class CurrentRenderSceneOracle :
|
|||
Snapshot = Snapshot with { AbortedFrames = _abortedFrames };
|
||||
}
|
||||
|
||||
public void BeginPViewFrame()
|
||||
{
|
||||
if (_pviewFrameOpen)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"The current render-scene oracle cannot begin a second PView frame before completing or aborting the first.");
|
||||
}
|
||||
|
||||
_pviewFrameOpen = true;
|
||||
_pviewCandidates.Clear();
|
||||
_pviewHash = StableRenderHash128.Create();
|
||||
}
|
||||
|
||||
public void ObservePViewBucket(
|
||||
CurrentRenderPViewRoute route,
|
||||
int routeIndex,
|
||||
uint cellId,
|
||||
IReadOnlyList<WorldEntity> entities)
|
||||
{
|
||||
if (!_pviewFrameOpen)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"The current render-scene oracle received a PView bucket outside an open frame.");
|
||||
}
|
||||
ArgumentNullException.ThrowIfNull(entities);
|
||||
|
||||
_pviewHash.Add((byte)0xB1);
|
||||
_pviewHash.Add((byte)route);
|
||||
_pviewHash.Add(routeIndex);
|
||||
_pviewHash.Add(cellId);
|
||||
_pviewHash.Add(entities.Count);
|
||||
foreach (WorldEntity entity in entities)
|
||||
{
|
||||
if (!_projectionByEntity.TryGetValue(entity, out var projection))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"PView routed entity 0x{entity.Id:X8} that was absent from the current partition.");
|
||||
}
|
||||
|
||||
var candidate = new CurrentRenderPViewCandidateFingerprint(
|
||||
Sequence: _pviewCandidates.Count,
|
||||
Route: route,
|
||||
RouteIndex: routeIndex,
|
||||
CellId: cellId,
|
||||
Projection: projection);
|
||||
_pviewCandidates.Add(candidate);
|
||||
AddPViewCandidate(ref _pviewHash, in candidate);
|
||||
}
|
||||
}
|
||||
|
||||
public void CompletePViewFrame()
|
||||
{
|
||||
if (!_pviewFrameOpen)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"The current render-scene oracle cannot complete PView without an open frame.");
|
||||
}
|
||||
|
||||
_pviewFrameOpen = false;
|
||||
Snapshot = Snapshot with
|
||||
{
|
||||
CompletedPViewFrameSequence = checked(++_pviewFrameSequence),
|
||||
AbortedPViewFrames = _abortedPViewFrames,
|
||||
PViewCandidateCount = _pviewCandidates.Count,
|
||||
PViewDigest = _pviewHash.Finish(),
|
||||
};
|
||||
}
|
||||
|
||||
public void AbortPViewFrame()
|
||||
{
|
||||
if (!_pviewFrameOpen)
|
||||
return;
|
||||
|
||||
_pviewFrameOpen = false;
|
||||
_pviewCandidates.Clear();
|
||||
_abortedPViewFrames = checked(_abortedPViewFrames + 1);
|
||||
Snapshot = Snapshot with
|
||||
{
|
||||
AbortedPViewFrames = _abortedPViewFrames,
|
||||
};
|
||||
}
|
||||
|
||||
public void BeginDispatcherFrame()
|
||||
{
|
||||
_dispatcherFrameOpen = true;
|
||||
_dispatcherCandidates.Clear();
|
||||
_dispatcherProjectionByEntity.Clear();
|
||||
_dispatcherDrawCount = 0;
|
||||
_dispatcherEntityCount = 0;
|
||||
_dispatcherInstanceCount = 0;
|
||||
_dispatcherOpaqueGroupCount = 0;
|
||||
_dispatcherTransparentGroupCount = 0;
|
||||
_dispatcherHash = StableRenderHash128.Create();
|
||||
Snapshot = Snapshot with
|
||||
{
|
||||
DispatcherFrameSequence = checked(++_dispatcherFrameSequence),
|
||||
AbortedDispatcherFrames = _abortedDispatcherFrames,
|
||||
DispatcherDrawCount = 0,
|
||||
DispatcherEntityCount = 0,
|
||||
DispatcherMeshRefCount = 0,
|
||||
DispatcherInstanceCount = 0,
|
||||
DispatcherOpaqueGroupCount = 0,
|
||||
DispatcherTransparentGroupCount = 0,
|
||||
DispatcherDigest = _dispatcherHash.Finish(),
|
||||
};
|
||||
}
|
||||
|
||||
public void ObserveDispatcherDraw(
|
||||
WbDrawDispatcher.EntitySet set,
|
||||
int entitiesWalked,
|
||||
IReadOnlyList<(
|
||||
WorldEntity Entity,
|
||||
int MeshRefIndex,
|
||||
uint LandblockId)> accepted)
|
||||
{
|
||||
if (!_dispatcherFrameOpen)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"The current render-scene oracle received dispatcher input outside an open frame.");
|
||||
}
|
||||
ArgumentNullException.ThrowIfNull(accepted);
|
||||
|
||||
int drawSequence = _dispatcherDrawCount++;
|
||||
_dispatcherEntityCount = checked(_dispatcherEntityCount + entitiesWalked);
|
||||
_dispatcherHash.Add((byte)0xD1);
|
||||
_dispatcherHash.Add(drawSequence);
|
||||
_dispatcherHash.Add((int)set);
|
||||
_dispatcherHash.Add(entitiesWalked);
|
||||
_dispatcherHash.Add(accepted.Count);
|
||||
|
||||
foreach (var tuple in accepted)
|
||||
{
|
||||
uint cacheLandblockId =
|
||||
WbDrawDispatcher.ResolveCacheLandblockHint(
|
||||
tuple.Entity,
|
||||
tuple.LandblockId);
|
||||
if (!_dispatcherProjectionByEntity.TryGetValue(
|
||||
tuple.Entity,
|
||||
out CurrentRenderProjectionFingerprint projection)
|
||||
|| projection.LandblockId != cacheLandblockId)
|
||||
{
|
||||
projection = CreateFingerprint(
|
||||
cacheLandblockId,
|
||||
tuple.Entity,
|
||||
ProjectionClassOf(tuple.Entity));
|
||||
_dispatcherProjectionByEntity[tuple.Entity] = projection;
|
||||
}
|
||||
var candidate = new CurrentRenderDispatcherFingerprint(
|
||||
Sequence: _dispatcherCandidates.Count,
|
||||
DrawSequence: drawSequence,
|
||||
Set: set,
|
||||
MeshRefIndex: tuple.MeshRefIndex,
|
||||
TupleLandblockId: tuple.LandblockId,
|
||||
CacheLandblockId: cacheLandblockId,
|
||||
Projection: projection);
|
||||
_dispatcherCandidates.Add(candidate);
|
||||
AddDispatcherCandidate(ref _dispatcherHash, in candidate);
|
||||
}
|
||||
|
||||
Snapshot = Snapshot with
|
||||
{
|
||||
DispatcherFrameSequence = _dispatcherFrameSequence,
|
||||
AbortedDispatcherFrames = _abortedDispatcherFrames,
|
||||
DispatcherDrawCount = _dispatcherDrawCount,
|
||||
DispatcherEntityCount = _dispatcherEntityCount,
|
||||
DispatcherMeshRefCount = _dispatcherCandidates.Count,
|
||||
DispatcherInstanceCount = _dispatcherInstanceCount,
|
||||
DispatcherOpaqueGroupCount = _dispatcherOpaqueGroupCount,
|
||||
DispatcherTransparentGroupCount =
|
||||
_dispatcherTransparentGroupCount,
|
||||
DispatcherDigest = _dispatcherHash.Finish(),
|
||||
};
|
||||
}
|
||||
|
||||
public void ObserveDispatcherSubmission(
|
||||
in CurrentRenderDispatcherSubmission submission)
|
||||
{
|
||||
if (!_dispatcherFrameOpen)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"The current render-scene oracle received dispatcher submission outside an open frame.");
|
||||
}
|
||||
if (_dispatcherDrawCount == 0)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"A dispatcher submission cannot precede its accepted candidate set.");
|
||||
}
|
||||
|
||||
_dispatcherInstanceCount = checked(
|
||||
_dispatcherInstanceCount + submission.VisibleInstanceCount);
|
||||
_dispatcherOpaqueGroupCount = checked(
|
||||
_dispatcherOpaqueGroupCount + submission.OpaqueGroupCount);
|
||||
_dispatcherTransparentGroupCount = checked(
|
||||
_dispatcherTransparentGroupCount
|
||||
+ submission.TransparentGroupCount);
|
||||
_dispatcherHash.Add((byte)0xD2);
|
||||
_dispatcherHash.Add(_dispatcherDrawCount - 1);
|
||||
_dispatcherHash.Add(submission.VisibleInstanceCount);
|
||||
_dispatcherHash.Add(submission.ImmediateInstanceCount);
|
||||
_dispatcherHash.Add(submission.OpaqueGroupCount);
|
||||
_dispatcherHash.Add(submission.TransparentGroupCount);
|
||||
_dispatcherHash.Add(submission.TransparentDeferred);
|
||||
_dispatcherHash.Add(submission.Digest.Low);
|
||||
_dispatcherHash.Add(submission.Digest.High);
|
||||
|
||||
Snapshot = Snapshot with
|
||||
{
|
||||
DispatcherInstanceCount = _dispatcherInstanceCount,
|
||||
DispatcherOpaqueGroupCount = _dispatcherOpaqueGroupCount,
|
||||
DispatcherTransparentGroupCount =
|
||||
_dispatcherTransparentGroupCount,
|
||||
DispatcherDigest = _dispatcherHash.Finish(),
|
||||
};
|
||||
}
|
||||
|
||||
public void AbortDispatcherFrame()
|
||||
{
|
||||
if (!_dispatcherFrameOpen)
|
||||
return;
|
||||
|
||||
_dispatcherFrameOpen = false;
|
||||
_dispatcherCandidates.Clear();
|
||||
_dispatcherProjectionByEntity.Clear();
|
||||
_abortedDispatcherFrames = checked(_abortedDispatcherFrames + 1);
|
||||
Snapshot = Snapshot with
|
||||
{
|
||||
AbortedDispatcherFrames = _abortedDispatcherFrames,
|
||||
};
|
||||
}
|
||||
|
||||
public void BeginSelectionFrame()
|
||||
{
|
||||
if (_selectionFrameOpen)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"The current render-scene oracle cannot begin a second selection frame before completing or aborting the first.");
|
||||
}
|
||||
|
||||
_selectionFrameOpen = true;
|
||||
_selectionParts.Clear();
|
||||
_selectionHash = StableRenderHash128.Create();
|
||||
}
|
||||
|
||||
public void ObserveSelectionPart(
|
||||
uint serverGuid,
|
||||
uint localEntityId,
|
||||
int partIndex,
|
||||
uint gfxObjId,
|
||||
Matrix4x4 localToWorld,
|
||||
RetailSelectionMesh mesh)
|
||||
{
|
||||
if (!_selectionFrameOpen)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"The current render-scene oracle received a selection part outside an open frame.");
|
||||
}
|
||||
ArgumentNullException.ThrowIfNull(mesh);
|
||||
|
||||
StableRenderHash128 geometry = StableRenderHash128.Create();
|
||||
geometry.Add(mesh.SphereCenter);
|
||||
geometry.Add(mesh.SphereRadius);
|
||||
geometry.Add(mesh.Polygons.Count);
|
||||
foreach (RetailSelectionPolygon polygon in mesh.Polygons)
|
||||
{
|
||||
geometry.Add(polygon.SingleSided);
|
||||
geometry.Add(polygon.Vertices.Count);
|
||||
foreach (Vector3 vertex in polygon.Vertices)
|
||||
geometry.Add(vertex);
|
||||
}
|
||||
|
||||
var fingerprint = new CurrentRenderSelectionFingerprint(
|
||||
Sequence: _selectionParts.Count,
|
||||
ServerGuid: serverGuid,
|
||||
LocalEntityId: localEntityId,
|
||||
PartIndex: partIndex,
|
||||
GfxObjId: gfxObjId,
|
||||
LocalToWorld: localToWorld,
|
||||
Geometry: geometry.Finish());
|
||||
_selectionParts.Add(fingerprint);
|
||||
_selectionHash.Add(fingerprint.Sequence);
|
||||
_selectionHash.Add(fingerprint.ServerGuid);
|
||||
_selectionHash.Add(fingerprint.LocalEntityId);
|
||||
_selectionHash.Add(fingerprint.PartIndex);
|
||||
_selectionHash.Add(fingerprint.GfxObjId);
|
||||
_selectionHash.Add(fingerprint.LocalToWorld);
|
||||
_selectionHash.Add(fingerprint.Geometry.Low);
|
||||
_selectionHash.Add(fingerprint.Geometry.High);
|
||||
}
|
||||
|
||||
public void CompleteSelectionFrame()
|
||||
{
|
||||
if (!_selectionFrameOpen)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"The current render-scene oracle cannot complete selection without an open frame.");
|
||||
}
|
||||
|
||||
_selectionFrameOpen = false;
|
||||
Snapshot = Snapshot with
|
||||
{
|
||||
CompletedSelectionFrameSequence =
|
||||
checked(++_selectionFrameSequence),
|
||||
AbortedSelectionFrames = _abortedSelectionFrames,
|
||||
SelectionPartCount = _selectionParts.Count,
|
||||
SelectionDigest = _selectionHash.Finish(),
|
||||
};
|
||||
}
|
||||
|
||||
public void AbortSelectionFrame()
|
||||
{
|
||||
if (!_selectionFrameOpen)
|
||||
return;
|
||||
|
||||
_selectionFrameOpen = false;
|
||||
_selectionParts.Clear();
|
||||
_abortedSelectionFrames = checked(_abortedSelectionFrames + 1);
|
||||
Snapshot = Snapshot with
|
||||
{
|
||||
AbortedSelectionFrames = _abortedSelectionFrames,
|
||||
};
|
||||
}
|
||||
|
||||
private static CurrentRenderProjectionFingerprint CreateFingerprint(
|
||||
uint landblockId,
|
||||
WorldEntity entity,
|
||||
|
|
@ -261,6 +775,40 @@ internal sealed class CurrentRenderSceneOracle :
|
|||
Appearance: appearance.Finish());
|
||||
}
|
||||
|
||||
private static InteriorEntityPartition.ProjectionClass ProjectionClassOf(
|
||||
WorldEntity entity) =>
|
||||
entity.ServerGuid != 0
|
||||
? InteriorEntityPartition.ProjectionClass.Dynamic
|
||||
: InteriorEntityPartition.IsIndoorCellId(entity.ParentCellId)
|
||||
? InteriorEntityPartition.ProjectionClass.CellStatic
|
||||
: InteriorEntityPartition.ProjectionClass.OutdoorStatic;
|
||||
|
||||
private static void AddPViewCandidate(
|
||||
ref StableRenderHash128 hash,
|
||||
in CurrentRenderPViewCandidateFingerprint candidate)
|
||||
{
|
||||
hash.Add(candidate.Sequence);
|
||||
hash.Add((byte)candidate.Route);
|
||||
hash.Add(candidate.RouteIndex);
|
||||
hash.Add(candidate.CellId);
|
||||
CurrentRenderProjectionFingerprint projection = candidate.Projection;
|
||||
AddProjection(ref hash, in projection);
|
||||
}
|
||||
|
||||
private static void AddDispatcherCandidate(
|
||||
ref StableRenderHash128 hash,
|
||||
in CurrentRenderDispatcherFingerprint candidate)
|
||||
{
|
||||
hash.Add(candidate.Sequence);
|
||||
hash.Add(candidate.DrawSequence);
|
||||
hash.Add((int)candidate.Set);
|
||||
hash.Add(candidate.MeshRefIndex);
|
||||
hash.Add(candidate.TupleLandblockId);
|
||||
hash.Add(candidate.CacheLandblockId);
|
||||
CurrentRenderProjectionFingerprint projection = candidate.Projection;
|
||||
AddProjection(ref hash, in projection);
|
||||
}
|
||||
|
||||
private static void AddProjection(
|
||||
ref StableRenderHash128 hash,
|
||||
in CurrentRenderProjectionFingerprint projection)
|
||||
|
|
@ -371,6 +919,12 @@ internal struct StableRenderHash128
|
|||
Add(value.Z);
|
||||
}
|
||||
|
||||
public void Add(Vector2 value)
|
||||
{
|
||||
Add(value.X);
|
||||
Add(value.Y);
|
||||
}
|
||||
|
||||
public void Add(Quaternion value)
|
||||
{
|
||||
Add(value.X);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue