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
4 changes: 3 additions & 1 deletion src/engine/ChunkManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export interface ChunkGPUData {
uniformBuffer: GPUBuffer
bindGroup: GPUBindGroup
indexCount: number
biomeId: number
}

const RESOLUTION = 129
Expand Down Expand Up @@ -70,7 +71,7 @@ export default class ChunkManager {
}

async generateChunk(cx: number, cz: number): Promise<ChunkGPUData> {
const { heightmap, normals } = await this.wasmClient.generateChunk(
const { heightmap, normals, biomeId } = await this.wasmClient.generateChunk(
{}, cx, cz, RESOLUTION, CHUNK_SIZE, HEIGHT_SCALE,
)

Expand Down Expand Up @@ -111,6 +112,7 @@ export default class ChunkManager {
uniformBuffer,
bindGroup,
indexCount,
biomeId: biomeId ?? 0,
}
}

Expand Down
8 changes: 6 additions & 2 deletions src/engine/WasmClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,18 +63,22 @@ export default class WasmClient {
await this.call('initWorld', [JSON.stringify(config)])
}

async loadWorldConfig(config: object): Promise<void> {
await this.call('loadWorldConfig', [JSON.stringify(config)])
}

async generateChunk(
config: object,
chunkX: number,
chunkZ: number,
resolution: number,
chunkSize: number,
heightScale: number,
): Promise<{ heightmap: Float32Array; normals: Float32Array }> {
): Promise<{ heightmap: Float32Array; normals: Float32Array; biomeId: number }> {
return this.call(
'generateChunk',
[JSON.stringify(config), chunkX, chunkZ, resolution, chunkSize, heightScale],
) as Promise<{ heightmap: Float32Array; normals: Float32Array }>
) as Promise<{ heightmap: Float32Array; normals: Float32Array; biomeId: number }>
}

async worldUpdate(playerX: number, playerZ: number): Promise<WorldUpdate> {
Expand Down
21 changes: 21 additions & 0 deletions src/engine/biome/BiomeRegistry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { BiomeType, BIOME_NAMES } from './BiomeTypes'

export interface BiomeClientDefinition {
name: string
type: BiomeType
}

// BiomeRegistry provides client-side biome metadata.
// Terrain noise params live in Go; this holds display/rendering info.
export const BiomeRegistry: Record<number, BiomeClientDefinition> = {
[BiomeType.Grassland]: { name: BIOME_NAMES[BiomeType.Grassland], type: BiomeType.Grassland },
[BiomeType.Desert]: { name: BIOME_NAMES[BiomeType.Desert], type: BiomeType.Desert },
[BiomeType.Mountains]: { name: BIOME_NAMES[BiomeType.Mountains], type: BiomeType.Mountains },
[BiomeType.Valley]: { name: BIOME_NAMES[BiomeType.Valley], type: BiomeType.Valley },
[BiomeType.Swamp]: { name: BIOME_NAMES[BiomeType.Swamp], type: BiomeType.Swamp },
[BiomeType.Forest]: { name: BIOME_NAMES[BiomeType.Forest], type: BiomeType.Forest },
}

export function getBiomeName(biomeId: number): string {
return BiomeRegistry[biomeId]?.name ?? 'Unknown'
}
30 changes: 30 additions & 0 deletions src/engine/biome/BiomeTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// BiomeType mirrors the Go biome.BiomeType enum values.
export const BiomeType = {
Grassland: 0,
Desert: 1,
Mountains: 2,
Valley: 3,
Swamp: 4,
Forest: 5,
} as const

export type BiomeType = typeof BiomeType[keyof typeof BiomeType]

export const BIOME_NAMES: Record<number, string> = {
[BiomeType.Grassland]: 'Grassland',
[BiomeType.Desert]: 'Desert',
[BiomeType.Mountains]: 'Mountains',
[BiomeType.Valley]: 'Valley',
[BiomeType.Swamp]: 'Swamp',
[BiomeType.Forest]: 'Forest',
}

export interface WorldConfig {
seed: number
biomeScale: number
}

export const DEFAULT_WORLD_CONFIG: WorldConfig = {
seed: 42,
biomeScale: 1.0,
}
5 changes: 4 additions & 1 deletion src/engine/worker/WasmBridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ declare global {
chunkSize: number,
heightScale: number,
): Float32Array
/** Combined heightmap+normals generation in pure Go — returns flat [hm..., normals...] */
/** Combined heightmap+normals+biomeId generation in pure Go.
* Returns flat Float32Array: [hm(res*res)..., normals(res*res*3)..., biomeId(1)] */
function go_generateChunk(
configJSON: string,
chunkX: number,
Expand All @@ -39,6 +40,8 @@ declare global {
chunkSize: number,
heightScale: number,
): Float32Array
/** Load a WorldConfig JSON to configure biome placement before chunk generation. */
function go_loadWorldConfig(configJSON: string): void
}

export default class WasmBridge {
Expand Down
13 changes: 9 additions & 4 deletions src/engine/worker/terrain.worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,22 +37,27 @@ function handleCall(event: MessageEvent): void {
if (method === 'initWorld') {
go_initWorld(args[0] as string)
result = null
} else if (method === 'loadWorldConfig') {
go_loadWorldConfig(args[0] as string)
result = null
} else if (method === 'generateChunk') {
const [configJSON, cx, cz, resolution, chunkSize, heightScale] =
args as [string, number, number, number, number, number]

// go_generateChunk runs both heightmap generation and normal computation
// entirely inside Go using pure Go slices — no JS Float32Array is ever
// passed between two Go WASM functions (which silently produces length 0).
// Returns flat Float32Array: [heightmap(res×res)..., normals(res×res×3)...]
// Returns flat Float32Array: [heightmap(res×res)..., normals(res×res×3)..., biomeId(1)]
const combined = go_generateChunk(configJSON, cx, cz, resolution, chunkSize, heightScale)
if (!combined || !combined.buffer) throw new Error('go_generateChunk returned no data')

const hmLen = resolution * resolution
const heightmap = combined.slice(0, hmLen) // copy, own buffer
const normals = combined.slice(hmLen) // copy, own buffer
const normLen = resolution * resolution * 3
const heightmap = combined.slice(0, hmLen) // copy, own buffer
const normals = combined.slice(hmLen, hmLen + normLen) // copy, own buffer
const biomeId = Math.round(combined[hmLen + normLen])

result = { heightmap, normals }
result = { heightmap, normals, biomeId }
transfer = [heightmap.buffer as ArrayBuffer, normals.buffer as ArrayBuffer]
} else if (method === 'worldUpdate') {
const [playerX, playerZ] = args as [number, number]
Expand Down
29 changes: 29 additions & 0 deletions wasm/biome/biome.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Package biome defines terrain biome types and their generation parameters.
package biome

// BiomeType identifies a biome variant.
type BiomeType int

const (
Grassland BiomeType = 0
Desert BiomeType = 1
Mountains BiomeType = 2
Valley BiomeType = 3
Swamp BiomeType = 4
Forest BiomeType = 5
)

// BiomeDefinition holds all parameters that control terrain generation for a biome.
type BiomeDefinition struct {
Name string
Type BiomeType
// Noise parameters
Octaves int
Frequency float64
Lacunarity float64
Persistence float64
Amplitude float64
// HeightMultiplier scales the normalized [0,1] heightmap values.
// Values > 1.0 create taller terrain; < 1.0 creates flatter terrain.
HeightMultiplier float64
}
Loading