From e3c36b5bf87ad9c7ea722e122c55f48c71aa294b Mon Sep 17 00:00:00 2001 From: Erik Date: Fri, 8 May 2026 07:53:04 +0200 Subject: [PATCH] =?UTF-8?q?revert:=20remove=20obj=5Fwithin=5Fblock=20?= =?UTF-8?q?=E2=80=94=20sorting=20sphere=20radii=20too=20large?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- src/AcDream.Core/World/SceneryGenerator.cs | 58 ---------------------- 1 file changed, 58 deletions(-) diff --git a/src/AcDream.Core/World/SceneryGenerator.cs b/src/AcDream.Core/World/SceneryGenerator.cs index d4ae229..a5dc0ce 100644 --- a/src/AcDream.Core/World/SceneryGenerator.cs +++ b/src/AcDream.Core/World/SceneryGenerator.cs @@ -59,7 +59,6 @@ public static class SceneryGenerator float[]? heightTable = null) { var result = new List(); - var sortingRadiusCache = new Dictionary(); if (region.TerrainInfo?.TerrainTypes is null || region.SceneInfo?.SceneTypes is null) return result; @@ -183,15 +182,6 @@ public static class SceneryGenerator 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 // the ground (e.g., flowers planted at -0.1m so they // don't float above grass). The renderer adds groundZ @@ -370,54 +360,6 @@ public static class SceneryGenerator private const int CellsPerSide = 8; - /// - /// 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]. - /// - 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; - } - - /// - /// 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). - /// - private static float GetSortingSphereRadius( - DatCollection dats, uint objectId, Dictionary 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(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; - } - /// /// 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