using System.Collections.Generic;
using System.Numerics;
using DatReaderWriter.DBObjs;
using DatReaderWriter.Types;
namespace AcDream.Core.Lighting;
///
/// Converts a 's Lights dictionary (dat-level
/// records) into runtime
/// instances the can consume.
///
///
/// Retail fields (r13 §1):
///
/// - ViewSpaceLocation: local Frame relative to the owning part.
/// - Color: packed ARGB. Alpha is ignored; channels go through /255.
/// - Intensity: multiplies color for final diffuse.
/// - Falloff: world metres — acts as the hard cutoff.
/// - ConeAngle: radians; 0 = point, >0 = spot cone.
///
///
///
public static class LightInfoLoader
{
///
/// Extract all lights from a Setup, positioned in the entity's
/// world frame (via +
/// ). The dat's per-light Frame is
/// treated as a local offset relative to the entity root; acdream
/// doesn't yet transform through the animated part chain (retail's
/// hand-held torches), so held lights render at the entity root
/// until the animation hook layer handles per-part placement.
///
public static IReadOnlyList Load(
Setup setup,
uint ownerId,
Vector3 entityPosition,
Quaternion entityRotation)
{
var results = new List();
if (setup?.Lights is null || setup.Lights.Count == 0) return results;
foreach (var kvp in setup.Lights)
{
var info = kvp.Value;
if (info is null) continue;
// Local Frame offset into world space.
Vector3 localOffset = Vector3.Zero;
Quaternion localRot = Quaternion.Identity;
if (info.ViewSpaceLocation is not null)
{
localOffset = new Vector3(
info.ViewSpaceLocation.Origin.X,
info.ViewSpaceLocation.Origin.Y,
info.ViewSpaceLocation.Origin.Z);
localRot = new Quaternion(
info.ViewSpaceLocation.Orientation.X,
info.ViewSpaceLocation.Orientation.Y,
info.ViewSpaceLocation.Orientation.Z,
info.ViewSpaceLocation.Orientation.W);
}
// Transform local offset into world space via the entity's
// rotation + translation. No per-part chain yet — held
// torches track the entity's root for now.
Vector3 worldPos = entityPosition + Vector3.Transform(localOffset, entityRotation);
Quaternion worldRot = entityRotation * localRot;
Vector3 forward = Vector3.Transform(Vector3.UnitY, worldRot);
var light = new LightSource
{
Kind = info.ConeAngle > 0f ? LightKind.Spot : LightKind.Point,
WorldPosition = worldPos,
WorldForward = forward,
ColorLinear = new Vector3(
(info.Color?.Red ?? 255) / 255f,
(info.Color?.Green ?? 255) / 255f,
(info.Color?.Blue ?? 255) / 255f),
Intensity = info.Intensity,
Range = info.Falloff,
ConeAngle = info.ConeAngle,
OwnerId = ownerId,
IsLit = true,
};
results.Add(light);
}
return results;
}
}