feat(rendering): journal live scene projections
Mirror exact live-root and equipped-child lifetimes behind the non-drawing render-scene boundary. Ready, visibility, final-pose, rebucket, removal, and resource teardown edges retain canonical LiveEntityRuntime identity while active-only synchronization avoids resident-static scans. Co-authored-by: Erik Nilsson <erikn@users.noreply.github.com>
This commit is contained in:
parent
5d19c56d15
commit
58e7c2eb99
10 changed files with 949 additions and 148 deletions
130
src/AcDream.App/Rendering/Scene/RenderProjectionRecordFactory.cs
Normal file
130
src/AcDream.App/Rendering/Scene/RenderProjectionRecordFactory.cs
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
using System.Numerics;
|
||||
using AcDream.Core.World;
|
||||
|
||||
namespace AcDream.App.Rendering.Scene;
|
||||
|
||||
/// <summary>
|
||||
/// Pure conversion from an accepted <see cref="WorldEntity"/> presentation
|
||||
/// snapshot into the packed render-scene record. This method never mutates
|
||||
/// source AABB state or acquires gameplay identity.
|
||||
/// </summary>
|
||||
internal static class RenderProjectionRecordFactory
|
||||
{
|
||||
private const float DefaultAabbRadius = 5.0f;
|
||||
|
||||
public static RenderProjectionRecord ProjectEntity(
|
||||
RenderProjectionId id,
|
||||
RenderProjectionClass projectionClass,
|
||||
RenderOwnerIncarnation incarnation,
|
||||
uint ownerLandblockId,
|
||||
uint fullCellId,
|
||||
WorldEntity entity,
|
||||
bool spatiallyVisible)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(entity);
|
||||
CurrentRenderProjectionFingerprint fingerprint =
|
||||
CurrentRenderSceneOracle.CreateProjectionFingerprint(
|
||||
ownerLandblockId,
|
||||
entity);
|
||||
RenderProjectionFlags flags = RenderProjectionFlags.Selectable;
|
||||
if (spatiallyVisible
|
||||
&& entity.IsDrawVisible
|
||||
&& entity.IsAncestorDrawVisible)
|
||||
{
|
||||
flags |= RenderProjectionFlags.Draw;
|
||||
}
|
||||
if (!entity.IsDrawVisible)
|
||||
flags |= RenderProjectionFlags.Hidden;
|
||||
if (!entity.IsAncestorDrawVisible)
|
||||
flags |= RenderProjectionFlags.AncestorHidden;
|
||||
|
||||
RenderTransform transform = RenderTransform.FromRoot(
|
||||
entity.Position,
|
||||
entity.Rotation,
|
||||
entity.Scale);
|
||||
(Vector3 minimum, Vector3 maximum) = CalculateBounds(entity);
|
||||
return new RenderProjectionRecord(
|
||||
id,
|
||||
projectionClass,
|
||||
incarnation,
|
||||
transform,
|
||||
new PreviousRenderTransform(transform.LocalToWorld),
|
||||
new RenderMeshSet(
|
||||
RenderAssetHandle.FromRaw(
|
||||
fingerprint.Geometry.Low ^ fingerprint.Geometry.High),
|
||||
entity.MeshRefs.Count,
|
||||
0),
|
||||
new RenderMaterialVariant(
|
||||
fingerprint.Appearance.Low,
|
||||
fingerprint.Appearance.High,
|
||||
1.0f),
|
||||
new RenderSpatialResidency(
|
||||
RenderSpatialBucket.FromRaw(fullCellId),
|
||||
ownerLandblockId,
|
||||
fullCellId),
|
||||
new RenderWorldBounds(minimum, maximum),
|
||||
flags,
|
||||
default,
|
||||
id.ToSortKey(),
|
||||
RenderDirtyMask.All,
|
||||
new RenderSourceMetadata(
|
||||
entity.Id,
|
||||
entity.ServerGuid,
|
||||
entity.SourceGfxObjOrSetupId,
|
||||
entity.ParentCellId ?? 0,
|
||||
entity.EffectCellId ?? 0,
|
||||
entity.BuildingShellAnchorCellId ?? 0,
|
||||
fingerprint.Transform,
|
||||
fingerprint.Geometry,
|
||||
fingerprint.Appearance));
|
||||
}
|
||||
|
||||
private static (Vector3 Minimum, Vector3 Maximum) CalculateBounds(
|
||||
WorldEntity entity)
|
||||
{
|
||||
Vector3 position = entity.Position;
|
||||
if (entity.HasLocalBounds)
|
||||
{
|
||||
Vector3 localMin = entity.LocalBoundMin;
|
||||
Vector3 localMax = entity.LocalBoundMax;
|
||||
Vector3 minimum = default;
|
||||
Vector3 maximum = default;
|
||||
for (int cornerIndex = 0; cornerIndex < 8; cornerIndex++)
|
||||
{
|
||||
Vector3 corner = new(
|
||||
(cornerIndex & 1) == 0 ? localMin.X : localMax.X,
|
||||
(cornerIndex & 2) == 0 ? localMin.Y : localMax.Y,
|
||||
(cornerIndex & 4) == 0 ? localMin.Z : localMax.Z);
|
||||
Vector3 transformed =
|
||||
Vector3.Transform(corner, entity.Rotation);
|
||||
if (cornerIndex == 0)
|
||||
{
|
||||
minimum = transformed;
|
||||
maximum = transformed;
|
||||
}
|
||||
else
|
||||
{
|
||||
minimum = Vector3.Min(minimum, transformed);
|
||||
maximum = Vector3.Max(maximum, transformed);
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 margin = new(DefaultAabbRadius);
|
||||
return (
|
||||
position + minimum - margin,
|
||||
position + maximum + margin);
|
||||
}
|
||||
|
||||
float radius = DefaultAabbRadius;
|
||||
for (int i = 0; i < entity.MeshRefs.Count; i++)
|
||||
{
|
||||
radius = MathF.Max(
|
||||
radius,
|
||||
DefaultAabbRadius
|
||||
+ entity.MeshRefs[i].PartTransform.Translation.Length());
|
||||
}
|
||||
|
||||
Vector3 extent = new(radius);
|
||||
return (position - extent, position + extent);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue