85 lines
3.6 KiB
C#
85 lines
3.6 KiB
C#
using System;
|
||
using System.Numerics;
|
||
|
||
namespace AcDream.Core.Lighting;
|
||
|
||
/// <summary>
|
||
/// Retail light-source kinds (r13 §12.1).
|
||
/// </summary>
|
||
public enum LightKind
|
||
{
|
||
Directional = 0, // sun, moon — no position, infinite range
|
||
Point = 1, // torch, fireplace, spell aura
|
||
Spot = 2, // cone-shaped (rare in AC, used for a few specific lamps)
|
||
}
|
||
|
||
/// <summary>
|
||
/// Per-frame light record. Used by <see cref="LightManager"/> and fed to
|
||
/// the shader UBO on every draw call.
|
||
///
|
||
/// <para>
|
||
/// Retail semantics (r13 §10.2):
|
||
/// <list type="bullet">
|
||
/// <item><description>
|
||
/// Hard cutoff at <see cref="Range"/> — no smoothstep, no distance
|
||
/// attenuation inside the range. "Crisp bubble of illumination."
|
||
/// </description></item>
|
||
/// <item><description>
|
||
/// Max 8 active lights (<see cref="LightManager.MaxActiveLights"/>),
|
||
/// ranked by distance-to-viewer. Slot 0 is reserved for the sun.
|
||
/// </description></item>
|
||
/// <item><description>
|
||
/// <see cref="IsLit"/> latches the <c>SetLightHook</c> value so
|
||
/// animations can toggle a light on/off (torch being lit, light
|
||
/// crystal activating).
|
||
/// </description></item>
|
||
/// </list>
|
||
/// </para>
|
||
/// </summary>
|
||
public sealed class LightSource
|
||
{
|
||
public LightKind Kind;
|
||
public Vector3 WorldPosition;
|
||
public Vector3 WorldForward; // for Spot/Directional
|
||
public Vector3 ColorLinear = Vector3.One; // R,G,B in [0,1], pre-brightness
|
||
public float Intensity = 1f;
|
||
public float Range = 10f; // metres, hard cutoff
|
||
public float ConeAngle = 0f; // radians, Spot only
|
||
public uint OwnerId; // attached entity id; 0 = world-global
|
||
public uint CellId; // owning cell id (0xLLLLNNNN); 0 = cell-less/global (viewer fill, sun).
|
||
// Retail carries this on the RenderLight (insert_light arg6, +0x6c) for the
|
||
// cross-cell block-offset distance math. #176 correction (2026-07-06): it is
|
||
// NOT a pool filter — retail collects from ALL resident cells
|
||
// (CEnvCell::visible_cell_table = the loaded-cell registry, not the flood);
|
||
// acdream keeps the tag for probes ([indoor-light]/[seam-*]) + future parity.
|
||
public bool IsLit = true; // SetLightHook latch
|
||
public bool IsDynamic; // #143: true = D3D hardware path (1/d att, range×1.5);
|
||
// false = static dat-baked bake (1/d³, range×1.3)
|
||
/// <summary>
|
||
/// True when this light belongs to a live physics object whose root can
|
||
/// move. DAT-static lights keep their hydration-time world frame and are
|
||
/// deliberately excluded from the per-frame pose refresh.
|
||
/// </summary>
|
||
public bool TracksOwnerPose;
|
||
|
||
/// <summary>
|
||
/// Setup-local light frame retained from <c>LIGHTINFO::view_space_location</c>.
|
||
/// Object-borne lights compose it with their owner's current root each
|
||
/// frame, matching <c>CPartArray::SetFrame</c> (<c>0x00519310</c>) calling
|
||
/// <c>LIGHTLIST::set_frame</c> (<c>0x00517C60</c>).
|
||
/// </summary>
|
||
public Matrix4x4 LocalPose = Matrix4x4.Identity;
|
||
|
||
// Cached each frame by LightManager.
|
||
public float DistSq;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Per-cell ambient + sun state. For outdoor cells this comes from the
|
||
/// R12 sky state; for indoor cells the EnvCell dat carries a per-cell
|
||
/// ambient override (r13 §3).
|
||
/// </summary>
|
||
public readonly record struct CellAmbientState(
|
||
Vector3 AmbientColor,
|
||
Vector3 SunColor,
|
||
Vector3 SunDirection);
|