@needle-tools/engine 2.32.0-pre → 2.34.0-pre
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/CHANGELOG.md +15 -0
- package/dist/needle-engine.d.ts +195 -164
- package/dist/needle-engine.js +394 -344
- package/dist/needle-engine.js.map +4 -4
- package/dist/needle-engine.min.js +70 -20
- package/dist/needle-engine.min.js.map +4 -4
- package/lib/engine/engine_create_objects.d.ts +11 -0
- package/lib/engine/engine_create_objects.js +20 -0
- package/lib/engine/engine_create_objects.js.map +1 -0
- package/lib/engine/engine_instancing.d.ts +5 -0
- package/lib/engine/engine_instancing.js +22 -0
- package/lib/engine/engine_instancing.js.map +1 -0
- package/lib/engine/engine_physics.d.ts +2 -0
- package/lib/engine/engine_physics.js +7 -1
- package/lib/engine/engine_physics.js.map +1 -1
- package/lib/engine/extensions/NEEDLE_components.js +2 -0
- package/lib/engine/extensions/NEEDLE_components.js.map +1 -1
- package/lib/engine/extensions/NEEDLE_techniques_webgl.d.ts +1 -0
- package/lib/engine/extensions/NEEDLE_techniques_webgl.js +26 -0
- package/lib/engine/extensions/NEEDLE_techniques_webgl.js.map +1 -1
- package/lib/engine-components/Animation.js +1 -1
- package/lib/engine-components/Animation.js.map +1 -1
- package/lib/engine-components/Collider.d.ts +1 -0
- package/lib/engine-components/Collider.js +6 -2
- package/lib/engine-components/Collider.js.map +1 -1
- package/lib/engine-components/Component.js +4 -51
- package/lib/engine-components/Component.js.map +1 -1
- package/lib/engine-components/DragControls.js +1 -1
- package/lib/engine-components/DragControls.js.map +1 -1
- package/lib/engine-components/Renderer.d.ts +0 -3
- package/lib/engine-components/Renderer.js +1 -20
- package/lib/engine-components/Renderer.js.map +1 -1
- package/lib/engine-components/ScreenCapture.d.ts +8 -14
- package/lib/engine-components/ScreenCapture.js +62 -74
- package/lib/engine-components/ScreenCapture.js.map +1 -1
- package/lib/engine-components/SyncedCamera.js +1 -1
- package/lib/engine-components/SyncedCamera.js.map +1 -1
- package/lib/engine-components/SyncedTransform.js +1 -1
- package/lib/engine-components/SyncedTransform.js.map +1 -1
- package/lib/engine-components/VideoPlayer.d.ts +14 -3
- package/lib/engine-components/VideoPlayer.js +390 -47
- package/lib/engine-components/VideoPlayer.js.map +1 -1
- package/lib/engine-components/WebARSessionRoot.js +1 -1
- package/lib/engine-components/WebARSessionRoot.js.map +1 -1
- package/lib/engine-components/WebXRAvatar.js +1 -1
- package/lib/engine-components/WebXRAvatar.js.map +1 -1
- package/lib/engine-components/WebXRController.d.ts +1 -0
- package/lib/engine-components/WebXRController.js +12 -1
- package/lib/engine-components/WebXRController.js.map +1 -1
- package/lib/engine-components/codegen/{exports.d.ts → components.d.ts} +2 -2
- package/{src/engine-components/codegen/exports.ts → lib/engine-components/codegen/components.js} +3 -2
- package/lib/engine-components/codegen/components.js.map +1 -0
- package/lib/engine-components/js-extensions/Object3D.d.ts +2 -0
- package/lib/engine-components/js-extensions/Object3D.js +75 -0
- package/lib/engine-components/js-extensions/Object3D.js.map +1 -0
- package/lib/engine-components/ui/EventSystem.d.ts +3 -0
- package/lib/engine-components/ui/EventSystem.js +17 -8
- package/lib/engine-components/ui/EventSystem.js.map +1 -1
- package/lib/needle-engine.d.ts +2 -1
- package/lib/needle-engine.js +3 -2
- package/lib/needle-engine.js.map +1 -1
- package/package.json +2 -2
- package/src/engine/codegen/register_types.js +2 -4
- package/src/engine/engine_create_objects.ts +27 -0
- package/src/engine/engine_instancing.ts +25 -0
- package/src/engine/engine_physics.ts +9 -1
- package/src/engine/extensions/NEEDLE_components.ts +3 -0
- package/src/engine/extensions/NEEDLE_techniques_webgl.ts +34 -2
- package/src/engine-components/Animation.ts +1 -1
- package/src/engine-components/Collider.ts +7 -2
- package/src/engine-components/Component.ts +4 -64
- package/src/engine-components/DragControls.ts +1 -1
- package/src/engine-components/Renderer.ts +2 -20
- package/src/engine-components/ScreenCapture.ts +62 -75
- package/src/engine-components/SyncedCamera.ts +1 -1
- package/src/engine-components/SyncedTransform.ts +1 -1
- package/src/engine-components/VideoPlayer.ts +401 -48
- package/src/engine-components/WebARSessionRoot.ts +1 -1
- package/src/engine-components/WebXRAvatar.ts +1 -1
- package/src/engine-components/WebXRController.ts +11 -1
- package/{lib/engine-components/codegen/exports.js → src/engine-components/codegen/components.ts} +1 -3
- package/src/engine-components/js-extensions/Object3D.ts +91 -0
- package/src/engine-components/ui/EventSystem.ts +19 -10
- package/src/needle-engine.ts +3 -3
- package/lib/engine-components/ComponentExtensions.d.ts +0 -2
- package/lib/engine-components/ComponentExtensions.js +0 -3
- package/lib/engine-components/ComponentExtensions.js.map +0 -1
- package/lib/engine-components/codegen/exports.js.map +0 -1
- package/src/engine-components/ComponentExtensions.ts +0 -7
|
@@ -7,7 +7,7 @@ import { Builder } from "flatbuffers";
|
|
|
7
7
|
import { SyncedCameraModel } from "../engine-schemes/synced-camera-model";
|
|
8
8
|
import { Vec3 } from "../engine-schemes/vec3";
|
|
9
9
|
import { registerType } from "../engine-schemes/schemes";
|
|
10
|
-
import { InstancingUtil } from "
|
|
10
|
+
import { InstancingUtil } from "../engine/engine_instancing";
|
|
11
11
|
import { serializeable } from "../engine/engine_serialization_decorator";
|
|
12
12
|
import { Object3D } from "three";
|
|
13
13
|
import { AvatarMarker } from "./WebXRAvatar";
|
|
@@ -4,7 +4,7 @@ import { Behaviour, GameObject } from "./Component";
|
|
|
4
4
|
import { Rigidbody } from "./RigidBody";
|
|
5
5
|
import * as utils from "../engine/engine_utils"
|
|
6
6
|
import { sendDestroyed } from '../engine/engine_networking_instantiate';
|
|
7
|
-
import { InstancingUtil } from
|
|
7
|
+
import { InstancingUtil } from "../engine/engine_instancing";
|
|
8
8
|
import { SyncedTransformModel } from '../engine-schemes/synced-transform-model';
|
|
9
9
|
import * as flatbuffers from "flatbuffers";
|
|
10
10
|
import { Transform } from '../engine-schemes/transform';
|
|
@@ -1,14 +1,24 @@
|
|
|
1
|
-
import { Behaviour } from "./Component";
|
|
1
|
+
import { Behaviour, GameObject } from "./Component";
|
|
2
2
|
import * as THREE from "three";
|
|
3
3
|
import { Material } from "material/Material";
|
|
4
4
|
import { serializeable } from "../engine/engine_serialization_decorator";
|
|
5
|
-
import { LinearFilter, Object3D, Texture } from "three";
|
|
5
|
+
import { LinearFilter, Mesh, Object3D, RawShaderMaterial, ShaderMaterial, Texture, TextureLoader, Vector2, Vector4, VideoTexture } from "three";
|
|
6
6
|
import { awaitInput } from "../engine/engine_input_utils";
|
|
7
7
|
import { getParam } from "../engine/engine_utils";
|
|
8
8
|
import { Renderer } from "./Renderer";
|
|
9
|
+
import { getWorldScale } from "../engine/engine_three_utils";
|
|
10
|
+
import { ObjectUtils, PrimitiveType } from "../engine/engine_create_objects";
|
|
11
|
+
import { Context } from "../engine/engine_setup";
|
|
9
12
|
|
|
10
13
|
const debug = getParam("debugvideo");
|
|
11
14
|
|
|
15
|
+
|
|
16
|
+
export enum AspectMode {
|
|
17
|
+
None = 0,
|
|
18
|
+
AdjustHeight = 1,
|
|
19
|
+
AdjustWidth = 2,
|
|
20
|
+
}
|
|
21
|
+
|
|
12
22
|
export enum VideoSource {
|
|
13
23
|
/// <summary>
|
|
14
24
|
/// <para>Use the current clip as the video content source.</para>
|
|
@@ -43,6 +53,9 @@ export class VideoPlayer extends Behaviour {
|
|
|
43
53
|
@serializeable()
|
|
44
54
|
playOnEnable?: boolean;
|
|
45
55
|
|
|
56
|
+
@serializeable()
|
|
57
|
+
aspectMode: AspectMode = AspectMode.None;
|
|
58
|
+
|
|
46
59
|
@serializeable()
|
|
47
60
|
private renderMode?: VideoRenderMode;
|
|
48
61
|
|
|
@@ -110,6 +123,10 @@ export class VideoPlayer extends Behaviour {
|
|
|
110
123
|
}
|
|
111
124
|
}
|
|
112
125
|
|
|
126
|
+
get videoTexture() {
|
|
127
|
+
return this._videoTexture;
|
|
128
|
+
}
|
|
129
|
+
|
|
113
130
|
private _crossOrigin: string | null = "anonymous";
|
|
114
131
|
|
|
115
132
|
private audioOutputMode: VideoAudioOutputMode = VideoAudioOutputMode.AudioSource;
|
|
@@ -119,7 +136,7 @@ export class VideoPlayer extends Behaviour {
|
|
|
119
136
|
private url?: string | null = null;
|
|
120
137
|
|
|
121
138
|
private videoElement: HTMLVideoElement | null = null;
|
|
122
|
-
private
|
|
139
|
+
private _videoTexture: THREE.VideoTexture | null = null;
|
|
123
140
|
private videoMaterial: Material | null = null;
|
|
124
141
|
|
|
125
142
|
private _isPlaying: boolean = false;
|
|
@@ -134,6 +151,7 @@ export class VideoPlayer extends Behaviour {
|
|
|
134
151
|
this.videoElement.srcObject = video;
|
|
135
152
|
if (this._isPlaying)
|
|
136
153
|
this.videoElement.play();
|
|
154
|
+
this.updateAspect();
|
|
137
155
|
}
|
|
138
156
|
}
|
|
139
157
|
|
|
@@ -146,8 +164,10 @@ export class VideoPlayer extends Behaviour {
|
|
|
146
164
|
if (!this.videoElement) this.create(true);
|
|
147
165
|
else {
|
|
148
166
|
this.videoElement.src = url;
|
|
149
|
-
if (this._isPlaying)
|
|
150
|
-
this.
|
|
167
|
+
if (this._isPlaying) {
|
|
168
|
+
this.stop();
|
|
169
|
+
this.play();
|
|
170
|
+
}
|
|
151
171
|
}
|
|
152
172
|
}
|
|
153
173
|
|
|
@@ -171,6 +191,10 @@ export class VideoPlayer extends Behaviour {
|
|
|
171
191
|
if (this.playOnEnable === true) {
|
|
172
192
|
this.handleBeginPlaying(true);
|
|
173
193
|
}
|
|
194
|
+
if (this.screenspace) {
|
|
195
|
+
this._overlay?.start();
|
|
196
|
+
}
|
|
197
|
+
else this._overlay?.stop();
|
|
174
198
|
}
|
|
175
199
|
|
|
176
200
|
onDisable(): void {
|
|
@@ -182,9 +206,9 @@ export class VideoPlayer extends Behaviour {
|
|
|
182
206
|
this.videoElement.parentElement?.removeChild(this.videoElement);
|
|
183
207
|
this.videoElement = null;
|
|
184
208
|
}
|
|
185
|
-
if (this.
|
|
186
|
-
this.
|
|
187
|
-
this.
|
|
209
|
+
if (this._videoTexture) {
|
|
210
|
+
this._videoTexture.dispose();
|
|
211
|
+
this._videoTexture = null;
|
|
188
212
|
}
|
|
189
213
|
}
|
|
190
214
|
|
|
@@ -197,6 +221,14 @@ export class VideoPlayer extends Behaviour {
|
|
|
197
221
|
this.updateVideoElementSettings();
|
|
198
222
|
});
|
|
199
223
|
this._targetObjects = [];
|
|
224
|
+
|
|
225
|
+
if (getParam("videoscreenspace")) {
|
|
226
|
+
window.addEventListener("keydown", evt => {
|
|
227
|
+
if (evt.key === "f") {
|
|
228
|
+
this.screenspace = !this.screenspace;
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
}
|
|
200
232
|
}
|
|
201
233
|
|
|
202
234
|
play() {
|
|
@@ -245,24 +277,46 @@ export class VideoPlayer extends Behaviour {
|
|
|
245
277
|
// hide it because otherwise it would overlay the website with default css
|
|
246
278
|
this.updateVideoElementStyles();
|
|
247
279
|
}
|
|
248
|
-
if (typeof src === "string")
|
|
280
|
+
if (typeof src === "string") {
|
|
249
281
|
this.videoElement.src = src;
|
|
282
|
+
const str = this.videoElement["captureStream"]?.call(this.videoElement);
|
|
283
|
+
this.clip = str;
|
|
284
|
+
}
|
|
250
285
|
else
|
|
251
286
|
this.videoElement.srcObject = src;
|
|
252
287
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
this.
|
|
288
|
+
|
|
289
|
+
if (!this._videoTexture)
|
|
290
|
+
this._videoTexture = new THREE.VideoTexture(this.videoElement);
|
|
291
|
+
this._videoTexture.flipY = false;
|
|
292
|
+
this._videoTexture.encoding = THREE.sRGBEncoding;
|
|
257
293
|
this.handleBeginPlaying(playAutomatically);
|
|
294
|
+
console.log(this);
|
|
258
295
|
}
|
|
259
296
|
|
|
260
|
-
|
|
297
|
+
updateAspect() {
|
|
298
|
+
if (this.aspectMode === AspectMode.None) return;
|
|
299
|
+
this.startCoroutine(this.updateAspectImpl());
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
private _overlay: VideoOverlay | null = null;
|
|
303
|
+
|
|
304
|
+
get screenspace(): boolean {
|
|
305
|
+
return this._overlay?.enabled ?? false;
|
|
306
|
+
}
|
|
261
307
|
|
|
262
|
-
|
|
263
|
-
|
|
308
|
+
set screenspace(val: boolean) {
|
|
309
|
+
if (val) {
|
|
310
|
+
if (!this._videoTexture) return;
|
|
311
|
+
if (!this._overlay) this._overlay = new VideoOverlay(this.context);
|
|
312
|
+
this._overlay.add(this._videoTexture);
|
|
313
|
+
}
|
|
314
|
+
else this._overlay?.remove(this._videoTexture);
|
|
315
|
+
if (this._overlay) this._overlay.enabled = val;
|
|
264
316
|
}
|
|
265
317
|
|
|
318
|
+
private _targetObjects: Array<Object3D>;
|
|
319
|
+
|
|
266
320
|
private createVideoElement(): HTMLVideoElement {
|
|
267
321
|
const video = document.createElement("video") as HTMLVideoElement;
|
|
268
322
|
if (this._crossOrigin)
|
|
@@ -282,6 +336,7 @@ export class VideoPlayer extends Behaviour {
|
|
|
282
336
|
switch (this.renderMode) {
|
|
283
337
|
case VideoRenderMode.MaterialOverride:
|
|
284
338
|
target = this.targetMaterialRenderer?.gameObject;
|
|
339
|
+
if (!target) target = GameObject.getComponent(this.gameObject, Renderer)?.gameObject;
|
|
285
340
|
break;
|
|
286
341
|
case VideoRenderMode.RenderTexture:
|
|
287
342
|
console.error("VideoPlayer renderTexture not implemented yet. Please use material override instead");
|
|
@@ -289,25 +344,25 @@ export class VideoPlayer extends Behaviour {
|
|
|
289
344
|
}
|
|
290
345
|
|
|
291
346
|
if (!target) {
|
|
292
|
-
console.error("Missing target for video material renderer", this);
|
|
347
|
+
console.error("Missing target for video material renderer", this.name, VideoRenderMode[this.renderMode!], this);
|
|
293
348
|
return;
|
|
294
349
|
}
|
|
295
350
|
const mat = target["material"];
|
|
296
351
|
if (mat) {
|
|
297
352
|
this._targetObjects.push(target);
|
|
298
|
-
|
|
353
|
+
|
|
299
354
|
if (mat !== this.videoMaterial) {
|
|
300
355
|
this.videoMaterial = mat.clone();
|
|
301
356
|
target["material"] = this.videoMaterial;
|
|
302
357
|
}
|
|
303
358
|
|
|
304
359
|
if (!this.targetMaterialProperty) {
|
|
305
|
-
(this.videoMaterial as any).map = this.
|
|
360
|
+
(this.videoMaterial as any).map = this._videoTexture;
|
|
306
361
|
}
|
|
307
362
|
else {
|
|
308
363
|
switch (this.targetMaterialProperty) {
|
|
309
364
|
default:
|
|
310
|
-
(this.videoMaterial as any).map = this.
|
|
365
|
+
(this.videoMaterial as any).map = this._videoTexture;
|
|
311
366
|
break;
|
|
312
367
|
// doesnt render:
|
|
313
368
|
// case "emissiveTexture":
|
|
@@ -348,61 +403,359 @@ export class VideoPlayer extends Behaviour {
|
|
|
348
403
|
this.videoElement.style.userSelect = "none";
|
|
349
404
|
this.videoElement.style.visibility = "hidden";
|
|
350
405
|
this.videoElement.style.display = "none";
|
|
406
|
+
this.updateAspect();
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
private _updateAspectRoutineId: number = -1;
|
|
413
|
+
private *updateAspectImpl() {
|
|
414
|
+
const id = ++this._updateAspectRoutineId;
|
|
415
|
+
const lastAspect: number | undefined = undefined;
|
|
416
|
+
const stream = this.clip;
|
|
417
|
+
while (id === this._updateAspectRoutineId && this.aspectMode !== AspectMode.None && this.clip && stream === this.clip && this._isPlaying) {
|
|
418
|
+
if (!stream || typeof stream === "string") {
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
let aspect: number | undefined = undefined;
|
|
422
|
+
for (const track of stream.getVideoTracks()) {
|
|
423
|
+
const settings = track.getSettings();
|
|
424
|
+
if (settings && settings.width && settings.height) {
|
|
425
|
+
aspect = settings.width / settings.height;
|
|
426
|
+
break;
|
|
427
|
+
}
|
|
428
|
+
// on firefox capture canvas stream works but looks like
|
|
429
|
+
// the canvas stream track doesnt contain settings?!!?
|
|
430
|
+
else {
|
|
431
|
+
aspect = this.context.renderer.domElement.clientWidth / this.context.renderer.domElement.clientHeight;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
if (aspect === undefined) {
|
|
435
|
+
for (let i = 0; i < 10; i++)
|
|
436
|
+
yield;
|
|
437
|
+
if (!this.isPlaying) break;
|
|
438
|
+
continue;
|
|
439
|
+
}
|
|
440
|
+
if (lastAspect === aspect) {
|
|
441
|
+
yield;
|
|
442
|
+
continue;
|
|
443
|
+
}
|
|
444
|
+
for (const obj of this._targetObjects) {
|
|
445
|
+
let worldAspect = 1;
|
|
446
|
+
if (obj.parent) {
|
|
447
|
+
const parentScale = getWorldScale(obj.parent);
|
|
448
|
+
worldAspect = parentScale.x / parentScale.y;
|
|
449
|
+
}
|
|
450
|
+
switch (this.aspectMode) {
|
|
451
|
+
case AspectMode.AdjustHeight:
|
|
452
|
+
obj.scale.y = 1 / aspect * obj.scale.x * worldAspect;
|
|
453
|
+
break;
|
|
454
|
+
case AspectMode.AdjustWidth:
|
|
455
|
+
obj.scale.x = aspect * obj.scale.y * worldAspect;
|
|
456
|
+
break;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
for (let i = 0; i < 3; i++)
|
|
460
|
+
yield;
|
|
461
|
+
}
|
|
351
462
|
}
|
|
352
463
|
}
|
|
353
464
|
|
|
354
465
|
|
|
466
|
+
class VideoOverlay {
|
|
355
467
|
|
|
468
|
+
readonly context: Context;
|
|
356
469
|
|
|
357
|
-
|
|
470
|
+
constructor(context: Context) {
|
|
471
|
+
this.context = context;
|
|
472
|
+
this._input = new VideoOverlayInput(this);
|
|
473
|
+
}
|
|
358
474
|
|
|
359
|
-
|
|
475
|
+
get enabled() {
|
|
476
|
+
return this._isInScreenspaceMode;
|
|
477
|
+
}
|
|
360
478
|
|
|
361
|
-
|
|
479
|
+
set enabled(val: boolean) {
|
|
480
|
+
if (val) this.start();
|
|
481
|
+
else this.stop();
|
|
482
|
+
}
|
|
362
483
|
|
|
363
|
-
// super( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy );
|
|
364
484
|
|
|
365
|
-
|
|
366
|
-
|
|
485
|
+
add(video: VideoTexture) {
|
|
486
|
+
if (this._videos.indexOf(video) === -1) {
|
|
487
|
+
this._videos.push(video);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
367
490
|
|
|
368
|
-
|
|
491
|
+
remove(video: VideoTexture | null | undefined) {
|
|
492
|
+
if (!video) return;
|
|
493
|
+
const index = this._videos.indexOf(video);
|
|
494
|
+
if (index >= 0) {
|
|
495
|
+
this._videos.splice(index, 1);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
369
498
|
|
|
370
|
-
|
|
499
|
+
start() {
|
|
500
|
+
if (this._isInScreenspaceMode) return;
|
|
501
|
+
if (this._videos.length < 0) return;
|
|
502
|
+
const texture = this._videos[this._videos.length - 1];
|
|
503
|
+
if (!texture) return;
|
|
504
|
+
|
|
505
|
+
this._isInScreenspaceMode = true;
|
|
506
|
+
if (!this._screenspaceModeQuad) {
|
|
507
|
+
this._screenspaceModeQuad = ObjectUtils.createPrimitive(PrimitiveType.Quad, {
|
|
508
|
+
material: new ScreenspaceTexture(texture)
|
|
509
|
+
});
|
|
510
|
+
if (!this._screenspaceModeQuad) return;
|
|
511
|
+
this._screenspaceModeQuad.geometry.scale(2, 2, 2);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
const quad = this._screenspaceModeQuad;
|
|
515
|
+
this.context.scene.add(quad);
|
|
516
|
+
this.updateScreenspaceMaterialUniforms();
|
|
371
517
|
|
|
372
|
-
|
|
518
|
+
const mat = quad.material as ScreenspaceTexture;
|
|
519
|
+
mat?.reset();
|
|
373
520
|
|
|
374
|
-
|
|
375
|
-
|
|
521
|
+
this._input?.enable(mat);
|
|
522
|
+
}
|
|
376
523
|
|
|
377
|
-
|
|
524
|
+
stop() {
|
|
525
|
+
this._isInScreenspaceMode = false;
|
|
526
|
+
if (this._screenspaceModeQuad) {
|
|
527
|
+
this._input?.disable();
|
|
528
|
+
this._screenspaceModeQuad.removeFromParent();
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
updateScreenspaceMaterialUniforms() {
|
|
533
|
+
const mat = this._screenspaceModeQuad?.material as ScreenspaceTexture;
|
|
534
|
+
if (!mat) return;
|
|
535
|
+
// mat.videoAspect = this.videoTexture?.image?.width / this.videoTexture?.image?.height;
|
|
536
|
+
mat.screenAspect = this.context.domElement.clientWidth / this.context.domElement.clientHeight;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
private _videos: VideoTexture[] = [];
|
|
540
|
+
private _screenspaceModeQuad: Mesh | undefined;
|
|
541
|
+
private _isInScreenspaceMode: boolean = false;
|
|
542
|
+
private _input: VideoOverlayInput;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
class VideoOverlayInput {
|
|
546
|
+
|
|
547
|
+
private _onResizeScreenFn?: () => void;
|
|
548
|
+
private _onKeyUpFn?: (e: KeyboardEvent) => void;
|
|
549
|
+
private _onMouseWheelFn?: (e: WheelEvent) => void;
|
|
550
|
+
|
|
551
|
+
private readonly context: Context;
|
|
552
|
+
private readonly overlay: VideoOverlay;
|
|
553
|
+
|
|
554
|
+
constructor(overlay: VideoOverlay) {
|
|
555
|
+
this.overlay = overlay;
|
|
556
|
+
this.context = overlay.context;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
private _material?: ScreenspaceTexture;
|
|
560
|
+
|
|
561
|
+
enable(mat: ScreenspaceTexture) {
|
|
562
|
+
this._material = mat;
|
|
563
|
+
|
|
564
|
+
window.addEventListener("resize", this._onResizeScreenFn = () => {
|
|
565
|
+
this.overlay.updateScreenspaceMaterialUniforms();
|
|
566
|
+
});
|
|
567
|
+
window.addEventListener("keyup", this._onKeyUpFn = (args) => {
|
|
568
|
+
if (args.key === "Escape")
|
|
569
|
+
this.overlay.stop();
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
window.addEventListener("wheel", this._onMouseWheelFn = (args) => {
|
|
573
|
+
if (this.overlay.enabled) {
|
|
574
|
+
mat.zoom += args.deltaY * .0005;
|
|
575
|
+
args.preventDefault();
|
|
576
|
+
}
|
|
577
|
+
}, { passive: false });
|
|
578
|
+
|
|
579
|
+
|
|
580
|
+
const delta: Vector2 = new Vector2();
|
|
581
|
+
|
|
582
|
+
window.addEventListener("mousemove", (args: MouseEvent) => {
|
|
583
|
+
if (this.overlay.enabled && this.context.input.getPointerPressed(0)) {
|
|
584
|
+
const normalizedMovement = new Vector2(args.movementX, args.movementY);
|
|
585
|
+
normalizedMovement.x /= this.context.domElement.clientWidth;
|
|
586
|
+
normalizedMovement.y /= this.context.domElement.clientHeight;
|
|
587
|
+
delta.set(normalizedMovement.x, normalizedMovement.y);
|
|
588
|
+
delta.multiplyScalar(mat.zoom / -this.context.time.deltaTime * .01);
|
|
589
|
+
mat.offset = mat.offset.add(delta);
|
|
590
|
+
}
|
|
591
|
+
});
|
|
592
|
+
|
|
593
|
+
window.addEventListener("pointermove", (args: PointerEvent) => {
|
|
594
|
+
if (this.overlay.enabled && this.context.input.getPointerPressed(0)) {
|
|
595
|
+
const count = this.context.input.getTouchesPressedCount();
|
|
596
|
+
if (count === 1) {
|
|
597
|
+
delta.set(args.movementX, args.movementY);
|
|
598
|
+
delta.multiplyScalar(mat.zoom * -this.context.time.deltaTime * .05);
|
|
599
|
+
mat.offset = mat.offset.add(delta);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
});
|
|
378
603
|
|
|
379
|
-
|
|
604
|
+
let lastTouchStartTime = 0;
|
|
605
|
+
window.addEventListener("touchstart", e => {
|
|
606
|
+
if (e.touches.length < 2) {
|
|
607
|
+
if (this.context.time.time - lastTouchStartTime < .3) {
|
|
608
|
+
this.overlay.stop();
|
|
609
|
+
}
|
|
610
|
+
lastTouchStartTime = this.context.time.time;
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
this._isPinching = true;
|
|
614
|
+
this._lastPinch = 0;
|
|
615
|
+
})
|
|
616
|
+
window.addEventListener("touchmove", e => {
|
|
617
|
+
if (!this._isPinching || !this._material) return;
|
|
618
|
+
const touch1 = e.touches[0];
|
|
619
|
+
const touch2 = e.touches[1];
|
|
620
|
+
const dx = touch1.clientX - touch2.clientX;
|
|
621
|
+
const dy = touch1.clientY - touch2.clientY;
|
|
622
|
+
const distance = Math.sqrt(dx * dx + dy * dy);
|
|
623
|
+
if (this._lastPinch !== 0) {
|
|
624
|
+
const delta = distance - this._lastPinch;
|
|
625
|
+
this._material.zoom -= delta * .004;
|
|
626
|
+
}
|
|
627
|
+
this._lastPinch = distance;
|
|
628
|
+
})
|
|
629
|
+
window.addEventListener("touchend", () => {
|
|
630
|
+
this._isPinching = false;
|
|
631
|
+
})
|
|
632
|
+
}
|
|
380
633
|
|
|
381
|
-
|
|
634
|
+
private _isPinching: boolean = false;
|
|
635
|
+
private _lastPinch = 0;
|
|
382
636
|
|
|
383
|
-
|
|
637
|
+
disable() {
|
|
384
638
|
|
|
385
|
-
|
|
639
|
+
if (this._onResizeScreenFn) {
|
|
640
|
+
window.removeEventListener("resize", this._onResizeScreenFn);
|
|
641
|
+
this._onResizeScreenFn = undefined;
|
|
642
|
+
}
|
|
643
|
+
if (this._onKeyUpFn) {
|
|
644
|
+
window.removeEventListener("keyup", this._onKeyUpFn);
|
|
645
|
+
this._onKeyUpFn = undefined;
|
|
646
|
+
}
|
|
647
|
+
if (this._onMouseWheelFn) {
|
|
648
|
+
window.removeEventListener("wheel", this._onMouseWheelFn);
|
|
649
|
+
this._onMouseWheelFn = undefined;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
386
652
|
|
|
387
|
-
|
|
653
|
+
}
|
|
388
654
|
|
|
389
|
-
|
|
655
|
+
class ScreenspaceTexture extends ShaderMaterial {
|
|
390
656
|
|
|
391
|
-
|
|
657
|
+
set screenAspect(val: number) {
|
|
658
|
+
this.uniforms["screenAspect"].value = val;
|
|
659
|
+
this.needsUpdate = true;
|
|
660
|
+
}
|
|
392
661
|
|
|
393
|
-
|
|
662
|
+
set offset(vec: Vector2 | { x: number, y: number }) {
|
|
663
|
+
const val = this.uniforms["offsetScale"].value;
|
|
664
|
+
val.x = vec.x;
|
|
665
|
+
val.y = vec.y;
|
|
666
|
+
// console.log(val);
|
|
667
|
+
this.uniforms["offsetScale"].value = val;
|
|
668
|
+
this.needsUpdate = true;
|
|
669
|
+
}
|
|
394
670
|
|
|
395
|
-
|
|
396
|
-
|
|
671
|
+
private readonly _offset: Vector2 = new Vector2();
|
|
672
|
+
get offset(): Vector2 {
|
|
673
|
+
const val = this.uniforms["offsetScale"].value;
|
|
674
|
+
this._offset.set(val.x, val.y);
|
|
675
|
+
return this._offset;
|
|
676
|
+
}
|
|
397
677
|
|
|
398
|
-
|
|
678
|
+
set zoom(val: number) {
|
|
679
|
+
const zoom = this.uniforms["offsetScale"].value;
|
|
680
|
+
if (val < .001) val = .001;
|
|
681
|
+
zoom.z = val;
|
|
682
|
+
// zoom.z = this.maxZoom - val;
|
|
683
|
+
// zoom.z /= this.maxZoom;
|
|
684
|
+
this.needsUpdate = true;
|
|
685
|
+
}
|
|
399
686
|
|
|
400
|
-
|
|
687
|
+
get zoom(): number {
|
|
688
|
+
return this.uniforms["offsetScale"].value.z;// * this.maxZoom;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
reset() {
|
|
692
|
+
this.offset = this.offset.set(0, 0);
|
|
693
|
+
this.zoom = 1;
|
|
694
|
+
this.needsUpdate = true;
|
|
695
|
+
}
|
|
401
696
|
|
|
402
|
-
//
|
|
697
|
+
// maxZoom : number = 10
|
|
403
698
|
|
|
404
|
-
|
|
699
|
+
constructor(tex: Texture) {
|
|
700
|
+
super();
|
|
701
|
+
|
|
702
|
+
this.uniforms = {
|
|
703
|
+
map: { value: tex },
|
|
704
|
+
screenAspect: { value: 1 },
|
|
705
|
+
offsetScale: { value: new Vector4(0, 0, 1, 1) }
|
|
706
|
+
};
|
|
707
|
+
|
|
708
|
+
this.vertexShader = `
|
|
709
|
+
uniform sampler2D map;
|
|
710
|
+
uniform float screenAspect;
|
|
711
|
+
uniform vec4 offsetScale;
|
|
712
|
+
varying vec2 vUv;
|
|
713
|
+
|
|
714
|
+
void main() {
|
|
715
|
+
|
|
716
|
+
gl_Position = vec4( position , 1.0 );
|
|
717
|
+
vUv = uv;
|
|
718
|
+
vUv.y = 1. - vUv.y;
|
|
719
|
+
|
|
720
|
+
// fit into screen
|
|
721
|
+
ivec2 res = textureSize(map, 0);
|
|
722
|
+
float videoAspect = float(res.x) / float(res.y);
|
|
723
|
+
float aspect = videoAspect / screenAspect;
|
|
724
|
+
if(aspect >= 1.0)
|
|
725
|
+
{
|
|
726
|
+
vUv.y = vUv.y * aspect;
|
|
727
|
+
float offset = (1. - aspect) * .5;
|
|
728
|
+
vUv.y = vUv.y + offset;
|
|
729
|
+
}
|
|
730
|
+
else
|
|
731
|
+
{
|
|
732
|
+
vUv.x = vUv.x / aspect;
|
|
733
|
+
float offset = (1. - 1. / aspect) * .5;
|
|
734
|
+
vUv.x = vUv.x + offset;
|
|
735
|
+
}
|
|
405
736
|
|
|
406
|
-
|
|
737
|
+
vUv.x -= .5;
|
|
738
|
+
vUv.y -= .5;
|
|
407
739
|
|
|
408
|
-
|
|
740
|
+
vUv.x *= offsetScale.z;
|
|
741
|
+
vUv.y *= offsetScale.z;
|
|
742
|
+
vUv.x += offsetScale.x;
|
|
743
|
+
vUv.y += offsetScale.y;
|
|
744
|
+
|
|
745
|
+
vUv.x += .5;
|
|
746
|
+
vUv.y += .5;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
`
|
|
750
|
+
this.fragmentShader = `
|
|
751
|
+
uniform sampler2D map;
|
|
752
|
+
varying vec2 vUv;
|
|
753
|
+
void main() {
|
|
754
|
+
if(vUv.x < 0. || vUv.x > 1. || vUv.y < 0. || vUv.y > 1.)
|
|
755
|
+
gl_FragColor = vec4(0., 0., 0., 1.);
|
|
756
|
+
else
|
|
757
|
+
gl_FragColor = texture2D(map, vUv);
|
|
758
|
+
}
|
|
759
|
+
`
|
|
760
|
+
}
|
|
761
|
+
}
|
|
@@ -2,7 +2,7 @@ import { Behaviour, GameObject } from "./Component";
|
|
|
2
2
|
import * as THREE from 'three'
|
|
3
3
|
import { Matrix4, Object3D, XRPose } from "three";
|
|
4
4
|
import { WebAR, WebXR } from "./WebXR";
|
|
5
|
-
import { InstancingUtil } from "
|
|
5
|
+
import { InstancingUtil } from "../engine/engine_instancing";
|
|
6
6
|
import { serializeable } from "../engine/engine_serialization_decorator";
|
|
7
7
|
|
|
8
8
|
// https://github.com/takahirox/takahirox.github.io/blob/master/three.js.mmdeditor/examples/js/controls/DeviceOrientationControls.js
|
|
@@ -11,7 +11,7 @@ import { VRUserState } from "./WebXRSync";
|
|
|
11
11
|
import { getParam } from "../engine/engine_utils";
|
|
12
12
|
import { serializeable } from "../engine/engine_serialization_decorator";
|
|
13
13
|
import { ViewDevice } from "../engine/engine_playerview";
|
|
14
|
-
import { InstancingUtil } from "
|
|
14
|
+
import { InstancingUtil } from "../engine/engine_instancing";
|
|
15
15
|
|
|
16
16
|
export const debug = getParam("debugavatar");
|
|
17
17
|
|
|
@@ -11,7 +11,7 @@ import { XRInputSource, XRSession } from "three";
|
|
|
11
11
|
import { Mathf } from "../engine/engine_math";
|
|
12
12
|
import * as utils from "../engine/engine_three_utils"
|
|
13
13
|
import { Interactable, UsageMarker } from "./Interactable";
|
|
14
|
-
import { InstancingUtil } from "
|
|
14
|
+
import { InstancingUtil } from "../engine/engine_instancing";
|
|
15
15
|
import { Rigidbody } from "./RigidBody";
|
|
16
16
|
import { getParam } from "../engine/engine_utils";
|
|
17
17
|
|
|
@@ -119,12 +119,22 @@ export class WebXRController extends Behaviour {
|
|
|
119
119
|
return ctrl;
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
+
// TODO: replace with component events
|
|
122
123
|
public static addEventListener(evt: ControllerEvents, callback: (controller: WebXRController, args: any) => void) {
|
|
123
124
|
const list = this.eventSubs[evt] ?? [];
|
|
124
125
|
list.push(callback);
|
|
125
126
|
this.eventSubs[evt] = list;
|
|
126
127
|
}
|
|
127
128
|
|
|
129
|
+
// TODO: replace with component events
|
|
130
|
+
public static removeEventListener(evt: ControllerEvents, callback: (controller: WebXRController, args: any) => void) {
|
|
131
|
+
if(!callback) return;
|
|
132
|
+
const list = this.eventSubs[evt] ?? [];
|
|
133
|
+
const idx = list.indexOf(callback);
|
|
134
|
+
if (idx >= 0) list.splice(idx, 1);
|
|
135
|
+
this.eventSubs[evt] = list;
|
|
136
|
+
}
|
|
137
|
+
|
|
128
138
|
private static eventSubs: { [key: string]: Function[] } = {};
|
|
129
139
|
|
|
130
140
|
public webXR!: WebXR;
|