@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.
Files changed (89) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/needle-engine.d.ts +195 -164
  3. package/dist/needle-engine.js +394 -344
  4. package/dist/needle-engine.js.map +4 -4
  5. package/dist/needle-engine.min.js +70 -20
  6. package/dist/needle-engine.min.js.map +4 -4
  7. package/lib/engine/engine_create_objects.d.ts +11 -0
  8. package/lib/engine/engine_create_objects.js +20 -0
  9. package/lib/engine/engine_create_objects.js.map +1 -0
  10. package/lib/engine/engine_instancing.d.ts +5 -0
  11. package/lib/engine/engine_instancing.js +22 -0
  12. package/lib/engine/engine_instancing.js.map +1 -0
  13. package/lib/engine/engine_physics.d.ts +2 -0
  14. package/lib/engine/engine_physics.js +7 -1
  15. package/lib/engine/engine_physics.js.map +1 -1
  16. package/lib/engine/extensions/NEEDLE_components.js +2 -0
  17. package/lib/engine/extensions/NEEDLE_components.js.map +1 -1
  18. package/lib/engine/extensions/NEEDLE_techniques_webgl.d.ts +1 -0
  19. package/lib/engine/extensions/NEEDLE_techniques_webgl.js +26 -0
  20. package/lib/engine/extensions/NEEDLE_techniques_webgl.js.map +1 -1
  21. package/lib/engine-components/Animation.js +1 -1
  22. package/lib/engine-components/Animation.js.map +1 -1
  23. package/lib/engine-components/Collider.d.ts +1 -0
  24. package/lib/engine-components/Collider.js +6 -2
  25. package/lib/engine-components/Collider.js.map +1 -1
  26. package/lib/engine-components/Component.js +4 -51
  27. package/lib/engine-components/Component.js.map +1 -1
  28. package/lib/engine-components/DragControls.js +1 -1
  29. package/lib/engine-components/DragControls.js.map +1 -1
  30. package/lib/engine-components/Renderer.d.ts +0 -3
  31. package/lib/engine-components/Renderer.js +1 -20
  32. package/lib/engine-components/Renderer.js.map +1 -1
  33. package/lib/engine-components/ScreenCapture.d.ts +8 -14
  34. package/lib/engine-components/ScreenCapture.js +62 -74
  35. package/lib/engine-components/ScreenCapture.js.map +1 -1
  36. package/lib/engine-components/SyncedCamera.js +1 -1
  37. package/lib/engine-components/SyncedCamera.js.map +1 -1
  38. package/lib/engine-components/SyncedTransform.js +1 -1
  39. package/lib/engine-components/SyncedTransform.js.map +1 -1
  40. package/lib/engine-components/VideoPlayer.d.ts +14 -3
  41. package/lib/engine-components/VideoPlayer.js +390 -47
  42. package/lib/engine-components/VideoPlayer.js.map +1 -1
  43. package/lib/engine-components/WebARSessionRoot.js +1 -1
  44. package/lib/engine-components/WebARSessionRoot.js.map +1 -1
  45. package/lib/engine-components/WebXRAvatar.js +1 -1
  46. package/lib/engine-components/WebXRAvatar.js.map +1 -1
  47. package/lib/engine-components/WebXRController.d.ts +1 -0
  48. package/lib/engine-components/WebXRController.js +12 -1
  49. package/lib/engine-components/WebXRController.js.map +1 -1
  50. package/lib/engine-components/codegen/{exports.d.ts → components.d.ts} +2 -2
  51. package/{src/engine-components/codegen/exports.ts → lib/engine-components/codegen/components.js} +3 -2
  52. package/lib/engine-components/codegen/components.js.map +1 -0
  53. package/lib/engine-components/js-extensions/Object3D.d.ts +2 -0
  54. package/lib/engine-components/js-extensions/Object3D.js +75 -0
  55. package/lib/engine-components/js-extensions/Object3D.js.map +1 -0
  56. package/lib/engine-components/ui/EventSystem.d.ts +3 -0
  57. package/lib/engine-components/ui/EventSystem.js +17 -8
  58. package/lib/engine-components/ui/EventSystem.js.map +1 -1
  59. package/lib/needle-engine.d.ts +2 -1
  60. package/lib/needle-engine.js +3 -2
  61. package/lib/needle-engine.js.map +1 -1
  62. package/package.json +2 -2
  63. package/src/engine/codegen/register_types.js +2 -4
  64. package/src/engine/engine_create_objects.ts +27 -0
  65. package/src/engine/engine_instancing.ts +25 -0
  66. package/src/engine/engine_physics.ts +9 -1
  67. package/src/engine/extensions/NEEDLE_components.ts +3 -0
  68. package/src/engine/extensions/NEEDLE_techniques_webgl.ts +34 -2
  69. package/src/engine-components/Animation.ts +1 -1
  70. package/src/engine-components/Collider.ts +7 -2
  71. package/src/engine-components/Component.ts +4 -64
  72. package/src/engine-components/DragControls.ts +1 -1
  73. package/src/engine-components/Renderer.ts +2 -20
  74. package/src/engine-components/ScreenCapture.ts +62 -75
  75. package/src/engine-components/SyncedCamera.ts +1 -1
  76. package/src/engine-components/SyncedTransform.ts +1 -1
  77. package/src/engine-components/VideoPlayer.ts +401 -48
  78. package/src/engine-components/WebARSessionRoot.ts +1 -1
  79. package/src/engine-components/WebXRAvatar.ts +1 -1
  80. package/src/engine-components/WebXRController.ts +11 -1
  81. package/{lib/engine-components/codegen/exports.js → src/engine-components/codegen/components.ts} +1 -3
  82. package/src/engine-components/js-extensions/Object3D.ts +91 -0
  83. package/src/engine-components/ui/EventSystem.ts +19 -10
  84. package/src/needle-engine.ts +3 -3
  85. package/lib/engine-components/ComponentExtensions.d.ts +0 -2
  86. package/lib/engine-components/ComponentExtensions.js +0 -3
  87. package/lib/engine-components/ComponentExtensions.js.map +0 -1
  88. package/lib/engine-components/codegen/exports.js.map +0 -1
  89. 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 "./Renderer";
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 './Renderer';
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 videoTexture: THREE.VideoTexture | null = null;
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.videoElement.play();
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.videoTexture) {
186
- this.videoTexture.dispose();
187
- this.videoTexture = null;
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
- if (!this.videoTexture)
254
- this.videoTexture = new THREE.VideoTexture(this.videoElement);
255
- this.videoTexture.flipY = false;
256
- this.videoTexture.encoding = THREE.sRGBEncoding;
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
- private _targetObjects: Array<Object3D>;
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
- getTargetObjects(): Array<Object3D> {
263
- return Array.from(this._targetObjects);
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.videoTexture;
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.videoTexture;
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
- // class VideoTexture extends Texture {
470
+ constructor(context: Context) {
471
+ this.context = context;
472
+ this._input = new VideoOverlayInput(this);
473
+ }
358
474
 
359
- // get isVideoTexture() { return true; }
475
+ get enabled() {
476
+ return this._isInScreenspaceMode;
477
+ }
360
478
 
361
- // constructor( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) {
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
- // this.minFilter = minFilter !== undefined ? minFilter : LinearFilter;
366
- // this.magFilter = magFilter !== undefined ? magFilter : LinearFilter;
485
+ add(video: VideoTexture) {
486
+ if (this._videos.indexOf(video) === -1) {
487
+ this._videos.push(video);
488
+ }
489
+ }
367
490
 
368
- // this.generateMipmaps = false;
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
- // const scope = this;
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
- // function updateVideo() {
518
+ const mat = quad.material as ScreenspaceTexture;
519
+ mat?.reset();
373
520
 
374
- // scope.needsUpdate = true;
375
- // video.requestVideoFrameCallback( updateVideo );
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
- // if ( 'requestVideoFrameCallback' in video ) {
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
- // video.requestVideoFrameCallback( updateVideo );
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
- // // clone() {
653
+ }
388
654
 
389
- // // return new this.constructor( this.image ).copy( this );
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
- // update() {
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
- // const video = this.image;
396
- // const hasVideoFrameCallback = 'requestVideoFrameCallback' in video;
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
- // if ( hasVideoFrameCallback === false && video.readyState >= video.HAVE_CURRENT_DATA ) {
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
- // this.needsUpdate = true;
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
- // export { VideoTexture };
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 "./Renderer";
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 "./Renderer";
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 "./Renderer";
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;