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/README.md +29 -5
- package/dist/index.cjs +428 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +256 -2
- package/dist/index.d.ts +256 -2
- package/dist/index.js +436 -25
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
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
|
-
|
|
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 {
|
|
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
|
-
|
|
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 };
|