@vulfram/engine 0.14.8-alpha → 0.19.2-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.
- package/README.md +106 -0
- package/package.json +60 -4
- package/src/core.ts +14 -0
- package/src/ecs.ts +1 -0
- package/src/engine/api.ts +222 -24
- package/src/engine/bridge/dispatch.ts +260 -40
- package/src/engine/bridge/guards.ts +4 -1
- package/src/engine/bridge/protocol.ts +69 -52
- package/src/engine/ecs/components.ts +340 -0
- package/src/engine/ecs/index.ts +3 -518
- package/src/engine/ecs/intents.ts +184 -0
- package/src/engine/ecs/systems.ts +26 -0
- package/src/engine/intents/store.ts +72 -0
- package/src/engine/state.ts +136 -5
- package/src/engine/systems/command-intent.ts +159 -14
- package/src/engine/systems/constraint-solve.ts +167 -0
- package/src/engine/systems/core-command-builder.ts +9 -268
- package/src/engine/systems/diagnostics.ts +20 -29
- package/src/engine/systems/index.ts +3 -1
- package/src/engine/systems/input-mirror.ts +257 -21
- package/src/engine/systems/resource-upload.ts +108 -58
- package/src/engine/systems/response-decode.ts +86 -15
- package/src/engine/systems/scene-sync.ts +305 -0
- package/src/engine/systems/ui-bridge.ts +381 -0
- package/src/engine/systems/utils.ts +86 -1
- package/src/engine/systems/world-lifecycle.ts +43 -114
- package/src/engine/window/manager.ts +168 -0
- package/src/engine/world/entities.ts +998 -91
- package/src/engine/world/mount.ts +195 -0
- package/src/engine/world/types.ts +71 -0
- package/src/engine/world/world-ui.ts +313 -0
- package/src/engine/world/world3d.ts +529 -0
- package/src/helpers/collision.ts +487 -0
- package/src/helpers/index.ts +2 -0
- package/src/helpers/raycast.ts +442 -0
- package/src/index.ts +30 -1
- package/src/mount.ts +2 -0
- package/src/types/cmds/audio.ts +73 -48
- package/src/types/cmds/camera.ts +12 -8
- package/src/types/cmds/environment.ts +9 -3
- package/src/types/cmds/geometry.ts +15 -16
- package/src/types/cmds/index.ts +234 -162
- package/src/types/cmds/input.ts +39 -0
- package/src/types/cmds/light.ts +12 -11
- package/src/types/cmds/material.ts +19 -21
- package/src/types/cmds/model.ts +17 -15
- package/src/types/cmds/realm.ts +23 -0
- package/src/types/cmds/system.ts +29 -0
- package/src/types/cmds/target.ts +96 -0
- package/src/types/cmds/texture.ts +13 -3
- package/src/types/cmds/ui.ts +220 -0
- package/src/types/cmds/window.ts +41 -204
- package/src/types/events/index.ts +4 -1
- package/src/types/events/keyboard.ts +2 -2
- package/src/types/events/pointer.ts +85 -13
- package/src/types/events/system.ts +188 -30
- package/src/types/events/ui.ts +21 -0
- package/src/types/index.ts +1 -0
- package/src/types/json.ts +15 -0
- package/src/window.ts +8 -0
- package/src/world-ui.ts +2 -0
- package/src/world3d.ts +10 -0
- package/tsconfig.json +0 -29
|
@@ -0,0 +1,442 @@
|
|
|
1
|
+
import {
|
|
2
|
+
mat4,
|
|
3
|
+
vec3,
|
|
4
|
+
vec4,
|
|
5
|
+
type ReadonlyMat4,
|
|
6
|
+
type ReadonlyVec2,
|
|
7
|
+
type ReadonlyVec3,
|
|
8
|
+
type vec3 as Vec3,
|
|
9
|
+
} from 'gl-matrix';
|
|
10
|
+
|
|
11
|
+
export interface Ray3 {
|
|
12
|
+
origin: Vec3;
|
|
13
|
+
direction: Vec3;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface RayHit {
|
|
17
|
+
distance: number;
|
|
18
|
+
point: Vec3;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface PointerRaycastInput {
|
|
22
|
+
pointer: ReadonlyVec2;
|
|
23
|
+
viewMatrix: ReadonlyMat4;
|
|
24
|
+
projectionMatrix: ReadonlyMat4;
|
|
25
|
+
viewportSize: ReadonlyVec2;
|
|
26
|
+
viewportOrigin?: ReadonlyVec2;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface PointerRaycastWgpuInput extends PointerRaycastInput {}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Builds a right-handed perspective projection in ZO depth range (0..1),
|
|
33
|
+
* with optional reverse-Z by swapping near/far.
|
|
34
|
+
*
|
|
35
|
+
* Matches glam::Mat4::perspective_rh semantics used by core.
|
|
36
|
+
*/
|
|
37
|
+
export function createPerspectiveRhZo(
|
|
38
|
+
fovYRadians: number,
|
|
39
|
+
aspect: number,
|
|
40
|
+
near: number,
|
|
41
|
+
far: number,
|
|
42
|
+
): mat4 {
|
|
43
|
+
const f = 1 / Math.tan(fovYRadians / 2);
|
|
44
|
+
const out = mat4.create();
|
|
45
|
+
|
|
46
|
+
out[0] = f / Math.max(1e-8, aspect);
|
|
47
|
+
out[1] = 0;
|
|
48
|
+
out[2] = 0;
|
|
49
|
+
out[3] = 0;
|
|
50
|
+
|
|
51
|
+
out[4] = 0;
|
|
52
|
+
out[5] = f;
|
|
53
|
+
out[6] = 0;
|
|
54
|
+
out[7] = 0;
|
|
55
|
+
|
|
56
|
+
out[8] = 0;
|
|
57
|
+
out[9] = 0;
|
|
58
|
+
out[10] = far / (near - far);
|
|
59
|
+
out[11] = -1;
|
|
60
|
+
|
|
61
|
+
out[12] = 0;
|
|
62
|
+
out[13] = 0;
|
|
63
|
+
out[14] = (near * far) / (near - far);
|
|
64
|
+
out[15] = 0;
|
|
65
|
+
|
|
66
|
+
return out;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface PointerEventRaycastData {
|
|
70
|
+
position: ReadonlyVec2;
|
|
71
|
+
positionTarget?: ReadonlyVec2;
|
|
72
|
+
windowWidth?: number;
|
|
73
|
+
windowHeight?: number;
|
|
74
|
+
targetWidth?: number;
|
|
75
|
+
targetHeight?: number;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface PointerEventRaycastInput {
|
|
79
|
+
pointerEvent: PointerEventRaycastData;
|
|
80
|
+
viewMatrix: ReadonlyMat4;
|
|
81
|
+
projectionMatrix: ReadonlyMat4;
|
|
82
|
+
fallbackViewportSize?: ReadonlyVec2;
|
|
83
|
+
viewportOrigin?: ReadonlyVec2;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function pointOnRay(ray: Ray3, distance: number): Vec3 {
|
|
87
|
+
return vec3.fromValues(
|
|
88
|
+
ray.origin[0]! + ray.direction[0]! * distance,
|
|
89
|
+
ray.origin[1]! + ray.direction[1]! * distance,
|
|
90
|
+
ray.origin[2]! + ray.direction[2]! * distance,
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Creates a world-space ray from pointer coordinates plus camera matrices.
|
|
96
|
+
*
|
|
97
|
+
* Returns `null` if the camera matrix cannot be inverted.
|
|
98
|
+
*/
|
|
99
|
+
export function createPointerRay(input: PointerRaycastInput): Ray3 | null {
|
|
100
|
+
const pointerX = input.pointer[0]!;
|
|
101
|
+
const pointerY = input.pointer[1]!;
|
|
102
|
+
const viewportWidth = input.viewportSize[0]!;
|
|
103
|
+
const viewportHeight = input.viewportSize[1]!;
|
|
104
|
+
const viewportSource = input.viewportOrigin;
|
|
105
|
+
const viewportX = viewportSource ? viewportSource[0]! : 0;
|
|
106
|
+
const viewportY = viewportSource ? viewportSource[1]! : 0;
|
|
107
|
+
|
|
108
|
+
if (viewportWidth <= 0 || viewportHeight <= 0) {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const normalizedX = ((pointerX - viewportX) / viewportWidth) * 2 - 1;
|
|
113
|
+
const normalizedY = 1 - ((pointerY - viewportY) / viewportHeight) * 2;
|
|
114
|
+
|
|
115
|
+
const viewProjection = mat4.multiply(
|
|
116
|
+
mat4.create(),
|
|
117
|
+
input.projectionMatrix,
|
|
118
|
+
input.viewMatrix,
|
|
119
|
+
);
|
|
120
|
+
const inverseViewProjection = mat4.invert(mat4.create(), viewProjection);
|
|
121
|
+
if (!inverseViewProjection) {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const nearClip = vec4.fromValues(normalizedX, normalizedY, -1, 1);
|
|
126
|
+
const farClip = vec4.fromValues(normalizedX, normalizedY, 1, 1);
|
|
127
|
+
|
|
128
|
+
const nearWorld4 = vec4.transformMat4(vec4.create(), nearClip, inverseViewProjection);
|
|
129
|
+
const farWorld4 = vec4.transformMat4(vec4.create(), farClip, inverseViewProjection);
|
|
130
|
+
if (nearWorld4[3] === 0 || farWorld4[3] === 0) {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const nearWorld = vec3.fromValues(
|
|
135
|
+
nearWorld4[0] / nearWorld4[3],
|
|
136
|
+
nearWorld4[1] / nearWorld4[3],
|
|
137
|
+
nearWorld4[2] / nearWorld4[3],
|
|
138
|
+
);
|
|
139
|
+
const farWorld = vec3.fromValues(
|
|
140
|
+
farWorld4[0] / farWorld4[3],
|
|
141
|
+
farWorld4[1] / farWorld4[3],
|
|
142
|
+
farWorld4[2] / farWorld4[3],
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
const direction = vec3.normalize(
|
|
146
|
+
vec3.create(),
|
|
147
|
+
vec3.subtract(vec3.create(), farWorld, nearWorld),
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
origin: nearWorld,
|
|
152
|
+
direction,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Creates a world-space ray using WGPU reverse-Z clip conventions.
|
|
158
|
+
*
|
|
159
|
+
* This mirrors core picking logic:
|
|
160
|
+
* - NDC depth near=1 and far=0
|
|
161
|
+
* - direction corrected to follow camera forward when needed
|
|
162
|
+
*/
|
|
163
|
+
export function createPointerRayWgpuReverseZ(
|
|
164
|
+
input: PointerRaycastWgpuInput,
|
|
165
|
+
): Ray3 | null {
|
|
166
|
+
const pointerX = input.pointer[0]!;
|
|
167
|
+
const pointerY = input.pointer[1]!;
|
|
168
|
+
const viewportWidth = input.viewportSize[0]!;
|
|
169
|
+
const viewportHeight = input.viewportSize[1]!;
|
|
170
|
+
const viewportSource = input.viewportOrigin;
|
|
171
|
+
const viewportX = viewportSource ? viewportSource[0]! : 0;
|
|
172
|
+
const viewportY = viewportSource ? viewportSource[1]! : 0;
|
|
173
|
+
|
|
174
|
+
if (viewportWidth <= 0 || viewportHeight <= 0) {
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const normalizedX = ((pointerX - viewportX) / viewportWidth) * 2 - 1;
|
|
179
|
+
const normalizedY = 1 - ((pointerY - viewportY) / viewportHeight) * 2;
|
|
180
|
+
|
|
181
|
+
const viewProjection = mat4.multiply(
|
|
182
|
+
mat4.create(),
|
|
183
|
+
input.projectionMatrix,
|
|
184
|
+
input.viewMatrix,
|
|
185
|
+
);
|
|
186
|
+
const inverseViewProjection = mat4.invert(mat4.create(), viewProjection);
|
|
187
|
+
if (!inverseViewProjection) {
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const nearClip = vec4.fromValues(normalizedX, normalizedY, 1, 1);
|
|
192
|
+
const farClip = vec4.fromValues(normalizedX, normalizedY, 0, 1);
|
|
193
|
+
|
|
194
|
+
const nearWorld4 = vec4.transformMat4(vec4.create(), nearClip, inverseViewProjection);
|
|
195
|
+
const farWorld4 = vec4.transformMat4(vec4.create(), farClip, inverseViewProjection);
|
|
196
|
+
if (nearWorld4[3] === 0 || farWorld4[3] === 0) {
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const nearWorld = vec3.fromValues(
|
|
201
|
+
nearWorld4[0] / nearWorld4[3],
|
|
202
|
+
nearWorld4[1] / nearWorld4[3],
|
|
203
|
+
nearWorld4[2] / nearWorld4[3],
|
|
204
|
+
);
|
|
205
|
+
const farWorld = vec3.fromValues(
|
|
206
|
+
farWorld4[0] / farWorld4[3],
|
|
207
|
+
farWorld4[1] / farWorld4[3],
|
|
208
|
+
farWorld4[2] / farWorld4[3],
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
let direction = vec3.normalize(
|
|
212
|
+
vec3.create(),
|
|
213
|
+
vec3.subtract(vec3.create(), farWorld, nearWorld),
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
const inverseView = mat4.invert(mat4.create(), input.viewMatrix);
|
|
217
|
+
if (!inverseView) {
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
const origin4 = vec4.transformMat4(
|
|
221
|
+
vec4.create(),
|
|
222
|
+
vec4.fromValues(0, 0, 0, 1),
|
|
223
|
+
inverseView,
|
|
224
|
+
);
|
|
225
|
+
if (origin4[3] === 0) {
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
const origin = vec3.fromValues(
|
|
229
|
+
origin4[0] / origin4[3],
|
|
230
|
+
origin4[1] / origin4[3],
|
|
231
|
+
origin4[2] / origin4[3],
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
const forward4 = vec4.transformMat4(
|
|
235
|
+
vec4.create(),
|
|
236
|
+
vec4.fromValues(0, 0, -1, 0),
|
|
237
|
+
inverseView,
|
|
238
|
+
);
|
|
239
|
+
const cameraForward = vec3.normalize(
|
|
240
|
+
vec3.create(),
|
|
241
|
+
vec3.fromValues(forward4[0], forward4[1], forward4[2]),
|
|
242
|
+
);
|
|
243
|
+
if (vec3.squaredLength(cameraForward) > 1e-8 && vec3.dot(direction, cameraForward) < 0) {
|
|
244
|
+
direction = vec3.scale(vec3.create(), direction, -1);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (vec3.squaredLength(direction) <= 1e-8) {
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return { origin, direction };
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Creates a world-space ray using pointer event dimensions from core.
|
|
256
|
+
*
|
|
257
|
+
* Priority:
|
|
258
|
+
* 1) target-space pointer + target size (most accurate for routed targets)
|
|
259
|
+
* 2) window-space pointer + window size
|
|
260
|
+
* 3) fallback viewport size (if provided)
|
|
261
|
+
*
|
|
262
|
+
* Uses WGPU reverse-Z conventions to match core picking.
|
|
263
|
+
*/
|
|
264
|
+
export function createPointerRayFromEvent(
|
|
265
|
+
input: PointerEventRaycastInput,
|
|
266
|
+
): Ray3 | null {
|
|
267
|
+
const event = input.pointerEvent;
|
|
268
|
+
const targetWidth = event.targetWidth;
|
|
269
|
+
const targetHeight = event.targetHeight;
|
|
270
|
+
const windowWidth = event.windowWidth;
|
|
271
|
+
const windowHeight = event.windowHeight;
|
|
272
|
+
|
|
273
|
+
if (
|
|
274
|
+
event.positionTarget &&
|
|
275
|
+
typeof targetWidth === 'number' &&
|
|
276
|
+
typeof targetHeight === 'number' &&
|
|
277
|
+
targetWidth > 0 &&
|
|
278
|
+
targetHeight > 0
|
|
279
|
+
) {
|
|
280
|
+
return createPointerRayWgpuReverseZ({
|
|
281
|
+
pointer: event.positionTarget,
|
|
282
|
+
viewMatrix: input.viewMatrix,
|
|
283
|
+
projectionMatrix: input.projectionMatrix,
|
|
284
|
+
viewportSize: [targetWidth, targetHeight],
|
|
285
|
+
viewportOrigin: [0, 0],
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (
|
|
290
|
+
typeof windowWidth === 'number' &&
|
|
291
|
+
typeof windowHeight === 'number' &&
|
|
292
|
+
windowWidth > 0 &&
|
|
293
|
+
windowHeight > 0
|
|
294
|
+
) {
|
|
295
|
+
return createPointerRayWgpuReverseZ({
|
|
296
|
+
pointer: event.position,
|
|
297
|
+
viewMatrix: input.viewMatrix,
|
|
298
|
+
projectionMatrix: input.projectionMatrix,
|
|
299
|
+
viewportSize: [windowWidth, windowHeight],
|
|
300
|
+
viewportOrigin: input.viewportOrigin,
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (input.fallbackViewportSize) {
|
|
305
|
+
return createPointerRayWgpuReverseZ({
|
|
306
|
+
pointer: event.position,
|
|
307
|
+
viewMatrix: input.viewMatrix,
|
|
308
|
+
projectionMatrix: input.projectionMatrix,
|
|
309
|
+
viewportSize: input.fallbackViewportSize,
|
|
310
|
+
viewportOrigin: input.viewportOrigin,
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
return null;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Intersects a ray against a plane defined by one point and one normal.
|
|
319
|
+
*
|
|
320
|
+
* Returns `null` when the ray is parallel to the plane or the hit is behind the ray.
|
|
321
|
+
*/
|
|
322
|
+
export function intersectRayPlane(
|
|
323
|
+
ray: Ray3,
|
|
324
|
+
planePoint: ReadonlyVec3,
|
|
325
|
+
planeNormal: ReadonlyVec3,
|
|
326
|
+
): RayHit | null {
|
|
327
|
+
const origin = vec3.fromValues(ray.origin[0], ray.origin[1], ray.origin[2]);
|
|
328
|
+
const direction = vec3.fromValues(ray.direction[0], ray.direction[1], ray.direction[2]);
|
|
329
|
+
const normal = vec3.normalize(
|
|
330
|
+
vec3.create(),
|
|
331
|
+
vec3.fromValues(planeNormal[0], planeNormal[1], planeNormal[2]),
|
|
332
|
+
);
|
|
333
|
+
const point = vec3.fromValues(planePoint[0], planePoint[1], planePoint[2]);
|
|
334
|
+
|
|
335
|
+
const denominator = vec3.dot(direction, normal);
|
|
336
|
+
if (Math.abs(denominator) < 1e-6) {
|
|
337
|
+
return null;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const distance =
|
|
341
|
+
vec3.dot(vec3.subtract(vec3.create(), point, origin), normal) / denominator;
|
|
342
|
+
if (distance < 0) {
|
|
343
|
+
return null;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
return {
|
|
347
|
+
distance,
|
|
348
|
+
point: pointOnRay(ray, distance),
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Intersects a ray with a sphere and returns the closest hit in front of the ray origin.
|
|
354
|
+
*/
|
|
355
|
+
export function intersectRaySphere(
|
|
356
|
+
ray: Ray3,
|
|
357
|
+
center: ReadonlyVec3,
|
|
358
|
+
radius: number,
|
|
359
|
+
): RayHit | null {
|
|
360
|
+
const origin = vec3.fromValues(ray.origin[0], ray.origin[1], ray.origin[2]);
|
|
361
|
+
const direction = vec3.fromValues(ray.direction[0], ray.direction[1], ray.direction[2]);
|
|
362
|
+
const sphereCenter = vec3.fromValues(center[0], center[1], center[2]);
|
|
363
|
+
|
|
364
|
+
const oc = vec3.subtract(vec3.create(), origin, sphereCenter);
|
|
365
|
+
const a = vec3.dot(direction, direction);
|
|
366
|
+
const b = 2 * vec3.dot(oc, direction);
|
|
367
|
+
const c = vec3.dot(oc, oc) - radius * radius;
|
|
368
|
+
const discriminant = b * b - 4 * a * c;
|
|
369
|
+
|
|
370
|
+
if (discriminant < 0) {
|
|
371
|
+
return null;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
const sqrtDiscriminant = Math.sqrt(discriminant);
|
|
375
|
+
const t0 = (-b - sqrtDiscriminant) / (2 * a);
|
|
376
|
+
const t1 = (-b + sqrtDiscriminant) / (2 * a);
|
|
377
|
+
|
|
378
|
+
const distance = t0 >= 0 ? t0 : t1 >= 0 ? t1 : -1;
|
|
379
|
+
if (distance < 0) {
|
|
380
|
+
return null;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
return {
|
|
384
|
+
distance,
|
|
385
|
+
point: pointOnRay(ray, distance),
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Intersects a ray with an axis-aligned bounding box.
|
|
391
|
+
*/
|
|
392
|
+
export function intersectRayAabb(
|
|
393
|
+
ray: Ray3,
|
|
394
|
+
min: ReadonlyVec3,
|
|
395
|
+
max: ReadonlyVec3,
|
|
396
|
+
): RayHit | null {
|
|
397
|
+
const origin = ray.origin;
|
|
398
|
+
const direction = ray.direction;
|
|
399
|
+
|
|
400
|
+
let tMin = -Infinity;
|
|
401
|
+
let tMax = Infinity;
|
|
402
|
+
|
|
403
|
+
const axes = [0, 1, 2] as const;
|
|
404
|
+
for (const axis of axes) {
|
|
405
|
+
const o = origin[axis]!;
|
|
406
|
+
const d = direction[axis]!;
|
|
407
|
+
const minAxis = min[axis]!;
|
|
408
|
+
const maxAxis = max[axis]!;
|
|
409
|
+
|
|
410
|
+
if (Math.abs(d) < 1e-8) {
|
|
411
|
+
if (o < minAxis || o > maxAxis) {
|
|
412
|
+
return null;
|
|
413
|
+
}
|
|
414
|
+
continue;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
const invD = 1 / d;
|
|
418
|
+
let t0 = (minAxis - o) * invD;
|
|
419
|
+
let t1 = (maxAxis - o) * invD;
|
|
420
|
+
if (t0 > t1) {
|
|
421
|
+
const tmp = t0;
|
|
422
|
+
t0 = t1;
|
|
423
|
+
t1 = tmp;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
tMin = Math.max(tMin, t0);
|
|
427
|
+
tMax = Math.min(tMax, t1);
|
|
428
|
+
if (tMax < tMin) {
|
|
429
|
+
return null;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
const distance = tMin >= 0 ? tMin : tMax >= 0 ? tMax : -1;
|
|
434
|
+
if (distance < 0) {
|
|
435
|
+
return null;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
return {
|
|
439
|
+
distance,
|
|
440
|
+
point: pointOnRay(ray, distance),
|
|
441
|
+
};
|
|
442
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,35 @@
|
|
|
1
1
|
export { EngineError } from './engine/errors';
|
|
2
|
-
export * from './engine/api';
|
|
3
2
|
export * from './engine/ecs';
|
|
3
|
+
export * as Mount from './engine/world/mount';
|
|
4
|
+
export * as World3D from './engine/world/world3d';
|
|
5
|
+
export * as WorldUI from './engine/world/world-ui';
|
|
6
|
+
export {
|
|
7
|
+
disposeEngine,
|
|
8
|
+
initEngine,
|
|
9
|
+
registerComponent,
|
|
10
|
+
registerSystem,
|
|
11
|
+
tick,
|
|
12
|
+
uploadBuffer,
|
|
13
|
+
} from './engine/api';
|
|
14
|
+
export {
|
|
15
|
+
closeWindow,
|
|
16
|
+
createWindow,
|
|
17
|
+
focusWindow,
|
|
18
|
+
requestAttention,
|
|
19
|
+
updateWindow,
|
|
20
|
+
} from './engine/window/manager';
|
|
21
|
+
export type {
|
|
22
|
+
CommandId,
|
|
23
|
+
EntityId,
|
|
24
|
+
GeometryId,
|
|
25
|
+
MaterialId,
|
|
26
|
+
TargetId,
|
|
27
|
+
TextureId,
|
|
28
|
+
WindowId,
|
|
29
|
+
World3DId,
|
|
30
|
+
WorldId,
|
|
31
|
+
WorldUIId,
|
|
32
|
+
} from './engine/world/types';
|
|
4
33
|
export * as Types from './types';
|
|
5
34
|
export type {
|
|
6
35
|
BufferResult,
|
package/src/mount.ts
ADDED
package/src/types/cmds/audio.ts
CHANGED
|
@@ -19,13 +19,13 @@ export interface CmdAudioListenerUpdateArgs {
|
|
|
19
19
|
|
|
20
20
|
/** Command payload for binding the listener to a model. */
|
|
21
21
|
export interface CmdAudioListenerCreateArgs {
|
|
22
|
-
|
|
22
|
+
realmId: number;
|
|
23
23
|
modelId: number;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
/** Command payload for disposing the listener binding. */
|
|
27
27
|
export interface CmdAudioListenerDisposeArgs {
|
|
28
|
-
|
|
28
|
+
realmId: number;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
export interface CmdResultAudioListenerCreate {
|
|
@@ -43,30 +43,26 @@ export interface CmdResultAudioListenerUpdate {
|
|
|
43
43
|
message: string;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
/**
|
|
47
|
-
export
|
|
46
|
+
/** Upsert payload accepted by the core (`create` or `update`). */
|
|
47
|
+
export type CmdAudioListenerUpsertArgs =
|
|
48
|
+
| CmdAudioListenerCreateArgs
|
|
49
|
+
| CmdAudioListenerUpdateArgs;
|
|
50
|
+
|
|
51
|
+
/** Backward-compatible aliases. */
|
|
52
|
+
export type CmdResultAudioListenerUpsert = CmdResultAudioListenerUpdate;
|
|
53
|
+
|
|
54
|
+
/** Command payload for creating/updating an audio resource from uploaded bytes. */
|
|
55
|
+
export interface CmdAudioResourceUpsertArgs {
|
|
48
56
|
resourceId: number;
|
|
49
57
|
bufferId: number;
|
|
50
58
|
totalBytes?: number;
|
|
51
59
|
offsetBytes?: number;
|
|
52
60
|
}
|
|
53
61
|
|
|
54
|
-
export interface
|
|
62
|
+
export interface CmdResultAudioResourceUpsert {
|
|
55
63
|
success: boolean;
|
|
56
64
|
message: string;
|
|
57
65
|
pending: boolean;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/** Command payload for pushing a stream chunk. */
|
|
61
|
-
export interface CmdAudioResourcePushArgs {
|
|
62
|
-
resourceId: number;
|
|
63
|
-
bufferId: number;
|
|
64
|
-
offsetBytes: number;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export interface CmdResultAudioResourcePush {
|
|
68
|
-
success: boolean;
|
|
69
|
-
message: string;
|
|
70
66
|
receivedBytes: number;
|
|
71
67
|
totalBytes: number;
|
|
72
68
|
complete: boolean;
|
|
@@ -74,7 +70,7 @@ export interface CmdResultAudioResourcePush {
|
|
|
74
70
|
|
|
75
71
|
/** Command payload for creating a source bound to a model. */
|
|
76
72
|
export interface CmdAudioSourceCreateArgs {
|
|
77
|
-
|
|
73
|
+
realmId: number;
|
|
78
74
|
sourceId: number;
|
|
79
75
|
modelId: number;
|
|
80
76
|
position: [number, number, number];
|
|
@@ -93,12 +89,14 @@ export interface CmdResultAudioSourceCreate {
|
|
|
93
89
|
/** Command payload for updating source params. */
|
|
94
90
|
export interface CmdAudioSourceUpdateArgs {
|
|
95
91
|
sourceId: number;
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
92
|
+
realmId?: number;
|
|
93
|
+
modelId?: number;
|
|
94
|
+
position?: [number, number, number];
|
|
95
|
+
velocity?: [number, number, number];
|
|
96
|
+
orientation?: [number, number, number, number];
|
|
97
|
+
gain?: number;
|
|
98
|
+
pitch?: number;
|
|
99
|
+
spatial?: AudioSpatialParams;
|
|
102
100
|
}
|
|
103
101
|
|
|
104
102
|
export interface CmdResultAudioSourceUpdate {
|
|
@@ -106,59 +104,86 @@ export interface CmdResultAudioSourceUpdate {
|
|
|
106
104
|
message: string;
|
|
107
105
|
}
|
|
108
106
|
|
|
109
|
-
/**
|
|
110
|
-
export
|
|
107
|
+
/** Upsert payload accepted by the core (`create` or `update`). */
|
|
108
|
+
export type CmdAudioSourceUpsertArgs =
|
|
109
|
+
| CmdAudioSourceCreateArgs
|
|
110
|
+
| CmdAudioSourceUpdateArgs;
|
|
111
|
+
|
|
112
|
+
/** Backward-compatible aliases. */
|
|
113
|
+
export type CmdResultAudioSourceUpsert = CmdResultAudioSourceUpdate;
|
|
114
|
+
|
|
115
|
+
export type AudioSourceTransportAction = 'play' | 'pause' | 'stop';
|
|
116
|
+
|
|
117
|
+
/** Command payload for playback transport control. */
|
|
118
|
+
export interface CmdAudioSourceTransportArgs {
|
|
111
119
|
sourceId: number;
|
|
112
|
-
|
|
120
|
+
action: AudioSourceTransportAction;
|
|
121
|
+
resourceId?: number;
|
|
113
122
|
timelineId?: number;
|
|
114
|
-
intensity
|
|
123
|
+
intensity?: number;
|
|
115
124
|
delayMs?: number;
|
|
116
|
-
mode
|
|
125
|
+
mode?: AudioPlayMode;
|
|
117
126
|
}
|
|
118
127
|
|
|
119
|
-
export interface
|
|
128
|
+
export interface CmdResultAudioSourceTransport {
|
|
120
129
|
success: boolean;
|
|
121
130
|
message: string;
|
|
122
131
|
}
|
|
123
132
|
|
|
124
|
-
/** Command payload for
|
|
125
|
-
export interface
|
|
133
|
+
/** Command payload for disposing a source. */
|
|
134
|
+
export interface CmdAudioSourceDisposeArgs {
|
|
126
135
|
sourceId: number;
|
|
127
|
-
timelineId?: number;
|
|
128
136
|
}
|
|
129
137
|
|
|
130
|
-
export interface
|
|
138
|
+
export interface CmdResultAudioSourceDispose {
|
|
131
139
|
success: boolean;
|
|
132
140
|
message: string;
|
|
133
141
|
}
|
|
134
142
|
|
|
135
|
-
/** Command payload for
|
|
136
|
-
export interface
|
|
137
|
-
|
|
138
|
-
timelineId?: number;
|
|
143
|
+
/** Command payload for disposing an audio resource. */
|
|
144
|
+
export interface CmdAudioResourceDisposeArgs {
|
|
145
|
+
resourceId: number;
|
|
139
146
|
}
|
|
140
147
|
|
|
141
|
-
export interface
|
|
148
|
+
export interface CmdResultAudioResourceDispose {
|
|
142
149
|
success: boolean;
|
|
143
150
|
message: string;
|
|
144
151
|
}
|
|
145
152
|
|
|
146
|
-
/** Command payload for
|
|
147
|
-
export interface
|
|
148
|
-
|
|
153
|
+
/** Command payload for listing current audio runtime state. */
|
|
154
|
+
export interface CmdAudioStateGetArgs {
|
|
155
|
+
includeListener?: boolean;
|
|
156
|
+
includeSources?: boolean;
|
|
157
|
+
includeStreams?: boolean;
|
|
149
158
|
}
|
|
150
159
|
|
|
151
|
-
export interface
|
|
152
|
-
|
|
153
|
-
|
|
160
|
+
export interface AudioListenerBindingState {
|
|
161
|
+
realmId: number;
|
|
162
|
+
modelId: number;
|
|
154
163
|
}
|
|
155
164
|
|
|
156
|
-
|
|
157
|
-
|
|
165
|
+
export interface AudioSourceStateEntry {
|
|
166
|
+
sourceId: number;
|
|
167
|
+
realmId?: number;
|
|
168
|
+
modelId?: number;
|
|
169
|
+
position: [number, number, number];
|
|
170
|
+
velocity: [number, number, number];
|
|
171
|
+
orientation: [number, number, number, number];
|
|
172
|
+
gain: number;
|
|
173
|
+
pitch: number;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export interface AudioStreamStateEntry {
|
|
158
177
|
resourceId: number;
|
|
178
|
+
receivedBytes: number;
|
|
179
|
+
totalBytes: number;
|
|
180
|
+
complete: boolean;
|
|
159
181
|
}
|
|
160
182
|
|
|
161
|
-
export interface
|
|
183
|
+
export interface CmdResultAudioStateGet {
|
|
162
184
|
success: boolean;
|
|
163
185
|
message: string;
|
|
186
|
+
listener?: AudioListenerBindingState;
|
|
187
|
+
sources: AudioSourceStateEntry[];
|
|
188
|
+
streams: AudioStreamStateEntry[];
|
|
164
189
|
}
|