Post

CellGen - Procedural Dungeon Visibility System

A Unity system for efficient room-based visibility culling using spatial hashing and PVS baking.

CellGen - Procedural Dungeon Visibility System

CellGen is a procedural dungeon generation and visibility system I built for Unity. It’s designed to efficiently cull large interconnected room-based environments.

CellGen Demo

Core Components

Room Generator

The RoomGenerator is the heart of the procedural generation. It uses a socket-based approach:

  1. Start with an initial socket (spawn point)
  2. Pick a random open socket and random room prefab
  3. Check if the room would overlap existing geometry using Physics.CheckBox
  4. If no overlap, spawn the room and register it with the spatial hash
  5. Add the new room’s door sockets to the open socket pool
  6. Repeat until desired room count is reached
1
2
3
4
5
6
7
bool overlapping = Physics.CheckBox(
    targetSocket.position + localOffset,
    roomSize * 0.5f,
    targetSocket.rotation,
    roomLayer,
    QueryTriggerInteraction.Collide
);

The generator runs as a coroutine with frame budget management; if generation takes too long, it yields to prevent frame drops.

Spatial Hashing

The SpatialHash component provides O(1) lookups for rooms at any world position. Rooms register themselves by their bounds, and the hash supports radius-based neighbor queries for finding nearby rooms.

1
2
// Query rooms at player position
List<Room> nearbyRooms = spatialHash.QueryNearbyRooms(player.position, radius: 2);

Room Culling Manager

The RoomCullingManager tracks which room the player is currently inside and propagates visibility through connected neighbors. It only triggers updates when the player actually leaves the current room’s bounds, making it very efficient.

Key features:

  • Configurable neighbor propagation depth
  • Distance-based culling fallback
  • Efficient bounds-based room detection

PVS Baking (AutomagicGridPVS)

The most complex part is the Potentially Visible Set baking system. It pre-computes visibility between grid cells by shooting rays and checking for occluder intersections.

Two bake methods:

  • Custom Ray-Mesh: Uses Unity’s Job System + Burst for CPU raycasting against mesh triangles directly
  • Physics Raycast: Uses batched RaycastCommand for GPU-accelerated physics raycasting

At runtime, the system enables/disables renderers based on which cells can see each other from the player’s current position.

How It Works

  1. Bake time: The world is divided into a 3D grid. Each cell is validated (not inside solid geometry). Then rays are cast between all valid cell pairs to determine visibility.

  2. Runtime: When the player moves to a new cell, the system looks up that cell’s pre-computed visible cells and enables only those renderers.

This is similar to how Quake’s PVS worked, but adapted for Unity’s renderer system.

Use Cases

  • Procedural dungeons with many rooms
  • Large interior environments
  • Any scenario where you need efficient occlusion culling without Unity’s built-in solution

The code is designed to be dropped into existing projects with minimal setup; just assign the player reference and configure the layer masks.