@series-inc/rundot-3d-engine 0.6.19 → 0.6.21

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/SKILL.md CHANGED
@@ -13,10 +13,18 @@ class MyGame extends VenusGame {
13
13
  // Load assets
14
14
  const stowkit = StowKitSystem.getInstance()
15
15
  const buildJson = (await import("../prefabs/build.json")).default
16
- await stowkit.loadFromBuildJson(buildJson, {
17
- fetchBlob: (path) => fetch(path).then(r => r.blob()),
16
+
17
+ // Step 1: Parse build.json (pure, no network)
18
+ const { prefabs, mounts } = stowkit.parseBuildJson(buildJson, {
19
+ materialConverter: (mat) => mat, // optional
18
20
  })
19
21
 
22
+ // Step 2: Download and register each pack
23
+ await Promise.all(mounts.map(async (mount) => {
24
+ const blob = await fetch(mount.path).then(r => r.blob())
25
+ await stowkit.registerAssetsFromBlob(mount.alias, blob)
26
+ }))
27
+
20
28
  // Create a player
21
29
  const player = new GameObject("Player")
22
30
  player.position.set(0, 1, 0)
@@ -310,18 +318,32 @@ animGraph.getCurrentState()
310
318
 
311
319
  ### StowKitSystem — Asset Loading
312
320
 
321
+ Two-step loading: parse build.json first (pure, no network), then download and register each pack.
322
+
313
323
  ```typescript
314
324
  import { StowKitSystem } from "@series-inc/rundot-3d-engine/systems"
315
325
 
316
326
  const stowkit = StowKitSystem.getInstance()
317
-
318
- // Load from build.json
319
- await stowkit.loadFromBuildJson(buildJson, {
320
- fetchBlob: (path) => fetch(path).then(r => r.blob()),
327
+ const buildJson = (await import("../prefabs/build.json")).default
328
+
329
+ // Step 1: Parse build.json — returns prefab collection and mount list (no I/O)
330
+ const { prefabs, mounts } = stowkit.parseBuildJson(buildJson, {
331
+ materialConverter: (mat) => mat, // optional — transform materials at load time
332
+ decoderPaths: { // optional — override decoder locations
333
+ basis: "basis/",
334
+ draco: "stowkit/draco/",
335
+ wasm: "stowkit_reader.wasm",
336
+ },
321
337
  })
322
338
 
339
+ // Step 2: Download and register each .stow pack (WASM decode, no further network)
340
+ await Promise.all(mounts.map(async (mount) => {
341
+ const blob = await fetch(mount.path).then(r => r.blob())
342
+ await stowkit.registerAssetsFromBlob(mount.alias, blob)
343
+ }))
344
+
323
345
  // Access assets
324
- const mesh = await stowkit.getMesh("name") // async
346
+ const mesh = await stowkit.getMesh("name") // async, on-demand load & cache
325
347
  const mesh = stowkit.getMeshSync("name") // sync (null if not loaded)
326
348
  const tex = await stowkit.getTexture("name")
327
349
  const clip = await stowkit.getAnimation("walk", "character_mesh")
@@ -333,12 +355,234 @@ const clone = await stowkit.cloneMesh("name", castShadow, receiveShadow)
333
355
 
334
356
  // GPU instancing
335
357
  await stowkit.registerMeshForInstancing("coin_batch", "coin_mesh", true, true, 500)
358
+ await stowkit.registerBatchFromPrefab("burger", true, true, 100) // from prefab's stow_mesh component
359
+ ```
360
+
361
+ **Preloading** — pre-decode assets during load screen to avoid pop-in:
362
+
363
+ ```typescript
364
+ await stowkit.preloadAll({
365
+ meshes: ["level_geometry", "coin_mesh"],
366
+ skinnedMeshes: { names: ["hero_model"], scale: 1.0 },
367
+ textures: ["hero_diffuse"],
368
+ audio: ["bgm_main", "sfx_click"],
369
+ animations: [{ name: "hero_idle", meshName: "hero_model" }],
370
+ })
371
+
372
+ // Or individually:
373
+ await stowkit.preloadMeshes(["level_geometry", "coin_mesh"])
374
+ await stowkit.preloadSkinnedMeshes(["hero_model"], 1.0)
375
+ await stowkit.preloadAudio(["bgm_main"])
376
+ ```
377
+
378
+ ### InputManager — Keyboard, Pointer, Touch & Custom Actions
379
+
380
+ The `InputManager` is a singleton that tracks keyboard, pointer, and touch state. Use the `Input` convenience object for quick access.
381
+
382
+ **Built-in action polling (keyboard):**
383
+
384
+ ```typescript
385
+ import { Input, InputAction } from "@series-inc/rundot-3d-engine/systems"
386
+
387
+ // Check built-in movement actions
388
+ if (Input.isPressed(InputAction.MOVE_FORWARD)) { /* W key */ }
389
+ if (Input.isKeyPressed("Space")) { /* raw key check */ }
390
+ ```
391
+
392
+ **Pointer & touch state:**
393
+
394
+ ```typescript
395
+ Input.isPointerDown() // true while mouse/pen is held
396
+ Input.getPointerPosition() // { x, y } in client coords
397
+ ```
398
+
399
+ **Custom action system — register named actions with multiple trigger types:**
400
+
401
+ ```typescript
402
+ import { Input } from "@series-inc/rundot-3d-engine/systems"
403
+ import type { ActionTrigger } from "@series-inc/rundot-3d-engine/systems"
404
+
405
+ // Register an action with multiple triggers
406
+ Input.registerAction("jump", [
407
+ { type: "key", code: "Space" },
408
+ { type: "pointer" },
409
+ { type: "touch" },
410
+ ])
411
+
412
+ // Subscribe to action (fires on press) — returns unsubscribe function
413
+ const unsub = Input.onAction("jump", () => {
414
+ player.flap()
415
+ })
416
+
417
+ // Polling API
418
+ if (Input.isCustomActionActive("jump")) { /* any trigger held */ }
419
+
420
+ // Cleanup
421
+ unsub()
422
+ ```
423
+
424
+ **ActionTrigger types:**
425
+ - `{ type: "key", code: string }` — keyboard key (uses `event.code`, e.g. `"Space"`, `"KeyW"`)
426
+ - `{ type: "pointer" }` — mouse or pen pointer down (excludes touch — no double-firing)
427
+ - `{ type: "touch" }` — touch screen tap
428
+
429
+ **Notes:**
430
+ - On touch devices, pointer events from touch input are automatically skipped to prevent double-firing when an action has both `pointer` and `touch` triggers.
431
+ - `Input.setEnabled(false)` disables all input processing and clears all state.
432
+ - Custom actions registered with `registerAction` also get `preventDefault` on their key triggers.
433
+
434
+ ### ShaderRegistry & ShaderComponent — Shader Library
435
+
436
+ A registry of named shader presets with a component that applies them to meshes. Shared `time` and `deltaTime` uniforms are updated automatically once per frame.
437
+
438
+ **Built-in presets:**
439
+ - `"fresnel"` — Fresnel rim lighting, wrap diffuse, specular, under-lighting, directional light. Good for stylized characters and objects.
440
+ - `"wind"` — Simplex noise-based vertex displacement for vegetation animation. Includes depth shader pair for correct shadow casting with alpha clip.
441
+
442
+ **Usage — apply a shader preset to a GameObject:**
443
+
444
+ ```typescript
445
+ import { ShaderComponent } from "@series-inc/rundot-3d-engine/systems"
446
+
447
+ // Apply fresnel shader with default settings
448
+ gameObject.addComponent(new ShaderComponent("fresnel"))
449
+
450
+ // Apply with uniform overrides
451
+ gameObject.addComponent(new ShaderComponent("fresnel", {
452
+ fresnelIntensity: { value: 0.3 },
453
+ underLightIntensity: { value: 0.2 },
454
+ }))
455
+
456
+ // Apply wind shader (vegetation)
457
+ seaweedObject.addComponent(new ShaderComponent("wind"))
458
+ ```
459
+
460
+ **ShaderComponent behavior:**
461
+ - Waits for all `MeshRenderer` components in children to load before applying
462
+ - Traverses all child meshes and replaces materials with ShaderMaterial from the preset
463
+ - Automatically includes fog uniforms and shared time uniforms
464
+ - Sets `preventAutoInstancing = true` (custom materials can't be auto-batched)
465
+ - On cleanup, restores original materials and disposes created shader materials
466
+ - For presets with depth shaders (e.g. `"wind"`), creates `customDepthMaterial` and `customDistanceMaterial` for correct shadow casting
467
+
468
+ **Registering custom presets:**
469
+
470
+ ```typescript
471
+ import { ShaderRegistry } from "@series-inc/rundot-3d-engine/systems"
472
+ import type { ShaderPreset } from "@series-inc/rundot-3d-engine/systems"
473
+
474
+ ShaderRegistry.register({
475
+ name: "myShader",
476
+ vertexShader: `...`,
477
+ fragmentShader: `...`,
478
+ depthVertexShader: `...`, // optional — for shadow casting
479
+ depthFragmentShader: `...`, // optional
480
+ side: THREE.FrontSide, // optional (default: FrontSide)
481
+ transparent: false, // optional (default: false)
482
+ fog: true, // optional (default: true)
483
+ toneMapped: false, // optional (default: false)
484
+ defaultUniforms: {
485
+ myColor: { value: new THREE.Color(1, 0, 0) },
486
+ myFloat: { value: 1.0 },
487
+ },
488
+ })
489
+
490
+ // Then use it
491
+ gameObject.addComponent(new ShaderComponent("myShader"))
492
+ ```
493
+
494
+ **Shared uniforms (available in all shader presets):**
495
+ - `time` (float) — `performance.now() * 0.001` (seconds since page load)
496
+ - `deltaTime` (float) — frame delta time
497
+ - `baseMap` (sampler2D) — automatically set from the mesh's existing texture map
498
+
499
+ **Fresnel preset uniforms:** `sunDir`, `sunColor`, `sunIntensity`, `fresnelColor`, `fresnelPower`, `fresnelIntensity`, `ambientColor`, `ambientIntensity`, `specColor`, `specShininess`, `specIntensity`, `underLightColor`, `underLightIntensity`, `underLightPower`, `rimLightDir`, `rimLightColor`, `rimLightIntensity`
500
+
501
+ **Wind preset uniforms:** `windStrength`, `windSpeed`, `rootWorldOrigin`, `alphaClip`, `tintColor`, `lightDir`, `ambientColor`, `ambientStrength`
502
+
503
+ ### ObjectPool, GameObjectPool & ScrollingPool — Object Pooling
504
+
505
+ Generic and specialized pools for reusing objects instead of creating/destroying them each frame.
506
+
507
+ **ObjectPool — generic pool:**
508
+
509
+ ```typescript
510
+ import { ObjectPool } from "@series-inc/rundot-3d-engine/systems"
511
+
512
+ const pool = new ObjectPool({
513
+ create: () => new Bullet(),
514
+ onAcquire: (b) => b.activate(),
515
+ onRelease: (b) => b.deactivate(),
516
+ onDestroy: (b) => b.dispose(),
517
+ })
518
+
519
+ pool.warm(20) // pre-allocate 20 objects
520
+ const bullet = pool.acquire() // get from pool (or create new)
521
+ pool.release(bullet) // return to pool
522
+ pool.releaseAll() // return all active objects
523
+ pool.dispose() // destroy everything
524
+
525
+ pool.activeCount // currently in use
526
+ pool.availableCount // ready to acquire
527
+ pool.totalCount // active + available
528
+ ```
529
+
530
+ **GameObjectPool — pool for GameObjects with visibility management:**
531
+
532
+ ```typescript
533
+ import { GameObjectPool } from "@series-inc/rundot-3d-engine/systems"
534
+
535
+ const pipePool = new GameObjectPool(() => {
536
+ const go = new GameObject("Pipe")
537
+ go.addComponent(new MeshRenderer("pipe_mesh"))
538
+ return go
539
+ })
540
+
541
+ pipePool.warm(10)
542
+ const pipe = pipePool.acquire() // visible = true
543
+ pipe.position.set(0, 0, 50)
544
+
545
+ pipePool.release(pipe) // visible = false, moved offscreen (y = -9999)
546
+ pipePool.releaseAll()
547
+ pipePool.dispose() // calls gameObject.dispose() on all
548
+ ```
549
+
550
+ **ScrollingPool — infinite tile recycler for endless/runner games:**
551
+
552
+ ```typescript
553
+ import { ScrollingPool, GameObjectPool } from "@series-inc/rundot-3d-engine/systems"
554
+
555
+ const tilePool = new GameObjectPool(() => {
556
+ const tile = Prefabs.instantiate("Background").gameObject
557
+ return tile
558
+ })
559
+
560
+ const scroller = new ScrollingPool({
561
+ pool: tilePool,
562
+ tileSize: 250,
563
+ bufferCount: 3,
564
+ axis: "z", // "x" | "y" | "z" (default: "z")
565
+ onSpawn: (tile, index) => {
566
+ // customize each spawned tile
567
+ tile.position.x = 25
568
+ },
569
+ })
570
+
571
+ scroller.initialize() // spawn initial buffer
572
+
573
+ // In update loop:
574
+ scroller.update(playerZ) // spawns ahead, recycles behind
575
+
576
+ // On restart:
577
+ scroller.reset()
578
+ scroller.initialize()
579
+
580
+ scroller.dispose()
336
581
  ```
337
582
 
338
583
  ### Other Systems
339
584
 
340
585
  - **AudioSystem** — 2D/3D positional audio with AudioListener management
341
- - **InputManager** — keyboard, mouse, touch, and gamepad input
342
586
  - **LightingSystem** — directional, point, and spot lights with shadow management
343
587
  - **NavigationSystem** — A* pathfinding on navigation meshes
344
588
  - **ParticleSystem** — GPU particle effects