Move camera/root resolution, live settings and listener preview, sky/lighting/fog preparation, animated classification, and building visibility scratch behind a typed WorldRenderFrameBuilder while preserving retail frame order and borrowed lifetimes. Co-authored-by: Codex <codex@openai.com>
135 lines
4.6 KiB
C#
135 lines
4.6 KiB
C#
using System.Numerics;
|
|
using AcDream.App.Rendering.Vfx;
|
|
using AcDream.Core.Vfx;
|
|
using AcDream.Core.World;
|
|
|
|
namespace AcDream.App.Rendering;
|
|
|
|
/// <summary>
|
|
/// Owns the optional DAT-archaeology sky-PES experiment. Named retail shows
|
|
/// GameSky does not consume SkyObject.PesObjectId, so production invokes this
|
|
/// owner only when the explicit startup diagnostic is enabled.
|
|
/// </summary>
|
|
internal sealed class SkyPesFrameController
|
|
{
|
|
private readonly record struct SkyPesKey(
|
|
int ObjectIndex,
|
|
uint PesObjectId,
|
|
bool PostScene);
|
|
|
|
private readonly PhysicsScriptRunner _scripts;
|
|
private readonly ParticleHookSink _particles;
|
|
private readonly EntityEffectPoseRegistry _poses;
|
|
private readonly EntityEffectController? _effects;
|
|
private readonly HashSet<SkyPesKey> _active = [];
|
|
private readonly HashSet<SkyPesKey> _missing = [];
|
|
|
|
public SkyPesFrameController(
|
|
PhysicsScriptRunner scripts,
|
|
ParticleHookSink particles,
|
|
EntityEffectPoseRegistry poses,
|
|
EntityEffectController? effects)
|
|
{
|
|
_scripts = scripts ?? throw new ArgumentNullException(nameof(scripts));
|
|
_particles = particles ?? throw new ArgumentNullException(nameof(particles));
|
|
_poses = poses ?? throw new ArgumentNullException(nameof(poses));
|
|
_effects = effects;
|
|
}
|
|
|
|
public void Update(
|
|
float dayFraction,
|
|
DayGroupData? dayGroup,
|
|
Vector3 cameraWorldPosition,
|
|
bool suppressSky)
|
|
{
|
|
var seen = new HashSet<SkyPesKey>();
|
|
if (!suppressSky && dayGroup is not null)
|
|
{
|
|
for (int index = 0; index < dayGroup.SkyObjects.Count; index++)
|
|
{
|
|
SkyObjectData skyObject = dayGroup.SkyObjects[index];
|
|
if (skyObject.PesObjectId == 0 || !skyObject.IsVisible(dayFraction))
|
|
continue;
|
|
|
|
var key = new SkyPesKey(
|
|
index,
|
|
skyObject.PesObjectId,
|
|
skyObject.IsPostScene);
|
|
seen.Add(key);
|
|
uint ownerId = EntityId(key);
|
|
ParticleRenderPass renderPass = skyObject.IsPostScene
|
|
? ParticleRenderPass.SkyPostScene
|
|
: ParticleRenderPass.SkyPreScene;
|
|
_particles.SetEntityRenderPass(ownerId, renderPass);
|
|
Vector3 anchor = Anchor(skyObject, cameraWorldPosition);
|
|
Quaternion rotation = Rotation(skyObject, dayFraction);
|
|
_poses.Publish(
|
|
ownerId,
|
|
Matrix4x4.CreateFromQuaternion(rotation)
|
|
* Matrix4x4.CreateTranslation(anchor),
|
|
Array.Empty<Matrix4x4>(),
|
|
cellId: 0u);
|
|
|
|
if (_active.Contains(key) || _missing.Contains(key))
|
|
continue;
|
|
|
|
_effects?.RegisterSyntheticOwner(ownerId);
|
|
if (_scripts.Play(skyObject.PesObjectId, ownerId, anchor))
|
|
{
|
|
_active.Add(key);
|
|
}
|
|
else
|
|
{
|
|
_missing.Add(key);
|
|
_effects?.UnregisterSyntheticOwner(ownerId);
|
|
_particles.ClearEntityRenderPass(ownerId);
|
|
_poses.Remove(ownerId);
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach (SkyPesKey key in _active.ToArray())
|
|
{
|
|
if (seen.Contains(key))
|
|
continue;
|
|
|
|
uint ownerId = EntityId(key);
|
|
_scripts.StopAllForEntity(ownerId);
|
|
_effects?.UnregisterSyntheticOwner(ownerId);
|
|
_particles.StopAllForEntity(ownerId, fadeOut: true);
|
|
_poses.Remove(ownerId);
|
|
_active.Remove(key);
|
|
}
|
|
|
|
foreach (SkyPesKey key in _missing.ToArray())
|
|
{
|
|
if (!seen.Contains(key))
|
|
_missing.Remove(key);
|
|
}
|
|
}
|
|
|
|
private static uint EntityId(SkyPesKey key)
|
|
{
|
|
uint postScene = key.PostScene ? 0x08000000u : 0u;
|
|
return 0xF0000000u
|
|
| postScene
|
|
| ((uint)key.ObjectIndex & 0x07FFFFFFu);
|
|
}
|
|
|
|
private static Vector3 Anchor(
|
|
SkyObjectData skyObject,
|
|
Vector3 cameraWorldPosition)
|
|
{
|
|
if (skyObject.IsWeather && (skyObject.Properties & 0x08u) == 0u)
|
|
return cameraWorldPosition + new Vector3(0f, 0f, -120f);
|
|
return cameraWorldPosition;
|
|
}
|
|
|
|
private static Quaternion Rotation(
|
|
SkyObjectData skyObject,
|
|
float dayFraction)
|
|
{
|
|
float radians = skyObject.CurrentAngle(dayFraction) * (MathF.PI / 180f);
|
|
return Quaternion.CreateFromAxisAngle(Vector3.UnitY, -radians);
|
|
}
|
|
}
|