Four fixes from the ACME StaticObjectManager cross-reference: 1. GfxObjMesh: normalize vertex normals (1d). Dat normals may not be unit-length; without normalization, lighting is wrong per-vertex. 2. SetupMesh: add third-fallback placement frame (2a). If neither Resting nor Default exists, use the first available frame from PlacementFrames. Matches ACME's GetDefaultPlacementFrame. 3. SceneryGenerator: building cell exclusion (4d). Compute which terrain vertices have buildings (from LandBlockInfo.Objects + Buildings), skip scenery spawns in those cells. Prevents trees from spawning inside building footprints. 4. SceneryGenerator: slope filter (4e). Compute terrain normal Z at each displaced position and check against ObjectDesc.MinSlope / MaxSlope bounds. Prevents trees from spawning on cliff faces. Also confirmed 4f (scenery Z=0) is NOT a bug — GameWindow's hydrator lifts scenery to terrain Z at line 1213. The Z=0 in SceneryGenerator is a placeholder correctly overridden at render time. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
74 lines
3.2 KiB
C#
74 lines
3.2 KiB
C#
using System.Numerics;
|
|
using AcDream.Core.World;
|
|
using DatReaderWriter.DBObjs;
|
|
using DatReaderWriter.Enums;
|
|
using DatReaderWriter.Types;
|
|
|
|
namespace AcDream.Core.Meshing;
|
|
|
|
public static class SetupMesh
|
|
{
|
|
/// <summary>
|
|
/// Flatten a Setup into a list of (GfxObjId, PartTransform) refs.
|
|
/// Uses the default placement frame and DefaultScale per part.
|
|
/// Does NOT walk ParentIndex — each part's transform is local to the setup root.
|
|
/// This is simplification for Phase 2; complex hierarchical rigs are Phase 3.
|
|
/// </summary>
|
|
public static IReadOnlyList<MeshRef> Flatten(Setup setup, AnimationFrame? motionFrameOverride = null)
|
|
{
|
|
// Pose source priority:
|
|
// 1. motionFrameOverride — caller has resolved an idle/animation
|
|
// frame from the entity's MotionTable (Phase 6). This is the
|
|
// best source for creatures and characters because their
|
|
// Setup.PlacementFrames don't define an upright idle pose.
|
|
// 2. Setup.PlacementFrames[Resting] — used by static objects
|
|
// that have a Resting frame defined (e.g. signs, doors).
|
|
// 3. Setup.PlacementFrames[Default] — fallback for everything
|
|
// else (most scenery), which is the only frame those setups
|
|
// define and renders correctly.
|
|
//
|
|
// Without an override, creatures used to render in their Default
|
|
// pose (T-pose-ish or aggressive crouch) because their MotionTable
|
|
// wasn't consulted. The Phase 6 MotionResolver provides the override
|
|
// by walking Setup.DefaultMotionTable → MotionTable.Cycles →
|
|
// Animation.PartFrames[LowFrame].
|
|
AnimationFrame? defaultAnim = motionFrameOverride;
|
|
if (defaultAnim is null && setup.PlacementFrames.TryGetValue(Placement.Resting, out var resting))
|
|
defaultAnim = resting;
|
|
if (defaultAnim is null && setup.PlacementFrames.TryGetValue(Placement.Default, out var af))
|
|
defaultAnim = af;
|
|
// Last resort: use the first available placement frame (matches ACME's
|
|
// StaticObjectManager.GetDefaultPlacementFrame third fallback). Handles
|
|
// rare Setups that define only an unusual placement frame key.
|
|
if (defaultAnim is null)
|
|
{
|
|
foreach (var kvp in setup.PlacementFrames)
|
|
{
|
|
defaultAnim = kvp.Value;
|
|
break;
|
|
}
|
|
}
|
|
|
|
var result = new List<MeshRef>(setup.Parts.Count);
|
|
for (int i = 0; i < setup.Parts.Count; i++)
|
|
{
|
|
uint gfxObjId = (uint)setup.Parts[i];
|
|
|
|
Frame frame;
|
|
if (defaultAnim is not null && i < defaultAnim.Frames.Count)
|
|
frame = defaultAnim.Frames[i];
|
|
else
|
|
frame = new Frame { Origin = Vector3.Zero, Orientation = Quaternion.Identity };
|
|
|
|
Vector3 scale = i < setup.DefaultScale.Count ? setup.DefaultScale[i] : Vector3.One;
|
|
|
|
var transform =
|
|
Matrix4x4.CreateScale(scale) *
|
|
Matrix4x4.CreateFromQuaternion(frame.Orientation) *
|
|
Matrix4x4.CreateTranslation(frame.Origin);
|
|
|
|
result.Add(new MeshRef(gfxObjId, transform));
|
|
}
|
|
return result;
|
|
}
|
|
}
|