Skip to content

🌍 first and third person: load the world out to what the eye can see - #44

Open
vegerot wants to merge 1 commit into
artyrambles:mainfrom
vegerot:first-person-world-reach
Open

vegerot wants to merge 1 commit into
artyrambles:mainfrom
vegerot:first-person-world-reach

Conversation

@vegerot

@vegerot vegerot commented Aug 18, 2026

Copy link
Copy Markdown

Closes #43. 🕳️

Previously, standing at the edge of a load zone would look like staring into the void. This commit renders more neighbors dynamically to make load zone transitions smoother.

AI slop

The hole 🕳️

Standing in the world on the 1ST or 3RD rungs, the ground stops a
short way out and the rest of the horizon is empty. 🌫️ The next town is
absent until the player crosses a seam, and then it arrives all at
once. 💥

Why 🔍

The engine draws the current map plus a set of connected neighbours,
and sizes that set for the flat camera 📐: two connection hops out,
widened by the top-down view's own half-extents
(OverworldState:rebuildNeighbors). VoxelScene draws exactly that set
and nothing else.

On every orbit rung that is the right answer, and not by luck 🎯 —
Voxel3D.viewProjection derives its field of view from the same view
height the widening is measured in, so the loaded set and the framed rect
are one rectangle and the two cannot disagree. ✅

The free-cam rungs are the case that sum was never written for. 👁️ The eye
stands in the world and looks along the ground through a 65 degree
lens, so the horizon runs far past a rect measured in top-down
screenfuls. 🔭 Past the last loaded map there is no terrain to draw at all.
The pop-in is the same fact from the other side 👣: a crossing re-roots the
walk from the map just entered, and everything within reach of the new
root appears at once.

This is why the older Dramatic Shape line never showed it. 🕰️ Its lowest
rung was still an angled shot from above the world; there was no camera
that could point past the loaded set.

The change 🔧

The widening term is the engine's and not ours to set. The hop count
is — constants.world.neighborHops, which rebuildNeighbors re-reads on
every call. So:

  • 🗺️ lib/WorldReach.lua (new) raises the hops while the eye is in the
    world and puts back whatever was there when it leaves, letting the
    engine do the loading. Going through its own rebuild is the point: the
    pass that places the maps is also the pass that tells the eviction
    which maps to keep, so nothing loads here that the next trim quietly
    takes away again. 🧹
  • ⚙️ lib/Quality.lua answers the existing R.DIST ladder as hops.
  • ⏱️ main.lua ticks it, ahead of the voxel-active gate.

Two details worth naming:

  • 🎥 The raise is held for the whole blend, not for the rung. The
    fly-out is half a second of camera still standing in the world and
    rising out of it; dropping the far maps on the keypress would empty the
    horizon in front of a player who is watching it.
  • 🧍 The rebuild runs only on a real change to the hop count — entering the
    rung and leaving it — because a rebuild loads maps and re-places every
    ghost NPC standing on them.

What R.DIST means now 📏

It was a cull of distant characters only, which is why turning it up
never filled the horizon 🌱: ground is not drawn out to a distance this mod
picks. Both halves move together now, so the row means one thing to a
player who reads its name. ✨

Rung Figures 🧍 World 🌍
SHORT 16 3 hops
MEDIUM 32 4 hops
FAR 64 5 hops
FULL no limit 6 hops

Every rung sits at least one hop above the engine's default of 2. That
default is sized against the flat camera's view rect, and an eye standing
inside the world sees past it on any device 📱 — so the cheap rung stays
cheaper than the rest, but no rung is the one that leaves a hole in front
of the player. The orbit rungs are untouched and still pay nothing. 💸

Testing 🧪

All three files parse under LOVE's Lua. ✅ I have not run this in-game ⚠️,
and my install has other mods in it, so the numbers in the ladder are
reasoned rather than measured — the shape of the fix is the part I am
confident in 🎯, and the four rungs are easy to retune if they land wrong on
a phone. 🔁

🤖 Generated with Claude Code

Copilot AI lite review requested due to automatic review settings August 18, 2026 05:22
@vegerot
vegerot force-pushed the first-person-world-reach branch from abb7679 to 9b6d3b5 Compare August 18, 2026 05:24
@vegerot vegerot changed the title first and third person: load the world out to what the eye can see 🌍 first and third person: load the world out to what the eye can see Aug 18, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adjusts voxel-world streaming reach to avoid “empty horizon” voids in first-/third-person camera rungs by increasing the engine’s neighbor-map hop radius while the camera is standing in-world, then restoring the prior value when leaving/blending out.

Changes:

  • Add lib/WorldReach.lua to dynamically raise/restore constants.world.neighborHops and trigger rebuildNeighbors on actual hop-count changes.
  • Extend lib/Quality.lua so the existing R.DIST setting also maps to world reach (connection hops) via Quality.reachHops().
  • Call WorldReach.update() early in the voxel pipeline update loop and clarify R.DIST’s help text in main.lua.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
main.lua Wires in WorldReach.update() before the voxel-active gate; updates R.DIST help text to reflect world-loading impact.
lib/WorldReach.lua New module that increases/restores neighbor hop count during 1ST/3RD and blend, rebuilding neighbors on change.
lib/Quality.lua Adds Quality.reachHops() mapping R.DIST rungs to connection-hop counts for world streaming reach.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread lib/WorldReach.lua
Comment on lines +59 to +61
if ow and ow.map and ow.rebuildNeighbors then
pcall(ow.rebuildNeighbors, ow)
end
Comment thread lib/WorldReach.lua Outdated
Comment on lines +51 to +54
-- Set the hop count, and rebuild only on a real change. A rebuild loads
-- maps and re-places every ghost NPC standing on them, which is not a
-- thing to do per frame; stepping onto the rung and stepping off it are
-- the only times this moves.
Closes artyrambles#43. 🕳️

Previously, standing at the edge of a load zone would look like staring into the void.  This commit renders more neighbors dynamically to make load zone transitions smoother.

<details><summary>AI slop</summary>

## The hole 🕳️

Standing in the world on the `1ST` or `3RD` rungs, the ground stops a
short way out and the rest of the horizon is empty. 🌫️ The next town is
absent until the player crosses a seam, and then it arrives all at
once. 💥

## Why 🔍

The engine draws the current map plus a set of connected **neighbours**,
and sizes that set for the **flat** camera 📐: two connection hops out,
widened by the top-down view's own half-extents
(`OverworldState:rebuildNeighbors`). `VoxelScene` draws exactly that set
and nothing else.

On every orbit rung that is the right answer, and not by luck 🎯 —
`Voxel3D.viewProjection` derives its field of view from the same view
height the widening is measured in, so the loaded set and the framed rect
are one rectangle and the two cannot disagree. ✅

The free-cam rungs are the case that sum was never written for. 👁️ The eye
stands *in* the world and looks along the ground through a 65 degree
lens, so the horizon runs far past a rect measured in top-down
screenfuls. 🔭 Past the last loaded map there is no terrain to draw at all.
The pop-in is the same fact from the other side 👣: a crossing re-roots the
walk from the map just entered, and everything within reach of the new
root appears at once.

This is why the older Dramatic Shape line never showed it. 🕰️ Its lowest
rung was still an angled shot from above the world; there was no camera
that could point past the loaded set.

## The change 🔧

The widening term is the engine's and not ours to set. The hop count
is — `constants.world.neighborHops`, which `rebuildNeighbors` re-reads on
every call. So:

- 🗺️ **`lib/WorldReach.lua`** (new) raises the hops while the eye is in the
  world and puts back whatever was there when it leaves, letting the
  engine do the loading. Going through its own rebuild is the point: the
  pass that places the maps is also the pass that tells the eviction
  which maps to keep, so nothing loads here that the next trim quietly
  takes away again. 🧹
- ⚙️ **`lib/Quality.lua`** answers the existing `R.DIST` ladder as hops.
- ⏱️ **`main.lua`** ticks it, ahead of the voxel-active gate.

Two details worth naming:

- 🎥 The raise is held for the whole **blend**, not for the rung. The
  fly-out is half a second of camera still standing in the world and
  rising out of it; dropping the far maps on the keypress would empty the
  horizon in front of a player who is watching it.
- 🧍 The rebuild runs only on a real change to the hop count — entering the
  rung and leaving it — because a rebuild loads maps and re-places every
  ghost NPC standing on them.

## What `R.DIST` means now 📏

It was a cull of distant *characters* only, which is why turning it up
never filled the horizon 🌱: ground is not drawn out to a distance this mod
picks. Both halves move together now, so the row means one thing to a
player who reads its name. ✨

| Rung | Figures 🧍 | World 🌍 |
|---|---|---|
| `SHORT` | 16 | 3 hops |
| `MEDIUM` | 32 | 4 hops |
| `FAR` | 64 | 5 hops |
| `FULL` | no limit | 6 hops |

Every rung sits at least one hop above the engine's default of 2. That
default is sized against the flat camera's view rect, and an eye standing
inside the world sees past it on any device 📱 — so the cheap rung stays
cheaper than the rest, but no rung is the one that leaves a hole in front
of the player. The orbit rungs are untouched and still pay nothing. 💸

## Testing 🧪

All three files parse under LOVE's Lua. ✅ **I have not run this in-game** ⚠️,
and my install has other mods in it, so the numbers in the ladder are
reasoned rather than measured — the shape of the fix is the part I am
confident in 🎯, and the four rungs are easy to retune if they land wrong on
a phone. 🔁

🤖 Generated with [Claude Code](https://claude.com/claude-code)

</details>

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@vegerot
vegerot force-pushed the first-person-world-reach branch from 9b6d3b5 to 5e9b840 Compare August 18, 2026 05:29
@artyrambles

Copy link
Copy Markdown
Owner

The render distance feature is still an early wip. The neighboring maps are still being loaded, but their geometry is just not being drawn right now, and I am working on this feature so that a flat 2D version of them is being drawn if they're out of render distance. The render distance itself was also being incorrectly calculated at some map positions because I'm not that good at math, which however is fixed in the next upcoming release version.

The description of the PR that you added is kind of overly verbose and all the emojis make it hard to read, but from what I understand you are adding an entirely new lua file and making edits to the main.lua and Quality.lua that amount to loading more neighbor maps than before. Have you been able to test if this impacts the performance negatively? Because the goal of the render distance feature is to reduce the negative impact of the neighboring maps being drawn in 3D when 2D stand-ins would suffice instead.

@vegerot

vegerot commented Aug 20, 2026

Copy link
Copy Markdown
Author

@artyrambles I haven't experienced any performance issues whatsoever, but I'm playing on a 5090 😁 .

a flat 2D version of them is being drawn if they're out of render distance

That's a great idea! I hadn't thought of that. I agree that would be better.

Either way, I've been playing with this patch for the last few days and it's been more fun. I don't know what the performance hit would be to people on Android

@artyrambles

Copy link
Copy Markdown
Owner

It doesn't seem to be working. When I test it, the void at the connections is still showing in third or first person view.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: empty voids at map boundaries when in first-person mode

3 participants