feat(shared): collapse LOD occupancy by majority fill over 2x2x2 groups #5
No reviewers
Labels
No labels
Compat/Breaking
Kind/Bug
Kind/Documentation
Kind/Enhancement
Kind/Feature
Kind/Security
Kind/Testing
Priority
Critical
Priority
High
Priority
Low
Priority
Medium
Reviewed
Confirmed
Reviewed
Duplicate
Reviewed
Invalid
Reviewed
Won't Fix
scope/assets
scope/client
scope/networking
scope/renderer
scope/scripting
scope/server
scope/shared
scope/workspace
Status
Abandoned
Status
Blocked
Status
Need More Info
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Synvael/synvael!5
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/lod-occupancy-collapse"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What does this PR do?
I added
crates/shared/src/world/lod.rs, a new private module re-exported fromcrates/shared/src/world.rsasCOLLAPSE_CHILDREN,OccupancyGrid, andcollapse_occupancy.collapse_occupancytakes the eight solid-or-empty flags of a 2x2x2 group and returns the occupancy of their parent cell. It counts the solid children and returns true at four or more, so the halfway threshold is inclusive. It isconst fnand uses an index loop rather than an iterator because iterator adapters are not usable in const context.OccupancyGridis the cubic grid those flags live in: asizeextent and aBox<[bool]>ofsize * size * sizecells.indexlays them out x-major, y-minor, matching the orderChunkalready stores voxels in, so a grid built from a chunk needs no transposition.newallocates an empty grid,from_cellswraps an existingVec<bool>and returnsNonewhen the length does not match the extent, andget/setaddress single cells.OccupancyGrid::collapseproduces the next coarser tier. It returnsNonewhen the extent is zero or odd, since neither has a whole 2x2x2 partition, and otherwise walks every parent cell, gathers its eight children by treating the loop counter's three low bits as the x, y, and z offsets within the group, and writescollapse_occupancyof them into the coarser grid.crates/shared/src/tests/world_lod.rsholds eight tests. The first is exhaustive over all 256 child arrangements, which pins the rule to the solid count and proves the outcome cannot depend on which particular children are solid.Why is this change necessary?
The LOD ladder collapses a 2x2x2 group of tier-N cells into one tier-N+1 cell at every step, giving 8x the volume per step to match the distance-doubling tiers. Deciding whether that parent cell is solid is the first thing a step has to do, and nothing in the workspace did it yet, so the ladder had no data layer to stand on. This is the occupancy decision on its own, as a pure function over flags, with no chunk or worldgen coupling.
Two constraints drove the shape of it. Averaging occupancy into a fraction and thresholding late is what produces the blobby, muddy look distant-LOD systems are known for, so the collapse yields a flag and the tiers stay binary. The test also has to run against the immediate children at each step rather than against the original 0.5 m voxels, which is what lets a thin feature that survives one step stay eligible to survive the next. Both are structural, and unwinding either once the mesher and the streaming path consume this output would be expensive.
Scope of Changes
shared
Testing
Automated. Everything below was run on this branch with a clean working tree:
cargo check -p shared: passed.cargo test -p shared: 226 passed, 0 failed, including the eight newworld::lod::tests.cargo clippy --all-targets --all-features -- -D warnings: passed across all six crates.cargo fmt --all -- --check: passed.No Lua changed on this branch, so
seleneandstyluawere not run.Manual. There was no in-game verification. Nothing on this branch reaches the renderer or the network path, so there is nothing to observe at runtime yet. The exhaustive 256-arrangement test is the substitute, and it covers the rule's whole input domain.
Additional Context
The intended rule has a second half that is not implemented here, and I want that called out rather than discovered in review. A parent is meant to be solid if either any child on the parent's outward-facing boundary shell is solid (the silhouette rule, which biases toward keeping thin features like ridge lips and cliff edges) or child occupancy reaches 50% (majority fill, which handles bulk volume). Only the majority fill half is here.
The silhouette half does not resolve at this scale. It splits children into a boundary shell and an interior, and in a 2x2x2 group all eight children are corners, so the shell is the entire group and the interior is empty. Taken literally it would make any group with a single solid child solid, which fattens isolated specks into floating blobs and is the failure the OR of the two rules exists to avoid. Either the shell formulation assumes a larger collapse factor, or it needs restating in terms of the parent's own neighbours instead of its children. That is a design question rather than an implementation one, so I left it open instead of picking an interpretation in code. The module doc records the omission.
collapse_occupancyisconst fn, which makes the threshold checkable at compile time by later code that wants to specialise on it.The 50% threshold is a starting point and will need tuning against a profile once there is something to look at. It lives in one private constant,
MAJORITY_FILL_THRESHOLD, so moving it is a one-line change.Related Issues
No response
Checklist
dev(or a feature branch offdev) and my PR targetsdev.///doc comments for Rust) where necessary.