Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 11 additions & 6 deletions src/engine/ChunkManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,17 @@ export default class ChunkManager {
this.removeChunk(coord.X, coord.Z)
}

for (const chunkRef of update.chunksToAdd ?? []) {
const { X: cx, Z: cz } = chunkRef.coord
if (this.activeChunks.some(c => c.coord.x === cx && c.coord.z === cz)) continue
const chunk = await this.generateChunk(cx, cz)
this.activeChunks.push(chunk)
}
const toGenerate = (update.chunksToAdd ?? []).filter(ref => {
const { X: cx, Z: cz } = ref.coord
return !this.activeChunks.some(c => c.coord.x === cx && c.coord.z === cz)
})

if (toGenerate.length === 0) return

const newChunks = await Promise.all(
toGenerate.map(ref => this.generateChunk(ref.coord.X, ref.coord.Z))
)
this.activeChunks.push(...newChunks)
}

private removeChunk(cx: number, cz: number): void {
Expand Down
6 changes: 3 additions & 3 deletions src/engine/GameEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ export default class GameEngine {
this.pointerLocked = document.pointerLockElement === canvas
}

private lastStreamX = 0
private lastStreamZ = 0
private lastStreamX = -99999
private lastStreamZ = -99999

constructor(device: GPUDevice, context: GPUCanvasContext, format: GPUTextureFormat) {
this.device = device
Expand Down Expand Up @@ -137,7 +137,7 @@ export default class GameEngine {
if (this.playerState) {
const dx = this.playerState.x - this.lastStreamX
const dz = this.playerState.z - this.lastStreamZ
if (dx * dx + dz * dz > 256 * 256) {
if (dx * dx + dz * dz > 128 * 128) {
this.lastStreamX = this.playerState.x
this.lastStreamZ = this.playerState.z
this.chunkManager?.streamUpdate(this.playerState.x, this.playerState.z)
Expand Down
4 changes: 1 addition & 3 deletions wasm/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,7 @@ func goWorldUpdate(_ js.Value, args []js.Value) any {
playerZ := args[1].Float()
update := globalWorld.Update(playerX, playerZ)

for _, r := range update.ChunksToAdd {
globalHeightmaps[r.Coord] = r.Heightmap
}
// Clean up heightmap cache for evicted chunks
for _, c := range update.ChunksToRemove {
delete(globalHeightmaps, c)
}
Expand Down
6 changes: 3 additions & 3 deletions wasm/world/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ package world
// Constants mirror terra-major World.cs
const (
InitialRenderRadius = 2048.0
RenderRadius = 512.0
DistanceThreshold = 1100.0
MaxConcurrentChunks = 3
RenderRadius = 1536.0
DistanceThreshold = 2560.0
MaxConcurrentChunks = 64
)

// ChunkCoord identifies a chunk by grid position.
Expand Down
38 changes: 19 additions & 19 deletions wasm/world/world.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package world

import (
"math"
"sort"

"github.com/maxfelker/terrain-webgpu/wasm/terrain"
)
Expand Down Expand Up @@ -51,40 +52,39 @@ func (w *World) Update(playerX, playerZ float64) WorldUpdate {
cx := playerChunkX + dx
cz := playerChunkZ + dz

worldDX := float64(cx)*chunkSize - playerX
worldDZ := float64(cz)*chunkSize - playerZ
if worldDX*worldDX+worldDZ*worldDZ > RenderRadius*RenderRadius {
// Use chunk center for circular distance check
centerDX := (float64(cx)+0.5)*chunkSize - playerX
centerDZ := (float64(cz)+0.5)*chunkSize - playerZ
if centerDX*centerDX+centerDZ*centerDZ > RenderRadius*RenderRadius {
continue
}

coord := ChunkCoord{X: cx, Z: cz}
if w.registry.IsActive(coord) {
continue
}
if !w.registry.CanDispatch() {
break
}

w.registry.MarkGenerating(coord)
hm := terrain.GenerateHeightmap(cx, cz, w.cfg)
normals := terrain.ComputeNormals(hm, w.cfg.HeightmapResolution, float64(w.cfg.Dimension), float64(w.cfg.Height))
w.registry.MarkReady(coord)
w.registry.MarkActive(coord)

toAdd = append(toAdd, ChunkGenResult{
Coord: coord,
Heightmap: hm,
Normals: normals,
})
toAdd = append(toAdd, ChunkGenResult{Coord: coord})
}
}

// Sort toAdd by distance from player (nearest first for better UX)
sort.Slice(toAdd, func(i, j int) bool {
ci, cj := toAdd[i].Coord, toAdd[j].Coord
di := (float64(ci.X)+0.5)*chunkSize - playerX
dj := (float64(cj.X)+0.5)*chunkSize - playerX
diz := (float64(ci.Z)+0.5)*chunkSize - playerZ
djz := (float64(cj.Z)+0.5)*chunkSize - playerZ
return di*di+diz*diz < dj*dj+djz*djz
})

// Evict chunks beyond distance threshold
toRemove := make([]ChunkCoord, 0)
for _, coord := range w.registry.ActiveCoords() {
worldDX := float64(coord.X)*chunkSize - playerX
worldDZ := float64(coord.Z)*chunkSize - playerZ
if worldDX*worldDX+worldDZ*worldDZ > DistanceThreshold*DistanceThreshold {
centerDX := (float64(coord.X)+0.5)*chunkSize - playerX
centerDZ := (float64(coord.Z)+0.5)*chunkSize - playerZ
if centerDX*centerDX+centerDZ*centerDZ > DistanceThreshold*DistanceThreshold {
w.registry.Remove(coord)
toRemove = append(toRemove, coord)
}
Expand Down