@@ -26,9 +26,6 @@
|
|
26
26
|
// Added to the threejs Object3D prototype
|
27
27
|
abstract destroy();
|
28
28
|
|
29
|
-
// The actual implementation / prototype of threejs is modified in js-extensions/Object3D
|
30
|
-
abstract get transform(): Object3D;
|
31
|
-
|
32
29
|
public static isDestroyed(go: Object3D): boolean {
|
33
30
|
return isDestroyed(go);
|
34
31
|
}
|
@@ -274,6 +271,10 @@
|
|
274
271
|
|
275
272
|
// these are implemented via threejs object extensions
|
276
273
|
abstract activeSelf: boolean;
|
274
|
+
|
275
|
+
// The actual implementation / prototype of threejs is modified in js-extensions/Object3D
|
276
|
+
abstract get transform(): GameObject;
|
277
|
+
|
277
278
|
/** creates a new component on this gameObject */
|
278
279
|
abstract addNewComponent<T>(type: ConstructorConcrete<T>): T | null;
|
279
280
|
/** adds an existing component to this gameObject */
|
@@ -296,6 +297,10 @@
|
|
296
297
|
abstract get worldRotation(): Vector3;
|
297
298
|
abstract set worldScale(val: Vector3);
|
298
299
|
abstract get worldScale(): Vector3;
|
300
|
+
|
301
|
+
abstract get worldForward(): Vector3;
|
302
|
+
abstract get worldRight(): Vector3;
|
303
|
+
abstract get worldUp(): Vector3;
|
299
304
|
}
|
300
305
|
|
301
306
|
|
@@ -45,6 +45,13 @@
|
|
45
45
|
object.lookAt(lookTarget);
|
46
46
|
}
|
47
47
|
|
48
|
+
|
49
|
+
const _tempVecs = new CircularBuffer(() => new Vector3(), 100);
|
50
|
+
export function getTempVector() {
|
51
|
+
return _tempVecs.get();
|
52
|
+
}
|
53
|
+
|
54
|
+
|
48
55
|
const _worldPositions = new CircularBuffer(() => new Vector3(), 100);
|
49
56
|
const _lastMatrixWorldUpdateKey = Symbol("lastMatrixWorldUpdateKey");
|
50
57
|
|
@@ -111,7 +111,7 @@
|
|
111
111
|
* @augments Object3D
|
112
112
|
* @tutorial https://fwd.needle.tools/needle-engine/docs/transform
|
113
113
|
* */
|
114
|
-
get transform():
|
114
|
+
get transform(): IGameObject;
|
115
115
|
|
116
116
|
addNewComponent<T>(type: Constructor<T>): T | null;
|
117
117
|
removeComponent(comp: IComponent): IComponent;
|
@@ -131,6 +131,10 @@
|
|
131
131
|
set worldRotation(val: Vector3);
|
132
132
|
get worldScale(): Vector3;
|
133
133
|
set worldScale(val: Vector3);
|
134
|
+
|
135
|
+
get worldForward(): Vector3;
|
136
|
+
get worldRight(): Vector3;
|
137
|
+
get worldUp(): Vector3;
|
134
138
|
}
|
135
139
|
|
136
140
|
export interface IHasGuid {
|
@@ -11,7 +11,8 @@
|
|
11
11
|
getWorldScale,
|
12
12
|
setWorldScale,
|
13
13
|
setWorldRotation,
|
14
|
-
getWorldRotation
|
14
|
+
getWorldRotation,
|
15
|
+
getTempVector
|
15
16
|
}
|
16
17
|
from "../../engine/engine_three_utils.js";
|
17
18
|
|
@@ -142,9 +143,29 @@
|
|
142
143
|
});
|
143
144
|
}
|
144
145
|
|
146
|
+
if (!Object.getOwnPropertyDescriptor(Object3D.prototype, "worldForward")) {
|
147
|
+
Object.defineProperty(Object3D.prototype, "worldForward", {
|
148
|
+
get: function () {
|
149
|
+
return getTempVector().set(0, 0, 1).applyQuaternion(getWorldQuaternion(this));
|
150
|
+
},
|
151
|
+
});
|
152
|
+
}
|
153
|
+
if (!Object.getOwnPropertyDescriptor(Object3D.prototype, "worldRight")) {
|
154
|
+
Object.defineProperty(Object3D.prototype, "worldRight", {
|
155
|
+
get: function () {
|
156
|
+
return getTempVector().set(1, 0, 0).applyQuaternion(getWorldQuaternion(this));
|
157
|
+
},
|
158
|
+
});
|
159
|
+
}
|
160
|
+
if (!Object.getOwnPropertyDescriptor(Object3D.prototype, "worldUp")) {
|
161
|
+
Object.defineProperty(Object3D.prototype, "worldUp", {
|
162
|
+
get: function () {
|
163
|
+
return getTempVector().set(0, 1, 0).applyQuaternion(getWorldQuaternion(this));
|
164
|
+
},
|
165
|
+
});
|
166
|
+
}
|
145
167
|
|
146
168
|
|
147
169
|
|
148
|
-
|
149
170
|
// do this after adding the component extensions
|
150
171
|
registerPrototypeExtensions(Object3D);
|
@@ -21,8 +21,6 @@
|
|
21
21
|
get model(): WebXRImageTrackingModel { return this._trackedImage; }
|
22
22
|
readonly measuredSize: number;
|
23
23
|
readonly state: "tracked" | "emulated";
|
24
|
-
/** realtime since startup, is used to keep the object visible even if tracking is gone for a few frames */
|
25
|
-
lastTrackingTime: number = 0;
|
26
24
|
|
27
25
|
/** Copy the image position to a vector */
|
28
26
|
getPosition(vec: Vector3) {
|
@@ -238,7 +236,7 @@
|
|
238
236
|
}
|
239
237
|
};
|
240
238
|
|
241
|
-
private readonly imageToObjectMap = new Map<WebXRImageTrackingModel, { object: GameObject | null, frames: number }>();
|
239
|
+
private readonly imageToObjectMap = new Map<WebXRImageTrackingModel, { object: GameObject | null, frames: number, lastTrackingTime:number }>();
|
242
240
|
private readonly currentImages: WebXRTrackedImage[] = [];
|
243
241
|
|
244
242
|
|
@@ -267,8 +265,6 @@
|
|
267
265
|
if (trackedImage) {
|
268
266
|
const pose = frame.getPose(result.imageSpace, space);
|
269
267
|
const imageData = new WebXRTrackedImage(this, trackedImage, result.image, result.measuredSize, state, pose);
|
270
|
-
if (imageData.state === "tracked")
|
271
|
-
imageData.lastTrackingTime = this.context.time.realtimeSinceStartup;
|
272
268
|
this.currentImages.push(imageData);
|
273
269
|
}
|
274
270
|
else {
|
@@ -293,21 +289,23 @@
|
|
293
289
|
}
|
294
290
|
|
295
291
|
// disable any objects that are no longer tracked
|
296
|
-
|
297
|
-
|
298
|
-
|
292
|
+
/** time in millis */
|
293
|
+
const hysteresis = 1000;
|
294
|
+
for (const [key, value] of this.imageToObjectMap) {
|
295
|
+
if (!value.object || !key) continue;
|
299
296
|
let found = false;
|
300
297
|
for (const trackedImage of this.currentImages) {
|
301
|
-
if (trackedImage.model ===
|
298
|
+
if (trackedImage.model === key) {
|
302
299
|
// Make sure to keep the object visible if it's marked as static OR is tracked OR was tracked very recently (e.g. low framerate or bad tracking on device)
|
303
|
-
const timeSinceLastTracking =
|
304
|
-
if (
|
300
|
+
const timeSinceLastTracking = Date.now() - value.lastTrackingTime;
|
301
|
+
if (key.imageDoesNotMove || trackedImage.state === "tracked" || timeSinceLastTracking <= hysteresis) {
|
305
302
|
found = true;
|
306
|
-
|
303
|
+
break;
|
304
|
+
}
|
307
305
|
}
|
308
306
|
}
|
309
307
|
if (!found) {
|
310
|
-
GameObject.setActive(
|
308
|
+
GameObject.setActive(value.object, false);
|
311
309
|
}
|
312
310
|
}
|
313
311
|
}
|
@@ -318,12 +316,13 @@
|
|
318
316
|
|
319
317
|
for (const image of images) {
|
320
318
|
const model = image.model;
|
319
|
+
const isTracked = image.state === "tracked";
|
321
320
|
// don't do anything if we don't have an object to track - can be handled externally through events
|
322
321
|
if (!model.object) continue;
|
323
322
|
|
324
323
|
let trackedData = this.imageToObjectMap.get(model);
|
325
324
|
if (trackedData === undefined) {
|
326
|
-
trackedData = { object: null, frames: 0 };
|
325
|
+
trackedData = { object: null, frames: 0, lastTrackingTime: Date.now() };
|
327
326
|
this.imageToObjectMap.set(model, trackedData);
|
328
327
|
|
329
328
|
model.object.loadAssetAsync().then((asset: GameObject | null) => {
|
@@ -347,6 +346,8 @@
|
|
347
346
|
}
|
348
347
|
else {
|
349
348
|
trackedData.frames++;
|
349
|
+
if(isTracked)
|
350
|
+
trackedData.lastTrackingTime = Date.now();
|
350
351
|
|
351
352
|
// TODO we could do a bit more here: e.g. sample for the first 1s or so of getting pose data
|
352
353
|
// to improve the tracking quality a bit.
|