murow 0.0.73 → 0.1.3

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.
Files changed (188) hide show
  1. package/README.md +15 -1
  2. package/dist/cjs/core/binary-codec/binary-codec.js +1 -1
  3. package/dist/cjs/core/clock/clock.js +1 -0
  4. package/dist/cjs/core/clock/index.js +1 -0
  5. package/dist/cjs/core/driver/driver.js +1 -1
  6. package/dist/cjs/core/driver/drivers/immediate.js +1 -1
  7. package/dist/cjs/core/driver/drivers/raf.js +1 -1
  8. package/dist/cjs/core/driver/drivers/timeout.js +1 -1
  9. package/dist/cjs/core/hitbox/hitbox-library.js +1 -0
  10. package/dist/cjs/core/hitbox/hitbox.js +1 -0
  11. package/dist/cjs/core/hitbox/index.js +1 -0
  12. package/dist/cjs/core/hitbox/test.js +1 -0
  13. package/dist/cjs/core/index.js +1 -1
  14. package/dist/cjs/core/input/index.js +1 -1
  15. package/dist/cjs/core/input/mouse-look/index.js +1 -0
  16. package/dist/cjs/core/input/mouse-look/mouse-look.js +1 -0
  17. package/dist/cjs/core/input/scroll-zoom/index.js +1 -0
  18. package/dist/cjs/core/input/scroll-zoom/scroll-zoom.js +1 -0
  19. package/dist/cjs/core/prediction/prediction.js +1 -1
  20. package/dist/cjs/core/ray/ray-3d.js +1 -1
  21. package/dist/cjs/core/raycast/hit-buffer.js +1 -0
  22. package/dist/cjs/core/raycast/index.js +1 -0
  23. package/dist/cjs/core/raycast/raycaster.js +1 -0
  24. package/dist/cjs/core/slot-map/index.js +1 -0
  25. package/dist/cjs/core/slot-map/slot-map.js +1 -0
  26. package/dist/cjs/core/state-machine/index.js +1 -0
  27. package/dist/cjs/core/state-machine/state-machine.js +1 -0
  28. package/dist/cjs/core/timeline/index.js +1 -0
  29. package/dist/cjs/core/timeline/timeline.js +1 -0
  30. package/dist/cjs/ecs/component.js +1 -1
  31. package/dist/cjs/ecs/system-builder.js +1 -1
  32. package/dist/cjs/ecs/world.js +1 -1
  33. package/dist/cjs/game/loop/loop.js +1 -1
  34. package/dist/cjs/game/loop/ticker-schedule.js +1 -0
  35. package/dist/cjs/net/adapters/bun-websocket.js +1 -1
  36. package/dist/cjs/renderer/index.js +1 -1
  37. package/dist/cjs/renderer/prefab-bucket/concrete.js +1 -1
  38. package/dist/cjs/renderer/prefab-bucket/index.js +1 -1
  39. package/dist/cjs/renderer/prefab-bucket/parsers.js +1 -1
  40. package/dist/cjs/renderer/prefab-bucket/specs.js +1 -1
  41. package/dist/cjs/renderer/raycast/index.js +1 -0
  42. package/dist/cjs/renderer/raycast/raycast.js +1 -0
  43. package/dist/esm/core/binary-codec/binary-codec.js +1 -1
  44. package/dist/esm/core/clock/clock.js +1 -0
  45. package/dist/esm/core/clock/index.js +1 -0
  46. package/dist/esm/core/driver/drivers/immediate.js +1 -1
  47. package/dist/esm/core/driver/drivers/raf.js +1 -1
  48. package/dist/esm/core/driver/drivers/timeout.js +1 -1
  49. package/dist/esm/core/hitbox/hitbox-library.js +1 -0
  50. package/dist/esm/core/hitbox/hitbox.js +1 -0
  51. package/dist/esm/core/hitbox/index.js +1 -0
  52. package/dist/esm/core/hitbox/test.js +1 -0
  53. package/dist/esm/core/index.js +1 -1
  54. package/dist/esm/core/input/index.js +1 -1
  55. package/dist/esm/core/input/mouse-look/index.js +1 -0
  56. package/dist/esm/core/input/mouse-look/mouse-look.js +1 -0
  57. package/dist/esm/core/input/scroll-zoom/index.js +1 -0
  58. package/dist/esm/core/input/scroll-zoom/scroll-zoom.js +1 -0
  59. package/dist/esm/core/prediction/prediction.js +1 -1
  60. package/dist/esm/core/ray/ray-3d.js +1 -1
  61. package/dist/esm/core/raycast/hit-buffer.js +1 -0
  62. package/dist/esm/core/raycast/index.js +1 -0
  63. package/dist/esm/core/raycast/raycaster.js +1 -0
  64. package/dist/esm/core/slot-map/index.js +1 -0
  65. package/dist/esm/core/slot-map/slot-map.js +1 -0
  66. package/dist/esm/core/state-machine/index.js +1 -0
  67. package/dist/esm/core/state-machine/state-machine.js +1 -0
  68. package/dist/esm/core/timeline/index.js +1 -0
  69. package/dist/esm/core/timeline/timeline.js +1 -0
  70. package/dist/esm/ecs/component.js +1 -1
  71. package/dist/esm/ecs/system-builder.js +1 -1
  72. package/dist/esm/ecs/world.js +1 -1
  73. package/dist/esm/game/loop/loop.js +1 -1
  74. package/dist/esm/game/loop/ticker-schedule.js +1 -0
  75. package/dist/esm/net/adapters/bun-websocket.js +1 -1
  76. package/dist/esm/renderer/index.js +1 -1
  77. package/dist/esm/renderer/prefab-bucket/concrete.js +1 -1
  78. package/dist/esm/renderer/prefab-bucket/index.js +1 -1
  79. package/dist/esm/renderer/prefab-bucket/parsers.js +1 -1
  80. package/dist/esm/renderer/raycast/index.js +1 -0
  81. package/dist/esm/renderer/raycast/raycast.js +1 -0
  82. package/dist/netcode/cjs/index.js +1556 -0
  83. package/dist/netcode/esm/index.js +1534 -0
  84. package/dist/netcode/types/client/game-client.d.ts +139 -0
  85. package/dist/netcode/types/client/index.d.ts +1 -0
  86. package/dist/netcode/types/client/strategies/snapshot-interpolation.d.ts +33 -0
  87. package/dist/netcode/types/client/strategies/snapshot-interpolation.test.d.ts +1 -0
  88. package/dist/netcode/types/codec/delta-codec.d.ts +17 -0
  89. package/dist/netcode/types/codec/delta-codec.test.d.ts +1 -0
  90. package/dist/netcode/types/codec/index.d.ts +1 -0
  91. package/dist/netcode/types/components/index.d.ts +1 -0
  92. package/dist/netcode/types/components/sync-spec.d.ts +49 -0
  93. package/dist/netcode/types/components/sync-spec.test.d.ts +1 -0
  94. package/dist/netcode/types/ctx.d.ts +105 -0
  95. package/dist/netcode/types/ctx.test.d.ts +1 -0
  96. package/dist/netcode/types/handlers/define-handlers.d.ts +47 -0
  97. package/dist/netcode/types/handlers/index.d.ts +1 -0
  98. package/dist/netcode/types/index.d.ts +11 -0
  99. package/dist/netcode/types/integration.test.d.ts +1 -0
  100. package/dist/netcode/types/intents/define-intents.d.ts +53 -0
  101. package/dist/netcode/types/intents/define-intents.test.d.ts +1 -0
  102. package/dist/netcode/types/intents/index.d.ts +1 -0
  103. package/dist/netcode/types/network/base.d.ts +120 -0
  104. package/dist/netcode/types/network/index.d.ts +2 -0
  105. package/dist/netcode/types/network/transport.d.ts +1 -0
  106. package/dist/netcode/types/packets/convergence.test.d.ts +1 -0
  107. package/dist/netcode/types/packets/harness.d.ts +103 -0
  108. package/dist/netcode/types/packets/index.d.ts +2 -0
  109. package/dist/netcode/types/packets/intermittent-intents.test.d.ts +1 -0
  110. package/dist/netcode/types/packets/pathological.test.d.ts +1 -0
  111. package/dist/netcode/types/packets/peer-interpolation.test.d.ts +1 -0
  112. package/dist/netcode/types/packets/reordering.test.d.ts +1 -0
  113. package/dist/netcode/types/packets/virtual-network.d.ts +65 -0
  114. package/dist/netcode/types/predictions/define-predictions.d.ts +45 -0
  115. package/dist/netcode/types/predictions/define-predictions.test.d.ts +1 -0
  116. package/dist/netcode/types/predictions/index.d.ts +1 -0
  117. package/dist/netcode/types/reconciliation.test.d.ts +1 -0
  118. package/dist/netcode/types/rpcs/define-rpcs.d.ts +44 -0
  119. package/dist/netcode/types/rpcs/define-rpcs.test.d.ts +1 -0
  120. package/dist/netcode/types/rpcs/index.d.ts +1 -0
  121. package/dist/netcode/types/server/game-server.d.ts +77 -0
  122. package/dist/netcode/types/server/index.d.ts +2 -0
  123. package/dist/netcode/types/server/plugins/aoi-grid.d.ts +34 -0
  124. package/dist/netcode/types/server/plugins/index.d.ts +3 -0
  125. package/dist/netcode/types/server/plugins/lag-compensation.d.ts +34 -0
  126. package/dist/netcode/types/server/plugins/plugin.d.ts +24 -0
  127. package/dist/netcode/types/tick-rate.test.d.ts +1 -0
  128. package/dist/netcode/types/transports/index.d.ts +1 -0
  129. package/dist/netcode/types/transports/memory-transport.d.ts +51 -0
  130. package/dist/netcode/types/types.test.d.ts +1 -0
  131. package/dist/types/core/binary-codec/binary-codec.d.ts +89 -31
  132. package/dist/types/core/clock/clock.d.ts +37 -0
  133. package/dist/types/core/clock/index.d.ts +1 -0
  134. package/dist/types/core/driver/driver.d.ts +8 -8
  135. package/dist/types/core/driver/drivers/immediate.d.ts +4 -4
  136. package/dist/types/core/driver/drivers/raf.d.ts +6 -6
  137. package/dist/types/core/driver/drivers/timeout.d.ts +4 -4
  138. package/dist/types/core/hitbox/hitbox-library.d.ts +29 -0
  139. package/dist/types/core/hitbox/hitbox.d.ts +50 -0
  140. package/dist/types/core/hitbox/index.d.ts +3 -0
  141. package/dist/types/core/hitbox/test.d.ts +44 -0
  142. package/dist/types/core/index.d.ts +6 -0
  143. package/dist/types/core/input/index.d.ts +2 -0
  144. package/dist/types/core/input/mouse-look/index.d.ts +1 -0
  145. package/dist/types/core/input/mouse-look/mouse-look.d.ts +139 -0
  146. package/dist/types/core/input/scroll-zoom/index.d.ts +1 -0
  147. package/dist/types/core/input/scroll-zoom/scroll-zoom.d.ts +38 -0
  148. package/dist/types/core/prediction/prediction.d.ts +35 -58
  149. package/dist/types/core/ray/ray-3d.d.ts +21 -1
  150. package/dist/types/core/raycast/hit-buffer.d.ts +43 -0
  151. package/dist/types/core/raycast/index.d.ts +2 -0
  152. package/dist/types/core/raycast/raycaster.d.ts +54 -0
  153. package/dist/types/core/slot-map/index.d.ts +1 -0
  154. package/dist/types/core/slot-map/slot-map.d.ts +109 -0
  155. package/dist/types/core/state-machine/index.d.ts +1 -0
  156. package/dist/types/core/state-machine/state-machine.d.ts +114 -0
  157. package/dist/types/core/timeline/index.d.ts +1 -0
  158. package/dist/types/core/timeline/timeline.d.ts +34 -0
  159. package/dist/types/ecs/component.d.ts +67 -11
  160. package/dist/types/ecs/entity-handle.d.ts +5 -5
  161. package/dist/types/ecs/system-builder.d.ts +13 -0
  162. package/dist/types/ecs/world.d.ts +72 -4
  163. package/dist/types/game/loop/loop.d.ts +51 -2
  164. package/dist/types/game/loop/ticker-schedule.d.ts +52 -0
  165. package/dist/types/net/adapters/bun-websocket.d.ts +19 -3
  166. package/dist/types/renderer/index.d.ts +1 -0
  167. package/dist/types/renderer/prefab-bucket/concrete.d.ts +16 -6
  168. package/dist/types/renderer/prefab-bucket/index.d.ts +11 -7
  169. package/dist/types/renderer/prefab-bucket/specs.d.ts +10 -0
  170. package/dist/types/renderer/raycast/index.d.ts +1 -0
  171. package/dist/types/renderer/raycast/raycast.d.ts +24 -0
  172. package/dist/types/renderer/types.d.ts +1 -0
  173. package/dist/webgpu/cjs/index.js +1897 -592
  174. package/dist/webgpu/esm/index.js +1889 -578
  175. package/dist/webgpu/types/2d/raycast.d.ts +45 -0
  176. package/dist/webgpu/types/2d/renderer.d.ts +11 -0
  177. package/dist/webgpu/types/2d/sprite-accessor.d.ts +3 -1
  178. package/dist/webgpu/types/3d/hitbox.d.ts +32 -0
  179. package/dist/webgpu/types/3d/lights.d.ts +113 -0
  180. package/dist/webgpu/types/3d/lights.test.d.ts +1 -0
  181. package/dist/webgpu/types/3d/raycast.d.ts +44 -0
  182. package/dist/webgpu/types/3d/renderer.d.ts +88 -1
  183. package/dist/webgpu/types/3d/shader.d.ts +88 -5
  184. package/dist/webgpu/types/core/types.d.ts +55 -0
  185. package/dist/webgpu/types/geometry/geometry-builder.d.ts +1 -4
  186. package/dist/webgpu/types/index.d.ts +1 -0
  187. package/dist/webgpu/types/shaders/utils.d.ts +24 -0
  188. package/package.json +6 -1
@@ -0,0 +1,45 @@
1
+ import type { InputSnapshot } from 'murow/core/input';
2
+ import { HitBuffer } from 'murow/core/raycast';
3
+ import { Raycast, RaycastMemo, type RaycastHit, type RaycastOptions } from 'murow/renderer';
4
+ import type { SpriteHandle } from 'murow/renderer';
5
+ import type { WebGPU2DRenderer } from './renderer';
6
+ type Point = [number, number];
7
+ type Hit = RaycastHit<SpriteHandle, Point>;
8
+ type Opts = RaycastOptions<SpriteHandle>;
9
+ export type RaycastState2D = HitBuffer<SpriteHandle, Point>;
10
+ export declare class WebGPURaycast2D extends Raycast<SpriteHandle, Point> {
11
+ private renderer;
12
+ readonly state: RaycastState2D;
13
+ private resultBuffer;
14
+ private memos;
15
+ constructor(renderer: WebGPU2DRenderer);
16
+ update(input: InputSnapshot): void;
17
+ /**
18
+ * Topmost sprite under the cursor, or null. The returned object is
19
+ * pool-backed and valid only until the next `update()` -- copy what
20
+ * you need, or use `memo` for results that persist across frames.
21
+ */
22
+ hit(opts?: Opts): Hit | null;
23
+ /**
24
+ * Every sprite under the cursor, topmost first. The array and its
25
+ * entries are reused and overwritten by the next `update()`.
26
+ */
27
+ hitAll(opts?: Opts): readonly Hit[];
28
+ memo(opts: Opts): WebGPURaycastMemo2D;
29
+ clearMemos(): void;
30
+ }
31
+ export declare class WebGPURaycastMemo2D extends RaycastMemo<SpriteHandle, Point> {
32
+ private state;
33
+ private opts;
34
+ private onDispose;
35
+ private dirty;
36
+ private detached;
37
+ private cached;
38
+ constructor(state: RaycastState2D, opts: Opts, onDispose: () => void);
39
+ get hits(): readonly Hit[];
40
+ get first(): Hit | null;
41
+ dispose(): void;
42
+ _invalidate(): void;
43
+ _detach(): void;
44
+ }
45
+ export {};
@@ -1,5 +1,6 @@
1
1
  import { Base2DRenderer } from 'murow/renderer';
2
2
  import type { Renderer2DOptions, SpriteHandle, SpriteOptions, SpritesheetHandle, SpritesheetSource } from 'murow/renderer';
3
+ import { WebGPURaycast2D, type RaycastState2D } from './raycast';
3
4
  import { Camera2D } from '../camera/camera-2d';
4
5
  import { type ParsedSpritesheet } from 'murow/renderer';
5
6
  import { GeometryBuilder, type GeometryOptions } from '../geometry/geometry-builder';
@@ -49,7 +50,11 @@ export declare class WebGPU2DRenderer extends Base2DRenderer {
49
50
  private sheets;
50
51
  private nextSheetId;
51
52
  readonly camera: Camera2D;
53
+ readonly raycast: WebGPURaycast2D;
52
54
  private uniformData;
55
+ private spriteHandles;
56
+ private spriteHitboxes;
57
+ private nextSpriteId;
53
58
  private resizeObserver;
54
59
  private resizeCallbacks;
55
60
  private readonly _prefabs;
@@ -78,6 +83,12 @@ export declare class WebGPU2DRenderer extends Base2DRenderer {
78
83
  sheet: SpritesheetHandle | Prefab2D;
79
84
  }): SpriteHandle;
80
85
  removeSprite(sprite: SpriteHandle): void;
86
+ /**
87
+ * Point-test every sprite against the unprojected cursor and push the
88
+ * hits into the buffer. Sort key is `-layer` so the topmost sprite is
89
+ * "nearest". A declared hitbox overrides the default rendered quad.
90
+ */
91
+ _collectRaycastHitsInto(screenX: number, screenY: number, rc: RaycastState2D): void;
81
92
  storePreviousState(): void;
82
93
  createGeometry(name: string, options: GeometryOptions): GeometryBuilder;
83
94
  createCompute(name: string, options: ComputeOptions): ComputeBuilder;
@@ -4,10 +4,12 @@ export declare class SpriteAccessor implements SpriteHandle {
4
4
  private staticData;
5
5
  private dynamicBase;
6
6
  private staticBase;
7
+ private _id;
7
8
  private _slot;
8
9
  private _sheetId;
9
10
  private _onStaticDirty;
10
- constructor(dynamicData: Float32Array, staticData: Float32Array, slot: number, sheetId: number, onStaticDirty: () => void);
11
+ constructor(dynamicData: Float32Array, staticData: Float32Array, id: number, slot: number, sheetId: number, onStaticDirty: () => void);
12
+ get id(): number;
11
13
  get slot(): number;
12
14
  get sheetId(): number;
13
15
  get x(): number;
@@ -0,0 +1,32 @@
1
+ import { type HitboxPart } from 'murow/core/hitbox';
2
+ /**
3
+ * Line-list wireframe renderer for instance hitboxes. Owns its pipeline,
4
+ * the three unit-shape vertex buffers, and a dynamic-offset uniform buffer
5
+ * (one MVP + color per entry). The caller walks instances and feeds each
6
+ * hitbox into `emit`; `flush` issues the per-entry draws.
7
+ */
8
+ export declare class HitboxDebugRenderer {
9
+ private device;
10
+ private pipeline;
11
+ private bindGroup;
12
+ private uniformBuffer;
13
+ private sphereBuffer;
14
+ private sphereVertexCount;
15
+ private boxBuffer;
16
+ private boxVertexCount;
17
+ private cylinderBuffer;
18
+ private cylinderVertexCount;
19
+ private stage;
20
+ private entries;
21
+ private vp;
22
+ init(device: GPUDevice, format: GPUTextureFormat): void;
23
+ begin(vp: Float32Array): void;
24
+ emit(hb: HitboxPart<'3d'>, hovered: boolean, px: number, py: number, pz: number, sx: number, sy: number, sz: number): void;
25
+ flush(pass: GPURenderPassEncoder): void;
26
+ /**
27
+ * The model matrix is pure scale-then-translate, so `MVP = VP * M`
28
+ * collapses to scaling VP's first three columns by the extents and
29
+ * replacing the fourth with `VP * (center, 1)` -- no matrix multiply.
30
+ */
31
+ private collect;
32
+ }
@@ -0,0 +1,113 @@
1
+ /**
2
+ * A dynamic point or spot light. Directional/ambient terms are global and set
3
+ * via `setDirectionalLight` / `setAmbient`, not added here.
4
+ */
5
+ export type LightSpec = {
6
+ type: 'point';
7
+ position: readonly [x: number, y: number, z: number];
8
+ /** Light color RGB. Defaults to `[1, 1, 1]`. */
9
+ color?: readonly [r: number, g: number, b: number];
10
+ /** Brightness multiplier. Defaults to `1`. */
11
+ intensity?: number;
12
+ /** World-unit radius past which the light contributes nothing. Defaults to `10`. */
13
+ range?: number;
14
+ } | {
15
+ type: 'spot';
16
+ position: readonly [x: number, y: number, z: number];
17
+ /** Direction the cone points along. */
18
+ direction: readonly [x: number, y: number, z: number];
19
+ color?: readonly [r: number, g: number, b: number];
20
+ intensity?: number;
21
+ range?: number;
22
+ /** Cone half-angle in radians (the outer edge). Defaults to `0.5`. */
23
+ angle?: number;
24
+ /**
25
+ * Edge softness, 0..1. `0` = hard edge at `angle`; `1` = the cone fades
26
+ * all the way from its center. The feathered band is `angle * smoothness`
27
+ * wide, measured inward from the edge. Defaults to `0.5`.
28
+ */
29
+ smoothness?: number;
30
+ };
31
+ /**
32
+ * Live handle to a dynamic light. All properties are readable and mutable every
33
+ * frame — unlike a mesh instance's spawn-frozen color. `destroy()` frees the slot.
34
+ *
35
+ * The `position` / `direction` / `color` getters return a per-handle reused
36
+ * tuple (mutated on each read), matching `MeshInstanceHandle`. Copy the values
37
+ * out if you need to retain them past the next read on the same handle.
38
+ */
39
+ export interface LightHandle {
40
+ readonly slot: number;
41
+ setPosition(x: number, y: number, z: number): void;
42
+ setDirection(x: number, y: number, z: number): void;
43
+ /** Snap to a position without interpolating from the previous one (use after a discontinuous move). */
44
+ teleport(x: number, y: number, z: number): void;
45
+ setColor(r: number, g: number, b: number): void;
46
+ readonly position: readonly [number, number, number];
47
+ readonly direction: readonly [number, number, number];
48
+ readonly color: readonly [number, number, number];
49
+ intensity: number;
50
+ range: number;
51
+ /** Cone half-angle in radians (spot only; `0` for point lights). Readable + settable. */
52
+ angle: number;
53
+ /** Edge softness 0..1 (spot only). `0` = hard edge, `1` = fades from center. Readable + settable. */
54
+ smoothness: number;
55
+ /** Whether the light contributes this frame. Toggling does not free the slot. */
56
+ enabled: boolean;
57
+ destroy(): void;
58
+ }
59
+ export declare class LightSystem {
60
+ private readonly maxLights;
61
+ private readonly data;
62
+ private readonly slots;
63
+ private readonly enabled;
64
+ private readonly handles;
65
+ /** Dense scratch buffer of enabled lights, packed each frame for upload. */
66
+ private readonly uploadData;
67
+ /** CPU-side spot cone params per slot (the GPU only needs the derived cosines). */
68
+ private readonly angle;
69
+ private readonly smoothness;
70
+ private dirDir;
71
+ private dirColor;
72
+ private dirIntensity;
73
+ private ambient;
74
+ constructor(maxLights: number);
75
+ /** Add a dynamic point or spot light. Throws past `maxLights`. */
76
+ add(spec: LightSpec): LightHandle;
77
+ /**
78
+ * Set the global directional light (the "sun"). `direction` points from the
79
+ * surface toward the light. Defaults to `(0.3, 0.8, 0.5)`, white, intensity 1.
80
+ */
81
+ setDirectional(direction: readonly [number, number, number], color?: readonly [number, number, number], intensity?: number): void;
82
+ /** Set the global ambient term. Defaults to `(0.3, 0.3, 0.3)`. */
83
+ setAmbient(color: readonly [number, number, number]): void;
84
+ /** Number of live dynamic lights. */
85
+ get count(): number;
86
+ /**
87
+ * Pack enabled lights into a dense run for upload. Disabled lights are
88
+ * skipped so the shader loop only walks contributing lights. Returns the
89
+ * shared scratch buffer, the light count, and the byte length to upload
90
+ * (so the caller never needs the record layout).
91
+ */
92
+ pack(): {
93
+ data: Float32Array;
94
+ count: number;
95
+ byteLength: number;
96
+ };
97
+ /**
98
+ * Stamp the directional + ambient terms and the light count into the
99
+ * renderer's uniform array, starting at `offset` (the float index after the
100
+ * VP matrix + alpha). Layout: lightDir(3), dirColor(3), dirIntensity(1),
101
+ * ambient(3), then lightCount as a u32 reinterpret at `offset + 10`.
102
+ */
103
+ writeUniforms(uniformData: Float32Array, offset: number, count: number): void;
104
+ /**
105
+ * Snapshot every live light's current position/direction into its prev
106
+ * slot. Called from the renderer's `storePreviousState()` in `pre-tick`, so
107
+ * the shader can `mix(prev, curr, alpha)` and moving lights interpolate at
108
+ * render rate instead of snapping at the tick boundary.
109
+ */
110
+ storePrevious(): void;
111
+ /** Write a light spec into its CPU slot. Prev is seeded to curr (no spawn lerp). */
112
+ private writeSlot;
113
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,44 @@
1
+ import type { InputSnapshot } from 'murow/core/input';
2
+ import { HitBuffer } from 'murow/core/raycast';
3
+ import { Raycast, RaycastMemo, type RaycastHit, type RaycastOptions } from 'murow/renderer';
4
+ import type { MeshInstanceHandle, WebGPU3DRenderer } from './renderer';
5
+ type Point = [number, number, number];
6
+ type Hit = RaycastHit<MeshInstanceHandle, Point>;
7
+ type Opts = RaycastOptions<MeshInstanceHandle>;
8
+ export type RaycastState = HitBuffer<MeshInstanceHandle, Point>;
9
+ export declare class WebGPURaycast3D extends Raycast<MeshInstanceHandle, Point> {
10
+ private renderer;
11
+ readonly state: RaycastState;
12
+ private resultBuffer;
13
+ private memos;
14
+ constructor(renderer: WebGPU3DRenderer);
15
+ update(input: InputSnapshot): void;
16
+ /**
17
+ * Nearest hit, or null. The returned object is pool-backed and valid
18
+ * only until the next `update()` -- copy what you need, or use `memo`
19
+ * for results that persist across frames.
20
+ */
21
+ hit(opts?: Opts): Hit | null;
22
+ /**
23
+ * All hits, nearest first. The array and its entries are reused across
24
+ * calls and overwritten by the next `update()`; do not retain them.
25
+ */
26
+ hitAll(opts?: Opts): readonly Hit[];
27
+ memo(opts: Opts): WebGPURaycastMemo3D;
28
+ clearMemos(): void;
29
+ }
30
+ export declare class WebGPURaycastMemo3D extends RaycastMemo<MeshInstanceHandle, Point> {
31
+ private state;
32
+ private opts;
33
+ private onDispose;
34
+ private dirty;
35
+ private detached;
36
+ private cached;
37
+ constructor(state: RaycastState, opts: Opts, onDispose: () => void);
38
+ get hits(): readonly Hit[];
39
+ get first(): Hit | null;
40
+ dispose(): void;
41
+ _invalidate(): void;
42
+ _detach(): void;
43
+ }
44
+ export {};
@@ -1,8 +1,10 @@
1
1
  import { Base3DRenderer } from 'murow/renderer';
2
2
  import type { Renderer3DOptions } from 'murow/renderer';
3
3
  import { ComputeBuilder, type ComputeOptions } from '../compute/compute-builder';
4
+ import { type LightSpec, type LightHandle } from './lights';
4
5
  import { Camera3D } from '../camera/camera-3d';
5
- import { type ParsedGltf, type PrefabBucket3D, type Prefab3D, type PlayOptions } from 'murow/renderer';
6
+ import { type ParsedGltf, type PrefabBucket3D, type Prefab3D, type PlayOptions, Raycast as RaycastBase, RaycastMemo as RaycastMemoBase, type RaycastHit as RaycastHitBase, type RaycastOptions as RaycastOptionsBase } from 'murow/renderer';
7
+ import { WebGPURaycast3D, type RaycastState } from './raycast';
6
8
  export interface ModelData {
7
9
  positions: Float32Array;
8
10
  normals?: Float32Array;
@@ -17,12 +19,32 @@ export interface ModelHandle {
17
19
  readonly skinned: boolean;
18
20
  }
19
21
  export interface MeshInstanceHandle {
22
+ readonly id: number;
20
23
  readonly slot: number;
21
24
  readonly modelId: number;
22
25
  readonly skinned: boolean;
26
+ /** Source prefab id, or `null` if spawned from a raw model handle. */
27
+ readonly prefabId: string | null;
23
28
  setPosition(x: number, y: number, z: number): void;
24
29
  setRotation(x: number, y: number, z: number): void;
25
30
  setScale(x: number, y: number, z: number): void;
31
+ /**
32
+ * Set position WITHOUT GPU interpolation from the previous frame.
33
+ * Writes both PREV and CURR to the same value, so the next render
34
+ * produces no lerp slide. Use after teleports, network snapshot
35
+ * snaps, or any time the entity should appear at the new location
36
+ * immediately rather than smoothly moving toward it.
37
+ */
38
+ teleport(x: number, y: number, z: number): void;
39
+ /**
40
+ * Logical current position (what `setPosition` last wrote, not the interpolated render value).
41
+ * Returns a per-handle reusable tuple — do not retain across subsequent gets on the same handle.
42
+ */
43
+ readonly position: readonly [number, number, number];
44
+ /** Logical current rotation in radians. Reusable tuple; see `position`. */
45
+ readonly rotation: readonly [number, number, number];
46
+ /** Logical current scale. Reusable tuple; see `position`. */
47
+ readonly scale: readonly [number, number, number];
26
48
  play?(name: string, opts?: PlayOptions): void;
27
49
  stop?(): void;
28
50
  /** Free this instance's renderer slot. Safe to call once per handle. */
@@ -40,12 +62,32 @@ export interface GltfModel {
40
62
  }
41
63
  /** Handle to a spawned instance (single primitive or multi-part glTF). */
42
64
  export interface InstanceHandle {
65
+ readonly id: number;
43
66
  setPosition(x: number, y: number, z: number): void;
44
67
  setRotation(x: number, y: number, z: number): void;
45
68
  setScale(x: number, y: number, z: number): void;
69
+ /**
70
+ * Set position WITHOUT GPU interpolation from the previous frame.
71
+ * Writes both PREV and CURR to the same value, so the next render
72
+ * produces no lerp slide. Use after teleports, network snapshot
73
+ * snaps, or any time the entity should appear at the new location
74
+ * immediately.
75
+ */
76
+ teleport(x: number, y: number, z: number): void;
77
+ /**
78
+ * Logical current position (what `setPosition` last wrote, not the interpolated render value).
79
+ * Returns a per-handle reusable tuple — do not retain across subsequent gets on the same handle.
80
+ */
81
+ readonly position: readonly [number, number, number];
82
+ /** Logical current rotation in radians. Reusable tuple; see `position`. */
83
+ readonly rotation: readonly [number, number, number];
84
+ /** Logical current scale. Reusable tuple; see `position`. */
85
+ readonly scale: readonly [number, number, number];
46
86
  play?(name: string, opts?: PlayOptions): void;
47
87
  stop?(): void;
48
88
  readonly skinned: boolean;
89
+ /** Source prefab id, or `null` if spawned from a raw model handle. */
90
+ readonly prefabId: string | null;
49
91
  /** Free this instance's renderer slot(s). Safe to call once per handle. */
50
92
  destroy(): void;
51
93
  }
@@ -65,6 +107,10 @@ export interface MeshInstanceOptions {
65
107
  /** Tint color RGB. Defaults to `[1, 1, 1]`. */
66
108
  color?: readonly [r: number, g: number, b: number];
67
109
  }
110
+ export type RaycastHit = RaycastHitBase<MeshInstanceHandle, [number, number, number]>;
111
+ export type RaycastOptions = RaycastOptionsBase<MeshInstanceHandle>;
112
+ export type Raycast = RaycastBase<MeshInstanceHandle, [number, number, number]>;
113
+ export type RaycastMemo = RaycastMemoBase<MeshInstanceHandle, [number, number, number]>;
68
114
  export interface WebGPU3DRendererOptions extends Renderer3DOptions {
69
115
  maxSkinnedInstances?: number;
70
116
  maxBonesPerSkin?: number;
@@ -102,6 +148,8 @@ export declare class WebGPU3DRenderer extends Base3DRenderer {
102
148
  private batcher;
103
149
  private staticDirty;
104
150
  private instanceModelIds;
151
+ private instanceHandles;
152
+ private nextInstanceId;
105
153
  private dynamicBuffer;
106
154
  private staticBuffer;
107
155
  private uniformBuffer;
@@ -146,6 +194,7 @@ export declare class WebGPU3DRenderer extends Base3DRenderer {
146
194
  private skinnedInstanceModelIds;
147
195
  private skinnedInstanceBoneOffsets;
148
196
  private skinnedAnimStates;
197
+ private skinnedInstanceHandles;
149
198
  private nextBoneOffset;
150
199
  /** Reusable bone-offset blocks per skinIndex. Pushed on remove, popped on add. Indexed by skinIndex (low cardinality), so a Map of lists is fine. */
151
200
  private freedBoneOffsets;
@@ -161,10 +210,18 @@ export declare class WebGPU3DRenderer extends Base3DRenderer {
161
210
  private rawSkinnedUniformBuffer;
162
211
  private rawSkinnedSlotIndexBuffer;
163
212
  private frustumPlanes;
213
+ private lights;
214
+ private lightBuffer;
215
+ private rawLightBuffer;
164
216
  readonly camera: Camera3D;
217
+ readonly raycast: WebGPURaycast3D;
165
218
  private uniformData;
166
219
  private lastRenderTime;
167
220
  private readonly _prefabs;
221
+ debug: {
222
+ hitboxes: boolean;
223
+ };
224
+ private hitboxDebug;
168
225
  constructor(canvas: HTMLCanvasElement, options: WebGPU3DRendererOptions);
169
226
  init(): Promise<void>;
170
227
  /**
@@ -205,6 +262,25 @@ export declare class WebGPU3DRenderer extends Base3DRenderer {
205
262
  * separate set of GPU buffers. Read-only.
206
263
  */
207
264
  get maxSkinned(): number;
265
+ /**
266
+ * Add a dynamic point or spot light. Returns a live handle whose position,
267
+ * color, intensity, range, and enabled state can all be changed every frame.
268
+ * Up to `MAX_LIGHTS` (64) lights may be live at once; throws past that.
269
+ *
270
+ * The global directional + ambient terms are separate — see
271
+ * `setDirectionalLight` / `setAmbient`.
272
+ */
273
+ addLight(spec: LightSpec): LightHandle;
274
+ /**
275
+ * Set the global directional light (the "sun"). `direction` points from the
276
+ * surface toward the light. Defaults to `(0.3, 0.8, 0.5)`, white, intensity 1
277
+ * — the engine's classic fixed look.
278
+ */
279
+ setDirectionalLight(direction: readonly [number, number, number], color?: readonly [number, number, number], intensity?: number): void;
280
+ /** Set the global ambient term. Defaults to `(0.3, 0.3, 0.3)`. */
281
+ setAmbient(color: readonly [number, number, number]): void;
282
+ /** Number of live dynamic lights. */
283
+ get lightCount(): number;
208
284
  /**
209
285
  * Create a flat grid mesh on the XZ plane at Y=0.
210
286
  *
@@ -319,7 +395,18 @@ export declare class WebGPU3DRenderer extends Base3DRenderer {
319
395
  */
320
396
  removeInstance(handle: InstanceHandle): void;
321
397
  storePreviousState(): void;
398
+ /** Resolve an instance's declared hitbox name to its Hitbox via the bucket's library. */
399
+ private resolveHitbox;
400
+ /**
401
+ * Pick test for a single instance. Returns the ray-`t` and the struck
402
+ * part name, or `null`. Uses the prefab's declared hitbox when
403
+ * available; falls back to the model's axis-aligned bounding box.
404
+ */
405
+ private testInstanceRay;
406
+ /** Push every instance the screen ray hits within [near, far] into the hit buffer. Unsorted. */
407
+ _collectRaycastHitsInto(screenX: number, screenY: number, rc: RaycastState): void;
322
408
  render(alpha: number): void;
409
+ private drawDebugHitboxes;
323
410
  /**
324
411
  * Extract 6 frustum planes from a column-major VP matrix.
325
412
  * Each plane is [a, b, c, d] where ax + by + cz + d >= 0 means inside.
@@ -1,4 +1,6 @@
1
1
  import * as d from 'typegpu/data';
2
+ /** Max point/spot lights the mesh shaders' storage buffer holds. */
3
+ export declare const MAX_LIGHTS = 64;
2
4
  export declare function createMeshLayout(maxInstances: number): import("typegpu").TgpuBindGroupLayout<{
3
5
  uniforms: {
4
6
  uniform: d.WgslStruct<{
@@ -7,6 +9,14 @@ export declare function createMeshLayout(maxInstances: number): import("typegpu"
7
9
  lightDirX: d.F32;
8
10
  lightDirY: d.F32;
9
11
  lightDirZ: d.F32;
12
+ lightDirR: d.F32;
13
+ lightDirG: d.F32;
14
+ lightDirB: d.F32;
15
+ lightDirIntensity: d.F32;
16
+ ambientR: d.F32;
17
+ ambientG: d.F32;
18
+ ambientB: d.F32;
19
+ lightCount: d.U32;
10
20
  }>;
11
21
  };
12
22
  dynamicInstances: {
@@ -38,6 +48,32 @@ export declare function createMeshLayout(maxInstances: number): import("typegpu"
38
48
  slotIndices: {
39
49
  storage: d.WgslArray<d.U32>;
40
50
  };
51
+ lights: {
52
+ storage: d.WgslArray<d.WgslStruct<{
53
+ kind: d.F32;
54
+ currPosX: d.F32;
55
+ currPosY: d.F32;
56
+ currPosZ: d.F32;
57
+ prevPosX: d.F32;
58
+ prevPosY: d.F32;
59
+ prevPosZ: d.F32;
60
+ currDirX: d.F32;
61
+ currDirY: d.F32;
62
+ currDirZ: d.F32;
63
+ prevDirX: d.F32;
64
+ prevDirY: d.F32;
65
+ prevDirZ: d.F32;
66
+ colorR: d.F32;
67
+ colorG: d.F32;
68
+ colorB: d.F32;
69
+ intensity: d.F32;
70
+ range: d.F32;
71
+ innerCos: d.F32;
72
+ outerCos: d.F32;
73
+ castsShadow: d.F32;
74
+ shadowMapIndex: d.F32;
75
+ }>>;
76
+ };
41
77
  }>;
42
78
  export type MeshDataLayout = ReturnType<typeof createMeshLayout>;
43
79
  export declare function createMeshVertex(meshLayout: MeshDataLayout): import("typegpu").TgpuVertexFn<{
@@ -46,10 +82,12 @@ export declare function createMeshVertex(meshLayout: MeshDataLayout): import("ty
46
82
  }, {
47
83
  vNormal: d.Vec3f;
48
84
  vColor: d.Vec3f;
85
+ vWorldPos: d.Vec3f;
49
86
  }>;
50
- export declare function createMeshFragment(meshLayout: MeshDataLayout): import("typegpu").TgpuFragmentFn<{
87
+ export declare function createMeshFragment(meshLayout: MeshDataLayout | SkinnedMeshDataLayout): import("typegpu").TgpuFragmentFn<{
51
88
  vNormal: d.Vec3f;
52
89
  vColor: d.Vec3f;
90
+ vWorldPos: d.Vec3f;
53
91
  }, d.Vec4f>;
54
92
  export declare function createTextureBindGroupLayout(): import("typegpu").TgpuBindGroupLayout<{
55
93
  modelTexture: import("typegpu").TgpuLayoutTexture<d.WgslTexture2d<d.F32>>;
@@ -66,11 +104,13 @@ export declare function createTexturedMeshVertex(meshLayout: MeshDataLayout): im
66
104
  vNormal: d.Vec3f;
67
105
  vColor: d.Vec3f;
68
106
  vUV: d.Vec2f;
107
+ vWorldPos: d.Vec3f;
69
108
  }>;
70
- export declare function createTexturedMeshFragment(meshLayout: MeshDataLayout, texLayout: TextureBindGroupLayout): import("typegpu").TgpuFragmentFn<{
109
+ export declare function createTexturedMeshFragment(meshLayout: MeshDataLayout | SkinnedMeshDataLayout, texLayout: TextureBindGroupLayout): import("typegpu").TgpuFragmentFn<{
71
110
  vNormal: d.Vec3f;
72
111
  vColor: d.Vec3f;
73
112
  vUV: d.Vec2f;
113
+ vWorldPos: d.Vec3f;
74
114
  }, d.Vec4f>;
75
115
  export declare function createSkinnedMeshLayout(maxInstances: number, maxBones: number): import("typegpu").TgpuBindGroupLayout<{
76
116
  uniforms: {
@@ -80,6 +120,14 @@ export declare function createSkinnedMeshLayout(maxInstances: number, maxBones:
80
120
  lightDirX: d.F32;
81
121
  lightDirY: d.F32;
82
122
  lightDirZ: d.F32;
123
+ lightDirR: d.F32;
124
+ lightDirG: d.F32;
125
+ lightDirB: d.F32;
126
+ lightDirIntensity: d.F32;
127
+ ambientR: d.F32;
128
+ ambientG: d.F32;
129
+ ambientB: d.F32;
130
+ lightCount: d.U32;
83
131
  }>;
84
132
  };
85
133
  dynamicInstances: {
@@ -115,6 +163,32 @@ export declare function createSkinnedMeshLayout(maxInstances: number, maxBones:
115
163
  boneMatrices: {
116
164
  storage: d.WgslArray<d.Mat4x4f>;
117
165
  };
166
+ lights: {
167
+ storage: d.WgslArray<d.WgslStruct<{
168
+ kind: d.F32;
169
+ currPosX: d.F32;
170
+ currPosY: d.F32;
171
+ currPosZ: d.F32;
172
+ prevPosX: d.F32;
173
+ prevPosY: d.F32;
174
+ prevPosZ: d.F32;
175
+ currDirX: d.F32;
176
+ currDirY: d.F32;
177
+ currDirZ: d.F32;
178
+ prevDirX: d.F32;
179
+ prevDirY: d.F32;
180
+ prevDirZ: d.F32;
181
+ colorR: d.F32;
182
+ colorG: d.F32;
183
+ colorB: d.F32;
184
+ intensity: d.F32;
185
+ range: d.F32;
186
+ innerCos: d.F32;
187
+ outerCos: d.F32;
188
+ castsShadow: d.F32;
189
+ shadowMapIndex: d.F32;
190
+ }>>;
191
+ };
118
192
  }>;
119
193
  export type SkinnedMeshDataLayout = ReturnType<typeof createSkinnedMeshLayout>;
120
194
  export declare function createSkinnedMeshVertex(layout: SkinnedMeshDataLayout): import("typegpu").TgpuVertexFn<{
@@ -127,10 +201,19 @@ export declare function createSkinnedMeshVertex(layout: SkinnedMeshDataLayout):
127
201
  vNormal: d.Vec3f;
128
202
  vColor: d.Vec3f;
129
203
  vUV: d.Vec2f;
204
+ vWorldPos: d.Vec3f;
130
205
  }>;
131
206
  /**
132
- * Fragment shader for skinned meshes reuses the textured fragment shader.
133
- * For untextured skinned meshes, reuse createMeshFragment.
207
+ * Fragment shader for untextured skinned meshes. The skinned vertex shader
208
+ * always emits `vUV` (location 2) because it is shared with the textured path,
209
+ * so this fragment must declare `vUV` in its `in` too, otherwise `vWorldPos`
210
+ * lands on location 2 and collides with the vertex's `vUV` (vec2 vs vec3). The
211
+ * UV is simply unused here. Lighting matches createMeshFragment.
134
212
  */
135
- export { createMeshFragment as createSkinnedMeshFragment };
213
+ export declare function createSkinnedMeshFragment(meshLayout: SkinnedMeshDataLayout): import("typegpu").TgpuFragmentFn<{
214
+ vNormal: d.Vec3f;
215
+ vColor: d.Vec3f;
216
+ vUV: d.Vec2f;
217
+ vWorldPos: d.Vec3f;
218
+ }, d.Vec4f>;
136
219
  export { createTexturedMeshFragment as createSkinnedTexturedMeshFragment };
@@ -104,7 +104,62 @@ export declare const MeshUniforms: d.WgslStruct<{
104
104
  lightDirX: d.F32;
105
105
  lightDirY: d.F32;
106
106
  lightDirZ: d.F32;
107
+ lightDirR: d.F32;
108
+ lightDirG: d.F32;
109
+ lightDirB: d.F32;
110
+ lightDirIntensity: d.F32;
111
+ ambientR: d.F32;
112
+ ambientG: d.F32;
113
+ ambientB: d.F32;
114
+ lightCount: d.U32;
107
115
  }>;
116
+ /** viewProjection occupies floats [0, 16). */
117
+ export declare const MESH_UNIFORM_ALPHA_OFFSET = 16;
118
+ /** The directional/ambient/count block starts right after `alpha`. */
119
+ export declare const MESH_UNIFORM_LIGHT_OFFSET = 17;
120
+ /** Total f32 slots in MeshUniforms (mat4x4 16 + alpha 1 + 10 light terms + count 1). */
121
+ export declare const MESH_UNIFORM_FLOATS = 28;
122
+ /**
123
+ * Point/spot light record. One entry per slot in the renderer's light storage
124
+ * buffer; the fragment shader loops `[0, lightCount)`. Kept f32-contiguous (no
125
+ * vec types) to match the flat Float32Array the renderer uploads.
126
+ *
127
+ * Position and direction carry `prev` + `curr` snapshots so the fragment shader
128
+ * can `mix(prev, curr, alpha)`, moving lights interpolate at render rate, the
129
+ * same as mesh instances, instead of snapping at the tick boundary.
130
+ *
131
+ * `kind`: 1 = point, 2 = spot (0/directional is the global term in MeshUniforms).
132
+ * `castsShadow` / `shadowMapIndex` are reserved for shadow-map support: the
133
+ * fragment loop already multiplies each light by a shadow factor (currently
134
+ * 1.0), so a shadow pass can populate these without reshaping the buffer.
135
+ */
136
+ export declare const Light: d.WgslStruct<{
137
+ kind: d.F32;
138
+ currPosX: d.F32;
139
+ currPosY: d.F32;
140
+ currPosZ: d.F32;
141
+ prevPosX: d.F32;
142
+ prevPosY: d.F32;
143
+ prevPosZ: d.F32;
144
+ currDirX: d.F32;
145
+ currDirY: d.F32;
146
+ currDirZ: d.F32;
147
+ prevDirX: d.F32;
148
+ prevDirY: d.F32;
149
+ prevDirZ: d.F32;
150
+ colorR: d.F32;
151
+ colorG: d.F32;
152
+ colorB: d.F32;
153
+ intensity: d.F32;
154
+ range: d.F32;
155
+ innerCos: d.F32;
156
+ outerCos: d.F32;
157
+ castsShadow: d.F32;
158
+ shadowMapIndex: d.F32;
159
+ }>;
160
+ export declare const LIGHT_FLOATS = 22;
161
+ export declare const LIGHT_KIND_POINT = 1;
162
+ export declare const LIGHT_KIND_SPOT = 2;
108
163
  /** Per-instance animation state, uploaded from CPU each frame. */
109
164
  export declare const InstanceAnimStateGPU: d.WgslStruct<{
110
165
  clipId: d.I32;