scena3d 0.3.0 → 0.4.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/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { Vector3, Group, Mesh, DirectionalLight, AmbientLight, HemisphereLight, Scene, Object3D, Material } from 'three';
1
+ import { Group, Vector3, Mesh, DirectionalLight, AmbientLight, HemisphereLight, Scene, Object3D, Material } from 'three';
2
2
 
3
3
  /**
4
4
  * Deterministic seeded randomness — the backbone of SCENA. Same seed,
@@ -477,9 +477,73 @@ interface Village {
477
477
  */
478
478
  declare function createVillage(options?: VillageOptions): Village;
479
479
 
480
+ /**
481
+ * The kit grid: every kit piece snaps to this cell size (in world units).
482
+ * Shared snap dimensions are what make pieces combinable — a doorway cut
483
+ * in one wall lines up with the corridor on the other side.
484
+ */
485
+ declare const KIT_UNIT = 2;
486
+ interface KitOptions {
487
+ seed?: number;
488
+ /** Cell size override. Default KIT_UNIT. */
489
+ unit?: number;
490
+ /** Wall height. Default 2.6. */
491
+ wallHeight?: number;
492
+ /** Add real PointLights to this many torches. Default 4. */
493
+ torchLights?: number;
494
+ palette?: Palette;
495
+ }
496
+ interface Kit {
497
+ group: Group;
498
+ /** One obstacle per wall cell — feed GAMA's ObstacleAvoidance. */
499
+ obstacles: Obstacle[];
500
+ /** World positions of 'S' cells (player/NPC spawn points). */
501
+ spawns: Vector3[];
502
+ /** World positions of 'T' cells (torches), lit in placement order. */
503
+ torches: Vector3[];
504
+ /** Is (x, z) over a walkable floor cell? */
505
+ floorAt(x: number, z: number): boolean;
506
+ /** Footprint in world units: { width, depth } centered on the origin. */
507
+ size: {
508
+ width: number;
509
+ depth: number;
510
+ };
511
+ }
512
+ /**
513
+ * Assemble a dungeon/compound from an ASCII map — each character is one
514
+ * KIT_UNIT × KIT_UNIT cell, centered on the group origin:
515
+ *
516
+ * - `#` wall block (blocks movement, becomes an obstacle)
517
+ * - `.` floor tile
518
+ * - `D` doorway: floor + lintel spanning the gap overhead
519
+ * - `T` floor + standing torch (emissive flame; `createDayCycle` adopts it)
520
+ * - `S` floor + recorded spawn point
521
+ * - ` ` nothing
522
+ *
523
+ * ```ts
524
+ * const fort = assembleKit([
525
+ * '#######',
526
+ * '#..T..#',
527
+ * '#.....D',
528
+ * '#..S..#',
529
+ * '#######',
530
+ * ], { palette });
531
+ * fort.group.position.set(20, terrain.heightAt(20, 5), 5);
532
+ * ```
533
+ *
534
+ * Walls and floors render as two InstancedMeshes regardless of map size.
535
+ */
536
+ declare function assembleKit(rows: string[], options?: KitOptions): Kit;
537
+
480
538
  interface ScatterItem {
481
539
  /** Prop factory, called once per visual variant with a seeded Rng. */
482
540
  create(rng: Rng): Prop;
541
+ /**
542
+ * Simplified far-distance factory for LOD (e.g. a single cone for a
543
+ * tree). Only used when `ScatterOptions.lod` is set; items without one
544
+ * stay full-detail at every distance.
545
+ */
546
+ createFar?(rng: Rng): Prop;
483
547
  /** Relative frequency among items. Default 1. */
484
548
  weight?: number;
485
549
  /** Distinct variants generated per item (visual variety). Default 4. */
@@ -521,6 +585,18 @@ interface ScatterOptions {
521
585
  }[];
522
586
  /** Density-noise feature size in world units. Default 18. */
523
587
  clumpScale?: number;
588
+ /**
589
+ * Tile-based LOD: placements are bucketed into square tiles; tiles
590
+ * beyond `distance` from the camera swap full-detail instances for the
591
+ * items' `createFar` variants (with 10% hysteresis so tiles don't
592
+ * flicker at the boundary). Call `result.update(camera)` each frame.
593
+ */
594
+ lod?: {
595
+ /** Camera distance at which tiles switch to far variants. */
596
+ distance: number;
597
+ /** Tile side in world units. Default 16. */
598
+ tileSize?: number;
599
+ };
524
600
  }
525
601
  interface Placement {
526
602
  position: Vector3;
@@ -528,6 +604,14 @@ interface Placement {
528
604
  scale: number;
529
605
  itemIndex: number;
530
606
  }
607
+ interface ScatterTile {
608
+ center: {
609
+ x: number;
610
+ z: number;
611
+ };
612
+ near: Group;
613
+ far: Group;
614
+ }
531
615
  interface ScatterResult {
532
616
  /** One InstancedMesh per template part — a handful of draw calls total. */
533
617
  group: Group;
@@ -535,6 +619,12 @@ interface ScatterResult {
535
619
  /** World-space steering obstacles for everything with a footprint. */
536
620
  obstacles: Obstacle[];
537
621
  count: number;
622
+ /** LOD tiles (present when `lod` was requested). */
623
+ tiles?: ScatterTile[];
624
+ /** Re-evaluate tile LOD against the camera (present when `lod` was requested). */
625
+ update?(camera: {
626
+ position: Vector3;
627
+ }): void;
538
628
  }
539
629
  /**
540
630
  * Populate an area with seeded, instanced props: "empty plane → forest"
@@ -562,4 +652,168 @@ interface ScatterResult {
562
652
  */
563
653
  declare function scatter(options: ScatterOptions): ScatterResult;
564
654
 
565
- export { type BushOptions, type CrateOptions, DEFAULT_PALETTE, type DayCycle, type DayCycleOptions, type FenceOptions, type FogPreset, type GrassOptions, type HouseOptions, type LampOptions, type LightingPreset, type LightingRig, type Obstacle, PALETTES, type Palette, type PathOptions, type Placement, type Prop, Rng, type RockOptions, type RuinOptions, type ScatterItem, type ScatterOptions, type ScatterResult, type Sky, type SkyOptions, type Terrain, type TerrainOptions, type TowerOptions, type TreeOptions, type Village, type VillageOptions, type Water, type WaterOptions, type WellOptions, type Wind, type WindOptions, type WorldPath, aboveWater, applyFog, applyWind, collectObstacles, createBush, createCrate, createDayCycle, createFence, createGrassTuft, createHouse, createLamp, createLightingRig, createPath, createRock, createRuin, createSky, createTerrain, createTower, createTree, createVillage, createWater, createWell, fractalNoise2, hash2, scatter, valueNoise2 };
655
+ /** Prop vocabulary available to manifest scatters. */
656
+ type ScatterPropType = 'tree' | 'rock' | 'bush' | 'grass' | 'crate' | 'fence' | 'lamp';
657
+ interface ManifestScatterItem {
658
+ type: ScatterPropType;
659
+ weight?: number;
660
+ variants?: number;
661
+ scale?: [number, number];
662
+ }
663
+ interface ManifestScatter {
664
+ /** Defaults to the terrain footprint (slightly inset). */
665
+ area?: {
666
+ min: {
667
+ x: number;
668
+ z: number;
669
+ };
670
+ max: {
671
+ x: number;
672
+ z: number;
673
+ };
674
+ };
675
+ density?: number;
676
+ count?: number;
677
+ minSpacing?: number;
678
+ clumpScale?: number;
679
+ items: ManifestScatterItem[];
680
+ /** Reject spots above this ground height (keep peaks bare). */
681
+ maxHeight?: number;
682
+ /** Keep placements ashore. Default true when the scene has water. */
683
+ avoidWater?: boolean;
684
+ /** Keep placements off paths and out of the village. Default true. */
685
+ avoidPaths?: boolean;
686
+ /** Opt into tile-based LOD (see ScatterOptions.lod). */
687
+ lod?: {
688
+ distance: number;
689
+ tileSize?: number;
690
+ };
691
+ }
692
+ /**
693
+ * A whole world as plain JSON — every field is data, so manifests can be
694
+ * stored, diffed, sent over the network, or edited in a tool.
695
+ */
696
+ interface SceneManifest {
697
+ seed?: number;
698
+ palette?: keyof typeof PALETTES;
699
+ terrain?: {
700
+ size?: number;
701
+ resolution?: number;
702
+ amplitude?: number;
703
+ noiseScale?: number;
704
+ octaves?: number;
705
+ valleyFlatness?: number;
706
+ };
707
+ water?: {
708
+ level: number;
709
+ size?: number;
710
+ };
711
+ /** Sky dome. Default true. */
712
+ sky?: boolean;
713
+ lighting?: LightingPreset;
714
+ /** Fog preset, or false for none. Default 'haze'. */
715
+ fog?: FogPreset | false;
716
+ /** Animated day-night cycle. Off unless specified. */
717
+ dayCycle?: {
718
+ dayLength?: number;
719
+ timeOfDay?: number;
720
+ };
721
+ paths?: Array<{
722
+ points: Array<{
723
+ x: number;
724
+ z: number;
725
+ }>;
726
+ width?: number;
727
+ loop?: boolean;
728
+ }>;
729
+ village?: {
730
+ center?: {
731
+ x: number;
732
+ z: number;
733
+ };
734
+ radius?: number;
735
+ houses?: number;
736
+ lampLights?: number;
737
+ tower?: boolean;
738
+ ruin?: boolean;
739
+ };
740
+ scatters?: ManifestScatter[];
741
+ /** Vegetation sway. Default on; false disables, or set the strength. */
742
+ wind?: boolean | {
743
+ strength?: number;
744
+ };
745
+ }
746
+ interface BuiltScene {
747
+ /** Everything visual, ready to add to a scene (already added if one was passed). */
748
+ group: Group;
749
+ palette: Palette;
750
+ terrain?: Terrain;
751
+ water?: Water;
752
+ sky?: Sky;
753
+ rig: LightingRig;
754
+ cycle?: DayCycle;
755
+ paths: WorldPath[];
756
+ village?: Village;
757
+ scatters: ScatterResult[];
758
+ /** Combined steering obstacles from the village and every scatter. */
759
+ obstacles: Obstacle[];
760
+ /** Ground height (terrain's, or 0 for flat scenes). */
761
+ heightAt(x: number, z: number): number;
762
+ /** Advance water, wind and the day cycle. Call from your frame loop. */
763
+ update(dt: number): void;
764
+ }
765
+ /**
766
+ * Compile a `SceneManifest` into a live world: terrain, water, sky,
767
+ * lighting, fog, paths, a village, scatters and the day cycle — with all
768
+ * the cross-feature wiring the forest demo does by hand applied
769
+ * automatically (scatters stay ashore, off paths and out of the village;
770
+ * the village avoids water and roads; village lamps and windows feed the
771
+ * day cycle).
772
+ *
773
+ * ```ts
774
+ * const world = buildScene(JSON.parse(manifestJson), scene);
775
+ * game.onUpdate((t) => world.update(t.delta));
776
+ * agent.addBehavior(new ObstacleAvoidance(() => world.obstacles));
777
+ * ```
778
+ */
779
+ declare function buildScene(manifest: SceneManifest, scene?: Scene): BuiltScene;
780
+
781
+ interface Markers {
782
+ /** `spawn_<name>` → world position. */
783
+ spawns: Record<string, Vector3>;
784
+ /** `route_<name>_<index>` → world positions ordered by index. */
785
+ routes: Record<string, Vector3[]>;
786
+ /** `obstacle_<name>` → steering obstacle (radius from the node's scale). */
787
+ obstacles: Obstacle[];
788
+ /** `keepout_<name>` → scatter keep-out circle (radius from scale). */
789
+ keepOut: Array<{
790
+ center: {
791
+ x: number;
792
+ z: number;
793
+ };
794
+ radius: number;
795
+ }>;
796
+ }
797
+ /**
798
+ * Extract gameplay markers from an object tree by naming convention — the
799
+ * bridge between a DCC tool (Blender empties, glTF nodes) and SCENA/GAMA:
800
+ *
801
+ * - `spawn_player`, `spawn_boss` → named spawn points
802
+ * - `route_patrol_0`, `route_patrol_1`, … → ordered patrol routes
803
+ * - `obstacle_statue` → steering obstacle; radius = max(scale.x, scale.z)
804
+ * - `keepout_plaza` → scatter keep-out circle; radius = max(scale.x, scale.z)
805
+ *
806
+ * Blender's duplicate suffixes (`.001`) are stripped, so `route_a_0.001`
807
+ * still parses. Positions are world-space (the tree is updated first).
808
+ *
809
+ * ```ts
810
+ * const gltf = await new GLTFLoader().loadAsync('level.glb');
811
+ * const markers = extractMarkers(gltf.scene);
812
+ * player.position.copy(markers.spawns.player);
813
+ * guard.addBehavior(new FollowPath(new Path(markers.routes.patrol, true), 1.5));
814
+ * scatter({ ..., keepOut: markers.keepOut });
815
+ * ```
816
+ */
817
+ declare function extractMarkers(root: Object3D): Markers;
818
+
819
+ export { type BuiltScene, type BushOptions, type CrateOptions, DEFAULT_PALETTE, type DayCycle, type DayCycleOptions, type FenceOptions, type FogPreset, type GrassOptions, type HouseOptions, KIT_UNIT, type Kit, type KitOptions, type LampOptions, type LightingPreset, type LightingRig, type ManifestScatter, type ManifestScatterItem, type Markers, type Obstacle, PALETTES, type Palette, type PathOptions, type Placement, type Prop, Rng, type RockOptions, type RuinOptions, type ScatterItem, type ScatterOptions, type ScatterPropType, type ScatterResult, type ScatterTile, type SceneManifest, type Sky, type SkyOptions, type Terrain, type TerrainOptions, type TowerOptions, type TreeOptions, type Village, type VillageOptions, type Water, type WaterOptions, type WellOptions, type Wind, type WindOptions, type WorldPath, aboveWater, applyFog, applyWind, assembleKit, buildScene, collectObstacles, createBush, createCrate, createDayCycle, createFence, createGrassTuft, createHouse, createLamp, createLightingRig, createPath, createRock, createRuin, createSky, createTerrain, createTower, createTree, createVillage, createWater, createWell, extractMarkers, fractalNoise2, hash2, scatter, valueNoise2 };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Vector3, Group, Mesh, DirectionalLight, AmbientLight, HemisphereLight, Scene, Object3D, Material } from 'three';
1
+ import { Group, Vector3, Mesh, DirectionalLight, AmbientLight, HemisphereLight, Scene, Object3D, Material } from 'three';
2
2
 
3
3
  /**
4
4
  * Deterministic seeded randomness — the backbone of SCENA. Same seed,
@@ -477,9 +477,73 @@ interface Village {
477
477
  */
478
478
  declare function createVillage(options?: VillageOptions): Village;
479
479
 
480
+ /**
481
+ * The kit grid: every kit piece snaps to this cell size (in world units).
482
+ * Shared snap dimensions are what make pieces combinable — a doorway cut
483
+ * in one wall lines up with the corridor on the other side.
484
+ */
485
+ declare const KIT_UNIT = 2;
486
+ interface KitOptions {
487
+ seed?: number;
488
+ /** Cell size override. Default KIT_UNIT. */
489
+ unit?: number;
490
+ /** Wall height. Default 2.6. */
491
+ wallHeight?: number;
492
+ /** Add real PointLights to this many torches. Default 4. */
493
+ torchLights?: number;
494
+ palette?: Palette;
495
+ }
496
+ interface Kit {
497
+ group: Group;
498
+ /** One obstacle per wall cell — feed GAMA's ObstacleAvoidance. */
499
+ obstacles: Obstacle[];
500
+ /** World positions of 'S' cells (player/NPC spawn points). */
501
+ spawns: Vector3[];
502
+ /** World positions of 'T' cells (torches), lit in placement order. */
503
+ torches: Vector3[];
504
+ /** Is (x, z) over a walkable floor cell? */
505
+ floorAt(x: number, z: number): boolean;
506
+ /** Footprint in world units: { width, depth } centered on the origin. */
507
+ size: {
508
+ width: number;
509
+ depth: number;
510
+ };
511
+ }
512
+ /**
513
+ * Assemble a dungeon/compound from an ASCII map — each character is one
514
+ * KIT_UNIT × KIT_UNIT cell, centered on the group origin:
515
+ *
516
+ * - `#` wall block (blocks movement, becomes an obstacle)
517
+ * - `.` floor tile
518
+ * - `D` doorway: floor + lintel spanning the gap overhead
519
+ * - `T` floor + standing torch (emissive flame; `createDayCycle` adopts it)
520
+ * - `S` floor + recorded spawn point
521
+ * - ` ` nothing
522
+ *
523
+ * ```ts
524
+ * const fort = assembleKit([
525
+ * '#######',
526
+ * '#..T..#',
527
+ * '#.....D',
528
+ * '#..S..#',
529
+ * '#######',
530
+ * ], { palette });
531
+ * fort.group.position.set(20, terrain.heightAt(20, 5), 5);
532
+ * ```
533
+ *
534
+ * Walls and floors render as two InstancedMeshes regardless of map size.
535
+ */
536
+ declare function assembleKit(rows: string[], options?: KitOptions): Kit;
537
+
480
538
  interface ScatterItem {
481
539
  /** Prop factory, called once per visual variant with a seeded Rng. */
482
540
  create(rng: Rng): Prop;
541
+ /**
542
+ * Simplified far-distance factory for LOD (e.g. a single cone for a
543
+ * tree). Only used when `ScatterOptions.lod` is set; items without one
544
+ * stay full-detail at every distance.
545
+ */
546
+ createFar?(rng: Rng): Prop;
483
547
  /** Relative frequency among items. Default 1. */
484
548
  weight?: number;
485
549
  /** Distinct variants generated per item (visual variety). Default 4. */
@@ -521,6 +585,18 @@ interface ScatterOptions {
521
585
  }[];
522
586
  /** Density-noise feature size in world units. Default 18. */
523
587
  clumpScale?: number;
588
+ /**
589
+ * Tile-based LOD: placements are bucketed into square tiles; tiles
590
+ * beyond `distance` from the camera swap full-detail instances for the
591
+ * items' `createFar` variants (with 10% hysteresis so tiles don't
592
+ * flicker at the boundary). Call `result.update(camera)` each frame.
593
+ */
594
+ lod?: {
595
+ /** Camera distance at which tiles switch to far variants. */
596
+ distance: number;
597
+ /** Tile side in world units. Default 16. */
598
+ tileSize?: number;
599
+ };
524
600
  }
525
601
  interface Placement {
526
602
  position: Vector3;
@@ -528,6 +604,14 @@ interface Placement {
528
604
  scale: number;
529
605
  itemIndex: number;
530
606
  }
607
+ interface ScatterTile {
608
+ center: {
609
+ x: number;
610
+ z: number;
611
+ };
612
+ near: Group;
613
+ far: Group;
614
+ }
531
615
  interface ScatterResult {
532
616
  /** One InstancedMesh per template part — a handful of draw calls total. */
533
617
  group: Group;
@@ -535,6 +619,12 @@ interface ScatterResult {
535
619
  /** World-space steering obstacles for everything with a footprint. */
536
620
  obstacles: Obstacle[];
537
621
  count: number;
622
+ /** LOD tiles (present when `lod` was requested). */
623
+ tiles?: ScatterTile[];
624
+ /** Re-evaluate tile LOD against the camera (present when `lod` was requested). */
625
+ update?(camera: {
626
+ position: Vector3;
627
+ }): void;
538
628
  }
539
629
  /**
540
630
  * Populate an area with seeded, instanced props: "empty plane → forest"
@@ -562,4 +652,168 @@ interface ScatterResult {
562
652
  */
563
653
  declare function scatter(options: ScatterOptions): ScatterResult;
564
654
 
565
- export { type BushOptions, type CrateOptions, DEFAULT_PALETTE, type DayCycle, type DayCycleOptions, type FenceOptions, type FogPreset, type GrassOptions, type HouseOptions, type LampOptions, type LightingPreset, type LightingRig, type Obstacle, PALETTES, type Palette, type PathOptions, type Placement, type Prop, Rng, type RockOptions, type RuinOptions, type ScatterItem, type ScatterOptions, type ScatterResult, type Sky, type SkyOptions, type Terrain, type TerrainOptions, type TowerOptions, type TreeOptions, type Village, type VillageOptions, type Water, type WaterOptions, type WellOptions, type Wind, type WindOptions, type WorldPath, aboveWater, applyFog, applyWind, collectObstacles, createBush, createCrate, createDayCycle, createFence, createGrassTuft, createHouse, createLamp, createLightingRig, createPath, createRock, createRuin, createSky, createTerrain, createTower, createTree, createVillage, createWater, createWell, fractalNoise2, hash2, scatter, valueNoise2 };
655
+ /** Prop vocabulary available to manifest scatters. */
656
+ type ScatterPropType = 'tree' | 'rock' | 'bush' | 'grass' | 'crate' | 'fence' | 'lamp';
657
+ interface ManifestScatterItem {
658
+ type: ScatterPropType;
659
+ weight?: number;
660
+ variants?: number;
661
+ scale?: [number, number];
662
+ }
663
+ interface ManifestScatter {
664
+ /** Defaults to the terrain footprint (slightly inset). */
665
+ area?: {
666
+ min: {
667
+ x: number;
668
+ z: number;
669
+ };
670
+ max: {
671
+ x: number;
672
+ z: number;
673
+ };
674
+ };
675
+ density?: number;
676
+ count?: number;
677
+ minSpacing?: number;
678
+ clumpScale?: number;
679
+ items: ManifestScatterItem[];
680
+ /** Reject spots above this ground height (keep peaks bare). */
681
+ maxHeight?: number;
682
+ /** Keep placements ashore. Default true when the scene has water. */
683
+ avoidWater?: boolean;
684
+ /** Keep placements off paths and out of the village. Default true. */
685
+ avoidPaths?: boolean;
686
+ /** Opt into tile-based LOD (see ScatterOptions.lod). */
687
+ lod?: {
688
+ distance: number;
689
+ tileSize?: number;
690
+ };
691
+ }
692
+ /**
693
+ * A whole world as plain JSON — every field is data, so manifests can be
694
+ * stored, diffed, sent over the network, or edited in a tool.
695
+ */
696
+ interface SceneManifest {
697
+ seed?: number;
698
+ palette?: keyof typeof PALETTES;
699
+ terrain?: {
700
+ size?: number;
701
+ resolution?: number;
702
+ amplitude?: number;
703
+ noiseScale?: number;
704
+ octaves?: number;
705
+ valleyFlatness?: number;
706
+ };
707
+ water?: {
708
+ level: number;
709
+ size?: number;
710
+ };
711
+ /** Sky dome. Default true. */
712
+ sky?: boolean;
713
+ lighting?: LightingPreset;
714
+ /** Fog preset, or false for none. Default 'haze'. */
715
+ fog?: FogPreset | false;
716
+ /** Animated day-night cycle. Off unless specified. */
717
+ dayCycle?: {
718
+ dayLength?: number;
719
+ timeOfDay?: number;
720
+ };
721
+ paths?: Array<{
722
+ points: Array<{
723
+ x: number;
724
+ z: number;
725
+ }>;
726
+ width?: number;
727
+ loop?: boolean;
728
+ }>;
729
+ village?: {
730
+ center?: {
731
+ x: number;
732
+ z: number;
733
+ };
734
+ radius?: number;
735
+ houses?: number;
736
+ lampLights?: number;
737
+ tower?: boolean;
738
+ ruin?: boolean;
739
+ };
740
+ scatters?: ManifestScatter[];
741
+ /** Vegetation sway. Default on; false disables, or set the strength. */
742
+ wind?: boolean | {
743
+ strength?: number;
744
+ };
745
+ }
746
+ interface BuiltScene {
747
+ /** Everything visual, ready to add to a scene (already added if one was passed). */
748
+ group: Group;
749
+ palette: Palette;
750
+ terrain?: Terrain;
751
+ water?: Water;
752
+ sky?: Sky;
753
+ rig: LightingRig;
754
+ cycle?: DayCycle;
755
+ paths: WorldPath[];
756
+ village?: Village;
757
+ scatters: ScatterResult[];
758
+ /** Combined steering obstacles from the village and every scatter. */
759
+ obstacles: Obstacle[];
760
+ /** Ground height (terrain's, or 0 for flat scenes). */
761
+ heightAt(x: number, z: number): number;
762
+ /** Advance water, wind and the day cycle. Call from your frame loop. */
763
+ update(dt: number): void;
764
+ }
765
+ /**
766
+ * Compile a `SceneManifest` into a live world: terrain, water, sky,
767
+ * lighting, fog, paths, a village, scatters and the day cycle — with all
768
+ * the cross-feature wiring the forest demo does by hand applied
769
+ * automatically (scatters stay ashore, off paths and out of the village;
770
+ * the village avoids water and roads; village lamps and windows feed the
771
+ * day cycle).
772
+ *
773
+ * ```ts
774
+ * const world = buildScene(JSON.parse(manifestJson), scene);
775
+ * game.onUpdate((t) => world.update(t.delta));
776
+ * agent.addBehavior(new ObstacleAvoidance(() => world.obstacles));
777
+ * ```
778
+ */
779
+ declare function buildScene(manifest: SceneManifest, scene?: Scene): BuiltScene;
780
+
781
+ interface Markers {
782
+ /** `spawn_<name>` → world position. */
783
+ spawns: Record<string, Vector3>;
784
+ /** `route_<name>_<index>` → world positions ordered by index. */
785
+ routes: Record<string, Vector3[]>;
786
+ /** `obstacle_<name>` → steering obstacle (radius from the node's scale). */
787
+ obstacles: Obstacle[];
788
+ /** `keepout_<name>` → scatter keep-out circle (radius from scale). */
789
+ keepOut: Array<{
790
+ center: {
791
+ x: number;
792
+ z: number;
793
+ };
794
+ radius: number;
795
+ }>;
796
+ }
797
+ /**
798
+ * Extract gameplay markers from an object tree by naming convention — the
799
+ * bridge between a DCC tool (Blender empties, glTF nodes) and SCENA/GAMA:
800
+ *
801
+ * - `spawn_player`, `spawn_boss` → named spawn points
802
+ * - `route_patrol_0`, `route_patrol_1`, … → ordered patrol routes
803
+ * - `obstacle_statue` → steering obstacle; radius = max(scale.x, scale.z)
804
+ * - `keepout_plaza` → scatter keep-out circle; radius = max(scale.x, scale.z)
805
+ *
806
+ * Blender's duplicate suffixes (`.001`) are stripped, so `route_a_0.001`
807
+ * still parses. Positions are world-space (the tree is updated first).
808
+ *
809
+ * ```ts
810
+ * const gltf = await new GLTFLoader().loadAsync('level.glb');
811
+ * const markers = extractMarkers(gltf.scene);
812
+ * player.position.copy(markers.spawns.player);
813
+ * guard.addBehavior(new FollowPath(new Path(markers.routes.patrol, true), 1.5));
814
+ * scatter({ ..., keepOut: markers.keepOut });
815
+ * ```
816
+ */
817
+ declare function extractMarkers(root: Object3D): Markers;
818
+
819
+ export { type BuiltScene, type BushOptions, type CrateOptions, DEFAULT_PALETTE, type DayCycle, type DayCycleOptions, type FenceOptions, type FogPreset, type GrassOptions, type HouseOptions, KIT_UNIT, type Kit, type KitOptions, type LampOptions, type LightingPreset, type LightingRig, type ManifestScatter, type ManifestScatterItem, type Markers, type Obstacle, PALETTES, type Palette, type PathOptions, type Placement, type Prop, Rng, type RockOptions, type RuinOptions, type ScatterItem, type ScatterOptions, type ScatterPropType, type ScatterResult, type ScatterTile, type SceneManifest, type Sky, type SkyOptions, type Terrain, type TerrainOptions, type TowerOptions, type TreeOptions, type Village, type VillageOptions, type Water, type WaterOptions, type WellOptions, type Wind, type WindOptions, type WorldPath, aboveWater, applyFog, applyWind, assembleKit, buildScene, collectObstacles, createBush, createCrate, createDayCycle, createFence, createGrassTuft, createHouse, createLamp, createLightingRig, createPath, createRock, createRuin, createSky, createTerrain, createTower, createTree, createVillage, createWater, createWell, extractMarkers, fractalNoise2, hash2, scatter, valueNoise2 };