acdream/src/AcDream.Core/Lighting/LightSource.cs

85 lines
3.6 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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);