scena3d 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +95 -0
- package/dist/index.cjs +662 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +288 -0
- package/dist/index.d.ts +288 -0
- package/dist/index.js +665 -0
- package/dist/index.js.map +1 -0
- package/package.json +65 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/core/random.ts","../src/core/palette.ts","../src/core/types.ts","../src/props/tree.ts","../src/props/rock.ts","../src/props/crate.ts","../src/props/fence.ts","../src/props/lamp.ts","../src/environment/terrain.ts","../src/environment/sky.ts","../src/environment/lighting.ts","../src/scatter/scatter.ts"],"sourcesContent":["// SCENA — SCenes & ENvironment Assets\n// A 3D world-building library on top of three.js. Seeded procedural props,\n// terrain, sky, lighting and scattering — with gameplay metadata (steering\n// obstacles, walkable surfaces) that game libraries like GAMA understand.\n\n// Core\nexport { Rng, valueNoise2, fractalNoise2, hash2 } from './core/random';\nexport { PALETTES, DEFAULT_PALETTE, type Palette } from './core/palette';\nexport { collectObstacles, type Obstacle, type Prop } from './core/types';\n\n// Props\nexport { createTree, type TreeOptions } from './props/tree';\nexport { createRock, type RockOptions } from './props/rock';\nexport { createCrate, type CrateOptions } from './props/crate';\nexport { createFence, type FenceOptions } from './props/fence';\nexport { createLamp, type LampOptions } from './props/lamp';\n\n// Environment\nexport { createTerrain, type Terrain, type TerrainOptions } from './environment/terrain';\nexport { createSky, type Sky, type SkyOptions } from './environment/sky';\nexport {\n createLightingRig,\n applyFog,\n type LightingRig,\n type LightingPreset,\n type FogPreset,\n} from './environment/lighting';\n\n// Scattering\nexport {\n scatter,\n type ScatterOptions,\n type ScatterItem,\n type ScatterResult,\n type Placement,\n} from './scatter/scatter';\n","/**\n * Deterministic seeded randomness — the backbone of SCENA. Same seed,\n * same tree; forests are reproducible, diffable and network-syncable.\n */\nexport class Rng {\n private state: number;\n\n constructor(seed = 1) {\n this.state = seed >>> 0 || 1;\n }\n\n /** Next float in [0, 1) (mulberry32). */\n next(): number {\n this.state = (this.state + 0x6d2b79f5) >>> 0;\n let t = this.state;\n t = Math.imul(t ^ (t >>> 15), t | 1);\n t ^= t + Math.imul(t ^ (t >>> 7), t | 61);\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n }\n\n /** Float in [min, max). */\n range(min: number, max: number): number {\n return min + this.next() * (max - min);\n }\n\n /** Integer in [min, max] inclusive. */\n int(min: number, max: number): number {\n return min + Math.floor(this.next() * (max - min + 1));\n }\n\n /** Random element of a non-empty array. */\n pick<T>(items: readonly T[]): T {\n return items[Math.floor(this.next() * items.length)];\n }\n\n /** value ± spread (uniform). */\n jitter(value: number, spread: number): number {\n return value + (this.next() * 2 - 1) * spread;\n }\n\n /** A new independent Rng derived from this one. */\n fork(): Rng {\n return new Rng(Math.floor(this.next() * 0xffffffff) || 1);\n }\n}\n\n/** Integer-lattice hash to [0, 1) — the base of the value noise. */\nexport function hash2(ix: number, iz: number, seed: number): number {\n let h = (ix * 374761393 + iz * 668265263 + seed * 2246822519) >>> 0;\n h = Math.imul(h ^ (h >>> 13), 1274126177) >>> 0;\n return ((h ^ (h >>> 16)) >>> 0) / 4294967296;\n}\n\nconst smooth = (t: number): number => t * t * (3 - 2 * t);\n\n/** 2D value noise in [0, 1). Continuous; used by terrain and scatter density. */\nexport function valueNoise2(x: number, z: number, seed: number): number {\n const ix = Math.floor(x);\n const iz = Math.floor(z);\n const fx = smooth(x - ix);\n const fz = smooth(z - iz);\n const a = hash2(ix, iz, seed);\n const b = hash2(ix + 1, iz, seed);\n const c = hash2(ix, iz + 1, seed);\n const d = hash2(ix + 1, iz + 1, seed);\n return a + (b - a) * fx + (c - a) * fz + (a - b - c + d) * fx * fz;\n}\n\n/** Fractal (octaved) value noise in [0, 1). */\nexport function fractalNoise2(\n x: number,\n z: number,\n seed: number,\n octaves = 4,\n lacunarity = 2,\n gain = 0.5\n): number {\n let amplitude = 1;\n let frequency = 1;\n let sum = 0;\n let total = 0;\n for (let i = 0; i < octaves; i++) {\n sum += valueNoise2(x * frequency, z * frequency, seed + i * 101) * amplitude;\n total += amplitude;\n amplitude *= gain;\n frequency *= lacunarity;\n }\n return sum / total;\n}\n","/**\n * Theme palettes: one coherent set of colors shared by every generator,\n * so procedural props look like a matched set rather than a junk drawer.\n * Pass `palette` to any generator to restyle it; whole scenes retheme by\n * building with a different palette.\n */\nexport interface Palette {\n foliage: number[];\n trunk: number;\n rock: number[];\n wood: number;\n woodDark: number;\n metal: number;\n lampGlow: number;\n grassLow: number;\n grassHigh: number;\n cliff: number;\n peak: number;\n skyTop: number;\n skyBottom: number;\n fog: number;\n}\n\nexport const PALETTES: Record<'meadow' | 'autumn' | 'dusk', Palette> = {\n meadow: {\n foliage: [0x2f9e57, 0x37b26a, 0x2a8f4f, 0x45b878],\n trunk: 0x6b4a33,\n rock: [0x8a8f98, 0x767c86, 0x9aa0a8],\n wood: 0x8a6642,\n woodDark: 0x6b4a33,\n metal: 0x3d4451,\n lampGlow: 0xffd889,\n grassLow: 0x3f9d5a,\n grassHigh: 0x6fae66,\n cliff: 0x7d7a72,\n peak: 0xe8ecef,\n skyTop: 0x3d70b8,\n skyBottom: 0xbfd9e8,\n fog: 0xb8cfdd,\n },\n autumn: {\n foliage: [0xc9752f, 0xd98e3a, 0xb35c2a, 0xe0a545],\n trunk: 0x5d4030,\n rock: [0x8d8578, 0x776f63, 0x9c948a],\n wood: 0x7d5a3a,\n woodDark: 0x5d4030,\n metal: 0x463f3a,\n lampGlow: 0xffc571,\n grassLow: 0x9d8a3f,\n grassHigh: 0xb59b4a,\n cliff: 0x82746a,\n peak: 0xe3ded4,\n skyTop: 0x8e6ca8,\n skyBottom: 0xe8c9a8,\n fog: 0xd9c1a8,\n },\n dusk: {\n foliage: [0x1f5e46, 0x24684f, 0x1a5240, 0x2d7458],\n trunk: 0x413147,\n rock: [0x565672, 0x484861, 0x646484],\n wood: 0x5d4a63,\n woodDark: 0x413147,\n metal: 0x2b2b3d,\n lampGlow: 0xffb35c,\n grassLow: 0x2d6b52,\n grassHigh: 0x3d7a5e,\n cliff: 0x52516b,\n peak: 0xb8b8d9,\n skyTop: 0x1d2145,\n skyBottom: 0xc96a4a,\n fog: 0x6a5a7a,\n },\n};\n\nexport const DEFAULT_PALETTE: Palette = PALETTES.meadow;\n","import { Vector3, type Group, type Object3D } from 'three';\n\n/**\n * A steering obstacle in world space — structurally identical to GAMA's\n * `Obstacle`, so SCENA props plug straight into `ObstacleAvoidance`\n * without either library importing the other.\n */\nexport interface Obstacle {\n center: Vector3;\n radius: number;\n}\n\n/** What a prop generator returns: the visual plus gameplay metadata. */\nexport interface Prop {\n object: Group;\n /**\n * Footprint radius for steering/placement, in the prop's local space\n * (centered at its origin). 0 means walk-through (e.g. grass).\n */\n obstacleRadius: number;\n}\n\n/** Collect world-space obstacles from placed props (call after positioning). */\nexport function collectObstacles(props: Iterable<Prop>): Obstacle[] {\n const obstacles: Obstacle[] = [];\n for (const prop of props) {\n if (prop.obstacleRadius <= 0) continue;\n prop.object.updateWorldMatrix(true, false);\n obstacles.push({\n center: prop.object.getWorldPosition(new Vector3()),\n radius: prop.obstacleRadius * maxScale(prop.object),\n });\n }\n return obstacles;\n}\n\nfunction maxScale(object: Object3D): number {\n return Math.max(object.scale.x, object.scale.z);\n}\n","import {\n ConeGeometry,\n CylinderGeometry,\n Group,\n IcosahedronGeometry,\n Mesh,\n MeshStandardMaterial,\n} from 'three';\nimport { Rng } from '../core/random';\nimport { DEFAULT_PALETTE, type Palette } from '../core/palette';\nimport type { Prop } from '../core/types';\n\nexport interface TreeOptions {\n seed?: number;\n /** Overall height in world units. Default ~3.5–5 by seed. */\n height?: number;\n style?: 'pine' | 'oak';\n palette?: Palette;\n}\n\n/**\n * A seeded low-poly tree. `pine` stacks cones; `oak` clusters foliage\n * blobs on a forked trunk. Same seed → identical tree, forever.\n */\nexport function createTree(options: TreeOptions = {}): Prop {\n const rng = new Rng(options.seed ?? 1);\n const palette = options.palette ?? DEFAULT_PALETTE;\n const style = options.style ?? (rng.next() < 0.6 ? 'pine' : 'oak');\n const height = options.height ?? rng.range(3.2, 5.2);\n const group = new Group();\n group.name = `tree-${style}`;\n\n const trunkMaterial = new MeshStandardMaterial({ color: palette.trunk, flatShading: true });\n const foliageMaterial = new MeshStandardMaterial({\n color: rng.pick(palette.foliage),\n flatShading: true,\n });\n\n if (style === 'pine') {\n const trunkHeight = height * 0.25;\n const trunk = new Mesh(new CylinderGeometry(0.09, 0.14, trunkHeight, 6), trunkMaterial);\n trunk.position.y = trunkHeight / 2;\n group.add(trunk);\n\n const tiers = rng.int(3, 4);\n let y = trunkHeight;\n let radius = height * rng.range(0.24, 0.3);\n const tierHeight = (height - trunkHeight) / tiers + 0.15;\n for (let i = 0; i < tiers; i++) {\n const cone = new Mesh(new ConeGeometry(radius, tierHeight * 1.35, 7), foliageMaterial);\n cone.position.y = y + tierHeight * 0.55;\n cone.rotation.y = rng.range(0, Math.PI);\n group.add(cone);\n y += tierHeight * 0.8;\n radius *= 0.72;\n }\n } else {\n const trunkHeight = height * 0.45;\n const trunk = new Mesh(new CylinderGeometry(0.12, 0.2, trunkHeight, 6), trunkMaterial);\n trunk.position.y = trunkHeight / 2;\n trunk.rotation.z = rng.range(-0.08, 0.08);\n group.add(trunk);\n\n const blobs = rng.int(2, 4);\n for (let i = 0; i < blobs; i++) {\n const radius = height * rng.range(0.18, 0.28);\n const blob = new Mesh(new IcosahedronGeometry(radius, 0), foliageMaterial);\n blob.position.set(\n rng.jitter(0, height * 0.16),\n trunkHeight + radius * rng.range(0.5, 0.9) + i * radius * 0.35,\n rng.jitter(0, height * 0.16)\n );\n blob.rotation.set(rng.range(0, Math.PI), rng.range(0, Math.PI), 0);\n group.add(blob);\n }\n }\n\n return { object: group, obstacleRadius: style === 'pine' ? 0.5 : 0.6 };\n}\n","import {\n BufferAttribute,\n Group,\n IcosahedronGeometry,\n Mesh,\n MeshStandardMaterial,\n} from 'three';\nimport { Rng } from '../core/random';\nimport { DEFAULT_PALETTE, type Palette } from '../core/palette';\nimport type { Prop } from '../core/types';\n\nexport interface RockOptions {\n seed?: number;\n /** Approximate radius. Default ~0.4–1.1 by seed. */\n size?: number;\n palette?: Palette;\n}\n\n/**\n * A seeded low-poly boulder: an icosahedron with jittered vertices and a\n * flattened underside so it sits on the ground.\n */\nexport function createRock(options: RockOptions = {}): Prop {\n const rng = new Rng(options.seed ?? 1);\n const palette = options.palette ?? DEFAULT_PALETTE;\n const size = options.size ?? rng.range(0.4, 1.1);\n\n const geometry = new IcosahedronGeometry(size, 0);\n const positions = geometry.getAttribute('position') as BufferAttribute;\n // Jitter welded-by-value: identical vertex values get identical offsets,\n // so faces stay connected.\n const seen = new Map<string, [number, number, number]>();\n for (let i = 0; i < positions.count; i++) {\n const key = `${positions.getX(i).toFixed(4)},${positions.getY(i).toFixed(4)},${positions.getZ(i).toFixed(4)}`;\n let offset = seen.get(key);\n if (!offset) {\n offset = [rng.jitter(0, size * 0.22), rng.jitter(0, size * 0.16), rng.jitter(0, size * 0.22)];\n seen.set(key, offset);\n }\n positions.setXYZ(\n i,\n positions.getX(i) + offset[0],\n Math.max(positions.getY(i) + offset[1], -size * 0.15), // flatten the bottom\n positions.getZ(i) + offset[2]\n );\n }\n geometry.computeVertexNormals();\n\n const rock = new Mesh(\n geometry,\n new MeshStandardMaterial({ color: rng.pick(palette.rock), flatShading: true })\n );\n rock.position.y = size * 0.15;\n rock.scale.y = rng.range(0.6, 0.9);\n\n const group = new Group();\n group.name = 'rock';\n group.add(rock);\n return { object: group, obstacleRadius: size * 1.05 };\n}\n","import { BoxGeometry, Group, Mesh, MeshStandardMaterial } from 'three';\nimport { Rng } from '../core/random';\nimport { DEFAULT_PALETTE, type Palette } from '../core/palette';\nimport type { Prop } from '../core/types';\n\nexport interface CrateOptions {\n seed?: number;\n size?: number;\n /** 0–1 color wear. Default 0.3. */\n weathering?: number;\n palette?: Palette;\n}\n\n/** A wooden crate: panel box with darker edge framing, seeded wear tint. */\nexport function createCrate(options: CrateOptions = {}): Prop {\n const rng = new Rng(options.seed ?? 1);\n const palette = options.palette ?? DEFAULT_PALETTE;\n const size = options.size ?? 1;\n const wear = options.weathering ?? 0.3;\n\n const group = new Group();\n group.name = 'crate';\n\n const panelMaterial = new MeshStandardMaterial({ color: palette.wood, flatShading: true });\n panelMaterial.color.offsetHSL(0, 0, -rng.range(0, wear * 0.18));\n const frameMaterial = new MeshStandardMaterial({ color: palette.woodDark, flatShading: true });\n\n const body = new Mesh(new BoxGeometry(size * 0.92, size * 0.92, size * 0.92), panelMaterial);\n body.position.y = size / 2;\n group.add(body);\n\n const beam = size * 0.12;\n const long = size * 1.0;\n for (const y of [beam / 2, size - beam / 2]) {\n for (const [rx, rz, w, d] of [\n [0, size / 2 - beam / 2, long, beam],\n [0, -(size / 2 - beam / 2), long, beam],\n [size / 2 - beam / 2, 0, beam, long],\n [-(size / 2 - beam / 2), 0, beam, long],\n ] as const) {\n const rail = new Mesh(new BoxGeometry(w, beam, d), frameMaterial);\n rail.position.set(rx, y, rz);\n group.add(rail);\n }\n }\n for (const x of [-1, 1]) {\n for (const z of [-1, 1]) {\n const post = new Mesh(new BoxGeometry(beam, size, beam), frameMaterial);\n post.position.set(x * (size / 2 - beam / 2), size / 2, z * (size / 2 - beam / 2));\n group.add(post);\n }\n }\n group.rotation.y = rng.range(0, Math.PI / 2);\n\n return { object: group, obstacleRadius: size * 0.75 };\n}\n","import { BoxGeometry, CylinderGeometry, Group, Mesh, MeshStandardMaterial } from 'three';\nimport { Rng } from '../core/random';\nimport { DEFAULT_PALETTE, type Palette } from '../core/palette';\nimport type { Prop } from '../core/types';\n\nexport interface FenceOptions {\n seed?: number;\n /** Run length along local +x. Default 6. */\n length?: number;\n postSpacing?: number;\n height?: number;\n palette?: Palette;\n}\n\n/**\n * A rustic fence run along local +x, centered at the origin: posts with\n * two slightly-crooked rails. Chain several and rotate to enclose areas.\n * The obstacle radius covers the whole run (rough but steering-safe);\n * for tight navigation, bake a navmesh — the geometry is authoritative.\n */\nexport function createFence(options: FenceOptions = {}): Prop {\n const rng = new Rng(options.seed ?? 1);\n const palette = options.palette ?? DEFAULT_PALETTE;\n const length = options.length ?? 6;\n const spacing = options.postSpacing ?? 1.5;\n const height = options.height ?? 1.1;\n\n const group = new Group();\n group.name = 'fence';\n const postMaterial = new MeshStandardMaterial({ color: palette.woodDark, flatShading: true });\n const railMaterial = new MeshStandardMaterial({ color: palette.wood, flatShading: true });\n\n const posts = Math.max(2, Math.round(length / spacing) + 1);\n const step = length / (posts - 1);\n for (let i = 0; i < posts; i++) {\n const post = new Mesh(new CylinderGeometry(0.06, 0.075, height, 5), postMaterial);\n post.position.set(-length / 2 + i * step, height / 2, rng.jitter(0, 0.03));\n post.rotation.z = rng.range(-0.04, 0.04);\n group.add(post);\n }\n for (const railY of [height * 0.55, height * 0.85]) {\n const rail = new Mesh(new BoxGeometry(length, 0.07, 0.05), railMaterial);\n rail.position.y = rng.jitter(railY, 0.02);\n rail.rotation.x = rng.range(-0.02, 0.02);\n group.add(rail);\n }\n\n return { object: group, obstacleRadius: length / 2 };\n}\n","import {\n BoxGeometry,\n CylinderGeometry,\n Group,\n Mesh,\n MeshStandardMaterial,\n PointLight,\n SphereGeometry,\n} from 'three';\nimport { Rng } from '../core/random';\nimport { DEFAULT_PALETTE, type Palette } from '../core/palette';\nimport type { Prop } from '../core/types';\n\nexport interface LampOptions {\n seed?: number;\n height?: number;\n /** Add a real PointLight. Off by default — lights are a budget. */\n light?: boolean;\n lightIntensity?: number;\n palette?: Palette;\n}\n\n/** A street lamp: post, head, glowing bulb, optional real PointLight. */\nexport function createLamp(options: LampOptions = {}): Prop {\n const rng = new Rng(options.seed ?? 1);\n const palette = options.palette ?? DEFAULT_PALETTE;\n const height = options.height ?? rng.range(2.6, 3.1);\n\n const group = new Group();\n group.name = 'lamp';\n const metal = new MeshStandardMaterial({ color: palette.metal, flatShading: true });\n\n const post = new Mesh(new CylinderGeometry(0.05, 0.08, height, 6), metal);\n post.position.y = height / 2;\n group.add(post);\n\n const head = new Mesh(new BoxGeometry(0.34, 0.26, 0.34), metal);\n head.position.y = height + 0.1;\n group.add(head);\n\n const bulb = new Mesh(\n new SphereGeometry(0.11, 8, 6),\n new MeshStandardMaterial({\n color: palette.lampGlow,\n emissive: palette.lampGlow,\n emissiveIntensity: 1.6,\n })\n );\n bulb.position.y = height - 0.03;\n group.add(bulb);\n\n if (options.light) {\n const light = new PointLight(palette.lampGlow, options.lightIntensity ?? 6, 12, 1.8);\n light.position.y = height - 0.05;\n group.add(light);\n }\n\n return { object: group, obstacleRadius: 0.25 };\n}\n","import {\n BufferAttribute,\n Color,\n Mesh,\n MeshStandardMaterial,\n PlaneGeometry,\n} from 'three';\nimport { fractalNoise2 } from '../core/random';\nimport { DEFAULT_PALETTE, type Palette } from '../core/palette';\n\nexport interface TerrainOptions {\n seed?: number;\n /** Square side length. Default 80. */\n size?: number;\n /** Vertices per side. Default 96. */\n resolution?: number;\n /** Peak height. Default 6. */\n amplitude?: number;\n /** Noise feature size in world units. Default 28. */\n noiseScale?: number;\n octaves?: number;\n /** Flatten low areas into meadows (0–1, higher = flatter valleys). Default 0.55. */\n valleyFlatness?: number;\n palette?: Palette;\n}\n\nexport interface Terrain {\n mesh: Mesh;\n /** Exact analytic height at any (x, z) — same function that built the\n * mesh, so agents/navmesh queries never disagree with the visuals. */\n heightAt(x: number, z: number): number;\n size: number;\n seed: number;\n}\n\n/**\n * A seeded low-poly terrain: fractal value noise displacing a plane, with\n * height/slope-banded vertex colors (grass → high grass → cliff → peak).\n * The height function is exported, not just baked into vertices — that's\n * what lets gameplay (spawning, scattering, navmesh baking, agent ground\n * clamping) agree exactly with what's rendered.\n */\nexport function createTerrain(options: TerrainOptions = {}): Terrain {\n const seed = options.seed ?? 1;\n const size = options.size ?? 80;\n const resolution = options.resolution ?? 96;\n const amplitude = options.amplitude ?? 6;\n const noiseScale = options.noiseScale ?? 28;\n const octaves = options.octaves ?? 4;\n const flatness = options.valleyFlatness ?? 0.55;\n const palette = options.palette ?? DEFAULT_PALETTE;\n\n const heightAt = (x: number, z: number): number => {\n const n = fractalNoise2(x / noiseScale, z / noiseScale, seed, octaves);\n // Push valleys down and flatten them; keep peaks.\n const shaped = Math.pow(n, 1 + flatness * 2);\n return shaped * amplitude;\n };\n\n const geometry = new PlaneGeometry(size, size, resolution - 1, resolution - 1);\n geometry.rotateX(-Math.PI / 2);\n const positions = geometry.getAttribute('position') as BufferAttribute;\n for (let i = 0; i < positions.count; i++) {\n positions.setY(i, heightAt(positions.getX(i), positions.getZ(i)));\n }\n geometry.computeVertexNormals();\n\n // Vertex colors by height band + slope.\n const normals = geometry.getAttribute('normal') as BufferAttribute;\n const colors = new Float32Array(positions.count * 3);\n const grassLow = new Color(palette.grassLow);\n const grassHigh = new Color(palette.grassHigh);\n const cliff = new Color(palette.cliff);\n const peak = new Color(palette.peak);\n const scratch = new Color();\n for (let i = 0; i < positions.count; i++) {\n const h = positions.getY(i) / amplitude;\n const slope = 1 - normals.getY(i); // 0 flat … 1 vertical\n scratch.copy(grassLow).lerp(grassHigh, Math.min(1, h * 1.6));\n if (h > 0.75) scratch.lerp(peak, (h - 0.75) * 4);\n if (slope > 0.15) scratch.lerp(cliff, Math.min(1, (slope - 0.15) * 4));\n colors[i * 3] = scratch.r;\n colors[i * 3 + 1] = scratch.g;\n colors[i * 3 + 2] = scratch.b;\n }\n geometry.setAttribute('color', new BufferAttribute(colors, 3));\n\n const mesh = new Mesh(\n geometry,\n new MeshStandardMaterial({ vertexColors: true, flatShading: true })\n );\n mesh.name = 'terrain';\n\n return { mesh, heightAt, size, seed };\n}\n","import {\n BackSide,\n Color,\n Mesh,\n ShaderMaterial,\n SphereGeometry,\n} from 'three';\nimport { DEFAULT_PALETTE, type Palette } from '../core/palette';\n\nexport interface SkyOptions {\n topColor?: number;\n bottomColor?: number;\n radius?: number;\n palette?: Palette;\n}\n\nexport interface Sky {\n mesh: Mesh;\n setColors(top: number, bottom: number): void;\n}\n\n/**\n * A gradient sky dome (vertical color blend on an inverted sphere).\n * Colors default to the palette, so themed scenes get matching skies.\n */\nexport function createSky(options: SkyOptions = {}): Sky {\n const palette = options.palette ?? DEFAULT_PALETTE;\n const material = new ShaderMaterial({\n side: BackSide,\n depthWrite: false,\n uniforms: {\n topColor: { value: new Color(options.topColor ?? palette.skyTop) },\n bottomColor: { value: new Color(options.bottomColor ?? palette.skyBottom) },\n },\n vertexShader: /* glsl */ `\n varying vec3 vWorld;\n void main() {\n vWorld = (modelMatrix * vec4(position, 1.0)).xyz;\n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n }`,\n fragmentShader: /* glsl */ `\n uniform vec3 topColor;\n uniform vec3 bottomColor;\n varying vec3 vWorld;\n void main() {\n float t = clamp(normalize(vWorld).y * 0.5 + 0.5, 0.0, 1.0);\n gl_FragColor = vec4(mix(bottomColor, topColor, pow(t, 0.8)), 1.0);\n }`,\n });\n const mesh = new Mesh(new SphereGeometry(options.radius ?? 400, 16, 12), material);\n mesh.name = 'sky';\n\n return {\n mesh,\n setColors(top, bottom) {\n (material.uniforms.topColor.value as Color).setHex(top);\n (material.uniforms.bottomColor.value as Color).setHex(bottom);\n },\n };\n}\n","import {\n AmbientLight,\n DirectionalLight,\n Fog,\n Group,\n HemisphereLight,\n type Scene,\n} from 'three';\nimport { DEFAULT_PALETTE, type Palette } from '../core/palette';\n\nexport type LightingPreset = 'day' | 'golden-hour' | 'overcast' | 'night';\n\nexport interface LightingRig {\n group: Group;\n sun: DirectionalLight;\n ambient: AmbientLight;\n hemisphere: HemisphereLight;\n}\n\nconst PRESETS: Record<\n LightingPreset,\n { sun: number; sunIntensity: number; sunPos: [number, number, number]; ambient: number; ambientIntensity: number; skyTint: number; groundTint: number }\n> = {\n day: {\n sun: 0xfff4e0, sunIntensity: 1.6, sunPos: [30, 45, 20],\n ambient: 0xdfeaff, ambientIntensity: 0.35, skyTint: 0xbcd8ff, groundTint: 0x6a7d66,\n },\n 'golden-hour': {\n sun: 0xffb861, sunIntensity: 1.7, sunPos: [40, 14, -12],\n ambient: 0xffdcc0, ambientIntensity: 0.3, skyTint: 0xffcf9e, groundTint: 0x7a6a58,\n },\n overcast: {\n sun: 0xd8dee8, sunIntensity: 0.7, sunPos: [12, 40, 8],\n ambient: 0xcfd6e0, ambientIntensity: 0.65, skyTint: 0xc8d0dc, groundTint: 0x707a78,\n },\n night: {\n sun: 0x8ea6d8, sunIntensity: 0.35, sunPos: [-20, 30, -25],\n ambient: 0x3a4468, ambientIntensity: 0.35, skyTint: 0x2c3560, groundTint: 0x1e2430,\n },\n};\n\n/**\n * The three lights every scene rebuilds, as a preset: warm directional\n * \"sun\" (position doubles as light direction), ambient fill, and a\n * hemisphere tint. Retune any of them via the returned rig.\n */\nexport function createLightingRig(preset: LightingPreset = 'day'): LightingRig {\n const config = PRESETS[preset];\n const group = new Group();\n group.name = `lighting-${preset}`;\n const sun = new DirectionalLight(config.sun, config.sunIntensity);\n sun.position.set(...config.sunPos);\n const ambient = new AmbientLight(config.ambient, config.ambientIntensity);\n const hemisphere = new HemisphereLight(config.skyTint, config.groundTint, 0.5);\n group.add(sun, ambient, hemisphere);\n return { group, sun, ambient, hemisphere };\n}\n\nexport type FogPreset = 'clear' | 'haze' | 'thick' | 'eerie';\n\nconst FOG: Record<Exclude<FogPreset, 'clear'>, { near: number; far: number }> = {\n haze: { near: 45, far: 160 },\n thick: { near: 12, far: 70 },\n eerie: { near: 6, far: 42 },\n};\n\n/** Distance fog matched to the palette's fog color. 'clear' removes it. */\nexport function applyFog(scene: Scene, preset: FogPreset, palette: Palette = DEFAULT_PALETTE): void {\n if (preset === 'clear') {\n scene.fog = null;\n return;\n }\n const { near, far } = FOG[preset];\n scene.fog = new Fog(palette.fog, near, far);\n}\n","import {\n DynamicDrawUsage,\n Group,\n InstancedMesh,\n Matrix4,\n Mesh,\n Quaternion,\n Vector3,\n} from 'three';\nimport { Rng, valueNoise2 } from '../core/random';\nimport type { Obstacle, Prop } from '../core/types';\n\nexport interface ScatterItem {\n /** Prop factory, called once per visual variant with a seeded Rng. */\n create(rng: Rng): Prop;\n /** Relative frequency among items. Default 1. */\n weight?: number;\n /** Distinct variants generated per item (visual variety). Default 4. */\n variants?: number;\n /** Per-instance uniform scale range. Default [0.8, 1.25]. */\n scale?: [number, number];\n}\n\nexport interface ScatterOptions {\n seed?: number;\n items: ScatterItem[];\n /** Placement region in the XZ plane. */\n area: { min: { x: number; z: number }; max: { x: number; z: number } };\n /** Ground height lookup; a number means flat ground. Default 0. */\n surface?: number | ((x: number, z: number) => number);\n /** Expected instances per square unit (thinned by density noise). */\n density?: number;\n /** Or an exact target count (before masks/spacing rejections). */\n count?: number;\n /** Veto function: return false to reject a candidate point. */\n mask?: (x: number, z: number, y: number) => boolean;\n /** Minimum distance between any two placements. Default 1.2. */\n minSpacing?: number;\n /** World-space circles to keep clear (paths, spawns, buildings). */\n keepOut?: Obstacle[] | { center: { x: number; z: number }; radius: number }[];\n /** Density-noise feature size in world units. Default 18. */\n clumpScale?: number;\n}\n\nexport interface Placement {\n position: Vector3;\n rotationY: number;\n scale: number;\n itemIndex: number;\n}\n\nexport interface ScatterResult {\n /** One InstancedMesh per template part — a handful of draw calls total. */\n group: Group;\n placements: Placement[];\n /** World-space steering obstacles for everything with a footprint. */\n obstacles: Obstacle[];\n count: number;\n}\n\n/**\n * Populate an area with seeded, instanced props: \"empty plane → forest\"\n * in one call. Placement uses density noise for natural clumping, a\n * spatial hash for minimum spacing, and masks/keep-out circles for\n * exclusion. Rendering merges every placement into InstancedMeshes — a\n * few draw calls for thousands of props.\n *\n * ```ts\n * const forest = scatter({\n * seed: 7,\n * area: { min: { x: -40, z: -40 }, max: { x: 40, z: 40 } },\n * surface: terrain.heightAt,\n * density: 0.04,\n * items: [\n * { create: (rng) => createTree({ seed: rng.int(1, 1e9) }), weight: 3 },\n * { create: (rng) => createRock({ seed: rng.int(1, 1e9) }) },\n * ],\n * mask: (x, z, y) => y > 0.5 && y < 5, // between shore and peaks\n * keepOut: [{ center: village, radius: 12 }],\n * });\n * scene.add(forest.group);\n * agent.addBehavior(new ObstacleAvoidance(() => forest.obstacles));\n * ```\n */\nexport function scatter(options: ScatterOptions): ScatterResult {\n const seed = options.seed ?? 1;\n const rng = new Rng(seed);\n const { area } = options;\n const width = area.max.x - area.min.x;\n const depth = area.max.z - area.min.z;\n const surface = options.surface ?? 0;\n const heightAt =\n typeof surface === 'number' ? () => surface : (x: number, z: number) => surface(x, z);\n const minSpacing = options.minSpacing ?? 1.2;\n const clumpScale = options.clumpScale ?? 18;\n const attempts = options.count ?? Math.round(width * depth * (options.density ?? 0.03));\n\n const keepOut = (options.keepOut ?? []).map((k) => ({\n x: k.center.x,\n z: 'z' in k.center ? k.center.z : (k.center as { z: number }).z,\n radius: k.radius,\n }));\n\n // Weighted item choice.\n const weights = options.items.map((i) => i.weight ?? 1);\n const totalWeight = weights.reduce((a, b) => a + b, 0);\n const pickItem = (): number => {\n let r = rng.next() * totalWeight;\n for (let i = 0; i < weights.length; i++) {\n r -= weights[i];\n if (r <= 0) return i;\n }\n return weights.length - 1;\n };\n\n // Spatial hash for min-spacing rejection.\n const cell = Math.max(minSpacing, 0.001);\n const occupied = new Map<string, Array<{ x: number; z: number }>>();\n const tooClose = (x: number, z: number): boolean => {\n const cx = Math.floor(x / cell);\n const cz = Math.floor(z / cell);\n for (let dx = -1; dx <= 1; dx++) {\n for (let dz = -1; dz <= 1; dz++) {\n const bucket = occupied.get(`${cx + dx},${cz + dz}`);\n if (!bucket) continue;\n for (const p of bucket) {\n if ((p.x - x) ** 2 + (p.z - z) ** 2 < minSpacing * minSpacing) return true;\n }\n }\n }\n return false;\n };\n\n const placements: Placement[] = [];\n for (let i = 0; i < attempts; i++) {\n const x = rng.range(area.min.x, area.max.x);\n const z = rng.range(area.min.z, area.max.z);\n // Density noise: clumps and clearings instead of uniform confetti.\n if (valueNoise2(x / clumpScale, z / clumpScale, seed + 77) < rng.range(0.15, 0.55)) continue;\n if (keepOut.some((k) => (k.x - x) ** 2 + (k.z - z) ** 2 < k.radius * k.radius)) continue;\n if (tooClose(x, z)) continue;\n const y = heightAt(x, z);\n if (options.mask && !options.mask(x, z, y)) continue;\n\n const itemIndex = pickItem();\n const [minScale, maxScale] = options.items[itemIndex].scale ?? [0.8, 1.25];\n placements.push({\n position: new Vector3(x, y, z),\n rotationY: rng.range(0, Math.PI * 2),\n scale: rng.range(minScale, maxScale),\n itemIndex,\n });\n const key = `${Math.floor(x / cell)},${Math.floor(z / cell)}`;\n let bucket = occupied.get(key);\n if (!bucket) occupied.set(key, (bucket = []));\n bucket.push({ x, z });\n }\n\n // Build variants per item, bucket placements per variant, then emit one\n // InstancedMesh per (variant, mesh part).\n const group = new Group();\n group.name = 'scatter';\n const obstacles: Obstacle[] = [];\n const placementMatrix = new Matrix4();\n const composed = new Matrix4();\n const quaternion = new Quaternion();\n const up = new Vector3(0, 1, 0);\n const scaleVector = new Vector3();\n\n options.items.forEach((item, itemIndex) => {\n const variantCount = item.variants ?? 4;\n const variants: Prop[] = [];\n for (let v = 0; v < variantCount; v++) variants.push(item.create(rng.fork()));\n\n const byVariant: Placement[][] = variants.map(() => []);\n for (const placement of placements) {\n if (placement.itemIndex !== itemIndex) continue;\n // Deterministic variant choice from position.\n const v = Math.abs(Math.floor(placement.position.x * 31 + placement.position.z * 17)) % variantCount;\n byVariant[v].push(placement);\n }\n\n variants.forEach((variant, v) => {\n const list = byVariant[v];\n if (list.length === 0) return;\n variant.object.updateMatrixWorld(true);\n variant.object.traverse((child) => {\n if (!(child instanceof Mesh)) return;\n const instanced = new InstancedMesh(child.geometry, child.material, list.length);\n instanced.instanceMatrix.setUsage(DynamicDrawUsage);\n list.forEach((placement, index) => {\n quaternion.setFromAxisAngle(up, placement.rotationY);\n scaleVector.setScalar(placement.scale);\n placementMatrix.compose(placement.position, quaternion, scaleVector);\n composed.multiplyMatrices(placementMatrix, child.matrixWorld);\n instanced.setMatrixAt(index, composed);\n });\n instanced.instanceMatrix.needsUpdate = true;\n group.add(instanced);\n });\n if (variant.obstacleRadius > 0) {\n for (const placement of list) {\n obstacles.push({\n center: placement.position.clone(),\n radius: variant.obstacleRadius * placement.scale,\n });\n }\n }\n });\n });\n\n return { group, placements, obstacles, count: placements.length };\n}\n"],"mappings":"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","names":["import_three","import_three","import_three","import_three","import_three","import_three","import_three","import_three","import_three","maxScale"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
import { Vector3, Group, Mesh, DirectionalLight, AmbientLight, HemisphereLight, Scene } from 'three';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Deterministic seeded randomness — the backbone of SCENA. Same seed,
|
|
5
|
+
* same tree; forests are reproducible, diffable and network-syncable.
|
|
6
|
+
*/
|
|
7
|
+
declare class Rng {
|
|
8
|
+
private state;
|
|
9
|
+
constructor(seed?: number);
|
|
10
|
+
/** Next float in [0, 1) (mulberry32). */
|
|
11
|
+
next(): number;
|
|
12
|
+
/** Float in [min, max). */
|
|
13
|
+
range(min: number, max: number): number;
|
|
14
|
+
/** Integer in [min, max] inclusive. */
|
|
15
|
+
int(min: number, max: number): number;
|
|
16
|
+
/** Random element of a non-empty array. */
|
|
17
|
+
pick<T>(items: readonly T[]): T;
|
|
18
|
+
/** value ± spread (uniform). */
|
|
19
|
+
jitter(value: number, spread: number): number;
|
|
20
|
+
/** A new independent Rng derived from this one. */
|
|
21
|
+
fork(): Rng;
|
|
22
|
+
}
|
|
23
|
+
/** Integer-lattice hash to [0, 1) — the base of the value noise. */
|
|
24
|
+
declare function hash2(ix: number, iz: number, seed: number): number;
|
|
25
|
+
/** 2D value noise in [0, 1). Continuous; used by terrain and scatter density. */
|
|
26
|
+
declare function valueNoise2(x: number, z: number, seed: number): number;
|
|
27
|
+
/** Fractal (octaved) value noise in [0, 1). */
|
|
28
|
+
declare function fractalNoise2(x: number, z: number, seed: number, octaves?: number, lacunarity?: number, gain?: number): number;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Theme palettes: one coherent set of colors shared by every generator,
|
|
32
|
+
* so procedural props look like a matched set rather than a junk drawer.
|
|
33
|
+
* Pass `palette` to any generator to restyle it; whole scenes retheme by
|
|
34
|
+
* building with a different palette.
|
|
35
|
+
*/
|
|
36
|
+
interface Palette {
|
|
37
|
+
foliage: number[];
|
|
38
|
+
trunk: number;
|
|
39
|
+
rock: number[];
|
|
40
|
+
wood: number;
|
|
41
|
+
woodDark: number;
|
|
42
|
+
metal: number;
|
|
43
|
+
lampGlow: number;
|
|
44
|
+
grassLow: number;
|
|
45
|
+
grassHigh: number;
|
|
46
|
+
cliff: number;
|
|
47
|
+
peak: number;
|
|
48
|
+
skyTop: number;
|
|
49
|
+
skyBottom: number;
|
|
50
|
+
fog: number;
|
|
51
|
+
}
|
|
52
|
+
declare const PALETTES: Record<'meadow' | 'autumn' | 'dusk', Palette>;
|
|
53
|
+
declare const DEFAULT_PALETTE: Palette;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* A steering obstacle in world space — structurally identical to GAMA's
|
|
57
|
+
* `Obstacle`, so SCENA props plug straight into `ObstacleAvoidance`
|
|
58
|
+
* without either library importing the other.
|
|
59
|
+
*/
|
|
60
|
+
interface Obstacle {
|
|
61
|
+
center: Vector3;
|
|
62
|
+
radius: number;
|
|
63
|
+
}
|
|
64
|
+
/** What a prop generator returns: the visual plus gameplay metadata. */
|
|
65
|
+
interface Prop {
|
|
66
|
+
object: Group;
|
|
67
|
+
/**
|
|
68
|
+
* Footprint radius for steering/placement, in the prop's local space
|
|
69
|
+
* (centered at its origin). 0 means walk-through (e.g. grass).
|
|
70
|
+
*/
|
|
71
|
+
obstacleRadius: number;
|
|
72
|
+
}
|
|
73
|
+
/** Collect world-space obstacles from placed props (call after positioning). */
|
|
74
|
+
declare function collectObstacles(props: Iterable<Prop>): Obstacle[];
|
|
75
|
+
|
|
76
|
+
interface TreeOptions {
|
|
77
|
+
seed?: number;
|
|
78
|
+
/** Overall height in world units. Default ~3.5–5 by seed. */
|
|
79
|
+
height?: number;
|
|
80
|
+
style?: 'pine' | 'oak';
|
|
81
|
+
palette?: Palette;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* A seeded low-poly tree. `pine` stacks cones; `oak` clusters foliage
|
|
85
|
+
* blobs on a forked trunk. Same seed → identical tree, forever.
|
|
86
|
+
*/
|
|
87
|
+
declare function createTree(options?: TreeOptions): Prop;
|
|
88
|
+
|
|
89
|
+
interface RockOptions {
|
|
90
|
+
seed?: number;
|
|
91
|
+
/** Approximate radius. Default ~0.4–1.1 by seed. */
|
|
92
|
+
size?: number;
|
|
93
|
+
palette?: Palette;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* A seeded low-poly boulder: an icosahedron with jittered vertices and a
|
|
97
|
+
* flattened underside so it sits on the ground.
|
|
98
|
+
*/
|
|
99
|
+
declare function createRock(options?: RockOptions): Prop;
|
|
100
|
+
|
|
101
|
+
interface CrateOptions {
|
|
102
|
+
seed?: number;
|
|
103
|
+
size?: number;
|
|
104
|
+
/** 0–1 color wear. Default 0.3. */
|
|
105
|
+
weathering?: number;
|
|
106
|
+
palette?: Palette;
|
|
107
|
+
}
|
|
108
|
+
/** A wooden crate: panel box with darker edge framing, seeded wear tint. */
|
|
109
|
+
declare function createCrate(options?: CrateOptions): Prop;
|
|
110
|
+
|
|
111
|
+
interface FenceOptions {
|
|
112
|
+
seed?: number;
|
|
113
|
+
/** Run length along local +x. Default 6. */
|
|
114
|
+
length?: number;
|
|
115
|
+
postSpacing?: number;
|
|
116
|
+
height?: number;
|
|
117
|
+
palette?: Palette;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* A rustic fence run along local +x, centered at the origin: posts with
|
|
121
|
+
* two slightly-crooked rails. Chain several and rotate to enclose areas.
|
|
122
|
+
* The obstacle radius covers the whole run (rough but steering-safe);
|
|
123
|
+
* for tight navigation, bake a navmesh — the geometry is authoritative.
|
|
124
|
+
*/
|
|
125
|
+
declare function createFence(options?: FenceOptions): Prop;
|
|
126
|
+
|
|
127
|
+
interface LampOptions {
|
|
128
|
+
seed?: number;
|
|
129
|
+
height?: number;
|
|
130
|
+
/** Add a real PointLight. Off by default — lights are a budget. */
|
|
131
|
+
light?: boolean;
|
|
132
|
+
lightIntensity?: number;
|
|
133
|
+
palette?: Palette;
|
|
134
|
+
}
|
|
135
|
+
/** A street lamp: post, head, glowing bulb, optional real PointLight. */
|
|
136
|
+
declare function createLamp(options?: LampOptions): Prop;
|
|
137
|
+
|
|
138
|
+
interface TerrainOptions {
|
|
139
|
+
seed?: number;
|
|
140
|
+
/** Square side length. Default 80. */
|
|
141
|
+
size?: number;
|
|
142
|
+
/** Vertices per side. Default 96. */
|
|
143
|
+
resolution?: number;
|
|
144
|
+
/** Peak height. Default 6. */
|
|
145
|
+
amplitude?: number;
|
|
146
|
+
/** Noise feature size in world units. Default 28. */
|
|
147
|
+
noiseScale?: number;
|
|
148
|
+
octaves?: number;
|
|
149
|
+
/** Flatten low areas into meadows (0–1, higher = flatter valleys). Default 0.55. */
|
|
150
|
+
valleyFlatness?: number;
|
|
151
|
+
palette?: Palette;
|
|
152
|
+
}
|
|
153
|
+
interface Terrain {
|
|
154
|
+
mesh: Mesh;
|
|
155
|
+
/** Exact analytic height at any (x, z) — same function that built the
|
|
156
|
+
* mesh, so agents/navmesh queries never disagree with the visuals. */
|
|
157
|
+
heightAt(x: number, z: number): number;
|
|
158
|
+
size: number;
|
|
159
|
+
seed: number;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* A seeded low-poly terrain: fractal value noise displacing a plane, with
|
|
163
|
+
* height/slope-banded vertex colors (grass → high grass → cliff → peak).
|
|
164
|
+
* The height function is exported, not just baked into vertices — that's
|
|
165
|
+
* what lets gameplay (spawning, scattering, navmesh baking, agent ground
|
|
166
|
+
* clamping) agree exactly with what's rendered.
|
|
167
|
+
*/
|
|
168
|
+
declare function createTerrain(options?: TerrainOptions): Terrain;
|
|
169
|
+
|
|
170
|
+
interface SkyOptions {
|
|
171
|
+
topColor?: number;
|
|
172
|
+
bottomColor?: number;
|
|
173
|
+
radius?: number;
|
|
174
|
+
palette?: Palette;
|
|
175
|
+
}
|
|
176
|
+
interface Sky {
|
|
177
|
+
mesh: Mesh;
|
|
178
|
+
setColors(top: number, bottom: number): void;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* A gradient sky dome (vertical color blend on an inverted sphere).
|
|
182
|
+
* Colors default to the palette, so themed scenes get matching skies.
|
|
183
|
+
*/
|
|
184
|
+
declare function createSky(options?: SkyOptions): Sky;
|
|
185
|
+
|
|
186
|
+
type LightingPreset = 'day' | 'golden-hour' | 'overcast' | 'night';
|
|
187
|
+
interface LightingRig {
|
|
188
|
+
group: Group;
|
|
189
|
+
sun: DirectionalLight;
|
|
190
|
+
ambient: AmbientLight;
|
|
191
|
+
hemisphere: HemisphereLight;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* The three lights every scene rebuilds, as a preset: warm directional
|
|
195
|
+
* "sun" (position doubles as light direction), ambient fill, and a
|
|
196
|
+
* hemisphere tint. Retune any of them via the returned rig.
|
|
197
|
+
*/
|
|
198
|
+
declare function createLightingRig(preset?: LightingPreset): LightingRig;
|
|
199
|
+
type FogPreset = 'clear' | 'haze' | 'thick' | 'eerie';
|
|
200
|
+
/** Distance fog matched to the palette's fog color. 'clear' removes it. */
|
|
201
|
+
declare function applyFog(scene: Scene, preset: FogPreset, palette?: Palette): void;
|
|
202
|
+
|
|
203
|
+
interface ScatterItem {
|
|
204
|
+
/** Prop factory, called once per visual variant with a seeded Rng. */
|
|
205
|
+
create(rng: Rng): Prop;
|
|
206
|
+
/** Relative frequency among items. Default 1. */
|
|
207
|
+
weight?: number;
|
|
208
|
+
/** Distinct variants generated per item (visual variety). Default 4. */
|
|
209
|
+
variants?: number;
|
|
210
|
+
/** Per-instance uniform scale range. Default [0.8, 1.25]. */
|
|
211
|
+
scale?: [number, number];
|
|
212
|
+
}
|
|
213
|
+
interface ScatterOptions {
|
|
214
|
+
seed?: number;
|
|
215
|
+
items: ScatterItem[];
|
|
216
|
+
/** Placement region in the XZ plane. */
|
|
217
|
+
area: {
|
|
218
|
+
min: {
|
|
219
|
+
x: number;
|
|
220
|
+
z: number;
|
|
221
|
+
};
|
|
222
|
+
max: {
|
|
223
|
+
x: number;
|
|
224
|
+
z: number;
|
|
225
|
+
};
|
|
226
|
+
};
|
|
227
|
+
/** Ground height lookup; a number means flat ground. Default 0. */
|
|
228
|
+
surface?: number | ((x: number, z: number) => number);
|
|
229
|
+
/** Expected instances per square unit (thinned by density noise). */
|
|
230
|
+
density?: number;
|
|
231
|
+
/** Or an exact target count (before masks/spacing rejections). */
|
|
232
|
+
count?: number;
|
|
233
|
+
/** Veto function: return false to reject a candidate point. */
|
|
234
|
+
mask?: (x: number, z: number, y: number) => boolean;
|
|
235
|
+
/** Minimum distance between any two placements. Default 1.2. */
|
|
236
|
+
minSpacing?: number;
|
|
237
|
+
/** World-space circles to keep clear (paths, spawns, buildings). */
|
|
238
|
+
keepOut?: Obstacle[] | {
|
|
239
|
+
center: {
|
|
240
|
+
x: number;
|
|
241
|
+
z: number;
|
|
242
|
+
};
|
|
243
|
+
radius: number;
|
|
244
|
+
}[];
|
|
245
|
+
/** Density-noise feature size in world units. Default 18. */
|
|
246
|
+
clumpScale?: number;
|
|
247
|
+
}
|
|
248
|
+
interface Placement {
|
|
249
|
+
position: Vector3;
|
|
250
|
+
rotationY: number;
|
|
251
|
+
scale: number;
|
|
252
|
+
itemIndex: number;
|
|
253
|
+
}
|
|
254
|
+
interface ScatterResult {
|
|
255
|
+
/** One InstancedMesh per template part — a handful of draw calls total. */
|
|
256
|
+
group: Group;
|
|
257
|
+
placements: Placement[];
|
|
258
|
+
/** World-space steering obstacles for everything with a footprint. */
|
|
259
|
+
obstacles: Obstacle[];
|
|
260
|
+
count: number;
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Populate an area with seeded, instanced props: "empty plane → forest"
|
|
264
|
+
* in one call. Placement uses density noise for natural clumping, a
|
|
265
|
+
* spatial hash for minimum spacing, and masks/keep-out circles for
|
|
266
|
+
* exclusion. Rendering merges every placement into InstancedMeshes — a
|
|
267
|
+
* few draw calls for thousands of props.
|
|
268
|
+
*
|
|
269
|
+
* ```ts
|
|
270
|
+
* const forest = scatter({
|
|
271
|
+
* seed: 7,
|
|
272
|
+
* area: { min: { x: -40, z: -40 }, max: { x: 40, z: 40 } },
|
|
273
|
+
* surface: terrain.heightAt,
|
|
274
|
+
* density: 0.04,
|
|
275
|
+
* items: [
|
|
276
|
+
* { create: (rng) => createTree({ seed: rng.int(1, 1e9) }), weight: 3 },
|
|
277
|
+
* { create: (rng) => createRock({ seed: rng.int(1, 1e9) }) },
|
|
278
|
+
* ],
|
|
279
|
+
* mask: (x, z, y) => y > 0.5 && y < 5, // between shore and peaks
|
|
280
|
+
* keepOut: [{ center: village, radius: 12 }],
|
|
281
|
+
* });
|
|
282
|
+
* scene.add(forest.group);
|
|
283
|
+
* agent.addBehavior(new ObstacleAvoidance(() => forest.obstacles));
|
|
284
|
+
* ```
|
|
285
|
+
*/
|
|
286
|
+
declare function scatter(options: ScatterOptions): ScatterResult;
|
|
287
|
+
|
|
288
|
+
export { type CrateOptions, DEFAULT_PALETTE, type FenceOptions, type FogPreset, type LampOptions, type LightingPreset, type LightingRig, type Obstacle, PALETTES, type Palette, type Placement, type Prop, Rng, type RockOptions, type ScatterItem, type ScatterOptions, type ScatterResult, type Sky, type SkyOptions, type Terrain, type TerrainOptions, type TreeOptions, applyFog, collectObstacles, createCrate, createFence, createLamp, createLightingRig, createRock, createSky, createTerrain, createTree, fractalNoise2, hash2, scatter, valueNoise2 };
|