@needle-tools/engine 4.10.0-beta.3 → 4.10.0-beta.4
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/components.needle.json +1 -1
- package/dist/{needle-engine.bundle-CLzMgxkO.min.js → needle-engine.bundle-BFTyp4Pf.min.js} +132 -130
- package/dist/{needle-engine.bundle-Ddybtee9.js → needle-engine.bundle-CsVLA8Ze.js} +6114 -6075
- package/dist/{needle-engine.bundle-Ckr5KE6m.umd.cjs → needle-engine.bundle-D9nl4ea6.umd.cjs} +134 -132
- package/dist/needle-engine.js +106 -106
- package/dist/needle-engine.min.js +1 -1
- package/dist/needle-engine.umd.cjs +1 -1
- package/lib/engine/codegen/register_types.js +2 -2
- package/lib/engine/codegen/register_types.js.map +1 -1
- package/lib/engine/engine_camera.js +5 -5
- package/lib/engine/engine_camera.js.map +1 -1
- package/lib/engine/engine_gizmos.d.ts +11 -10
- package/lib/engine/engine_gizmos.js +24 -10
- package/lib/engine/engine_gizmos.js.map +1 -1
- package/lib/engine/extensions/extension_utils.js +1 -1
- package/lib/engine/extensions/extension_utils.js.map +1 -1
- package/lib/engine/xr/NeedleXRController.d.ts +3 -3
- package/lib/engine/xr/NeedleXRController.js +28 -0
- package/lib/engine/xr/NeedleXRController.js.map +1 -1
- package/lib/engine-components/codegen/components.d.ts +1 -1
- package/lib/engine-components/codegen/components.js +1 -1
- package/lib/engine-components/codegen/components.js.map +1 -1
- package/lib/engine-components/debug/LogStats.d.ts +1 -0
- package/lib/engine-components/debug/LogStats.js +1 -0
- package/lib/engine-components/debug/LogStats.js.map +1 -1
- package/lib/engine-components/timeline/PlayableDirector.js +1 -1
- package/lib/engine-components/timeline/PlayableDirector.js.map +1 -1
- package/lib/engine-components/timeline/TimelineTracks.d.ts +2 -1
- package/lib/engine-components/timeline/TimelineTracks.js +24 -19
- package/lib/engine-components/timeline/TimelineTracks.js.map +1 -1
- package/lib/engine-components/web/ScrollFollow.js +36 -34
- package/lib/engine-components/web/ScrollFollow.js.map +1 -1
- package/lib/engine-components/web/ViewBox.d.ts +2 -2
- package/lib/engine-components/web/ViewBox.js +35 -26
- package/lib/engine-components/web/ViewBox.js.map +1 -1
- package/lib/engine-components-experimental/Presentation.d.ts +1 -0
- package/lib/engine-components-experimental/Presentation.js +1 -0
- package/lib/engine-components-experimental/Presentation.js.map +1 -1
- package/package.json +1 -1
- package/src/engine/codegen/register_types.ts +2 -2
- package/src/engine/engine_camera.ts +5 -7
- package/src/engine/engine_gizmos.ts +37 -23
- package/src/engine/extensions/extension_utils.ts +1 -1
- package/src/engine/xr/NeedleXRController.ts +36 -4
- package/src/engine-components/codegen/components.ts +1 -1
- package/src/engine-components/debug/LogStats.ts +1 -0
- package/src/engine-components/timeline/PlayableDirector.ts +1 -1
- package/src/engine-components/timeline/TimelineTracks.ts +24 -19
- package/src/engine-components/web/ScrollFollow.ts +40 -36
- package/src/engine-components/web/ViewBox.ts +35 -23
- package/src/engine-components-experimental/Presentation.ts +1 -0
|
@@ -2,6 +2,7 @@ import { Camera, HemisphereLightHelper, Object3D, PerspectiveCamera, Vector2, We
|
|
|
2
2
|
|
|
3
3
|
import { Mathf } from "./engine_math.js";
|
|
4
4
|
import type { ICameraController } from "./engine_types.js";
|
|
5
|
+
import { getParam } from "./engine_utils.js";
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
const $cameraController = "needle:cameraController";
|
|
@@ -66,11 +67,15 @@ export type FocusRect = DOMRect | Element | { x: number, y: number, width: numbe
|
|
|
66
67
|
let rendererRect: DOMRect | undefined = undefined;
|
|
67
68
|
const overlapRect = { x: 0, y: 0, width: 0, height: 0 };
|
|
68
69
|
const _testTime = 1;
|
|
70
|
+
const debug = getParam("debugfocusrect");
|
|
69
71
|
|
|
70
72
|
/** Used internally by the Needle Engine context via 'setFocusRect(<rect>)' */
|
|
71
73
|
export function updateCameraFocusRect(focusRect: FocusRect, settings: FocusRectSettings, dt: number, camera: PerspectiveCamera, renderer: WebGLRenderer) {
|
|
72
74
|
|
|
73
75
|
if (focusRect instanceof Element) {
|
|
76
|
+
if(debug && focusRect instanceof HTMLElement) {
|
|
77
|
+
focusRect.style.outline = "2px dashed rgba(255, 150, 0, .8)";
|
|
78
|
+
}
|
|
74
79
|
focusRect = focusRect.getBoundingClientRect();
|
|
75
80
|
}
|
|
76
81
|
rendererRect = renderer.domElement.getBoundingClientRect();
|
|
@@ -136,10 +141,3 @@ export function updateCameraFocusRect(focusRect: FocusRect, settings: FocusRectS
|
|
|
136
141
|
settings.damping = Math.max(0, settings.damping);
|
|
137
142
|
}
|
|
138
143
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
function fit(width1: number, height1: number, width2: number, height2: number) {
|
|
142
|
-
const scaleX = width2 / width1;
|
|
143
|
-
const scaleY = height2 / height1;
|
|
144
|
-
return Math.max(scaleX, scaleY);
|
|
145
|
-
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AxesHelper, Box3, BoxGeometry, BufferAttribute, BufferGeometry, Color, type ColorRepresentation, CylinderGeometry, EdgesGeometry, Line, LineBasicMaterial, LineSegments, Matrix4, Mesh, MeshBasicMaterial, Object3D, Quaternion, SphereGeometry, Vector3 } from 'three';
|
|
1
|
+
import { AxesHelper, Box3, BoxGeometry, BufferAttribute, BufferGeometry, Color, type ColorRepresentation, CylinderGeometry, EdgesGeometry, Line, LineBasicMaterial, LineSegments, Material,Matrix4, Mesh, MeshBasicMaterial, Object3D, Quaternion, SphereGeometry, Vector3 } from 'three';
|
|
2
2
|
import ThreeMeshUI, { Inline, Text } from "three-mesh-ui"
|
|
3
3
|
import { type Options } from 'three-mesh-ui/build/types/core/elements/MeshUIBaseElement.js';
|
|
4
4
|
|
|
@@ -8,6 +8,7 @@ import { getTempVector, getWorldPosition, lookAtObject, setWorldPositionXYZ } fr
|
|
|
8
8
|
import type { Vec3, Vec4 } from './engine_types.js';
|
|
9
9
|
import { getParam } from './engine_utils.js';
|
|
10
10
|
import { NeedleXRSession } from './engine_xr.js';
|
|
11
|
+
import { RGBAColor } from './js-extensions/RGBAColor.js';
|
|
11
12
|
|
|
12
13
|
const _tmp = new Vector3();
|
|
13
14
|
const _tmp2 = new Vector3();
|
|
@@ -21,7 +22,7 @@ const circleSegments: number = 32;
|
|
|
21
22
|
export type LabelHandle = {
|
|
22
23
|
setText(str: string);
|
|
23
24
|
}
|
|
24
|
-
|
|
25
|
+
type GizmoColor = ColorRepresentation | (Color & { a: number }) | RGBAColor;
|
|
25
26
|
|
|
26
27
|
/** Gizmos are temporary objects that are drawn in the scene for debugging or visualization purposes
|
|
27
28
|
* They are automatically removed after a given duration and cached internally to reduce overhead.
|
|
@@ -62,7 +63,7 @@ export class Gizmos {
|
|
|
62
63
|
* @param parent the parent object to attach the label to. If no parent is provided the label will be attached to the scene
|
|
63
64
|
* @returns a handle to the label that can be used to update the text
|
|
64
65
|
*/
|
|
65
|
-
static DrawLabel(position: Vec3, text: string, size: number = .05, duration: number = 0, color?: ColorRepresentation, backgroundColor?: ColorRepresentation |
|
|
66
|
+
static DrawLabel(position: Vec3, text: string, size: number = .05, duration: number = 0, color?: ColorRepresentation, backgroundColor?: ColorRepresentation | GizmoColor, parent?: Object3D,) {
|
|
66
67
|
if (!Gizmos.enabled) return null;
|
|
67
68
|
if (!color) color = defaultColor;
|
|
68
69
|
const rigScale = NeedleXRSession.active?.rigScale ?? 1;
|
|
@@ -82,7 +83,7 @@ export class Gizmos {
|
|
|
82
83
|
* @param duration the duration in seconds the ray will be rendered. If 0 it will be rendered for one frame
|
|
83
84
|
* @param depthTest if true the ray will be rendered with depth test
|
|
84
85
|
*/
|
|
85
|
-
static DrawRay(origin: Vec3, dir: Vec3, color:
|
|
86
|
+
static DrawRay(origin: Vec3, dir: Vec3, color: GizmoColor = defaultColor, duration: number = 0, depthTest: boolean = true) {
|
|
86
87
|
if (!Gizmos.enabled) return;
|
|
87
88
|
const obj = Internal.getLine(duration);
|
|
88
89
|
const positions = obj.geometry.getAttribute("position");
|
|
@@ -90,9 +91,10 @@ export class Gizmos {
|
|
|
90
91
|
_tmp.set(dir.x, dir.y, dir.z).multiplyScalar(999999999);
|
|
91
92
|
positions.setXYZ(1, origin.x + _tmp.x, origin.y + _tmp.y, origin.z + _tmp.z);
|
|
92
93
|
positions.needsUpdate = true;
|
|
93
|
-
obj.material["color"].set(color);
|
|
94
94
|
obj.material["depthTest"] = depthTest;
|
|
95
95
|
obj.material["depthWrite"] = false;
|
|
96
|
+
obj.material["fog"] = false;
|
|
97
|
+
applyGizmoColor(obj.material, color);
|
|
96
98
|
}
|
|
97
99
|
|
|
98
100
|
/**
|
|
@@ -104,7 +106,7 @@ export class Gizmos {
|
|
|
104
106
|
* @param depthTest if true the line will be rendered with depth test
|
|
105
107
|
* @param lengthFactor the length of the line. Default is 1
|
|
106
108
|
*/
|
|
107
|
-
static DrawDirection(pt: Vec3, direction: Vec3 | Vec4, color:
|
|
109
|
+
static DrawDirection(pt: Vec3, direction: Vec3 | Vec4, color: GizmoColor = defaultColor, duration: number = 0, depthTest: boolean = true, lengthFactor: number = 1) {
|
|
108
110
|
if (!Gizmos.enabled) return;
|
|
109
111
|
const obj = Internal.getLine(duration);
|
|
110
112
|
const positions = obj.geometry.getAttribute("position");
|
|
@@ -120,10 +122,9 @@ export class Gizmos {
|
|
|
120
122
|
}
|
|
121
123
|
positions.setXYZ(1, pt.x + _tmp.x, pt.y + _tmp.y, pt.z + _tmp.z);
|
|
122
124
|
positions.needsUpdate = true;
|
|
123
|
-
obj.material["color"].set(color);
|
|
124
125
|
obj.material["depthTest"] = depthTest;
|
|
125
126
|
obj.material["depthWrite"] = false;
|
|
126
|
-
|
|
127
|
+
applyGizmoColor(obj.material, color);
|
|
127
128
|
}
|
|
128
129
|
|
|
129
130
|
/**
|
|
@@ -134,17 +135,17 @@ export class Gizmos {
|
|
|
134
135
|
* @param duration the duration in seconds the line will be rendered. If 0 it will be rendered for one frame
|
|
135
136
|
* @param depthTest if true the line will be rendered with depth test
|
|
136
137
|
*/
|
|
137
|
-
static DrawLine(pt0: Vec3, pt1: Vec3, color:
|
|
138
|
+
static DrawLine(pt0: Vec3, pt1: Vec3, color: GizmoColor = defaultColor, duration: number = 0, depthTest: boolean = true) {
|
|
138
139
|
if (!Gizmos.enabled) return;
|
|
139
140
|
const obj = Internal.getLine(duration);
|
|
140
141
|
const positions = obj.geometry.getAttribute("position");
|
|
141
142
|
positions.setXYZ(0, pt0.x, pt0.y, pt0.z);
|
|
142
143
|
positions.setXYZ(1, pt1.x, pt1.y, pt1.z);
|
|
143
144
|
positions.needsUpdate = true;
|
|
144
|
-
obj.material["color"].set(color);
|
|
145
145
|
obj.material["depthTest"] = depthTest;
|
|
146
146
|
obj.material["depthWrite"] = false;
|
|
147
147
|
obj.material["fog"] = false;
|
|
148
|
+
applyGizmoColor(obj.material, color);
|
|
148
149
|
}
|
|
149
150
|
|
|
150
151
|
/**
|
|
@@ -162,10 +163,10 @@ export class Gizmos {
|
|
|
162
163
|
obj.position.set(pt0.x, pt0.y, pt0.z);
|
|
163
164
|
obj.scale.set(radius, radius, radius);
|
|
164
165
|
obj.quaternion.setFromUnitVectors(this._up, _tmp.set(normal.x, normal.y, normal.z).normalize());
|
|
165
|
-
obj.material["color"].set(color);
|
|
166
166
|
obj.material["depthTest"] = depthTest;
|
|
167
167
|
obj.material["depthWrite"] = false;
|
|
168
168
|
obj.material["fog"] = false;
|
|
169
|
+
applyGizmoColor(obj.material, color);
|
|
169
170
|
}
|
|
170
171
|
|
|
171
172
|
/**
|
|
@@ -176,14 +177,14 @@ export class Gizmos {
|
|
|
176
177
|
* @param duration the duration in seconds the sphere will be rendered. If 0 it will be rendered for one frame
|
|
177
178
|
* @param depthTest if true the sphere will be rendered with depth test
|
|
178
179
|
*/
|
|
179
|
-
static DrawWireSphere(center: Vec3, radius: number, color:
|
|
180
|
+
static DrawWireSphere(center: Vec3, radius: number, color: GizmoColor = defaultColor, duration: number = 0, depthTest: boolean = true) {
|
|
180
181
|
if (!Gizmos.enabled) return;
|
|
181
182
|
const obj = Internal.getSphere(radius, duration, true);
|
|
182
183
|
setWorldPositionXYZ(obj, center.x, center.y, center.z);
|
|
183
|
-
obj.material["color"].set(color);
|
|
184
184
|
obj.material["depthTest"] = depthTest;
|
|
185
185
|
obj.material["depthWrite"] = false;
|
|
186
186
|
obj.material["fog"] = false;
|
|
187
|
+
applyGizmoColor(obj.material, color);
|
|
187
188
|
}
|
|
188
189
|
|
|
189
190
|
/**
|
|
@@ -194,13 +195,13 @@ export class Gizmos {
|
|
|
194
195
|
* @param duration the duration in seconds the sphere will be rendered. If 0 it will be rendered for one frame
|
|
195
196
|
* @param depthTest if true the sphere will be rendered with depth test
|
|
196
197
|
*/
|
|
197
|
-
static DrawSphere(center: Vec3, radius: number, color:
|
|
198
|
+
static DrawSphere(center: Vec3, radius: number, color: GizmoColor = defaultColor, duration: number = 0, depthTest: boolean = true) {
|
|
198
199
|
if (!Gizmos.enabled) return;
|
|
199
200
|
const obj = Internal.getSphere(radius, duration, false);
|
|
200
201
|
setWorldPositionXYZ(obj, center.x, center.y, center.z);
|
|
201
|
-
obj.material["color"].set(color);
|
|
202
202
|
obj.material["depthTest"] = depthTest;
|
|
203
203
|
obj.material["depthWrite"] = false;
|
|
204
|
+
applyGizmoColor(obj.material, color);
|
|
204
205
|
}
|
|
205
206
|
|
|
206
207
|
/**
|
|
@@ -212,18 +213,18 @@ export class Gizmos {
|
|
|
212
213
|
* @param duration the duration in seconds the box will be rendered. If 0 it will be rendered for one frame
|
|
213
214
|
* @param depthTest if true the box will be rendered with depth test
|
|
214
215
|
*/
|
|
215
|
-
static DrawWireBox(center: Vec3, size: Vec3, color:
|
|
216
|
+
static DrawWireBox(center: Vec3, size: Vec3, color: GizmoColor = defaultColor, duration: number = 0, depthTest: boolean = true, rotation: Quaternion | undefined = undefined) {
|
|
216
217
|
if (!Gizmos.enabled) return;
|
|
217
218
|
const obj = Internal.getBox(duration);
|
|
218
219
|
obj.position.set(center.x, center.y, center.z);
|
|
219
220
|
obj.scale.set(size.x, size.y, size.z);
|
|
220
|
-
if(rotation) obj.quaternion.copy(rotation);
|
|
221
|
+
if (rotation) obj.quaternion.copy(rotation);
|
|
221
222
|
else obj.quaternion.identity();
|
|
222
|
-
obj.material["color"].set(color);
|
|
223
223
|
obj.material["depthTest"] = depthTest;
|
|
224
224
|
obj.material["wireframe"] = true;
|
|
225
225
|
obj.material["depthWrite"] = false;
|
|
226
226
|
obj.material["fog"] = false;
|
|
227
|
+
applyGizmoColor(obj.material, color);
|
|
227
228
|
}
|
|
228
229
|
|
|
229
230
|
/**
|
|
@@ -233,16 +234,16 @@ export class Gizmos {
|
|
|
233
234
|
* @param duration the duration in seconds the box will be rendered. If 0 it will be rendered for one frame. Default: 0
|
|
234
235
|
* @param depthTest if true the box will be rendered with depth test. Default: true
|
|
235
236
|
*/
|
|
236
|
-
static DrawWireBox3(box: Box3, color:
|
|
237
|
+
static DrawWireBox3(box: Box3, color: GizmoColor = defaultColor, duration: number = 0, depthTest: boolean = true) {
|
|
237
238
|
if (!Gizmos.enabled) return;
|
|
238
239
|
const obj = Internal.getBox(duration);
|
|
239
240
|
obj.position.copy(box.getCenter(_tmp));
|
|
240
241
|
obj.scale.copy(box.getSize(_tmp));
|
|
241
|
-
obj.material["color"].set(color);
|
|
242
242
|
obj.material["depthTest"] = depthTest;
|
|
243
243
|
obj.material["wireframe"] = true;
|
|
244
244
|
obj.material["depthWrite"] = false;
|
|
245
245
|
obj.material["fog"] = false;
|
|
246
|
+
applyGizmoColor(obj.material, color);
|
|
246
247
|
}
|
|
247
248
|
|
|
248
249
|
private static _up = new Vector3(0, 1, 0);
|
|
@@ -263,9 +264,9 @@ export class Gizmos {
|
|
|
263
264
|
const dist = _tmp.set(pt1.x, pt1.y, pt1.z).sub(_tmp2.set(pt0.x, pt0.y, pt0.z)).length();
|
|
264
265
|
const scale = dist * 0.1;
|
|
265
266
|
obj.scale.set(scale, scale, scale);
|
|
266
|
-
obj.material["color"].set(color);
|
|
267
267
|
obj.material["depthTest"] = depthTest;
|
|
268
268
|
obj.material["wireframe"] = wireframe;
|
|
269
|
+
applyGizmoColor(obj.material, color);
|
|
269
270
|
this.DrawLine(pt0, pt1, color, duration, depthTest);
|
|
270
271
|
}
|
|
271
272
|
|
|
@@ -296,9 +297,9 @@ export class Gizmos {
|
|
|
296
297
|
}
|
|
297
298
|
mesh.matrixAutoUpdate = false;
|
|
298
299
|
mesh.matrixWorldAutoUpdate = false;
|
|
299
|
-
mesh.material["color"].set(options.color ?? defaultColor);
|
|
300
300
|
mesh.material["depthTest"] = options.depthTest ?? true;
|
|
301
301
|
mesh.material["wireframe"] = true;
|
|
302
|
+
applyGizmoColor(mesh.material, options.color ?? defaultColor);
|
|
302
303
|
}
|
|
303
304
|
}
|
|
304
305
|
|
|
@@ -316,6 +317,19 @@ export function CreateWireCube(col: ColorRepresentation | null = null): LineSegm
|
|
|
316
317
|
}
|
|
317
318
|
|
|
318
319
|
|
|
320
|
+
function applyGizmoColor(material: Material | Material[], color: GizmoColor) {
|
|
321
|
+
if (Array.isArray(material)) {
|
|
322
|
+
for (const mat of material) {
|
|
323
|
+
applyGizmoColor(mat, color);
|
|
324
|
+
}
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
const alpha = (color instanceof RGBAColor) ? color.a : 1.0;
|
|
328
|
+
material["color"].set(color);
|
|
329
|
+
material["opacity"] = alpha;
|
|
330
|
+
material["transparent"] = alpha < 1.0;
|
|
331
|
+
}
|
|
332
|
+
|
|
319
333
|
|
|
320
334
|
const $cacheSymbol = Symbol("GizmoCache");
|
|
321
335
|
class Internal {
|
|
@@ -335,7 +349,7 @@ class Internal {
|
|
|
335
349
|
}
|
|
336
350
|
}
|
|
337
351
|
|
|
338
|
-
static getTextLabel(duration: number, text: string, size: number, color: ColorRepresentation, backgroundColor?: ColorRepresentation |
|
|
352
|
+
static getTextLabel(duration: number, text: string, size: number, color: ColorRepresentation, backgroundColor?: ColorRepresentation | GizmoColor): Text & LabelHandle {
|
|
339
353
|
this.ensureFont();
|
|
340
354
|
let element = this.textLabelCache.pop();
|
|
341
355
|
|
|
@@ -89,7 +89,7 @@ function internalResolve(paths: DependencyInfo[], parser: GLTFParserWithCache, o
|
|
|
89
89
|
for (let i = 0; i < val.length; i++) {
|
|
90
90
|
const entry = val[i];
|
|
91
91
|
const ext = resolveExtension(parser, entry);
|
|
92
|
-
if (ext !== null) {
|
|
92
|
+
if (ext !== null && ext !== undefined) {
|
|
93
93
|
if (typeof ext.then === "function")
|
|
94
94
|
promises.push(ext.then(res => val[i] = res));
|
|
95
95
|
else val[i] = ext;
|
|
@@ -23,10 +23,10 @@ const debugCustomGesture = getParam("debugcustomgesture");
|
|
|
23
23
|
// let _didReceiveSelectStartEvent = false;
|
|
24
24
|
|
|
25
25
|
// https://github.com/immersive-web/webxr-input-profiles/blob/4484a05e30bcd43fe86bb4e06b7a707861a26796/packages/registry/profiles/meta/meta-quest-touch-plus.json
|
|
26
|
-
declare type ControllerAxes = "xr-standard-thumbstick";
|
|
27
|
-
declare type StickName = "xr-standard-thumbstick";
|
|
26
|
+
declare type ControllerAxes = "xr-standard-thumbstick" | "xr-standard-touchpad";
|
|
27
|
+
declare type StickName = "xr-standard-thumbstick" | "xr-standard-touchpad";
|
|
28
28
|
declare type Mapping = "xr-standard";
|
|
29
|
-
declare type ComponentType = "button" | "thumbstick" | "squeeze";
|
|
29
|
+
declare type ComponentType = "button" | "thumbstick" | "squeeze" | "touchpad";
|
|
30
30
|
declare type GamepadKey = "button" | "xAxis" | "yAxis";
|
|
31
31
|
|
|
32
32
|
declare type NeedleXRControllerButtonName = ButtonName | "primary-button" | "primary";
|
|
@@ -407,6 +407,16 @@ C:${this.connected ? "x" : "-"} T:${this.isTracking ? "x" : "-"} Hand:${this.inp
|
|
|
407
407
|
gamepadStr += "\n[axes " + gp.axes.length + "]: " + gp.axes.map(a => a.toPrecision(1)).join(",");
|
|
408
408
|
debugStr += "\n" + gamepadStr;
|
|
409
409
|
}
|
|
410
|
+
if (this._layout) {
|
|
411
|
+
debugStr += "\nLayout: ";
|
|
412
|
+
for (const component of Object.keys(this._layout.components || {})) {
|
|
413
|
+
const val = this.getStick(component as StickName);
|
|
414
|
+
const indices = this._layout.components[component]?.gamepadIndices;
|
|
415
|
+
const indicesAsString = indices ? Object.entries(indices).map(e => e[0][0].toUpperCase() + e[0].slice(1) + "=" + e[1]).join(",") : "";
|
|
416
|
+
debugStr += `\n ${component}: ${this._layout.components[component]?.type} [${indicesAsString}] (${val.x.toPrecision(2)},${val.y.toPrecision(2)})`;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
410
420
|
Gizmos.DrawLabel(debugLabelPosition, debugStr, .006);
|
|
411
421
|
}
|
|
412
422
|
|
|
@@ -730,6 +740,7 @@ C:${this.connected ? "x" : "-"} T:${this.isTracking ? "x" : "-"} Hand:${this.inp
|
|
|
730
740
|
if (componentModel?.gamepadIndices) {
|
|
731
741
|
switch (componentModel.type) {
|
|
732
742
|
case "thumbstick":
|
|
743
|
+
case "touchpad":
|
|
733
744
|
if (this.inputSource.gamepad) {
|
|
734
745
|
const xIndex = componentModel.gamepadIndices!.xAxis!;
|
|
735
746
|
const yIndex = componentModel.gamepadIndices!.yAxis!;
|
|
@@ -760,7 +771,11 @@ C:${this.connected ? "x" : "-"} T:${this.isTracking ? "x" : "-"} Hand:${this.inp
|
|
|
760
771
|
this._isMetaQuestTouchController = this.profiles.includes("meta-quest-touch-plus") || this.profiles.includes("oculus-touch-v3");
|
|
761
772
|
|
|
762
773
|
// Proper profile starting with v69 and browser 35.1
|
|
763
|
-
this._isMxInk = this.profiles.includes("logitech-mx-ink")
|
|
774
|
+
this._isMxInk = this.profiles.includes("logitech-mx-ink");
|
|
775
|
+
|
|
776
|
+
// For debugging to see ALL available profiles
|
|
777
|
+
/** @ts-ignore */
|
|
778
|
+
// fetchProfilesList(DEFAULT_PROFILES_PATH).then(list => console.log("Available controller profiles", list));
|
|
764
779
|
|
|
765
780
|
if (!this._layout) {
|
|
766
781
|
// Ignore transient-pointer since we likely don't want to spawn a controller visual just for a temporary pointer.
|
|
@@ -780,6 +795,8 @@ C:${this.connected ? "x" : "-"} T:${this.isTracking ? "x" : "-"} Hand:${this.inp
|
|
|
780
795
|
res.assetPath || ""
|
|
781
796
|
);
|
|
782
797
|
|
|
798
|
+
// const overrideProfile = await fetch(DEFAULT_PROFILES_PATH + "/htc-vive-focus-3/profile.json").then(r => r.json());
|
|
799
|
+
|
|
783
800
|
const profile = res.profile as InputDeviceProfile;
|
|
784
801
|
const layout = profile.layouts[this.inputSource.handedness];
|
|
785
802
|
this._layout = layout;
|
|
@@ -791,6 +808,21 @@ C:${this.connected ? "x" : "-"} T:${this.isTracking ? "x" : "-"} Hand:${this.inp
|
|
|
791
808
|
this._layout.gamepad[component.gamepadIndices!.button!] = key as XRControllerButtonName;
|
|
792
809
|
}
|
|
793
810
|
}
|
|
811
|
+
|
|
812
|
+
// If we have 4 axes and no thumbstick defined, we define thumbstick for axis 3+4
|
|
813
|
+
// This is a workaround for HTC Vive Focus 3 controllers, which have the profile for Vive Focus Plus...
|
|
814
|
+
// This workaround fixes it for HTC Vive Focus 3 but does not change anything for Vive Focus Plus controllers
|
|
815
|
+
if (this.profiles.length >= 1 && this.profiles[0] === "htc-vive-focus-plus") {
|
|
816
|
+
if (this.inputSource.gamepad && this.inputSource.gamepad.axes.length === 4 && !this._layout.components["xr-standard-thumbstick"]) {
|
|
817
|
+
this._layout.components["xr-standard-thumbstick"] = {
|
|
818
|
+
type: "thumbstick",
|
|
819
|
+
gamepadIndices: {
|
|
820
|
+
xAxis: 2,
|
|
821
|
+
yAxis: 3,
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
}
|
|
794
826
|
}
|
|
795
827
|
// if (debug) console.log(this._layout, this.inputSource);
|
|
796
828
|
// debugger;
|
|
@@ -208,7 +208,7 @@ export { ClickThrough } from "../web/Clickthrough.js";
|
|
|
208
208
|
export { CursorFollow } from "../web/CursorFollow.js";
|
|
209
209
|
export { HoverAnimation } from "../web/HoverAnimation.js";
|
|
210
210
|
export { ScrollFollow } from "../web/ScrollFollow.js";
|
|
211
|
-
export {
|
|
211
|
+
export { ResponsiveBox } from "../web/ViewBox.js";
|
|
212
212
|
export { Avatar } from "../webxr/Avatar.js";
|
|
213
213
|
export { XRControllerFollow } from "../webxr/controllers/XRControllerFollow.js";
|
|
214
214
|
export { XRControllerModel } from "../webxr/controllers/XRControllerModel.js";
|
|
@@ -187,21 +187,22 @@ export class AnimationTrackHandler extends TrackHandler {
|
|
|
187
187
|
return;
|
|
188
188
|
}
|
|
189
189
|
// we only want to hook into the binding of the root object
|
|
190
|
-
// TODO: test with a clip with multiple roots
|
|
191
|
-
const parts = clip.tracks[0].name.split(".");
|
|
192
|
-
const rootName = parts[parts.length - 2];
|
|
193
|
-
const positionTrackName = rootName + ".position";
|
|
194
|
-
const rotationTrackName = rootName + ".quaternion";
|
|
195
190
|
let foundPositionTrack: boolean = false;
|
|
196
191
|
let foundRotationTrack: boolean = false;
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
192
|
+
const parts = clip.tracks.find(t => t.name.includes(".position") || t.name.includes(".quaternion"))?.name.split(".");
|
|
193
|
+
if (parts) {
|
|
194
|
+
const rootName = parts[parts.length - 2];
|
|
195
|
+
const positionTrackName = rootName + ".position";
|
|
196
|
+
const rotationTrackName = rootName + ".quaternion";
|
|
197
|
+
for (const t of clip.tracks) {
|
|
198
|
+
if (!foundPositionTrack && t.name.endsWith(positionTrackName)) {
|
|
199
|
+
foundPositionTrack = true;
|
|
200
|
+
this.createPositionInterpolant(clip, clipModel, t);
|
|
201
|
+
}
|
|
202
|
+
else if (!foundRotationTrack && t.name.endsWith(rotationTrackName)) {
|
|
203
|
+
foundRotationTrack = true;
|
|
204
|
+
this.createRotationInterpolant(clip, clipModel, t);
|
|
205
|
+
}
|
|
205
206
|
}
|
|
206
207
|
}
|
|
207
208
|
|
|
@@ -834,23 +835,27 @@ export class AudioTrackHandler extends TrackHandler {
|
|
|
834
835
|
|
|
835
836
|
export class MarkerTrackHandler extends TrackHandler {
|
|
836
837
|
models: Array<Models.MarkerModel & Record<string, any>> = [];
|
|
837
|
-
|
|
838
|
+
needsSorting = true;
|
|
838
839
|
|
|
839
840
|
*foreachMarker<T>(type: string | null = null) {
|
|
841
|
+
if(this.needsSorting) this.sort();
|
|
840
842
|
for (const model of this.models) {
|
|
841
843
|
if (model && model.type === type) yield model as T;
|
|
842
844
|
}
|
|
843
845
|
}
|
|
844
846
|
|
|
845
847
|
onEnable() {
|
|
846
|
-
this.
|
|
848
|
+
this.needsSorting = true;
|
|
847
849
|
}
|
|
848
850
|
|
|
849
851
|
evaluate(_time: number) {
|
|
850
|
-
if (this.
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
852
|
+
if (this.needsSorting) this.sort();
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
private sort() {
|
|
856
|
+
this.needsSorting = false;
|
|
857
|
+
this.models.sort((a, b) => a.time - b.time);
|
|
858
|
+
|
|
854
859
|
}
|
|
855
860
|
}
|
|
856
861
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Box3, Object3D } from "three";
|
|
2
2
|
import { element } from "three/src/nodes/TSL.js";
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { isDevEnvironment } from "../../engine/debug/debug.js";
|
|
5
5
|
import { Mathf } from "../../engine/engine_math.js";
|
|
6
6
|
import { serializable } from "../../engine/engine_serialization.js";
|
|
7
7
|
import { getBoundingBox, setVisibleInCustomShadowRendering } from "../../engine/engine_three_utils.js";
|
|
@@ -174,8 +174,8 @@ export class ScrollFollow extends Behaviour {
|
|
|
174
174
|
|
|
175
175
|
const value = this.invert ? 1 - this._current_value : this._current_value;
|
|
176
176
|
|
|
177
|
-
const height = this._rangeEndValue - this._rangeStartValue;
|
|
178
|
-
const pixelValue = this._rangeStartValue + value * height;
|
|
177
|
+
// const height = this._rangeEndValue - this._rangeStartValue;
|
|
178
|
+
// const pixelValue = this._rangeStartValue + value * height;
|
|
179
179
|
|
|
180
180
|
// apply scroll to target(s)
|
|
181
181
|
if (Array.isArray(this.target)) {
|
|
@@ -186,7 +186,7 @@ export class ScrollFollow extends Behaviour {
|
|
|
186
186
|
}
|
|
187
187
|
|
|
188
188
|
if (debug && this.context.time.frame % 30 === 0) {
|
|
189
|
-
console.debug(`[ScrollFollow] ${this._current_value.toFixed(5)} — ${(this._target_value * 100).toFixed(0)}
|
|
189
|
+
console.debug(`[ScrollFollow] ${this._current_value.toFixed(5)} — ${(this._target_value * 100).toFixed(0)}%, targets [${Array.isArray(this.target) ? this.target.length : 1}]`);
|
|
190
190
|
}
|
|
191
191
|
}
|
|
192
192
|
}
|
|
@@ -304,13 +304,15 @@ export class ScrollFollow extends Behaviour {
|
|
|
304
304
|
const index = markerIndex++;
|
|
305
305
|
|
|
306
306
|
// Get marker elements from DOM
|
|
307
|
-
if ((marker.element === undefined || marker.needsUpdate === true || /** element is not in DOM anymore? */ (!marker.element?.parentNode))) {
|
|
307
|
+
if ((marker.element === undefined || marker.needsUpdate === true || /** element is not in DOM anymore? */ (marker.element && !marker.element?.parentNode))) {
|
|
308
308
|
marker.needsUpdate = false;
|
|
309
309
|
try {
|
|
310
|
+
// TODO: with this it's currently not possible to remap markers from HTML. For example if I have two sections and I want to now use the marker["center"] multiple times to stay at that marker for a longer time
|
|
310
311
|
marker.element = tryGetElementsForSelector(index, marker.name) as HTMLElement | null;
|
|
311
|
-
if (debug) console.debug(
|
|
312
|
+
if (debug) console.debug(`ScrollMarker #${index} "${marker.name}" (${marker.time.toFixed(2)}) found`, marker.element);
|
|
312
313
|
if (!marker.element) {
|
|
313
314
|
marker.timeline = undefined;
|
|
315
|
+
if (debug || isDevEnvironment()) console.warn(`No HTML element found for ScrollMarker: ${marker.name} (index ${index})`);
|
|
314
316
|
continue;
|
|
315
317
|
}
|
|
316
318
|
else {
|
|
@@ -370,19 +372,20 @@ export class ScrollFollow extends Behaviour {
|
|
|
370
372
|
const time01 = calculateTimelinePositionNormalized(timeline);
|
|
371
373
|
// remap 0-1 to 0 - 1 - 0 (full weight at center)
|
|
372
374
|
const weight = 1 - Math.abs(time01 - 0.5) * 2;
|
|
375
|
+
const name = marker.name || `marker${i}`;
|
|
373
376
|
if (time01 > 0 && time01 <= 1) {
|
|
374
377
|
const lerpTime = marker.time + (nextTime - marker.time) * time01;
|
|
375
|
-
weightsArray.push({ time: lerpTime, weight: weight });
|
|
378
|
+
weightsArray.push({ name, time: lerpTime, weight: weight });
|
|
376
379
|
sum += weight;
|
|
377
380
|
}
|
|
378
381
|
// Before the first marker is reached
|
|
379
382
|
else if (i === 0 && time01 <= 0) {
|
|
380
|
-
weightsArray.push({ time: 0, weight: 1 });
|
|
383
|
+
weightsArray.push({ name, time: 0, weight: 1 });
|
|
381
384
|
sum += 1;
|
|
382
385
|
}
|
|
383
386
|
// After the last marker is reached
|
|
384
387
|
else if (i === markersArray.length - 1 && time01 >= 1) {
|
|
385
|
-
weightsArray.push({ time: duration, weight: 1 });
|
|
388
|
+
weightsArray.push({ name, time: duration, weight: 1 });
|
|
386
389
|
sum += 1;
|
|
387
390
|
}
|
|
388
391
|
}
|
|
@@ -435,13 +438,16 @@ export class ScrollFollow extends Behaviour {
|
|
|
435
438
|
time += diff * weight;
|
|
436
439
|
}
|
|
437
440
|
}
|
|
438
|
-
if (debug && this.context.time.frame % 20 === 0) console.log(time.toFixed(3), [...weightsArray])
|
|
439
441
|
if (this.damping <= 0) {
|
|
440
442
|
director.time = time;
|
|
441
443
|
}
|
|
442
444
|
else {
|
|
443
445
|
director.time = Mathf.lerp(director.time, time, this.context.time.deltaTime / this.damping);
|
|
444
446
|
}
|
|
447
|
+
|
|
448
|
+
if (debug && this.context.time.frame % 30 === 0) {
|
|
449
|
+
console.log(`[ScrollFollow ] Timeline ${director.name}: ${time.toFixed(3)}`, weightsArray.map(w => `[${w.name} ${(w.weight * 100).toFixed(0)}%]`).join(", "));
|
|
450
|
+
}
|
|
445
451
|
}
|
|
446
452
|
}
|
|
447
453
|
|
|
@@ -450,9 +456,13 @@ export class ScrollFollow extends Behaviour {
|
|
|
450
456
|
|
|
451
457
|
|
|
452
458
|
const weightsArray: OverlapInfo[] = [];
|
|
453
|
-
const markersArray:
|
|
459
|
+
const markersArray: Array<ScrollMarkerModel & {
|
|
460
|
+
element?: HTMLElement | null,
|
|
461
|
+
timeline?: ViewTimeline,
|
|
462
|
+
}> = [];
|
|
454
463
|
|
|
455
464
|
type OverlapInfo = {
|
|
465
|
+
name: string,
|
|
456
466
|
/** Marker time */
|
|
457
467
|
time: number,
|
|
458
468
|
/** Overlap in pixels */
|
|
@@ -468,17 +478,29 @@ type OverlapInfo = {
|
|
|
468
478
|
// }
|
|
469
479
|
// const querySelectorResults: Array<SelectorCache> = [];
|
|
470
480
|
|
|
471
|
-
const needleScrollMarkerCacheKey = "data-timeline-marker";
|
|
472
481
|
const needleScrollMarkerIndexCache = new Map<number, Element | null>();
|
|
473
482
|
const needleScrollMarkerNameCache = new Map<string, Element | null>();
|
|
474
483
|
let needsScrollMarkerRefresh = true;
|
|
475
484
|
|
|
476
|
-
function tryGetElementsForSelector(index: number, name: string): Element | null {
|
|
485
|
+
function tryGetElementsForSelector(index: number, name: string, _cycle: number = 0): Element | null {
|
|
477
486
|
|
|
478
487
|
if (!needsScrollMarkerRefresh) {
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
488
|
+
if (name?.length) {
|
|
489
|
+
const element = needleScrollMarkerNameCache.get(name) || null;
|
|
490
|
+
if (element) return element;
|
|
491
|
+
// const isNumber = !isNaN(Number(name));
|
|
492
|
+
// if (!isNumber) {
|
|
493
|
+
// }
|
|
494
|
+
}
|
|
495
|
+
const element = needleScrollMarkerIndexCache.get(index) || null;
|
|
496
|
+
const value = element?.getAttribute("data-timeline-marker");
|
|
497
|
+
// if (value?.length) {
|
|
498
|
+
// if (cycle === 0) {
|
|
499
|
+
// // if the HTML marker we found by index does define a different marker name we try to find the correct HTML element by name
|
|
500
|
+
// return tryGetElementsForSelector(index, value, 1);
|
|
501
|
+
// }
|
|
502
|
+
// if (isDevEnvironment()) console.warn(`ScrollMarker name mismatch: expected "${name}", got "${value}"`);
|
|
503
|
+
// }
|
|
482
504
|
return element;
|
|
483
505
|
}
|
|
484
506
|
needsScrollMarkerRefresh = false;
|
|
@@ -489,26 +511,8 @@ function tryGetElementsForSelector(index: number, name: string): Element | null
|
|
|
489
511
|
const name = m.getAttribute("data-timeline-marker");
|
|
490
512
|
if (name?.length) needleScrollMarkerNameCache.set(name, m);
|
|
491
513
|
});
|
|
492
|
-
|
|
493
|
-
return
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
/* e.g.
|
|
497
|
-
<div class="section behind start" data-needle-scroll-marker>
|
|
498
|
-
*/
|
|
499
|
-
// console.log(index, element)
|
|
500
|
-
if (element) return element;
|
|
501
|
-
|
|
502
|
-
// for (const entry of querySelectorResults) {
|
|
503
|
-
// if (entry.selector === selector) {
|
|
504
|
-
// const index = entry.usedElementCount++;
|
|
505
|
-
// return entry.elements && index < entry.elements.length ? entry.elements[index] : null;
|
|
506
|
-
// }
|
|
507
|
-
// }
|
|
508
|
-
// const elements = document.querySelectorAll(selector);
|
|
509
|
-
// querySelectorResults.push({ selector, elements: Array.from(elements), usedElementCount: 1 });
|
|
510
|
-
// if (elements.length > 0) return elements[0];
|
|
511
|
-
return null;
|
|
514
|
+
needsScrollMarkerRefresh = false;
|
|
515
|
+
return tryGetElementsForSelector(index, name);
|
|
512
516
|
}
|
|
513
517
|
|
|
514
518
|
|