revert: remove obj_within_block — sorting sphere radii too large
The obj_within_block check using Setup.SortingSphere.Radius rejects far too many spawns. Sorting spheres for trees are 5-10m, creating a wide exclusion zone around every landblock edge. WorldBuilder produces correct scenery with just bounds+road+building+slope checks and no bounding sphere check. Revert to match WorldBuilder's approach. The single extra tree near the road at vtx=(4,8) in 0xA9B1 remains as a known minor discrepancy from retail — root cause TBD. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
e8aa1c82f4
commit
e3c36b5bf8
1 changed files with 0 additions and 58 deletions
|
|
@ -59,7 +59,6 @@ public static class SceneryGenerator
|
||||||
float[]? heightTable = null)
|
float[]? heightTable = null)
|
||||||
{
|
{
|
||||||
var result = new List<ScenerySpawn>();
|
var result = new List<ScenerySpawn>();
|
||||||
var sortingRadiusCache = new Dictionary<uint, float>();
|
|
||||||
|
|
||||||
if (region.TerrainInfo?.TerrainTypes is null || region.SceneInfo?.SceneTypes is null)
|
if (region.TerrainInfo?.TerrainTypes is null || region.SceneInfo?.SceneTypes is null)
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -183,15 +182,6 @@ public static class SceneryGenerator
|
||||||
if (nz < obj.MinSlope || nz > obj.MaxSlope) continue;
|
if (nz < obj.MinSlope || nz > obj.MaxSlope) continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retail obj_within_block check: the model's sorting sphere must
|
|
||||||
// fit entirely within the landblock bounds. This rejects edge-vertex
|
|
||||||
// spawns whose bounding sphere would extend past the boundary.
|
|
||||||
// Named-retail: CPhysicsObj::obj_within_block (0x00461c30), called
|
|
||||||
// inside CLandBlock::get_land_scenes after find_terrain_poly/CheckSlope.
|
|
||||||
float sortRadius = GetSortingSphereRadius(dats, obj.ObjectId, sortingRadiusCache);
|
|
||||||
if (!IsWithinBlock(lx, ly, sortRadius))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// BaseLoc.Z offset: scenery-specific vertical offset from
|
// BaseLoc.Z offset: scenery-specific vertical offset from
|
||||||
// the ground (e.g., flowers planted at -0.1m so they
|
// the ground (e.g., flowers planted at -0.1m so they
|
||||||
// don't float above grass). The renderer adds groundZ
|
// don't float above grass). The renderer adds groundZ
|
||||||
|
|
@ -370,54 +360,6 @@ public static class SceneryGenerator
|
||||||
|
|
||||||
private const int CellsPerSide = 8;
|
private const int CellsPerSide = 8;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Retail CPhysicsObj::obj_within_block check — verifies the object's sorting
|
|
||||||
/// sphere stays entirely within the landblock bounds. Returns true if the object
|
|
||||||
/// is within bounds.
|
|
||||||
///
|
|
||||||
/// Named-retail: CPhysicsObj::obj_within_block (0x00461c30).
|
|
||||||
/// ACViewer: PhysicsObj.obj_within_block.
|
|
||||||
///
|
|
||||||
/// Retail loads the full PhysicsObj, transforms the sorting sphere center via
|
|
||||||
/// LocalToGlobal, then checks center ± radius against [0, BlockLength].
|
|
||||||
/// We approximate by ignoring the sorting sphere center offset (typically small
|
|
||||||
/// for scenery) and checking the spawn position directly against [radius, 192-radius].
|
|
||||||
/// </summary>
|
|
||||||
private static bool IsWithinBlock(float lx, float ly, float sortingSphereRadius)
|
|
||||||
{
|
|
||||||
if (sortingSphereRadius <= 0f) return true;
|
|
||||||
return lx >= sortingSphereRadius && ly >= sortingSphereRadius
|
|
||||||
&& lx < LandblockSize - sortingSphereRadius
|
|
||||||
&& ly < LandblockSize - sortingSphereRadius;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the sorting sphere radius for a scenery object, with caching.
|
|
||||||
/// Setup objects (0x02xxxxxx) use Setup.SortingSphere.Radius.
|
|
||||||
/// GfxObj objects (0x01xxxxxx) fall back to 0 (allows placement; these are
|
|
||||||
/// typically small items like grass patches whose BSP sphere is tiny).
|
|
||||||
/// </summary>
|
|
||||||
private static float GetSortingSphereRadius(
|
|
||||||
DatCollection dats, uint objectId, Dictionary<uint, float> cache)
|
|
||||||
{
|
|
||||||
if (cache.TryGetValue(objectId, out float cached))
|
|
||||||
return cached;
|
|
||||||
|
|
||||||
float radius = 0f;
|
|
||||||
if ((objectId >> 24) == 0x02)
|
|
||||||
{
|
|
||||||
// Setup — has an explicit SortingSphere
|
|
||||||
var setup = dats.Get<Setup>(objectId);
|
|
||||||
if (setup?.SortingSphere is { } sphere)
|
|
||||||
radius = sphere.Radius;
|
|
||||||
}
|
|
||||||
// GfxObj (0x01) — no Setup.SortingSphere; use 0 (permissive).
|
|
||||||
// These are typically small single-mesh scenery items.
|
|
||||||
|
|
||||||
cache[objectId] = radius;
|
|
||||||
return radius;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Pseudo-random displacement within a cell for a scenery object. Returns a
|
/// Pseudo-random displacement within a cell for a scenery object. Returns a
|
||||||
/// Vector3 in local cell-offset space (the caller adds it to the cell corner
|
/// Vector3 in local cell-offset space (the caller adds it to the cell corner
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue