Author atmospheric enclosure scanning and ventilation sinks
verifiedAgainst 3629ae9 · verifiedOn 2026-09-11.
What you will build
Section titled “What you will build”In Voxamine, hazardous gases (such as carbon monoxide emitted by a charcoal furnace or sulfur dioxide roasted from chalcopyrite) do not merely vanish into thin air (MASTERPLAN.md §33.2, §33.4). If a player operates an unvented metallurgical apparatus inside a sealed room, toxic matter accumulates in that enclosed control volume, creating acute hazard exposure.
To establish an atmospheric control volume, the system must scan player-built voxel structures without freezing the game or performing unbounded flood fills into infinite skies.
You will implement the enclosure scanning and ventilation pipeline using AtmosphericEnclosureScanner.cs:
- Run a bounded six-connected air flood-fill within an
AtmosphericDomainBoundsenvelope and strict air-cell budget. - Identify airtight enclosures while cleanly rejecting breaches that reach exterior skies.
- Discover player-built ventilation blocks (
Stone Vent) and verify that their far side connects to outside air. - Convert closed scan counts into physical control volume geometry ($V = N \cdot V_{\text{cell}}$) and air-change rate $\lambda = Q/V$.
- Transfer evacuated gases to the persistent regional outdoor ledger via well-mixed NIST decay in
AtmosphericTransportMath.cs.
// Scan a 3x3x3 room around a furnace anchor:AtmosphericEnclosureScanResult result = AtmosphericEnclosureScanner.Scan( world, seedPosition: new WorldBlockPosition(2, 2, 2), scanEnvelope: envelope, maximumAirCells: 64, ventilationSource: new BlockCatalogAtmosphericVentilationSource(catalog));
if (result.Status == AtmosphericEnclosureScanStatus.Enclosed){ // Evaluates: 27 air cells * 1.0 m³ = 27 m³ room volume // Discovers: 1 Stone Vent with outdoor contact -> Q = 0.025 m³/s // Air changes per second: λ = Q / V = 0.025 / 27 ≈ 0.000926 s⁻¹ result.TryCreateGeometry( cubicMetersPerAirCell: 1.0d, temperatureKelvin: 293.15d, outdoorAirChangesPerSecond: result.OutdoorAirflowCubicMetersPerSecond / (result.AirCellCount * 1.0d), out AtmosphericDomainGeometry geometry, out string error);}Where this sits
Section titled “Where this sits”Enclosure scanning bridges player-built voxel blocks to the chemical atmospheric transport ledger:
- Voxel World (
ChunkStreamingRuntime&DictionaryBlockSource) provides authoritative voxel block states. - Enclosure Scanner (
AtmosphericEnclosureScanner) executes bounded flood-fill topology checks and exterior vent discovery. - Ventilation Resolver (
IAtmosphericVentilationSource&BlockDefinition) maps built blocks to cited volumetric airflow rates ($Q = 0.025\ \text{m}^3/\text{s}$). - Domain Registry (
AtmosphericDomainRegistry) binds the scanned spatial geometry to a conserved gas mixture domain. - Atmospheric Transport (
AtmosphericTransportMath) applies NIST well-mixed multizone exponential decay, moving vented moles to the regional outdoor ledger.
flowchart TD
subgraph VOXELS["Voxel Terrain & Player Construction"]
BLOCKS["Authoritative Voxel Blocks\n(Stone walls, Air cells, Stone Vents)"]
EVENT["BlockChanged Event\n(Player breaks or places block)"]
end
subgraph SCANNER["AtmosphericEnclosureScanner"]
SEED["Seed Coordinate\n(Vessel anchor or Room centre)"]
FLOOD["Bounded 6-Connected Flood Fill\n(Envelope bounds & Air-cell budget)"]
STATUS{"Status Check"}
ENCLOSED["Enclosed\n(Calculates N cells & SpatialBounds)"]
REJECT["Rejected\n(SeedIsNotAir, ReachedScanBoundary, BudgetExceeded)"]
VENT_CHECK["Vent Exterior Air Check\n(Far side touches outside air)"]
end
subgraph DOMAIN["Atmospheric Domain & Physics"]
GEOM["AtmosphericDomainGeometry\n(Volume V = N · Vcell, λ = Q / V)"]
REGISTRY["AtmosphericDomainRegistry\n(Active room gas ledger)"]
TRANSPORT["AtmosphericTransportMath\n(Well-mixed decay C = C0 · exp(-λt))"]
REGIONAL["Persistent Regional Ledger\n(Outdoor atmosphere sink)"]
end
BLOCKS --> SEED
EVENT --> SEED
SEED --> FLOOD
FLOOD --> STATUS
STATUS -->|Boundary reached / Budget exceeded| REJECT
STATUS -->|Fully enclosed| ENCLOSED
ENCLOSED --> VENT_CHECK
VENT_CHECK --> GEOM
GEOM --> REGISTRY
REGISTRY --> TRANSPORT
TRANSPORT --> REGIONAL
Before you start
Section titled “Before you start”Read Docs/MASTERPLAN.md §33.2 (“Atmospheric domains”), §33.4 (“Enclosure scanning”), and Docs/CHEMISTRY_SOURCES.md §C3.6, then inspect:
AtmosphericEnclosureScanner.cs, the bounded flood fill algorithm.AtmosphericVentilationSource.cs, the block catalogue ventilation adapter.AtmosphericVentilationInspection.cs, the in-world construction inspection readouts.AtmosphericTransportMath.cs, the NIST multizone exponential decay calculations.
Keep the core scanner rules in mind:
| Scan Status | Trigger Condition | Resulting Action |
|---|---|---|
Enclosed |
All six-connected paths terminate at solid blocks or verified vents within budget. | Room geometry created; spatial domain configured in registry. |
SeedIsNotAir |
Starting coordinate is a solid block. | Instant refusal with $0$ air cells; no flood fill executed. |
ReachedScanBoundary |
Air path touches the outer edge of scanEnvelope (open window, missing roof). |
Rejected; room is open to outdoors, no indoor domain created. |
AirCellBudgetExceeded |
Room volume exceeds maximumAirCells. |
Scanner halts immediately to prevent CPU starvation on massive caves. |
The build, step by step
Section titled “The build, step by step”1. The Bounded Flood-Fill State Machine
Section titled “1. The Bounded Flood-Fill State Machine”In AtmosphericEnclosureScanner.cs, define the explicit scan termination states:
public enum AtmosphericEnclosureScanStatus{ Enclosed, SeedIsNotAir, ReachedScanBoundary, AirCellBudgetExceeded,}The scanner uses a breadth-first queue with a visited coordinate set. It rejects infinite loops by asserting two hard bounds:
scanEnvelope: A bounding box in block coordinates. Touching the outer shell immediately signalsReachedScanBoundary.maximumAirCells: A maximum allowed count (e.g. 64 or 512 cells). Exceeding it yieldsAirCellBudgetExceeded.
2. Discovering Built Ventilation Blocks
Section titled “2. Discovering Built Ventilation Blocks”During the flood-fill step, whenever a boundary voxel is solid, the scanner queries IAtmosphericVentilationSource:
if (ventilationSource != null && ventilationSource.TryGetOutdoorAirflow(boundaryBlock, out double airflow)){ // A vent only functions if its FAR SIDE touches exterior air! WorldBlockPosition exteriorCandidate = GetOppositeFaceCell(currentAirCell, boundaryPosition); if (TouchesExteriorAir(world, exteriorCandidate, scanEnvelope)) { totalOutdoorAirflow += airflow; }}This prevents a player from placing a decorative Stone Vent between two sealed indoor rooms and erroneously claiming outdoor ventilation. A vent must reach the outdoors.
3. Converting Scanned Topology to Physical Geometry
Section titled “3. Converting Scanned Topology to Physical Geometry”In AtmosphericEnclosureScanResult.TryCreateGeometry, translate voxel counts into continuous SI units:
public bool TryCreateGeometry( double cubicMetersPerAirCell, double temperatureKelvin, double outdoorAirChangesPerSecond, out AtmosphericDomainGeometry geometry, out string error){ if (Status != AtmosphericEnclosureScanStatus.Enclosed || !SpatialBounds.HasValue) { geometry = default; error = "Only a closed enclosure scan can create atmospheric control-volume geometry."; return false; }
geometry = new AtmosphericDomainGeometry( volumeCubicMeters: AirCellCount * cubicMetersPerAirCell, temperatureKelvin: temperatureKelvin, outdoorAirChangesPerSecond: outdoorAirChangesPerSecond); error = null; return true;}Block scale ($1.0\ \text{m}^3$ per cell) and ambient reference temperature are supplied via AtmosphericEnvironmentProfile.cs, based on the NASA standard tropospheric temperature model.
4. Well-Mixed Multizone Decay Transport
Section titled “4. Well-Mixed Multizone Decay Transport”In AtmosphericTransportMath.AdvanceVentilation, simulate well-mixed air change:
$$\lambda = \frac{Q_{\text{outdoor}}}{V_{\text{room}}},\quad C(t) = C_0 e^{-\lambda \Delta t}$$
Evacuated moles are never deleted. Every mole of gas leaving the enclosure domain is transferred directly into the persistent regional outdoor ledger.
5. In-World Construction Diagnostic Readouts
Section titled “5. In-World Construction Diagnostic Readouts”AtmosphericVentilationInspection.GetConstructionReadout provides contextual HUD feedback when looking at a built Stone Vent:
- Outdoor air path required: If the vent’s far side is blocked by solid stone.
- Room air exchange active: If the vent successfully connects an enclosed room domain to exterior air.
Verify
Section titled “Verify”1. Execute EditMode Tests Headlessly
Section titled “1. Execute EditMode Tests Headlessly”Run the dedicated enclosure scanner and ventilation test suites:
/opt/unity/Editor/Unity -batchmode -nographics \ -projectPath /home/soulwax/workspace/engines/unity/minecraft/Minecraft-HD \ -runTests -testPlatform EditMode \ -testFilter VoxelSandbox.Tests.AtmosphericEnclosureScannerTests/opt/unity/Editor/Unity -batchmode -nographics \ -projectPath /home/soulwax/workspace/engines/unity/minecraft/Minecraft-HD \ -runTests -testPlatform EditMode \ -testFilter VoxelSandbox.Tests.AtmosphericVentilationInspectionTestsVerify that all key assertions pass:
ClosedRoom_CountsAirAndBuildsBoundsAndExplicitGeometry: verifies that a $3 \times 3 \times 3$ air cavity enclosed by solid stone returnsStatus == Enclosedand exactly $27$ air cells.OpenRoom_ReachingTheFiniteScanBoundaryIsRejectedRatherThanClaimedAsEnclosed: verifies that missing wall blocks immediately reject the room withReachedScanBoundary.AirCellBudget_StopsLargeScansBeforeTheyCanBecomeAnUnboundedHotPath: confirms the scanner halts cleanly at the budget limit without allocating excess memory.SolidSeed_IsRejectedWithoutScanningAnyAir: verifies that seeding inside a wall immediately aborts with $0$ cells.Readout_RequiresAnExteriorConfiguredRoomRatherThanGasOrAContentTag: validates the contextual inspection text.
Now do your own
Section titled “Now do your own”To create a new high-throughput ventilation block (e.g. an Iron Louvred Exhaust Grate or Powered Duct Fan):
- Define Block: Add
IronExhaustGratetoBlockDefinition. - Set Flow Rate: Call
SetOutdoorVentilation(0.125d, "industrial fan")in the block definition ($5\times$ higher airflow than a stone vent). - Configure Scanning: Pass the block catalog to
BlockCatalogAtmosphericVentilationSource. - Test Dilution: Build an enclosure with the new grate and verify that toxic furnace exhaust clears $5\times$ faster!
Pitfalls
Section titled “Pitfalls”| Symptom | Cause | Fix |
|---|---|---|
| Game freezes or hitches when opening a door to the outside. | Scanner performed an unbounded flood fill into the open world. | Always specify a finite AtmosphericDomainBounds envelope and maximumAirCells budget. |
| Placing a vent between two indoor rooms ventilates the room. | Vent was counted without verifying exterior outdoor air contact. | Ensure the scanner checks TouchesExteriorAir on the far side of the vent block. |
| Vented carbon monoxide disappears from world persistence. | Subtracted gas from the room without adding it to the regional ledger. | Always transfer evacuated moles to the regional outdoor ledger via AtmosphericTransportMath. |
| Room is reported as open even though all walls are solid. | scanEnvelope was authored too small, intersecting the inner air space. |
Ensure scanEnvelope encompasses the room’s entire bounding box including wall thickness. |
| Air change rate $\lambda$ is zero despite active vents. | Passed $0$ or negative volume to TryCreateGeometry. |
Calculate $\lambda = Q / (N_{\text{cells}} \cdot V_{\text{cell}})$ with verified positive cell volume. |
User-contributed notes
Corrections, clarifications, and practical tips for this page. Anonymous is fine — a name is optional. Basic Markdown works:
**bold**,*italic*,`code`, and links.Notes policy
Notes are lightly filtered for spam and may be edited or removed. Keep them about this page — no support requests, no personal data, nothing you would not publish. Links are limited and marked
nofollow.