Skip to content

Commit e832add

Browse files
committed
render the closest loaded sections if the origin section is not loaded by iterating cube shells around the origin
1 parent 53d69f3 commit e832add

File tree

1 file changed

+69
-9
lines changed

1 file changed

+69
-9
lines changed

common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/occlusion/OcclusionCuller.java

Lines changed: 69 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -234,18 +234,16 @@ private void init(Visitor visitor,
234234
this.initOutsideWorldHeight(queue, viewport, searchDistance, frame,
235235
this.level.getMaxSectionY(), GraphDirection.UP);
236236
} else {
237-
this.initWithinWorld(visitor, queue, viewport, useOcclusionCulling, frame);
237+
var originSection = this.sections.get(origin.asLong());
238+
if (originSection != null) {
239+
this.initAtExistingSection(visitor, queue, originSection, useOcclusionCulling, frame);
240+
} else {
241+
this.initAtNonExistingSection(visitor, queue, viewport, useOcclusionCulling, searchDistance, frame);
242+
}
238243
}
239244
}
240245

241-
private void initWithinWorld(Visitor visitor, WriteQueue<RenderSection> queue, Viewport viewport, boolean useOcclusionCulling, int frame) {
242-
var origin = viewport.getChunkCoord();
243-
var section = this.getRenderSection(origin.getX(), origin.getY(), origin.getZ());
244-
245-
if (section == null) {
246-
return;
247-
}
248-
246+
private void initAtExistingSection(Visitor visitor, WriteQueue<RenderSection> queue, RenderSection section, boolean useOcclusionCulling, int frame) {
249247
section.setLastVisibleFrame(frame);
250248
section.setIncomingDirections(GraphDirectionSet.NONE);
251249

@@ -265,6 +263,68 @@ private void initWithinWorld(Visitor visitor, WriteQueue<RenderSection> queue, V
265263
visitNeighbors(queue, section, outgoing, frame);
266264
}
267265

266+
private void initAtNonExistingSection(Visitor visitor, WriteQueue<RenderSection> queue, Viewport viewport, boolean useOcclusionCulling, float searchDistance, int frame) {
267+
var origin = viewport.getChunkCoord();
268+
var minY = this.level.getMinSectionY();
269+
var maxY = this.level.getMaxSectionY();
270+
var originX = origin.getX();
271+
var originY = origin.getY();
272+
var originZ = origin.getZ();
273+
274+
// iterate shells until one is found with a loaded and visible section
275+
var foundAny = false;
276+
var radius = 1;
277+
while (!foundAny) {
278+
// iterate a shell around the origin
279+
var bigStep = radius * 2;
280+
for (var dy = -radius; dy <= radius; dy++) {
281+
var y = originY + dy;
282+
// skip layers outside the world's y range
283+
if (y < minY || y > maxY) {
284+
continue;
285+
}
286+
287+
// iterate only the perimeter with the current radius
288+
var notYFace = !(dy == -radius || dy == radius);
289+
for (var dx = -radius; dx <= radius; dx++) {
290+
var zStep = notYFace && (dx == -radius || dx == radius) ? bigStep : 1;
291+
for (var dz = -radius; dz <= radius; dz += zStep) {
292+
var x = originX + dx;
293+
var z = originZ + dz;
294+
295+
// visit loaded visible sections and queue their neighbors
296+
var section = this.getRenderSection(x, y, z);
297+
if (section != null && isSectionVisible(section, viewport, searchDistance)) {
298+
foundAny = true;
299+
300+
// use all directions as incoming, using just some yields a broken result
301+
var incoming = GraphDirectionSet.ALL;
302+
section.setIncomingDirections(incoming);
303+
section.setLastVisibleFrame(frame);
304+
305+
visitor.visit(section, true);
306+
307+
// reduce set of neighbors to visit based on visibility connections
308+
int connections = getOutwardDirections(origin, section);
309+
if (useOcclusionCulling) {
310+
connections &= VisibilityEncoding.getConnections(section.getVisibilityData(), incoming);
311+
}
312+
313+
visitNeighbors(queue, section, connections, frame);
314+
}
315+
}
316+
}
317+
}
318+
319+
radius++;
320+
321+
// don't exceed the search distance with the init search
322+
if (radius << 4 > searchDistance) {
323+
break;
324+
}
325+
}
326+
}
327+
268328
// Enqueues sections that are inside the viewport using diamond spiral iteration to avoid sorting and ensure a
269329
// consistent order. Innermost layers are enqueued first. Within each layer, iteration starts at the northernmost
270330
// section and proceeds counterclockwise (N->W->S->E).

0 commit comments

Comments
 (0)