@vulfram/engine 0.17.1-alpha → 0.19.3-alpha

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 (36) hide show
  1. package/README.md +3 -3
  2. package/package.json +10 -5
  3. package/src/engine/api.ts +12 -25
  4. package/src/engine/bridge/dispatch.ts +3 -8
  5. package/src/engine/ecs/components.ts +340 -0
  6. package/src/engine/ecs/index.ts +3 -661
  7. package/src/engine/ecs/intents.ts +184 -0
  8. package/src/engine/ecs/systems.ts +26 -0
  9. package/src/engine/intents/store.ts +72 -0
  10. package/src/engine/state.ts +3 -3
  11. package/src/engine/systems/command-intent.ts +11 -16
  12. package/src/engine/systems/diagnostics.ts +3 -13
  13. package/src/engine/systems/input-mirror.ts +156 -18
  14. package/src/engine/systems/resource-upload.ts +12 -14
  15. package/src/engine/systems/response-decode.ts +17 -0
  16. package/src/engine/systems/scene-sync.ts +12 -13
  17. package/src/engine/systems/ui-bridge.ts +31 -10
  18. package/src/engine/systems/utils.ts +46 -3
  19. package/src/engine/systems/world-lifecycle.ts +9 -15
  20. package/src/engine/world/entities.ts +201 -37
  21. package/src/engine/world/mount.ts +27 -6
  22. package/src/engine/world/world-ui.ts +77 -30
  23. package/src/engine/world/world3d.ts +282 -33
  24. package/src/helpers/collision.ts +487 -0
  25. package/src/helpers/index.ts +2 -0
  26. package/src/helpers/raycast.ts +442 -0
  27. package/src/types/cmds/geometry.ts +2 -2
  28. package/src/types/cmds/index.ts +42 -0
  29. package/src/types/cmds/input.ts +39 -0
  30. package/src/types/cmds/material.ts +10 -10
  31. package/src/types/cmds/realm.ts +0 -2
  32. package/src/types/cmds/system.ts +10 -0
  33. package/src/types/cmds/target.ts +14 -0
  34. package/src/types/events/keyboard.ts +2 -2
  35. package/src/types/events/pointer.ts +43 -0
  36. package/src/types/events/system.ts +44 -0
@@ -15,11 +15,22 @@ import type {
15
15
  CmdAudioSourceCreateArgs,
16
16
  CmdAudioSourceTransportArgs,
17
17
  } from '../../types/cmds/audio';
18
+ import type {
19
+ CmdInputTargetListenerDisposeArgs,
20
+ CmdInputTargetListenerListArgs,
21
+ CmdInputTargetListenerUpsertArgs,
22
+ } from '../../types/cmds/input';
18
23
  import type { EnvironmentConfig } from '../../types/cmds/environment';
19
- import type { CmdGizmoDrawAabbArgs, CmdGizmoDrawLineArgs } from '../../types/cmds/gizmo';
24
+ import type {
25
+ CmdGizmoDrawAabbArgs,
26
+ CmdGizmoDrawLineArgs,
27
+ } from '../../types/cmds/gizmo';
20
28
  import type { CmdPoseUpdateArgs } from '../../types/cmds/model';
21
29
  import type { ShadowConfig } from '../../types/cmds/shadow';
30
+ import type { CmdTargetMeasurementArgs } from '../../types/cmds/target';
22
31
  import type { NotificationLevel } from '../../types/kinds';
32
+ export { KeyCode } from '../../types/events/keyboard';
33
+ import type { SystemEvent } from '../../types/events/system';
23
34
  import {
24
35
  audioListenerUpdate as audioListenerUpdateRaw,
25
36
  audioResourceCreate as audioResourceCreateRaw,
@@ -35,12 +46,31 @@ import {
35
46
  createModel as createModelRaw,
36
47
  createTexture as createTextureRaw,
37
48
  createTag as createTagRaw,
49
+ disposeGeometry as disposeGeometryRaw,
50
+ disposeMaterial as disposeMaterialRaw,
51
+ disposeTexture as disposeTextureRaw,
38
52
  drawGizmoAabb as drawGizmoAabbRaw,
39
53
  drawGizmoLine as drawGizmoLineRaw,
54
+ disposeInputTargetListener as disposeInputTargetListenerRaw,
55
+ getSystemEvents as getSystemEventsRaw,
56
+ getImeCommitText as getImeCommitTextRaw,
57
+ getImeCursorRange as getImeCursorRangeRaw,
58
+ getImePreeditText as getImePreeditTextRaw,
40
59
  getModelId as getModelIdRaw,
60
+ getPointerDelta as getPointerDeltaRaw,
61
+ getPointerPosition as getPointerPositionRaw,
62
+ getPointerTargetSize as getPointerTargetSizeRaw,
63
+ getPointerTargetDelta as getPointerTargetDeltaRaw,
64
+ getPointerTargetId as getPointerTargetIdRaw,
65
+ getPointerTargetPosition as getPointerTargetPositionRaw,
66
+ getPointerTargetUv as getPointerTargetUvRaw,
67
+ getPointerWindowSize as getPointerWindowSizeRaw,
41
68
  getWindowSize as getWindowSizeRaw,
42
69
  getWorldRealmId as getWorldRealmIdRaw,
70
+ isImeEnabled as isImeEnabledRaw,
43
71
  isKeyPressed as isKeyPressedRaw,
72
+ isPointerButtonJustPressed as isPointerButtonJustPressedRaw,
73
+ isPointerButtonPressed as isPointerButtonPressedRaw,
44
74
  isWindowCloseRequested as isWindowCloseRequestedRaw,
45
75
  listCameras as listCamerasRaw,
46
76
  listGeometries as listGeometriesRaw,
@@ -48,10 +78,13 @@ import {
48
78
  listMaterials as listMaterialsRaw,
49
79
  listModels as listModelsRaw,
50
80
  listTextures as listTexturesRaw,
81
+ measureTarget as measureTargetRaw,
51
82
  poseUpdate as poseUpdateRaw,
52
83
  removeEntity as removeEntityRaw,
53
84
  sendNotification as sendNotificationRaw,
54
85
  setParent as setParentRaw,
86
+ upsertInputTargetListener as upsertInputTargetListenerRaw,
87
+ listInputTargetListeners as listInputTargetListenersRaw,
55
88
  updateTransform as updateTransformRaw,
56
89
  } from './entities';
57
90
  import type {
@@ -73,14 +106,16 @@ import {
73
106
  } from './types';
74
107
 
75
108
  export type Create3DWorldOptions = {
76
- outputSurfaceId?: number;
77
109
  importance?: number;
78
110
  cachePolicy?: number;
79
111
  flags?: number;
80
112
  };
81
113
 
82
- type Create3DAudioSourceArgs = Omit<CmdAudioSourceCreateArgs, 'realmId' | 'modelId'>
83
- & ({ modelId: number } | { entityId: EntityId });
114
+ type Create3DAudioSourceArgs = Omit<
115
+ CmdAudioSourceCreateArgs,
116
+ 'realmId' | 'modelId'
117
+ > &
118
+ ({ modelId: number } | { entityId: EntityId });
84
119
 
85
120
  /**
86
121
  * Creates a 3D world.
@@ -103,72 +138,148 @@ export function remove3DEntity(worldId: World3DId, entityId: EntityId): void {
103
138
  }
104
139
 
105
140
  /** Upserts camera component intent for an entity in a 3D world. */
106
- export function create3DCamera(worldId: World3DId, entityId: EntityId, props: CameraProps): void {
141
+ export function create3DCamera(
142
+ worldId: World3DId,
143
+ entityId: EntityId,
144
+ props: CameraProps,
145
+ ): void {
107
146
  createCameraRaw(asWorldNumber(worldId), entityId as number, props);
108
147
  }
109
148
 
110
149
  /** Upserts light component intent for an entity in a 3D world. */
111
- export function create3DLight(worldId: World3DId, entityId: EntityId, props: LightProps): void {
150
+ export function create3DLight(
151
+ worldId: World3DId,
152
+ entityId: EntityId,
153
+ props: LightProps,
154
+ ): void {
112
155
  createLightRaw(asWorldNumber(worldId), entityId as number, props);
113
156
  }
114
157
 
115
158
  /** Upserts model component intent for an entity in a 3D world. */
116
- export function create3DModel(worldId: World3DId, entityId: EntityId, props: ModelProps): void {
159
+ export function create3DModel(
160
+ worldId: World3DId,
161
+ entityId: EntityId,
162
+ props: ModelProps,
163
+ ): void {
117
164
  createModelRaw(asWorldNumber(worldId), entityId as number, props);
118
165
  }
119
166
 
120
167
  /** Upserts transform component intent for an entity in a 3D world. */
121
- export function update3DTransform(worldId: World3DId, entityId: EntityId, props: TransformProps): void {
168
+ export function update3DTransform(
169
+ worldId: World3DId,
170
+ entityId: EntityId,
171
+ props: TransformProps,
172
+ ): void {
122
173
  updateTransformRaw(asWorldNumber(worldId), entityId as number, props);
123
174
  }
124
175
 
125
176
  /** Attaches or updates a tag component in a 3D world. */
126
- export function create3DTag(worldId: World3DId, entityId: EntityId, props: TagProps): void {
177
+ export function create3DTag(
178
+ worldId: World3DId,
179
+ entityId: EntityId,
180
+ props: TagProps,
181
+ ): void {
127
182
  createTagRaw(asWorldNumber(worldId), entityId as number, props);
128
183
  }
129
184
 
130
185
  /** Sets parent-child relationship between entities. */
131
- export function set3DParent(worldId: World3DId, childEntityId: EntityId, parentEntityId: EntityId | null): void {
132
- setParentRaw(asWorldNumber(worldId), childEntityId as number, parentEntityId as number | null);
186
+ export function set3DParent(
187
+ worldId: World3DId,
188
+ childEntityId: EntityId,
189
+ parentEntityId: EntityId | null,
190
+ ): void {
191
+ setParentRaw(
192
+ asWorldNumber(worldId),
193
+ childEntityId as number,
194
+ parentEntityId as number | null,
195
+ );
133
196
  }
134
197
 
135
198
  /** Creates a material resource and returns its typed id. */
136
- export function create3DMaterial(worldId: World3DId, props: MaterialProps): MaterialId {
199
+ export function create3DMaterial(
200
+ worldId: World3DId,
201
+ props: MaterialProps,
202
+ ): MaterialId {
137
203
  return asMaterialId(createMaterialRaw(asWorldNumber(worldId), props));
138
204
  }
139
205
 
140
206
  /** Creates a geometry resource and returns its typed id. */
141
- export function create3DGeometry(worldId: World3DId, props: GeometryProps): GeometryId {
207
+ export function create3DGeometry(
208
+ worldId: World3DId,
209
+ props: GeometryProps,
210
+ ): GeometryId {
142
211
  return asGeometryId(createGeometryRaw(asWorldNumber(worldId), props));
143
212
  }
144
213
 
145
214
  /** Creates a texture resource and returns its typed id. */
146
- export function create3DTexture(worldId: World3DId, props: TextureProps): TextureId {
215
+ export function create3DTexture(
216
+ worldId: World3DId,
217
+ props: TextureProps,
218
+ ): TextureId {
147
219
  return asTextureId(createTextureRaw(asWorldNumber(worldId), props));
148
220
  }
149
221
 
222
+ /** Disposes a material resource from a 3D world. */
223
+ export function dispose3DMaterial(
224
+ worldId: World3DId,
225
+ materialId: MaterialId,
226
+ ): void {
227
+ disposeMaterialRaw(asWorldNumber(worldId), materialId as number);
228
+ }
229
+
230
+ /** Disposes a geometry resource from a 3D world. */
231
+ export function dispose3DGeometry(
232
+ worldId: World3DId,
233
+ geometryId: GeometryId,
234
+ ): void {
235
+ disposeGeometryRaw(asWorldNumber(worldId), geometryId as number);
236
+ }
237
+
238
+ /** Disposes a texture resource from a 3D world. */
239
+ export function dispose3DTexture(
240
+ worldId: World3DId,
241
+ textureId: TextureId,
242
+ ): void {
243
+ disposeTextureRaw(asWorldNumber(worldId), textureId as number);
244
+ }
245
+
150
246
  /** Configures environment/post-processing for a 3D world. */
151
- export function configure3DEnvironment(worldId: World3DId, config: EnvironmentConfig): void {
247
+ export function configure3DEnvironment(
248
+ worldId: World3DId,
249
+ config: EnvironmentConfig,
250
+ ): void {
152
251
  configureEnvironmentRaw(asWorldNumber(worldId), config);
153
252
  }
154
253
 
155
254
  /** Configures shadows for a 3D world. */
156
- export function configure3DShadows(worldId: World3DId, config: ShadowConfig): void {
255
+ export function configure3DShadows(
256
+ worldId: World3DId,
257
+ config: ShadowConfig,
258
+ ): void {
157
259
  configureShadowsRaw(asWorldNumber(worldId), config);
158
260
  }
159
261
 
160
262
  /** Draws a debug line gizmo. */
161
- export function draw3DGizmoLine(worldId: World3DId, args: CmdGizmoDrawLineArgs): void {
263
+ export function draw3DGizmoLine(
264
+ worldId: World3DId,
265
+ args: CmdGizmoDrawLineArgs,
266
+ ): void {
162
267
  drawGizmoLineRaw(asWorldNumber(worldId), args);
163
268
  }
164
269
 
165
270
  /** Draws a debug axis-aligned bounding box gizmo. */
166
- export function draw3DGizmoAabb(worldId: World3DId, args: CmdGizmoDrawAabbArgs): void {
271
+ export function draw3DGizmoAabb(
272
+ worldId: World3DId,
273
+ args: CmdGizmoDrawAabbArgs,
274
+ ): void {
167
275
  drawGizmoAabbRaw(asWorldNumber(worldId), args);
168
276
  }
169
277
 
170
278
  /** Updates pose data for XR/trackers. */
171
- export function update3DPose(worldId: World3DId, args: CmdPoseUpdateArgs): CommandId {
279
+ export function update3DPose(
280
+ worldId: World3DId,
281
+ args: CmdPoseUpdateArgs,
282
+ ): CommandId {
172
283
  return asCommandId(poseUpdateRaw(asWorldNumber(worldId), args));
173
284
  }
174
285
 
@@ -202,11 +313,86 @@ export function list3DCameras(worldId: World3DId): CommandId {
202
313
  return asCommandId(listCamerasRaw(asWorldNumber(worldId)));
203
314
  }
204
315
 
316
+ /** Requests target measurement for this world context. */
317
+ export function measure3DTarget(
318
+ worldId: World3DId,
319
+ args: CmdTargetMeasurementArgs,
320
+ ): CommandId {
321
+ return asCommandId(measureTargetRaw(asWorldNumber(worldId), args));
322
+ }
323
+
324
+ /** Creates or updates a pointer listener routed by target id. */
325
+ export function upsert3DInputTargetListener(
326
+ worldId: World3DId,
327
+ args: CmdInputTargetListenerUpsertArgs,
328
+ ): CommandId {
329
+ return asCommandId(
330
+ upsertInputTargetListenerRaw(asWorldNumber(worldId), args),
331
+ );
332
+ }
333
+
334
+ /** Disposes a pointer listener routed by target id. */
335
+ export function dispose3DInputTargetListener(
336
+ worldId: World3DId,
337
+ args: CmdInputTargetListenerDisposeArgs,
338
+ ): CommandId {
339
+ return asCommandId(
340
+ disposeInputTargetListenerRaw(asWorldNumber(worldId), args),
341
+ );
342
+ }
343
+
344
+ /** Requests current pointer listener list from core. */
345
+ export function list3DInputTargetListeners(
346
+ worldId: World3DId,
347
+ args: CmdInputTargetListenerListArgs = {},
348
+ ): CommandId {
349
+ return asCommandId(
350
+ listInputTargetListenersRaw(asWorldNumber(worldId), args),
351
+ );
352
+ }
353
+
354
+ /**
355
+ * Returns system events filtered to input-target-listener-event.
356
+ */
357
+ export function get3DTargetPointerEvents(
358
+ worldId: World3DId,
359
+ ): Extract<SystemEvent, { event: 'input-target-listener-event' }>[] {
360
+ const events = getSystemEventsRaw(asWorldNumber(worldId));
361
+ return events.filter(
362
+ (
363
+ event,
364
+ ): event is Extract<SystemEvent, { event: 'input-target-listener-event' }> =>
365
+ event.event === 'input-target-listener-event',
366
+ );
367
+ }
368
+
205
369
  /** Returns true while a key is pressed in this world input state. */
206
370
  export function is3DKeyPressed(worldId: World3DId, keyCode: number): boolean {
207
371
  return isKeyPressedRaw(asWorldNumber(worldId), keyCode);
208
372
  }
209
373
 
374
+ /** Returns true while IME composition is active in this world. */
375
+ export function is3DImeEnabled(worldId: World3DId): boolean {
376
+ return isImeEnabledRaw(asWorldNumber(worldId));
377
+ }
378
+
379
+ /** Returns current IME preedit text, if any. */
380
+ export function get3DImePreeditText(worldId: World3DId): string | null {
381
+ return getImePreeditTextRaw(asWorldNumber(worldId));
382
+ }
383
+
384
+ /** Returns current IME cursor range inside preedit text, if available. */
385
+ export function get3DImeCursorRange(
386
+ worldId: World3DId,
387
+ ): [number, number] | null {
388
+ return getImeCursorRangeRaw(asWorldNumber(worldId));
389
+ }
390
+
391
+ /** Returns last IME committed text for the current frame, if any. */
392
+ export function get3DImeCommitText(worldId: World3DId): string | null {
393
+ return getImeCommitTextRaw(asWorldNumber(worldId));
394
+ }
395
+
210
396
  /** Returns true when close was requested for this world's primary window. */
211
397
  export function is3DWindowCloseRequested(worldId: World3DId): boolean {
212
398
  return isWindowCloseRequestedRaw(asWorldNumber(worldId));
@@ -217,6 +403,72 @@ export function get3DWindowSize(worldId: World3DId): [number, number] {
217
403
  return getWindowSizeRaw(asWorldNumber(worldId));
218
404
  }
219
405
 
406
+ /** Returns current pointer position in window space. */
407
+ export function get3DPointerPosition(worldId: World3DId): [number, number] {
408
+ return getPointerPositionRaw(asWorldNumber(worldId));
409
+ }
410
+
411
+ /** Returns real drawn window size from the latest pointer event, if available. */
412
+ export function get3DPointerWindowSize(
413
+ worldId: World3DId,
414
+ ): [number, number] | null {
415
+ return getPointerWindowSizeRaw(asWorldNumber(worldId));
416
+ }
417
+
418
+ /** Returns pointer delta in window space for the current frame. */
419
+ export function get3DPointerDelta(worldId: World3DId): [number, number] {
420
+ return getPointerDeltaRaw(asWorldNumber(worldId));
421
+ }
422
+
423
+ /** Returns pointer position relative to routed target, if available. */
424
+ export function get3DPointerTargetPosition(
425
+ worldId: World3DId,
426
+ ): [number, number] | null {
427
+ return getPointerTargetPositionRaw(asWorldNumber(worldId));
428
+ }
429
+
430
+ /** Returns real drawn target size from the latest pointer event, if available. */
431
+ export function get3DPointerTargetSize(
432
+ worldId: World3DId,
433
+ ): [number, number] | null {
434
+ return getPointerTargetSizeRaw(asWorldNumber(worldId));
435
+ }
436
+
437
+ /** Returns pointer delta relative to routed target for the current frame. */
438
+ export function get3DPointerTargetDelta(
439
+ worldId: World3DId,
440
+ ): [number, number] | null {
441
+ return getPointerTargetDeltaRaw(asWorldNumber(worldId));
442
+ }
443
+
444
+ /** Returns routed target id under pointer, when available. */
445
+ export function get3DPointerTargetId(worldId: World3DId): number | null {
446
+ return getPointerTargetIdRaw(asWorldNumber(worldId));
447
+ }
448
+
449
+ /** Returns pointer UV (0..1) in routed target space, when available. */
450
+ export function get3DPointerTargetUv(
451
+ worldId: World3DId,
452
+ ): [number, number] | null {
453
+ return getPointerTargetUvRaw(asWorldNumber(worldId));
454
+ }
455
+
456
+ /** Returns true while a pointer button is pressed in this world. */
457
+ export function is3DPointerButtonPressed(
458
+ worldId: World3DId,
459
+ button: number,
460
+ ): boolean {
461
+ return isPointerButtonPressedRaw(asWorldNumber(worldId), button);
462
+ }
463
+
464
+ /** Returns true when a pointer button was pressed in this frame. */
465
+ export function is3DPointerButtonJustPressed(
466
+ worldId: World3DId,
467
+ button: number,
468
+ ): boolean {
469
+ return isPointerButtonJustPressedRaw(asWorldNumber(worldId), button);
470
+ }
471
+
220
472
  /** Sends a host notification scoped to this world. */
221
473
  export function send3DNotification(
222
474
  worldId: World3DId,
@@ -247,20 +499,17 @@ export function create3DAudioSource(
247
499
  args: Create3DAudioSourceArgs,
248
500
  ): CommandId {
249
501
  const rawWorldId = asWorldNumber(worldId);
250
- const realmId = getWorldRealmIdRaw(rawWorldId);
251
- if (realmId == null) {
252
- throw new Error('World is not ready for audio source creation.');
253
- }
254
-
255
- const modelId = 'modelId' in args
256
- ? args.modelId
257
- : getModelIdRaw(rawWorldId, args.entityId as number);
258
- if (modelId == null) {
259
- throw new Error('Audio source entity has no resolved model yet.');
260
- }
261
- const baseArgs = 'modelId' in args
262
- ? args
263
- : (({ entityId: _entityId, ...rest }) => rest)(args);
502
+ const realmId = getWorldRealmIdRaw(rawWorldId) ?? rawWorldId;
503
+
504
+ const modelId =
505
+ 'modelId' in args
506
+ ? args.modelId
507
+ : (getModelIdRaw(rawWorldId, args.entityId as number) ??
508
+ (args.entityId as number));
509
+ const baseArgs =
510
+ 'modelId' in args
511
+ ? args
512
+ : (({ entityId: _entityId, ...rest }) => rest)(args);
264
513
 
265
514
  return asCommandId(
266
515
  audioSourceCreateRaw(rawWorldId, {