@igneosoft/forge 0.1.1 → 0.1.2

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.ts CHANGED
@@ -1,18 +1,321 @@
1
1
  import * as THREE from 'three';
2
2
  export { THREE };
3
+ export { OrbitControls } from 'three/addons/controls/OrbitControls.js';
4
+ export { FBXLoader } from 'three/addons/loaders/FBXLoader.js';
5
+
6
+ interface CameraController {
7
+ readonly camera: THREE.Camera;
8
+ /** Whether the engine should rotate the character mesh to face movement direction */
9
+ readonly rotateCharacter: boolean;
10
+ /** Whether the character mesh should be hidden (e.g. first-person view) */
11
+ readonly hideCharacter: boolean;
12
+ /** Bind event listeners (mouse, scroll, pointer lock) to the canvas */
13
+ attach(canvas: HTMLCanvasElement): void;
14
+ /** Remove all event listeners */
15
+ detach(): void;
16
+ /** Per-frame update — positions/orients the camera relative to the target */
17
+ update(delta: number, target: THREE.Vector3): void;
18
+ /**
19
+ * Returns movement basis vectors from the camera's perspective.
20
+ * forward: direction "up on screen" maps to in world space (Y=0, normalized)
21
+ * right: direction "right on screen" maps to in world space (Y=0, normalized)
22
+ */
23
+ getMovementBasis(): {
24
+ forward: THREE.Vector3;
25
+ right: THREE.Vector3;
26
+ };
27
+ /** Handle viewport resize */
28
+ resize(width: number, height: number): void;
29
+ }
30
+
31
+ interface IsometricCameraOptions {
32
+ viewSize?: number;
33
+ minZoom?: number;
34
+ maxZoom?: number;
35
+ zoomSpeed?: number;
36
+ }
37
+ declare class IsometricCameraController implements CameraController {
38
+ readonly camera: THREE.OrthographicCamera;
39
+ readonly rotateCharacter = true;
40
+ readonly hideCharacter = false;
41
+ private viewSize;
42
+ private minZoom;
43
+ private maxZoom;
44
+ private zoomSpeed;
45
+ private canvas;
46
+ constructor(options?: IsometricCameraOptions);
47
+ attach(canvas: HTMLCanvasElement): void;
48
+ detach(): void;
49
+ update(_delta: number, target: THREE.Vector3): void;
50
+ getMovementBasis(): {
51
+ forward: THREE.Vector3;
52
+ right: THREE.Vector3;
53
+ };
54
+ resize(width: number, height: number): void;
55
+ private onWheel;
56
+ }
57
+
58
+ interface ThirdPersonCameraOptions {
59
+ distance?: number;
60
+ minDistance?: number;
61
+ maxDistance?: number;
62
+ pitchMin?: number;
63
+ pitchMax?: number;
64
+ initialYaw?: number;
65
+ initialPitch?: number;
66
+ followLerp?: number;
67
+ rotationSensitivity?: number;
68
+ zoomSpeed?: number;
69
+ autoReturnSpeed?: number;
70
+ autoReturnDelay?: number;
71
+ }
72
+ declare class ThirdPersonCameraController implements CameraController {
73
+ readonly camera: THREE.PerspectiveCamera;
74
+ readonly rotateCharacter = true;
75
+ readonly hideCharacter = false;
76
+ private yaw;
77
+ private pitch;
78
+ private distance;
79
+ private minDistance;
80
+ private maxDistance;
81
+ private pitchMin;
82
+ private pitchMax;
83
+ private followLerp;
84
+ private rotationSensitivity;
85
+ private zoomSpeed;
86
+ private autoReturnSpeed;
87
+ private autoReturnDelay;
88
+ private isOrbiting;
89
+ private lastOrbitTime;
90
+ private currentPosition;
91
+ private canvas;
92
+ constructor(options?: ThirdPersonCameraOptions);
93
+ attach(canvas: HTMLCanvasElement): void;
94
+ detach(): void;
95
+ update(delta: number, target: THREE.Vector3): void;
96
+ getMovementBasis(): {
97
+ forward: THREE.Vector3;
98
+ right: THREE.Vector3;
99
+ };
100
+ resize(width: number, height: number): void;
101
+ private onContextMenu;
102
+ private onMouseDown;
103
+ private onMouseUp;
104
+ private onMouseMove;
105
+ private onWheel;
106
+ }
107
+
108
+ interface FirstPersonCameraOptions {
109
+ pitchMin?: number;
110
+ pitchMax?: number;
111
+ sensitivity?: number;
112
+ headOffset?: THREE.Vector3;
113
+ }
114
+ declare class FirstPersonCameraController implements CameraController {
115
+ readonly camera: THREE.PerspectiveCamera;
116
+ readonly rotateCharacter = false;
117
+ readonly hideCharacter = true;
118
+ private yaw;
119
+ private pitch;
120
+ private pitchMin;
121
+ private pitchMax;
122
+ private sensitivity;
123
+ private headOffset;
124
+ private isLocked;
125
+ private canvas;
126
+ constructor(options?: FirstPersonCameraOptions);
127
+ attach(canvas: HTMLCanvasElement): void;
128
+ detach(): void;
129
+ update(_delta: number, target: THREE.Vector3): void;
130
+ getMovementBasis(): {
131
+ forward: THREE.Vector3;
132
+ right: THREE.Vector3;
133
+ };
134
+ resize(width: number, height: number): void;
135
+ private onClick;
136
+ private onPointerLockChange;
137
+ private onMouseMove;
138
+ }
139
+
140
+ interface VertexHeights {
141
+ nw: number;
142
+ ne: number;
143
+ sw: number;
144
+ se: number;
145
+ }
146
+ interface LinkedEdges {
147
+ north: boolean;
148
+ south: boolean;
149
+ east: boolean;
150
+ west: boolean;
151
+ }
152
+ interface TileData {
153
+ color: string;
154
+ walkable: boolean;
155
+ height: number;
156
+ vertexHeights?: VertexHeights;
157
+ linkedEdges?: LinkedEdges;
158
+ materialId?: string;
159
+ }
160
+ declare function getEffectiveVertexHeights(tile: TileData): VertexHeights;
161
+ declare function isTileFlat(tile: TileData): boolean;
162
+ declare function getLinkedEdges(tile: TileData): LinkedEdges;
163
+ /** Get tile base height in tile units (elevation) */
164
+ declare function getTileBaseElevation(mapData: MapData, x: number, z: number): number;
165
+ interface WallOpening {
166
+ id: string;
167
+ type: 'window' | 'door' | 'arch';
168
+ offsetAlongWall: number;
169
+ baseHeight: number;
170
+ width: number;
171
+ height: number;
172
+ }
173
+ interface ColumnPiece {
174
+ id: string;
175
+ type: 'column';
176
+ x: number;
177
+ z: number;
178
+ baseElevation: number;
179
+ topElevation: number;
180
+ material: string;
181
+ materialId?: string;
182
+ constructionId?: string;
183
+ }
184
+ interface WallPiece {
185
+ id: string;
186
+ type: 'wall';
187
+ columnIds: [string, string];
188
+ material: string;
189
+ materialId?: string;
190
+ openings: WallOpening[];
191
+ constructionId?: string;
192
+ }
193
+ interface FloorPiece {
194
+ id: string;
195
+ type: 'floor';
196
+ columnIds: string[];
197
+ material: string;
198
+ materialId?: string;
199
+ constructionId?: string;
200
+ }
201
+ type StructuralPiece = ColumnPiece | WallPiece | FloorPiece;
202
+ interface Construction {
203
+ id: string;
204
+ templateName: string;
205
+ instanceNumber: number;
206
+ pieceIds: string[];
207
+ }
208
+ interface MapEntity {
209
+ id: string;
210
+ characterId: string;
211
+ x: number;
212
+ z: number;
213
+ }
214
+ interface MapObject {
215
+ id: string;
216
+ type: string;
217
+ x: number;
218
+ z: number;
219
+ color: string;
220
+ height: number;
221
+ solid: boolean;
222
+ }
223
+ interface Portal {
224
+ x: number;
225
+ z: number;
226
+ targetScene: string;
227
+ targetSpawn: string;
228
+ }
229
+ interface SpawnPoint$1 {
230
+ x: number;
231
+ z: number;
232
+ }
233
+ interface MapData {
234
+ id: string;
235
+ name: string;
236
+ width: number;
237
+ height: number;
238
+ tileSize: number;
239
+ tiles: TileData[][];
240
+ objects: MapObject[];
241
+ portals: Portal[];
242
+ spawns: Record<string, SpawnPoint$1>;
243
+ structures: StructuralPiece[];
244
+ constructions: Construction[];
245
+ entities: MapEntity[];
246
+ colorFavorites?: string[];
247
+ }
248
+ declare function createEmptyMap(id: string, width: number, height: number, name?: string, baseColor?: string): MapData;
249
+
250
+ declare class CollisionMap {
251
+ private walkable;
252
+ private heights;
253
+ private vertexHeights;
254
+ private solidObjects;
255
+ private solidStructures;
256
+ private floorSurfaces;
257
+ private allStructures;
258
+ private playerHeight;
259
+ private width;
260
+ private height;
261
+ private tileSize;
262
+ constructor(mapData: MapData, playerHeight?: number);
263
+ /** Push an AABB for a wall segment defined along the wall axis */
264
+ private pushWallSegment;
265
+ canWalk(worldX: number, worldZ: number, fromWorldX?: number, fromWorldZ?: number, currentFeetY?: number): boolean;
266
+ /** Get floor elevation at world position using N-column polygon + fan interpolation */
267
+ private getFloorElevationAt;
268
+ /** Get effective height at position. If currentY is provided, only considers
269
+ * surfaces reachable from that height (below + step tolerance). */
270
+ getTileHeight(worldX: number, worldZ: number, currentY?: number): number;
271
+ isWater(worldX: number, worldZ: number, feetY?: number): boolean;
272
+ }
273
+
274
+ interface CharacterEntityLike {
275
+ readonly root: THREE.Group;
276
+ play(name: string, crossFadeDuration?: number): void;
277
+ update(delta: number): void;
278
+ dispose?(): void;
279
+ }
280
+ declare class Player {
281
+ readonly entity: CharacterEntityLike;
282
+ readonly mesh: THREE.Group;
283
+ /** Y offset set by the entity (morphology baseHeight) — preserved during movement */
284
+ private readonly baseY;
285
+ private targetPosition;
286
+ private collisionMap;
287
+ private wasMoving;
288
+ private targetFacingAngle;
289
+ constructor(entity: CharacterEntityLike);
290
+ get position(): THREE.Vector3;
291
+ setPosition(x: number, z: number): void;
292
+ getHeight(): number;
293
+ setCollisionMap(collisionMap: CollisionMap | null): void;
294
+ setFacingAngle(angle: number): void;
295
+ update(delta: number, direction: {
296
+ x: number;
297
+ z: number;
298
+ }): void;
299
+ dispose(): void;
300
+ }
3
301
 
302
+ interface EngineOptions {
303
+ cameraController?: CameraController;
304
+ player?: Player;
305
+ }
4
306
  declare class Engine {
5
307
  private renderer;
6
308
  private scene;
7
- private camera;
309
+ private cameraController;
8
310
  private player;
9
311
  private input;
10
312
  private sceneManager;
11
313
  private animationFrameId;
12
314
  private clock;
13
- constructor(canvas: HTMLCanvasElement);
315
+ constructor(canvas: HTMLCanvasElement, options?: EngineOptions);
14
316
  start(): void;
15
317
  stop(): void;
318
+ setCameraController(controller: CameraController): void;
16
319
  private loop;
17
320
  private handleResize;
18
321
  }
@@ -37,7 +340,7 @@ interface SceneStateData {
37
340
  visited: boolean;
38
341
  }
39
342
 
40
- interface SpawnPoint$1 {
343
+ interface SpawnPoint {
41
344
  x: number;
42
345
  z: number;
43
346
  }
@@ -48,7 +351,7 @@ declare abstract class GameScene {
48
351
  abstract buildInitialState(): SceneStateData;
49
352
  abstract populate(threeScene: THREE.Scene, state: SceneStateData): void;
50
353
  abstract update(delta: number): void;
51
- abstract getSpawnPoint(fromSceneId?: string): SpawnPoint$1;
354
+ abstract getSpawnPoint(fromSceneId?: string): SpawnPoint;
52
355
  saveState(): SceneStateData | null;
53
356
  clear(threeScene: THREE.Scene): void;
54
357
  }
@@ -61,87 +364,162 @@ declare class SceneManager {
61
364
  constructor(threeScene: THREE.Scene);
62
365
  get current(): GameScene | null;
63
366
  register(scene: GameScene): void;
64
- load(sceneId: string, fromSceneId?: string): SpawnPoint$1;
367
+ load(sceneId: string, fromSceneId?: string): SpawnPoint;
65
368
  getState(sceneId: string): SceneStateData | undefined;
66
369
  getAllStates(): Record<string, SceneStateData>;
67
370
  }
68
371
 
69
- interface TileData {
70
- color: string;
71
- walkable: boolean;
72
- height: number;
73
- }
74
- interface MapObject {
372
+ interface MaterialVariantData {
75
373
  id: string;
76
- type: string;
77
- x: number;
78
- z: number;
79
- color: string;
80
- height: number;
81
- solid: boolean;
82
- }
83
- interface Portal {
84
- x: number;
85
- z: number;
86
- targetScene: string;
87
- targetSpawn: string;
88
- }
89
- interface SpawnPoint {
90
- x: number;
91
- z: number;
374
+ sizeTiles: {
375
+ w: number;
376
+ h: number;
377
+ };
378
+ depth: number;
379
+ palette: string[];
380
+ data: string;
92
381
  }
93
- interface MapData {
94
- id: string;
382
+ interface MaterialDefData {
383
+ _id: string;
95
384
  name: string;
96
- width: number;
97
- height: number;
98
- tileSize: number;
99
- tiles: TileData[][];
100
- objects: MapObject[];
101
- portals: Portal[];
102
- spawns: Record<string, SpawnPoint>;
385
+ backgroundColor?: string;
386
+ variants: MaterialVariantData[];
387
+ }
388
+ declare class StructureRenderer {
389
+ private group;
390
+ private materialDefs;
391
+ private highlightedId;
392
+ private allStructures;
393
+ getGroup(): THREE.Group;
394
+ setMaterialDefs(defs: MaterialDefData[]): void;
395
+ getMaterialDefs(): Map<string, MaterialDefData>;
396
+ setHighlighted(structureId: string | null): void;
397
+ private applyHighlight;
398
+ render(mapData: MapData): THREE.Group;
399
+ /** Get floor elevation at world position — uses the same pushed geometry as renderFloor
400
+ * so interpolated values match exactly what the GPU renders. */
401
+ private getFloorElevAtWorld;
402
+ /** Get the 4 clipped top-Y values for a column's corners: [NW, NE, SW, SE] */
403
+ private getColumnTopCorners;
404
+ private renderColumn;
405
+ /** Build voxel-textured relief caps on visible column faces (faces without walls) */
406
+ private buildVoxelColumnMesh;
407
+ private renderWall;
408
+ private renderFloor;
409
+ /** Build voxel-textured mesh for a floor surface (8x8x1 materials on the XZ plane) */
410
+ private buildVoxelFloorMesh;
411
+ /** Pick the column corner height closest to a given direction from the column center */
412
+ private pickCornerHeight;
413
+ /** Build a voxel-textured mesh for a wall surface.
414
+ * Tiles material variants across the wall and returns a mesh, or null if no material. */
415
+ private buildVoxelWallMesh;
416
+ /** Simple seeded random for deterministic tiling per wall */
417
+ private seededRandom;
418
+ /** Fill a surface grid with material variants, avoiding opening areas */
419
+ private tileSurface;
420
+ applyHeightCutoff(cutoffWorld: number): void;
421
+ dispose(): void;
103
422
  }
104
- declare function createEmptyMap(id: string, width: number, height: number, name?: string, baseColor?: string): MapData;
105
423
 
106
424
  declare class MapRenderer {
107
425
  private group;
108
- private tileMeshes;
426
+ private terrainMesh;
427
+ private terrainVoxelMesh;
428
+ private wallMesh;
429
+ private waterMesh;
109
430
  private gridLines;
110
431
  private currentWidth;
111
432
  private currentHeight;
433
+ private heightSnapshot;
434
+ private spawnMarker;
435
+ private structureRenderer;
436
+ private entityMeshes;
112
437
  getGroup(): THREE.Group;
438
+ setMaterialDefs(defs: MaterialDefData[]): void;
439
+ private getMaterialBgColors;
113
440
  render(mapData: MapData): THREE.Group;
441
+ setGridVisible(visible: boolean): void;
114
442
  syncTiles(mapData: MapData): void;
115
- needsFullRebuildForHeight(mapData: MapData): boolean;
443
+ syncStructures(mapData: MapData): void;
444
+ applyHeightCutoff(cutoff: number | null, tileSize: number): void;
445
+ setHighlightedStructure(structureId: string | null): void;
116
446
  needsFullRebuild(mapData: MapData): boolean;
117
447
  needsGeometryUpdate(mapData: MapData): boolean;
448
+ private saveHeightSnapshot;
449
+ private renderStructures;
450
+ private renderSpawn;
118
451
  private renderObjects;
119
- private buildHeightWalls;
120
452
  private buildGrid;
453
+ private buildTerrainVoxels;
454
+ addEntity(id: string, mesh: THREE.Group, tileX: number, tileZ: number, updater: (delta: number) => void, mapData: MapData): void;
455
+ removeEntity(id: string): void;
456
+ updateEntities(delta: number): void;
121
457
  dispose(): void;
122
458
  }
123
459
 
124
- declare class CollisionMap {
125
- private walkable;
126
- private heights;
127
- private solidObjects;
128
- private width;
129
- private height;
130
- private tileSize;
131
- constructor(mapData: MapData);
132
- canWalk(worldX: number, worldZ: number, fromWorldX?: number, fromWorldZ?: number): boolean;
133
- getTileHeight(worldX: number, worldZ: number): number;
134
- isWater(worldX: number, worldZ: number): boolean;
460
+ declare function createWorldGrid(cols: number, rows: number, tileSize?: number): THREE.Group;
461
+
462
+ /**
463
+ * Compact voxel encoding: 8-bit indexed palette + RLE compression.
464
+ *
465
+ * Storage format:
466
+ * palette: string[] — up to 255 hex colors
467
+ * data: string — base64-encoded RLE byte pairs [count, paletteIndex]
468
+ *
469
+ * Traversal order: for z in [0,depth), for y in [0,height), for x in [0,width)
470
+ * Palette index 0 = empty (no voxel), 1..255 = palette[0..254]
471
+ */
472
+ interface CompactVoxelData {
473
+ palette: string[];
474
+ data: string;
135
475
  }
476
+ interface Voxel$1 {
477
+ x: number;
478
+ y: number;
479
+ z: number;
480
+ color: string;
481
+ }
482
+ /** Encode sparse voxels into palette + RLE base64. */
483
+ declare function encodeVoxels(voxels: Voxel$1[], width: number, height: number, depth: number): CompactVoxelData;
484
+ /** Decode palette + RLE to sparse voxel array. */
485
+ declare function decodeVoxels(compact: CompactVoxelData, width: number, height: number, depth: number): Voxel$1[];
136
486
 
137
- declare function createWorldGrid(cols: number, rows: number, tileSize?: number): THREE.Group;
487
+ /**
488
+ * Map serialization codec — compact tile format.
489
+ *
490
+ * Instead of storing each tile as a JSON object with repeated field names,
491
+ * uses flat arrays and sparse maps:
492
+ *
493
+ * palette: string[] — unique colors
494
+ * tileGrid.colors: string — base64 RLE palette indices (row-major)
495
+ * tileGrid.heights: number[] — flat height array (row-major)
496
+ * tileGrid.notWalkable: number[] — indices of non-walkable tiles (sparse)
497
+ * tileGrid.vertexHeights: Record<string, VertexHeights> — sparse "idx" → VH
498
+ * tileGrid.linkedEdges: Record<string, LinkedEdges> — sparse "idx" → LE
499
+ *
500
+ * Runtime MapData keeps TileData[][] with string colors.
501
+ * Backward-compatible: decodeMapData handles legacy format (tiles as objects).
502
+ */
503
+
504
+ /** Encode MapData for compact serialization. */
505
+ declare function encodeMapData(map: MapData): Record<string, unknown>;
506
+ /** Decode serialized JSON back to MapData. Handles compact tileGrid and legacy tile-object formats. */
507
+ declare function decodeMapData(json: Record<string, any>): MapData;
138
508
 
139
509
  declare class FieldScene extends GameScene {
140
510
  constructor();
141
511
  buildInitialState(): SceneStateData;
142
512
  populate(threeScene: THREE.Scene, _state: SceneStateData): void;
143
513
  update(_delta: number): void;
144
- getSpawnPoint(_fromSceneId?: string): SpawnPoint$1;
514
+ getSpawnPoint(_fromSceneId?: string): SpawnPoint;
515
+ }
516
+
517
+ interface ColorPalette {
518
+ skin: string;
519
+ eyes: string;
520
+ hair: string;
521
+ primary: string;
522
+ secondary: string;
145
523
  }
146
524
 
147
525
  declare class InputManager {
@@ -156,156 +534,135 @@ declare class InputManager {
156
534
  };
157
535
  }
158
536
 
159
- interface ColorPalette {
160
- skin: string;
161
- primary: string;
162
- secondary: string;
163
- accent: string;
164
- }
165
- type PartFactory = (palette: ColorPalette) => THREE.Mesh;
166
- type SlotName = 'head' | 'torso' | 'armL' | 'armR' | 'forearmL' | 'forearmR' | 'legL' | 'legR' | 'shinL' | 'shinR' | 'weapon';
167
- interface CharacterConfig {
168
- id: string;
169
- palette: ColorPalette;
170
- head: PartFactory;
171
- torso: PartFactory;
172
- armL: PartFactory;
173
- armR: PartFactory;
174
- forearmL: PartFactory;
175
- forearmR: PartFactory;
176
- legL: PartFactory;
177
- legR: PartFactory;
178
- shinL: PartFactory;
179
- shinR: PartFactory;
180
- weapon?: PartFactory;
181
- }
182
-
183
- declare class CharacterEntity {
184
- readonly root: THREE.Group;
185
- private bones;
186
- private mixer;
187
- private actions;
188
- private currentAction;
189
- private parts;
190
- private palette;
191
- constructor(config: CharacterConfig);
192
- /** Play an animation by name. Crossfades from current. */
193
- play(name: string, crossFadeDuration?: number): void;
194
- /** Swap a body part at runtime (e.g., equip new armor) */
195
- swapPart(slot: SlotName, factory: PartFactory): void;
196
- /** Must be called every frame with delta time */
197
- update(delta: number): void;
198
- /** Cleanup GPU resources */
199
- dispose(): void;
200
- }
201
-
202
- declare class Player {
203
- readonly entity: CharacterEntity;
204
- readonly mesh: THREE.Group;
205
- private targetPosition;
206
- private collisionMap;
207
- private wasMoving;
208
- constructor(config?: CharacterConfig);
209
- get position(): THREE.Vector3;
210
- setPosition(x: number, z: number): void;
211
- setCollisionMap(collisionMap: CollisionMap | null): void;
212
- update(delta: number, direction: {
213
- x: number;
214
- z: number;
215
- }): void;
216
- dispose(): void;
217
- }
218
-
219
537
  interface Vec3 {
220
538
  x: number;
221
539
  y: number;
222
540
  z: number;
223
541
  }
224
- interface PivotAxes {
225
- x: boolean;
226
- y: boolean;
227
- z: boolean;
228
- }
229
- interface RotationConstraints {
230
- axes: PivotAxes;
231
- min: Vec3;
232
- max: Vec3;
233
- }
234
- interface InternalPivot {
542
+ interface Section {
235
543
  name: string;
236
- position: Vec3;
237
- constraints: RotationConstraints;
544
+ size: Vec3;
238
545
  }
239
- interface Connector {
240
- name: string;
241
- position: Vec3;
546
+ interface AxisLimits {
547
+ min: number;
548
+ max: number;
549
+ initial: number;
242
550
  }
243
- interface BoneDefinition {
244
- start: Vec3;
245
- end: Vec3;
246
- pivots: InternalPivot[];
247
- connectors: Connector[];
551
+ interface LinkRotation {
552
+ x: AxisLimits;
553
+ y: AxisLimits;
554
+ z: AxisLimits;
248
555
  }
249
- interface Attachment {
250
- parentPart: string;
251
- parentConnector: string;
252
- constraints: RotationConstraints;
556
+ interface LinkTranslation {
557
+ x: AxisLimits;
558
+ y: AxisLimits;
559
+ z: AxisLimits;
253
560
  }
254
- interface BodyPartDefinition {
561
+ interface Link {
255
562
  name: string;
256
- size: Vec3;
257
- bone: BoneDefinition;
258
- attachment: Attachment | null;
563
+ parent: string;
564
+ child: string;
565
+ insert: Vec3;
566
+ eje: Vec3;
567
+ rotation: LinkRotation;
568
+ translation?: LinkTranslation;
569
+ }
570
+ interface MorphologyConfig {
571
+ baseSpeed?: number;
572
+ canWalk?: boolean;
573
+ canFly?: boolean;
259
574
  }
260
575
  interface MorphologyData {
261
576
  _id?: string;
262
577
  name: string;
263
- bodyParts: BodyPartDefinition[];
578
+ root: string;
579
+ baseHeight: number;
580
+ sections: Section[];
581
+ links: Link[];
582
+ config?: MorphologyConfig;
583
+ }
584
+
585
+ /**
586
+ * Serializable animation format for data-driven animations.
587
+ * Stored in MongoDB, exported via .igneokit, editable in the animation editor.
588
+ */
589
+ interface BonePose {
590
+ boneName: string;
591
+ rotation: {
592
+ x: number;
593
+ y: number;
594
+ z: number;
595
+ };
596
+ translation?: {
597
+ x: number;
598
+ y: number;
599
+ z: number;
600
+ };
601
+ }
602
+ interface Keyframe {
603
+ time: number;
604
+ poses: BonePose[];
605
+ }
606
+ interface AnimationData {
607
+ _id?: string;
608
+ name: string;
609
+ duration: number;
610
+ loop: boolean;
611
+ morphologyId: string;
612
+ keyframes: Keyframe[];
613
+ }
614
+
615
+ interface VoxelPartInput {
616
+ slot: string;
617
+ voxels: {
618
+ x: number;
619
+ y: number;
620
+ z: number;
621
+ color: string;
622
+ }[];
623
+ size: {
624
+ x: number;
625
+ y: number;
626
+ z: number;
627
+ };
628
+ voxelSize: number;
629
+ paletteMapping?: Record<string, 'skin' | 'eyes' | 'hair' | 'primary' | 'secondary'>;
630
+ }
631
+ interface DataDrivenCharacterConfig {
632
+ morphology: MorphologyData;
633
+ parts: VoxelPartInput[];
634
+ palette?: Record<string, string>;
635
+ animations: AnimationData[];
636
+ }
637
+ /**
638
+ * Character entity built from data (morphology + voxel parts + animations).
639
+ * Everything comes from the database — morphology defines the skeleton,
640
+ * voxel parts provide the visual content, animations drive movement.
641
+ */
642
+ declare class DataDrivenCharacterEntity {
643
+ readonly root: THREE.Group;
644
+ private mixer;
645
+ private actions;
646
+ private currentAction;
647
+ constructor(config: DataDrivenCharacterConfig);
648
+ /** Play an animation by name. Crossfades from current. */
649
+ play(name: string, crossFadeDuration?: number): void;
650
+ /** Must be called every frame with delta time */
651
+ update(delta: number): void;
652
+ /** Cleanup GPU resources */
653
+ dispose(): void;
264
654
  }
265
- declare const humanoidMorphology: MorphologyData;
266
655
 
267
656
  type MorphologyScene = {
268
657
  root: THREE.Group;
269
658
  groups: Record<string, THREE.Group>;
270
659
  };
271
660
  /**
272
- * Builds a THREE.Group hierarchy from the new bodyParts-based MorphologyData.
273
- * Each bodyPart becomes a group. Internal pivots become sub-groups.
274
- * Returns the root group + a map of all groups by name.
661
+ * Builds a THREE.Group hierarchy from sections + links MorphologyData.
662
+ * Each section becomes a group. Links define parent-child relationships.
663
+ * Child position = insert (position of child relative to parent).
275
664
  */
276
665
  declare function buildFromMorphology(data: MorphologyData): MorphologyScene;
277
- interface BoneRefs {
278
- root: THREE.Group;
279
- hips: THREE.Group;
280
- spine: THREE.Group;
281
- neck: THREE.Group;
282
- shoulderL: THREE.Group;
283
- armL: THREE.Group;
284
- forearmL: THREE.Group;
285
- shoulderR: THREE.Group;
286
- armR: THREE.Group;
287
- forearmR: THREE.Group;
288
- weaponMount: THREE.Group;
289
- hipL: THREE.Group;
290
- legL: THREE.Group;
291
- shinL: THREE.Group;
292
- hipR: THREE.Group;
293
- legR: THREE.Group;
294
- shinR: THREE.Group;
295
- }
296
- /**
297
- * Manual skeleton builder — maintains exact same hierarchy as the original.
298
- * Used by CharacterEntity, CharacterAssembler, Player, and animations.
299
- * Does NOT depend on MorphologyData.
300
- */
301
- declare function createSkeleton(): BoneRefs;
302
-
303
- declare class CharacterAssembler {
304
- /** Attach all parts from config to bones. Returns map of slot -> mesh for tracking. */
305
- static assemble(bones: BoneRefs, config: CharacterConfig, parts: Map<SlotName, THREE.Object3D>): void;
306
- /** Swap a single part. Disposes the old mesh. */
307
- static swapPart(bones: BoneRefs, parts: Map<SlotName, THREE.Object3D>, slot: SlotName, factory: PartFactory, palette: ColorPalette): void;
308
- }
309
666
 
310
667
  /**
311
668
  * Data-driven character definition.
@@ -344,7 +701,7 @@ interface VoxelPartData {
344
701
  z: number;
345
702
  };
346
703
  voxels: Voxel[];
347
- paletteMapping?: Record<string, 'skin' | 'primary' | 'secondary' | 'accent'>;
704
+ paletteMapping?: Record<string, 'skin' | 'eyes' | 'hair' | 'primary' | 'secondary'>;
348
705
  }
349
706
  interface VoxelEditorMesh {
350
707
  mesh: THREE.InstancedMesh;
@@ -353,7 +710,9 @@ interface VoxelEditorMesh {
353
710
  /** Maps "x,y,z" → instanceId */
354
711
  positionToInstance: Map<string, number>;
355
712
  }
356
- declare function buildEditorMesh(data: VoxelPartData): VoxelEditorMesh;
713
+ /** Resolve a voxel color: '@skin' → palette lookup, '#ff8800' → as-is */
714
+ declare function resolveVoxelColor(color: string, palette?: Record<string, string>, paletteMapping?: Record<string, string>): string;
715
+ declare function buildEditorMesh(data: VoxelPartData, palette?: Record<string, string>): VoxelEditorMesh;
357
716
  declare function buildRuntimeMesh(data: VoxelPartData, palette?: Record<string, string>): THREE.Mesh;
358
717
  /** Get the face normal and adjacent position for placing a new voxel */
359
718
  declare function getAdjacentPosition(_face: THREE.Face | null, intersect: THREE.Intersection, voxelSize: number, origin: {
@@ -396,108 +755,11 @@ interface ArmorSetData {
396
755
  pieceIds: string[];
397
756
  }
398
757
 
399
- /** Standard humanoid head */
400
- declare const humanHead: PartFactory;
401
- /** Skull */
402
- declare const skullHead: PartFactory;
403
- /** Helmet */
404
- declare const helmetHead: PartFactory;
405
- /** Hood */
406
- declare const hoodHead: PartFactory;
407
-
408
- /** Basic tunic/shirt */
409
- declare const tunicTorso: PartFactory;
410
- /** Skeleton ribcage */
411
- declare const ribcageTorso: PartFactory;
412
- /** Armored torso */
413
- declare const armorTorso: PartFactory;
414
- /** Rags */
415
- declare const ragsTorso: PartFactory;
416
- /** Robe with skirt */
417
- declare const robeTorso: PartFactory;
418
-
419
- declare const humanArm: PartFactory;
420
- declare const boneArm: PartFactory;
421
- declare const armorArm: PartFactory;
422
- declare const robeArm: PartFactory;
423
- declare const humanForearm: PartFactory;
424
- declare const boneForearm: PartFactory;
425
- declare const armorForearm: PartFactory;
426
- declare const robeForearm: PartFactory;
427
-
428
- declare const humanLeg: PartFactory;
429
- declare const boneLeg: PartFactory;
430
- declare const armorLeg: PartFactory;
431
- declare const robeLeg: PartFactory;
432
- declare const humanShin: PartFactory;
433
- declare const boneShin: PartFactory;
434
- declare const armorShin: PartFactory;
435
- declare const robeShin: PartFactory;
436
-
437
- /** Short sword */
438
- declare const sword: PartFactory;
439
- /** Battle axe */
440
- declare const axe: PartFactory;
441
- /** Staff */
442
- declare const staff: PartFactory;
443
-
444
- declare const playerConfig: CharacterConfig;
445
- declare const skeletonConfig: CharacterConfig;
446
- declare const zombieConfig: CharacterConfig;
447
- declare const warriorConfig: CharacterConfig;
448
- declare const mageConfig: CharacterConfig;
449
-
450
- declare function register(clip: THREE.AnimationClip): void;
451
- declare const AnimationLibrary: {
452
- get(name: string): THREE.AnimationClip;
453
- getAll(): THREE.AnimationClip[];
454
- register: typeof register;
455
- };
456
-
457
- /**
458
- * Serializable animation format for data-driven animations.
459
- * Stored in MongoDB, exported via .igneokit, editable in the animation editor.
460
- */
461
- interface BonePose {
462
- boneName: string;
463
- rotation: {
464
- x: number;
465
- y: number;
466
- z: number;
467
- };
468
- }
469
- interface Keyframe {
470
- time: number;
471
- poses: BonePose[];
472
- }
473
- interface AnimationData {
474
- _id?: string;
475
- name: string;
476
- duration: number;
477
- loop: boolean;
478
- morphologyId: string;
479
- keyframes: Keyframe[];
480
- }
481
-
482
758
  /**
483
759
  * Builds a THREE.AnimationClip from serializable AnimationData.
484
- * Uses the morphology's bone hierarchy to build dot-notation track paths
485
- * compatible with THREE's property binding system.
760
+ * Track names use flat `pivot_linkName.rotation[axis]` format,
761
+ * compatible with THREE's property binding system via findNode().
486
762
  */
487
763
  declare function buildAnimationClip(data: AnimationData, morphology?: MorphologyData): THREE.AnimationClip;
488
- /**
489
- * Converts an existing hardcoded AnimationClip to AnimationData format.
490
- * Useful for migrating existing animations.
491
- */
492
- declare function clipToAnimationData(clip: THREE.AnimationClip, morphologyId: string, loop?: boolean): AnimationData;
493
-
494
- /** Subtle breathing/bobbing animation */
495
- declare function createIdleClip(): THREE.AnimationClip;
496
-
497
- /** Walk cycle - arms and legs swing opposite */
498
- declare function createWalkClip(): THREE.AnimationClip;
499
-
500
- /** Single attack swing with right arm */
501
- declare function createAttackClip(): THREE.AnimationClip;
502
764
 
503
- export { type AnimationData, AnimationLibrary, type ArmorPieceData, type ArmorSetData, type Attachment, type BodyPartDefinition, type BoneDefinition, type BonePose, type BoneRefs, CharacterAssembler, type CharacterConfig, type CharacterDefinition, CharacterEntity, CollisionMap, type ColorPalette, type Connector, Engine, type EntityState, FieldScene, GameScene, InputManager, type InternalPivot, type Keyframe, type MapData, type MapObject, MapRenderer, type MorphologyData, type MorphologyScene, type PartFactory, type PivotAxes, Player, type Portal, type RotationConstraints, SceneManager, type SceneStateData, type SlotName, type SpawnPoint$1 as SpawnPoint, type TileData, type Vec3, type Voxel, type VoxelEditorMesh, type VoxelPartData, armorArm, armorForearm, armorLeg, armorShin, armorTorso, axe, boneArm, boneForearm, boneLeg, boneShin, buildAnimationClip, buildEditorMesh, buildFromMorphology, buildRuntimeMesh, clipToAnimationData, createAttackClip, createEmptyMap, createIdleClip, createIsometricCamera, createSkeleton, createWalkClip, createWorldGrid, getAdjacentPosition, getClickedVoxelPosition, helmetHead, hoodHead, humanArm, humanForearm, humanHead, humanLeg, humanShin, humanoidMorphology, mageConfig, playerConfig, ragsTorso, ribcageTorso, robeArm, robeForearm, robeLeg, robeShin, robeTorso, skeletonConfig, skullHead, staff, sword, tunicTorso, updateCameraTarget, warriorConfig, zombieConfig };
765
+ export { type AnimationData, type ArmorPieceData, type ArmorSetData, type AxisLimits, type BonePose, type CameraController, type CharacterDefinition, type CharacterEntityLike, CollisionMap, type ColorPalette, type ColumnPiece, type CompactVoxelData, type Construction, type DataDrivenCharacterConfig, DataDrivenCharacterEntity, Engine, type EngineOptions, type EntityState, FieldScene, FirstPersonCameraController, type FirstPersonCameraOptions, type FloorPiece, GameScene, InputManager, IsometricCameraController, type IsometricCameraOptions, type Keyframe, type Link, type LinkRotation, type LinkTranslation, type LinkedEdges, type MapData, type MapEntity, type MapObject, MapRenderer, type MaterialDefData, type MaterialVariantData, type MorphologyConfig, type MorphologyData, type MorphologyScene, Player, type Portal, SceneManager, type SceneStateData, type Section, type SpawnPoint, type StructuralPiece, StructureRenderer, ThirdPersonCameraController, type ThirdPersonCameraOptions, type TileData, type Vec3, type VertexHeights, type Voxel, type VoxelEditorMesh, type VoxelPartData, type VoxelPartInput, type WallOpening, type WallPiece, buildAnimationClip, buildEditorMesh, buildFromMorphology, buildRuntimeMesh, createEmptyMap, createIsometricCamera, createWorldGrid, decodeMapData, decodeVoxels, encodeMapData, encodeVoxels, getAdjacentPosition, getClickedVoxelPosition, getEffectiveVertexHeights, getLinkedEdges, getTileBaseElevation, isTileFlat, resolveVoxelColor, updateCameraTarget };