Speed up uncovering blocks in hulls

The steps that can lead into or past a changed block are computed once per
hull instead of searching all directions and parts on every block update,
which makes an update about three times faster.
This commit is contained in:
2026-10-31 21:58:48 +01:00
parent d7d452ebe6
commit 4dbe2ccac7
+38 -24
View File
@@ -77,6 +77,7 @@ public class Hull {
private final BitSet visibility;
private final HashMap<IntVector, BitSet> visibilityDirections = new HashMap<>(); // The blocks the rays of each direction got inside
private final HashMap<IntVector, IntVector[]> branches = new HashMap<>(); // The diagonal directions a ray along a single axis branches into
private final StepBeside[] stepsBeside; // The steps that can lead into or past a block, see uncoverBeside()
private final HashSet<IntVector> uncoveredSurface = new HashSet<>();
private final Viewers viewers;
@@ -108,6 +109,7 @@ public class Hull {
? Arrays.stream(directions).filter(v -> v.sqLength() >= 2 && dot(v, direction) == 1).toArray(IntVector[]::new)
: NO_BRANCHES);
}
stepsBeside = computeStepsBeside();
}
/**
@@ -333,49 +335,61 @@ public class Hull {
uncoverBeside(uncovered, root);
forEachBorderStart(root, direction -> startRay(uncovered, root, direction));
uncoveredSurface.addAll(new HashSet<>(uncovered));
uncoveredSurface.addAll(uncovered);
uncoveredSurface.remove(root);
}
/**
* Continues the rays inside the blocks in front of the block, or of which a diagonal ray moves past the block.
* The block lies in front of or beside the start of a step if it is the start moved along some or all axes of the direction.
*/
private void uncoverBeside(List<IntVector> uncovered, IntVector root) {
for (IntVector direction : directions) {
if (direction.sqLength() == 0) continue;
for (StepBeside step : stepsBeside) {
int x = root.x - step.part.x;
int y = root.y - step.part.y;
int z = root.z - step.part.z;
if (!region.contains(x, y, z)) continue;
for (IntVector part : directions) {
if (part.sqLength() == 0 || !isPartOf(part, direction)) continue;
IntVector from = root.add(-part.x, -part.y, -part.z);
if (!from.notInRegion(region) && isInside(from.toId(region), direction)) {
uncoverNext(uncovered, from, direction, visibilityDirections.get(direction), branches.get(direction));
int id = region.toIndex(x, y, z);
for (BitSet ray : step.rays) {
if (ray.get(id)) {
uncoverNext(uncovered, new IntVector(x, y, z), step.direction, step.directionalVisibility, step.branchDirections);
break;
}
}
}
}
/**
* The block lies in front of or beside the start of a step if it is the start moved along some or all axes of the direction.
* The rays of the direction and the rays along a single axis branching into it take the step.
*/
private StepBeside[] computeStepsBeside() {
List<StepBeside> steps = new ArrayList<>();
for (IntVector direction : directions) {
if (direction.sqLength() == 0) continue;
BitSet[] rays = Arrays.stream(directions)
.filter(ray -> ray.equals(direction) || ray.sqLength() == 1 && direction.sqLength() > 1 && dot(ray, direction) == 1)
.map(visibilityDirections::get)
.toArray(BitSet[]::new);
for (IntVector part : directions) {
if (part.sqLength() > 0 && isPartOf(part, direction)) {
steps.add(new StepBeside(direction, part, visibilityDirections.get(direction), branches.get(direction), rays));
}
}
}
return steps.toArray(new StepBeside[0]);
}
private record StepBeside(IntVector direction, IntVector part, BitSet directionalVisibility, IntVector[] branchDirections, BitSet[] rays) {
}
private static boolean isPartOf(IntVector part, IntVector direction) {
return (part.x == 0 || part.x == direction.x)
&& (part.y == 0 || part.y == direction.y)
&& (part.z == 0 || part.z == direction.z);
}
/**
* @return whether a ray moving in the direction is inside the block: a ray of this direction or a branch of a ray along a single axis
*/
private boolean isInside(int id, IntVector direction) {
if (visibilityDirections.get(direction).get(id)) return true;
for (IntVector cardinal : directions) {
if (cardinal.sqLength() == 1 && dot(cardinal, direction) == 1 && visibilityDirections.get(cardinal).get(id)) {
return true;
}
}
return false;
}
private static int dot(IntVector a, IntVector b) {
return a.x * b.x + a.y * b.y + a.z * b.z;
}