feat(render): portal-based EnvCell visibility (Step 4)
Port ACME's EnvCellManager portal visibility system: - New CellVisibility class: BFS portal traversal from camera cell, portal-side clip-plane test, FindCameraCell with grace period - LoadedCell data populated during streaming (portals, clip planes, world/inverse transforms, local AABB from CellStruct vertices) - WorldEntity.ParentCellId tags interior entities for filtering - InstancedMeshRenderer.Draw accepts optional visibleCellIds set — interior entities whose parent cell isn't visible are skipped - Conditional depth clear between terrain and static mesh when camera is inside a cell (ACME GameScene.cs pattern) When camera is outdoors, all interiors render (visibleCellIds=null). When camera enters a building, only BFS-reachable cells render. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
25090b6fc9
commit
cffc3ee343
4 changed files with 589 additions and 16 deletions
|
|
@ -132,26 +132,21 @@ public sealed unsafe class InstancedMeshRenderer : IDisposable
|
|||
public void Draw(ICamera camera,
|
||||
IEnumerable<(uint LandblockId, Vector3 AabbMin, Vector3 AabbMax, IReadOnlyList<WorldEntity> Entities)> landblockEntries,
|
||||
FrustumPlanes? frustum = null,
|
||||
uint? neverCullLandblockId = null)
|
||||
uint? neverCullLandblockId = null,
|
||||
HashSet<uint>? visibleCellIds = null)
|
||||
{
|
||||
_shader.Use();
|
||||
|
||||
// Compute combined view-projection once. System.Numerics uses row-major
|
||||
// convention; multiplying View * Projection gives the correct combined
|
||||
// matrix that maps world → clip space when applied as M*v in the shader.
|
||||
var vp = camera.View * camera.Projection;
|
||||
_shader.SetMatrix4("uViewProjection", vp);
|
||||
|
||||
// Lighting uniforms matching ACME StaticObject.vert:
|
||||
// LightingFactor = max(dot(Normal, -uLightDirection), 0.0) + uAmbientIntensity
|
||||
// LightDirection (0.5, 0.3, -0.3) from ACME GameScene.cs:238.
|
||||
// AmbientLightIntensity 0.45 from ACME LandscapeEditorSettings.cs:108.
|
||||
// Lighting uniforms matching ACME StaticObject.vert.
|
||||
var lightDir = Vector3.Normalize(new Vector3(0.5f, 0.3f, -0.3f));
|
||||
_shader.SetVec3("uLightDirection", lightDir);
|
||||
_shader.SetFloat("uAmbientIntensity", 0.45f);
|
||||
|
||||
// ── Collect and group instances ───────────────────────────────────────
|
||||
CollectGroups(landblockEntries, frustum, neverCullLandblockId);
|
||||
CollectGroups(landblockEntries, frustum, neverCullLandblockId, visibleCellIds);
|
||||
|
||||
// ── Build and upload the instance buffer ──────────────────────────────
|
||||
// Count total instances.
|
||||
|
|
@ -327,7 +322,8 @@ public sealed unsafe class InstancedMeshRenderer : IDisposable
|
|||
private void CollectGroups(
|
||||
IEnumerable<(uint LandblockId, Vector3 AabbMin, Vector3 AabbMax, IReadOnlyList<WorldEntity> Entities)> landblockEntries,
|
||||
FrustumPlanes? frustum,
|
||||
uint? neverCullLandblockId)
|
||||
uint? neverCullLandblockId,
|
||||
HashSet<uint>? visibleCellIds)
|
||||
{
|
||||
foreach (var grp in _groups.Values)
|
||||
grp.Entries.Clear();
|
||||
|
|
@ -344,6 +340,13 @@ public sealed unsafe class InstancedMeshRenderer : IDisposable
|
|||
if (entity.MeshRefs.Count == 0)
|
||||
continue;
|
||||
|
||||
// Step 4: portal visibility filter. If we have a visible cell set,
|
||||
// skip interior entities whose parent cell isn't visible.
|
||||
// visibleCellIds == null means camera is outdoors → show all interiors.
|
||||
if (entity.ParentCellId.HasValue && visibleCellIds is not null
|
||||
&& !visibleCellIds.Contains(entity.ParentCellId.Value))
|
||||
continue;
|
||||
|
||||
var entityRoot =
|
||||
Matrix4x4.CreateFromQuaternion(entity.Rotation) *
|
||||
Matrix4x4.CreateTranslation(entity.Position);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue