@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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@needle-tools/engine",
3
- "version": "2.32.0-pre",
3
+ "version": "2.34.0-pre",
4
4
  "description": "Needle Engine is a web-based runtime for 3D apps. It runs on your machine for development, and can be deployed anywhere. It is flexible, extensible, and collaboration and XR come naturally.",
5
5
  "main": "dist/needle-engine.js",
6
6
  "module": "lib/needle-engine.js",
@@ -48,7 +48,7 @@
48
48
  "md5": "^2.3.0",
49
49
  "peerjs": "1.3.2",
50
50
  "stats.js": "^0.17.0",
51
- "three": "github:needle-tools/three.js#needle-dev-6",
51
+ "three": "npm:@needle-tools/three@^0.145.0",
52
52
  "three-mesh-ui": "^6.4.5",
53
53
  "three.meshline": "^1.4.0",
54
54
  "uuid": "^9.0.0",
@@ -18,7 +18,6 @@ import { Collider } from "../../engine-components/Collider.ts";
18
18
  import { SphereCollider } from "../../engine-components/Collider.ts";
19
19
  import { BoxCollider } from "../../engine-components/Collider.ts";
20
20
  import { InstantiateOptions } from "../../engine-components/Component.ts";
21
- import { UnityObject } from "../../engine-components/ComponentExtensions.ts";
22
21
  import { DeleteBox } from "../../engine-components/DeleteBox.ts";
23
22
  import { Deletable } from "../../engine-components/DeleteBox.ts";
24
23
  import { DeviceFlag } from "../../engine-components/DeviceFlag.ts";
@@ -57,7 +56,6 @@ import { FieldWithDefault } from "../../engine-components/Renderer.ts";
57
56
  import { Renderer } from "../../engine-components/Renderer.ts";
58
57
  import { MeshRenderer } from "../../engine-components/Renderer.ts";
59
58
  import { SkinnedMeshRenderer } from "../../engine-components/Renderer.ts";
60
- import { InstancingUtil } from "../../engine-components/Renderer.ts";
61
59
  import { RendererLightmap } from "../../engine-components/RendererLightmap.ts";
62
60
  import { Rigidbody } from "../../engine-components/RigidBody.ts";
63
61
  import { ScreenCapture } from "../../engine-components/ScreenCapture.ts";
@@ -105,6 +103,7 @@ import { Avatar_POI } from "../../engine-components/avatar/Avatar_Brain_LookAt.t
105
103
  import { Avatar_Brain_LookAt } from "../../engine-components/avatar/Avatar_Brain_LookAt.ts";
106
104
  import { Avatar_MouthShapes } from "../../engine-components/avatar/Avatar_MouthShapes.ts";
107
105
  import { Avatar_MustacheShake } from "../../engine-components/avatar/Avatar_MustacheShake.ts";
106
+ import { __Ignore } from "../../engine-components/codegen/components.ts";
108
107
  import { LogStats } from "../../engine-components/debug/LogStats.ts";
109
108
  import { RGBAColor } from "../../engine-components/js-extensions/RGBAColor.ts";
110
109
  import { PlayableDirector } from "../../engine-components/timeline/PlayableDirector.ts";
@@ -165,7 +164,6 @@ TypeStore.add("Collider", Collider);
165
164
  TypeStore.add("SphereCollider", SphereCollider);
166
165
  TypeStore.add("BoxCollider", BoxCollider);
167
166
  TypeStore.add("InstantiateOptions", InstantiateOptions);
168
- TypeStore.add("UnityObject", UnityObject);
169
167
  TypeStore.add("DeleteBox", DeleteBox);
170
168
  TypeStore.add("Deletable", Deletable);
171
169
  TypeStore.add("DeviceFlag", DeviceFlag);
@@ -204,7 +202,6 @@ TypeStore.add("FieldWithDefault", FieldWithDefault);
204
202
  TypeStore.add("Renderer", Renderer);
205
203
  TypeStore.add("MeshRenderer", MeshRenderer);
206
204
  TypeStore.add("SkinnedMeshRenderer", SkinnedMeshRenderer);
207
- TypeStore.add("InstancingUtil", InstancingUtil);
208
205
  TypeStore.add("RendererLightmap", RendererLightmap);
209
206
  TypeStore.add("Rigidbody", Rigidbody);
210
207
  TypeStore.add("ScreenCapture", ScreenCapture);
@@ -252,6 +249,7 @@ TypeStore.add("Avatar_POI", Avatar_POI);
252
249
  TypeStore.add("Avatar_Brain_LookAt", Avatar_Brain_LookAt);
253
250
  TypeStore.add("Avatar_MouthShapes", Avatar_MouthShapes);
254
251
  TypeStore.add("Avatar_MustacheShake", Avatar_MustacheShake);
252
+ TypeStore.add("__Ignore", __Ignore);
255
253
  TypeStore.add("LogStats", LogStats);
256
254
  TypeStore.add("RGBAColor", RGBAColor);
257
255
  TypeStore.add("PlayableDirector", PlayableDirector);
@@ -0,0 +1,27 @@
1
+ import { PlaneGeometry, MeshBasicMaterial, DoubleSide, Mesh, Material } from "three"
2
+
3
+
4
+ export enum PrimitiveType {
5
+ Quad = 0
6
+ }
7
+
8
+ export type ObjectOptions = {
9
+ name?: string,
10
+ material?: Material,
11
+ }
12
+
13
+ export class ObjectUtils {
14
+
15
+ static createPrimitive(type: PrimitiveType, opts?: ObjectOptions): Mesh {
16
+ let obj: Mesh;
17
+ switch (type) {
18
+ case PrimitiveType.Quad:
19
+ const geometry = new PlaneGeometry(1, 1, 1, 1);
20
+ const material = opts?.material ?? new MeshBasicMaterial({ color: 0xffffff });
21
+ obj = new Mesh(geometry, material);
22
+ }
23
+ if (opts?.name)
24
+ obj.name = opts.name;
25
+ return obj;
26
+ }
27
+ }
@@ -0,0 +1,25 @@
1
+ export const NEED_UPDATE_INSTANCE_KEY = Symbol("NEEDLE_NEED_UPDATE_INSTANCE");
2
+
3
+
4
+
5
+ export class InstancingUtil {
6
+
7
+ static isUsingInstancing(instance: THREE.Object3D): boolean { return instance["__isUsingInstancing"] === true; }
8
+
9
+ // TODO: change this so it does not set matrix world directly but some flag that is only used by instancing
10
+ static markDirty(go: THREE.Object3D | null, recursive: boolean = true) {
11
+ if (!go) return;
12
+ // potential optimization:
13
+ // if(go.matrixWorldNeedsUpdate) return;
14
+ // console.trace(go, GameObject.isUsingInstancing(go));
15
+ if (this.isUsingInstancing(go)) {
16
+ go[NEED_UPDATE_INSTANCE_KEY] = true;
17
+ go.matrixWorldNeedsUpdate = true;
18
+ }
19
+ if (recursive) {
20
+ for (const child of go.children) {
21
+ InstancingUtil.markDirty(child, true);
22
+ }
23
+ }
24
+ }
25
+ }
@@ -5,11 +5,11 @@ import cannonDebugger from 'cannon-es-debugger'
5
5
  import { Rigidbody } from '../engine-components/Rigidbody';
6
6
  import * as utils from "./engine_utils"
7
7
  import * as threeutils from "./engine_three_utils"
8
- import { InstancingUtil } from '../engine-components/Renderer';
9
8
  import { Behaviour, Component, GameObject } from '../engine-components/Component';
10
9
  import { Body, RigidVehicle, Shape, Vec3 } from 'cannon-es';
11
10
  import { Matrix4, Object3D, RGBA_BPTC_Format, Vector3, Wrapping } from 'three';
12
11
  import { Collider } from '../engine-components/Collider';
12
+ import { InstancingUtil } from './engine_instancing';
13
13
 
14
14
  const debugPhysics = utils.getParam("debugphysics");
15
15
  const debugCollisions = utils.getParam("debugcollisions");
@@ -686,6 +686,14 @@ export class Physics {
686
686
 
687
687
  export class Collision {
688
688
 
689
+ get __internalCollision(): CannonCollision {
690
+ return this.collision;
691
+ }
692
+
693
+ get __internalInvertedSourceReceiver(): boolean {
694
+ return this.invert;
695
+ }
696
+
689
697
  private readonly invert: boolean;
690
698
  private readonly collision: CannonCollision;
691
699
  private readonly targetBody: CANNON.Body;
@@ -5,6 +5,7 @@ import { writeBuiltinComponentData } from "../engine_gltf_builtin_components";
5
5
  import { debugExtension } from "../engine_default_parameters";
6
6
  import { builtinComponentKeyName } from "../engine_constants";
7
7
  import { resolveReferences } from "./extension_utils";
8
+ import { apply } from "../../engine-components/js-extensions/Object3D";
8
9
 
9
10
  export const debug = debugExtension
10
11
  const componentsArrayExportKey = "$___Export_Components";
@@ -185,6 +186,8 @@ export class NEEDLE_components implements GLTFLoaderPlugin {
185
186
  continue;
186
187
  }
187
188
 
189
+ apply(obj);
190
+
188
191
  loadComponents.push(this.createComponents(obj, data));
189
192
  }
190
193
  }
@@ -62,6 +62,12 @@ export class CustomShader extends RawShaderMaterial {
62
62
  private identifier: SourceIdentifier;
63
63
  private onBeforeRenderSceneCallback = this.onBeforeRenderScene.bind(this);
64
64
 
65
+ clone() {
66
+ const clone = super.clone();
67
+ createUniformProperties(clone);
68
+ return clone;
69
+ }
70
+
65
71
  constructor(identifier: SourceIdentifier, ...args) {
66
72
  super(...args);
67
73
 
@@ -179,12 +185,12 @@ export class CustomShader extends RawShaderMaterial {
179
185
  CustomShader.viewProjection.copy(camera.projectionMatrix).multiply(camera.matrixWorldInverse);
180
186
  ToUnityMatrixArray(CustomShader.viewProjection, CustomShader._viewProjectionValues)
181
187
  }
182
-
188
+
183
189
  if (CustomShader.viewMatrix && this.uniforms[this._viewMatrixName]) {
184
190
  CustomShader.viewMatrix.copy(camera.matrixWorldInverse);
185
191
  ToUnityMatrixArray(CustomShader.viewMatrix, CustomShader._viewMatrixValues)
186
192
  }
187
-
193
+
188
194
  if (this.uniforms[CustomShader._worldSpaceCameraPosName]) {
189
195
  CustomShader._worldSpaceCameraPos.setFromMatrixPosition(camera.matrixWorld);
190
196
  }
@@ -475,8 +481,34 @@ export class NEEDLE_techniques_webgl implements GLTFLoaderPlugin {
475
481
  }
476
482
  if (debug)
477
483
  console.log(material.uuid, uniforms);
484
+
485
+
486
+ createUniformProperties(material);
487
+
478
488
  resolve(material);
479
489
  });
480
490
  }
481
491
 
482
492
  }
493
+
494
+
495
+ // when animating custom material properties (uniforms) the path resolver tries to access them via material._MyProperty.
496
+ // That doesnt exist by default for custom properties
497
+ // We could re-write the path in the khr path resolver but that would require it to know beforehand
498
+ // if the material uses as custom shader or not
499
+ // this way all properties of custom shaders are also accessible via material._MyProperty
500
+ function createUniformProperties(material: CustomShader) {
501
+ if (material.uniforms) {
502
+ for (const key in material.uniforms) {
503
+ if (!Object.getOwnPropertyDescriptor(material, key)) {
504
+ Object.defineProperty(material, key, {
505
+ get: () => material.uniforms[key].value,
506
+ set: (value) => {
507
+ material.uniforms[key].value = value
508
+ material.needsUpdate = true;
509
+ }
510
+ });
511
+ }
512
+ }
513
+ }
514
+ }
@@ -2,8 +2,8 @@ import { Behaviour } from "./Component";
2
2
  import * as THREE from 'three'
3
3
  import { AnimationAction, AnimationClip } from "three";
4
4
  import { MixerEvent } from "./Animator";
5
- import { InstancingUtil } from "./Renderer";
6
5
  import { serializeable } from "../engine/engine_serialization_decorator";
6
+ import { InstancingUtil } from "../engine/engine_instancing";
7
7
 
8
8
  export declare class PlayOptions {
9
9
  fadeDuration?: number;
@@ -17,6 +17,11 @@ export class Collider extends Behaviour {
17
17
  this._shape = null;
18
18
  }
19
19
 
20
+ start(){
21
+ if (!this.attachedRigidbody)
22
+ this.attachedRigidbody = this.gameObject.getComponentInParent(Rigidbody);
23
+ }
24
+
20
25
  onEnable() {
21
26
  // a rigidbody is not assigned if we export an asset
22
27
  if (!this.attachedRigidbody)
@@ -54,9 +59,9 @@ export class SphereCollider extends Collider {
54
59
  export class BoxCollider extends Collider {
55
60
 
56
61
  @serializeable(Vector3)
57
- size!: THREE.Vector3;
62
+ size: THREE.Vector3 = new Vector3(1, 1, 1);
58
63
  @serializeable(Vector3)
59
- center!: THREE.Vector3;
64
+ center: THREE.Vector3 = new Vector3(0, 0, 0);
60
65
 
61
66
  onEnable() {
62
67
  super.onEnable();
@@ -10,6 +10,8 @@ import { Object3D } from "three";
10
10
  import { InstantiateIdProvider, syncDestroy, syncInstantiate } from "../engine/engine_networking_instantiate";
11
11
  import { SourceIdentifier } from "../engine/engine_gltf";
12
12
  import { Collision } from "../engine/engine_physics";
13
+ import * as Object3DExtensions from "./js-extensions/Object3D";
14
+ import { InstancingUtil } from "../engine/engine_instancing";
13
15
 
14
16
  export interface UIDProvider {
15
17
  seed: number;
@@ -70,10 +72,6 @@ declare class NewGameObjectReferenceInfo {
70
72
  clone: THREE.Object3D;
71
73
  }
72
74
 
73
- THREE.Object3D.prototype["SetActive"] = function (active: boolean) {
74
- this.visible = active;
75
- }
76
-
77
75
 
78
76
  abstract class GameObject extends THREE.Object3D implements THREE.Object3D {
79
77
 
@@ -99,7 +97,7 @@ abstract class GameObject extends THREE.Object3D implements THREE.Object3D {
99
97
  go["__isUsingInstancing"] = instanced;
100
98
  }
101
99
 
102
- public static isUsingInstancing(instance: THREE.Object3D): boolean { return instance["__isUsingInstancing"] === true; }
100
+ public static isUsingInstancing(instance: THREE.Object3D): boolean { return InstancingUtil.isUsingInstancing(instance); }
103
101
 
104
102
  public static foreachComponent(instance: THREE.Object3D, cb: (comp: Component) => any, recursive: boolean = true): any {
105
103
  if (!instance) return;
@@ -217,6 +215,7 @@ abstract class GameObject extends THREE.Object3D implements THREE.Object3D {
217
215
  instance.children = [];
218
216
  let clone: THREE.Object3D | GameObject;
219
217
  clone = instance.clone(false);
218
+ Object3DExtensions.apply(clone);
220
219
  instance.userData = userData;
221
220
  instance.children = children;
222
221
 
@@ -684,65 +683,6 @@ abstract class GameObject extends THREE.Object3D implements THREE.Object3D {
684
683
  }
685
684
 
686
685
 
687
- // this is a fix to allow gameObject active animation be applied to a three object
688
- Object.defineProperty(THREE.Object3D.prototype, "activeSelf", {
689
- get: function () {
690
- return this.visible;
691
- },
692
- set: function (val: boolean | number) {
693
- const state = typeof val === "number" ? val > 0.5 : val;
694
- this.visible = state;
695
- }
696
- });
697
-
698
-
699
- THREE.Object3D.prototype["addNewComponent"] = function <T extends Behaviour>(type: ConstructorConcrete<T>) {
700
- return GameObject.addNewComponent(this, type);
701
- }
702
-
703
- THREE.Object3D.prototype["removeComponent"] = function (inst: Component) {
704
- return GameObject.removeComponent(inst);
705
- }
706
-
707
- THREE.Object3D.prototype["getOrAddComponent"] = function <T>(typeName: Constructor<T> | null): T {
708
- return GameObject.getOrAddComponent(this, typeName);
709
- }
710
-
711
- THREE.Object3D.prototype["getComponent"] = function <T>(type: Constructor<T>) {
712
- return GameObject.getComponent(this, type);
713
- }
714
-
715
- THREE.Object3D.prototype["getComponents"] = function <T>(type: Constructor<T>, arr?: []) {
716
- return GameObject.getComponents(this, type, arr);
717
- }
718
-
719
- THREE.Object3D.prototype["getComponentInChildren"] = function <T>(type: Constructor<T>) {
720
- return GameObject.getComponentInChildren(this, type);
721
- }
722
-
723
- THREE.Object3D.prototype["getComponentsInChildren"] = function <T>(type: Constructor<T>, arr?: []) {
724
- return GameObject.getComponentsInChildren(this, type, arr);
725
- }
726
-
727
- THREE.Object3D.prototype["getComponentInParent"] = function <T>(type: Constructor<T>) {
728
- return GameObject.getComponentInParent(this, type);
729
- }
730
-
731
- THREE.Object3D.prototype["getComponentsInParent"] = function <T>(type: Constructor<T>, arr?: []) {
732
- return GameObject.getComponentsInParent(this, type, arr);
733
- }
734
-
735
- // function patch_THREEObject3D() {
736
- // const props = Object.getOwnPropertyNames(GameObject.prototype);
737
- // for (const propName in props) {
738
- // const prop = props[propName];
739
- // if (prop === "constructor") continue;
740
- // const fn = GameObject.prototype[prop];
741
- // THREE.Object3D.prototype[prop] = fn;
742
- // }
743
- // }
744
- // patch_THREEObject3D();
745
-
746
686
 
747
687
  class Component implements EventTarget {
748
688
 
@@ -7,12 +7,12 @@ import { Context } from "../engine/engine_setup";
7
7
  import { Interactable, UsageMarker } from "./Interactable";
8
8
  import { Rigidbody } from "./RigidBody";
9
9
  import { WebXR } from "./WebXR";
10
- import { InstancingUtil } from "./Renderer";
11
10
  import { Avatar_POI } from "./avatar/Avatar_Brain_LookAt";
12
11
  import { RaycastOptions } from "../engine/engine_physics";
13
12
  import { getWorldPosition, getWorldQuaternion, setWorldPosition } from "../engine/engine_three_utils";
14
13
  import { KeyCode } from "../engine/engine_input";
15
14
  import { nameofFactory } from "../engine/engine_utils";
15
+ import { InstancingUtil } from "../engine/engine_instancing";
16
16
 
17
17
  const debug = false;
18
18
 
@@ -8,6 +8,7 @@ import { serializeable } from "../engine/engine_serialization_decorator";
8
8
  import { Material, Texture, Vector4 } from "three";
9
9
  import { NEEDLE_render_objects } from "../engine/extensions/NEEDLE_render_objects";
10
10
  import { NEEDLE_deferred_texture } from "../engine/extensions/NEEDLE_deferred_texture";
11
+ import { NEED_UPDATE_INSTANCE_KEY } from "../engine/engine_instancing";
11
12
 
12
13
  const suppressInstancing = getParam("noInstancing");
13
14
  const debugLightmap = getParam("debuglightmaps") ? true : false;
@@ -491,26 +492,7 @@ export enum ShadowCastingMode {
491
492
  ShadowsOnly,
492
493
  }
493
494
 
494
- const NEED_UPDATE_INSTANCE_KEY = "NEEDLE_NEED_UPDATE_INSTANCE";
495
-
496
- export class InstancingUtil {
497
- // TODO: change this so it does not set matrix world directly but some flag that is only used by instancing
498
- static markDirty(go: THREE.Object3D | null, recursive: boolean = true) {
499
- if (!go) return;
500
- // potential optimization:
501
- // if(go.matrixWorldNeedsUpdate) return;
502
- // console.trace(go, GameObject.isUsingInstancing(go));
503
- if (GameObject.isUsingInstancing(go)) {
504
- go[NEED_UPDATE_INSTANCE_KEY] = true;
505
- go.matrixWorldNeedsUpdate = true;
506
- }
507
- if (recursive) {
508
- for (const child of go.children) {
509
- InstancingUtil.markDirty(child, true);
510
- }
511
- }
512
- }
513
- }
495
+
514
496
 
515
497
  declare class InstancingSetupArgs { rend: Renderer; foundMeshes: number };
516
498
 
@@ -9,12 +9,14 @@ import { IModel } from "../engine/engine_networking";
9
9
  import { IPointerClickHandler } from "./ui/PointerEvents";
10
10
  import { EventDispatcher } from "three";
11
11
  import { AudioSource } from "./AudioSource";
12
- import { getWorldScale, setWorldScale } from "../engine/engine_three_utils";
12
+ import { getParam } from "../engine/engine_utils";
13
13
 
14
+ const debug = getParam("debugscreenshare");
14
15
 
15
16
  export enum ScreenCaptureDevice {
16
17
  Screen = 0,
17
- Camera = 1
18
+ Camera = 1,
19
+ Canvas = 2
18
20
  }
19
21
 
20
22
  export enum ScreenCaptureMode {
@@ -23,12 +25,6 @@ export enum ScreenCaptureMode {
23
25
  Receiving = 2
24
26
  }
25
27
 
26
- export enum AspectMode {
27
- None = 0,
28
- AdjustHeight = 1,
29
- AdjustWidth = 2,
30
- }
31
-
32
28
  function disposeStream(str: MediaStream | null | undefined) {
33
29
  if (!str) return;
34
30
  for (const cap of str.getTracks())
@@ -38,6 +34,11 @@ function disposeStream(str: MediaStream | null | undefined) {
38
34
  export class ScreenCapture extends Behaviour implements IPointerClickHandler {
39
35
 
40
36
  onPointerClick() {
37
+ if (this.isReceiving) {
38
+ if (this.videoPlayer)
39
+ this.videoPlayer.screenspace = !this.videoPlayer.screenspace;
40
+ return;
41
+ }
41
42
  if (this.isSending) {
42
43
  this.close();
43
44
  return;
@@ -46,40 +47,55 @@ export class ScreenCapture extends Behaviour implements IPointerClickHandler {
46
47
  }
47
48
 
48
49
 
49
-
50
+ @serializeable(VideoPlayer)
51
+ videoPlayer?: VideoPlayer;
50
52
 
51
53
  @serializeable()
52
54
  device: ScreenCaptureDevice = ScreenCaptureDevice.Screen;
53
55
 
54
- @serializeable()
55
- aspectMode: AspectMode = AspectMode.AdjustHeight;
56
+ get currentScream(): MediaStream | null {
57
+ return this._currentStream;
58
+ }
56
59
 
60
+ get currentMode(): ScreenCaptureMode {
61
+ return this._currentMode;
62
+ }
57
63
 
58
64
  get isSending() {
59
- return this._currentStream && this._currentMode === ScreenCaptureMode.Sending;
65
+ return this._currentStream?.active && this._currentMode === ScreenCaptureMode.Sending;
60
66
  }
61
67
  get isReceiving() {
62
- return this._currentStream && this._currentMode === ScreenCaptureMode.Receiving;
68
+ if (this._currentMode === ScreenCaptureMode.Receiving) {
69
+ if (!this._currentStream || this._currentStream.active === false) return false;
70
+ // if any track is still live consider it active
71
+ const tracks = this._currentStream.getTracks();
72
+ for (const track of tracks) {
73
+ if (track.readyState === "live") return true;
74
+ }
75
+ }
76
+ return false;
63
77
  }
64
78
 
65
79
  private _net?: NetworkedVideo;
66
- private _video?: VideoPlayer;
67
80
  private _requestOpen: boolean = false;
68
81
  private _currentStream: MediaStream | null = null;
69
82
  private _currentMode: ScreenCaptureMode = ScreenCaptureMode.Idle;
70
83
 
71
84
  awake() {
85
+ console.log(this);
72
86
  AudioSource.registerWaitForAllowAudio(() => {
73
- if (this._video && this._currentStream && this._currentMode === ScreenCaptureMode.Receiving) {
74
- this._video.setVideo(this._currentStream);
87
+ if (this.videoPlayer && this._currentStream && this._currentMode === ScreenCaptureMode.Receiving) {
88
+ this.videoPlayer.setVideo(this._currentStream);
75
89
  }
76
90
  });
77
91
  }
78
92
 
79
93
  start() {
80
- this._video = GameObject.getComponentInChildren(this.gameObject, VideoPlayer) ?? undefined;
81
- if (!this._video) {
82
- console.error("Missing VideoPlayer component");
94
+ if (!this.videoPlayer) {
95
+ this.videoPlayer = GameObject.getComponent(this.gameObject, VideoPlayer) ?? undefined;
96
+ }
97
+ if (!this.videoPlayer) {
98
+ console.error("Screencapture did not find a VideoPlayer component");
83
99
  return;
84
100
  }
85
101
  const handle = PeerHandle.getOrCreate(this.context, this.guid);
@@ -92,7 +108,7 @@ export class ScreenCapture extends Behaviour implements IPointerClickHandler {
92
108
  async share() {
93
109
  this._requestOpen = true;
94
110
  try {
95
- if (this._video) {
111
+ if (this.videoPlayer) {
96
112
 
97
113
  const settings: MediaTrackConstraints = {
98
114
  echoCancellation: true,
@@ -104,10 +120,12 @@ export class ScreenCapture extends Behaviour implements IPointerClickHandler {
104
120
  };
105
121
 
106
122
  switch (this.device) {
123
+ // Capture a connected camera
107
124
  case ScreenCaptureDevice.Camera:
108
125
  this.tryShareUserCamera(displayMediaOptions);
109
126
  break;
110
127
 
128
+ // capture any screen, will show a popup
111
129
  case ScreenCaptureDevice.Screen:
112
130
  if (!navigator.mediaDevices.getDisplayMedia) {
113
131
  console.error("No getDisplayMedia support");
@@ -119,6 +137,16 @@ export class ScreenCapture extends Behaviour implements IPointerClickHandler {
119
137
  }
120
138
  else disposeStream(myVideo);
121
139
  break;
140
+
141
+ // capture the canvas meaning the threejs view
142
+ case ScreenCaptureDevice.Canvas:
143
+ // looks like this doesnt work reliably on chrome https://stackoverflow.com/a/66848674
144
+ // firefox updates fine
145
+ // https://bugs.chromium.org/p/chromium/issues/detail?id=1156408
146
+ const fps = 0;
147
+ const stream = this.context.renderer.domElement.captureStream(fps);
148
+ this.setVideo(stream, ScreenCaptureMode.Sending);
149
+ break;
122
150
  }
123
151
  }
124
152
  } catch (err: any) {
@@ -136,8 +164,8 @@ export class ScreenCapture extends Behaviour implements IPointerClickHandler {
136
164
  this._requestOpen = false;
137
165
  if (this._currentStream) {
138
166
  console.warn("Close current stream / disposing resources");
139
- disposeStream(this._currentStream);
140
167
  this._net?.stopSendingVideo(this._currentStream);
168
+ disposeStream(this._currentStream);
141
169
  this._currentMode = ScreenCaptureMode.Idle;
142
170
  this._currentStream = null;
143
171
  }
@@ -146,16 +174,21 @@ export class ScreenCapture extends Behaviour implements IPointerClickHandler {
146
174
  private setVideo(stream: MediaStream, mode: ScreenCaptureMode) {
147
175
  if (stream === this._currentStream) return;
148
176
  this.close();
149
- if (!stream || !this._video) return;
177
+ if (!stream || !this.videoPlayer) return;
150
178
  this._currentStream = stream;
151
179
  this._requestOpen = true;
152
180
  this._currentMode = mode;
153
- this._video.setVideo(stream);
154
- if (mode === ScreenCaptureMode.Sending)
181
+ this.videoPlayer.setVideo(stream);
182
+
183
+ const isSending = mode === ScreenCaptureMode.Sending;
184
+ if (isSending) {
155
185
  this._net?.startSendingVideo(stream);
156
186
 
157
- this._targetObjects = undefined;
158
- this.updateAspect();
187
+ }
188
+
189
+ stream.addEventListener("ended", () => {
190
+ this.close();
191
+ });
159
192
  }
160
193
 
161
194
  private onReceiveVideo(evt: ReceiveVideoEvent) {
@@ -164,7 +197,6 @@ export class ScreenCapture extends Behaviour implements IPointerClickHandler {
164
197
 
165
198
 
166
199
 
167
-
168
200
  private async tryShareUserCamera(opts: DisplayMediaStreamConstraints) {
169
201
 
170
202
  // let newWindow = open('', 'example', 'width=300,height=300');
@@ -203,53 +235,6 @@ export class ScreenCapture extends Behaviour implements IPointerClickHandler {
203
235
  // private openWindowToSelectCamera(){
204
236
 
205
237
  // }
206
-
207
-
208
-
209
-
210
- private _targetObjects?: Array<THREE.Object3D>;
211
-
212
- private updateAspect() {
213
- this.startCoroutine(this.updateAspectImpl());
214
- }
215
-
216
- private _updateAspectRoutineId: number = -1;
217
- private *updateAspectImpl() {
218
- const id = ++this._updateAspectRoutineId;
219
- while (this.aspectMode !== AspectMode.None && this._currentStream && id === this._updateAspectRoutineId) {
220
- const video = this._video;
221
- const stream = this._currentStream;
222
- if (!video) return;
223
- if (!stream) return;
224
- let aspect: number | undefined = undefined;
225
- for (const track of stream.getVideoTracks()) {
226
- const settings = track.getSettings();
227
- if (settings.width && settings.height) {
228
- aspect = settings.width / settings.height;
229
- break;
230
- }
231
- }
232
- if (aspect === undefined) return;
233
- if (!this._targetObjects)
234
- this._targetObjects = video.getTargetObjects();
235
- for (const obj of this._targetObjects) {
236
- let worldAspect = 1;
237
- if (obj.parent) {
238
- const parentScale = getWorldScale(obj.parent);
239
- worldAspect = parentScale.x / parentScale.y;
240
- }
241
- switch (this.aspectMode) {
242
- case AspectMode.AdjustHeight:
243
- obj.scale.y = 1 / aspect * obj.scale.x * worldAspect;
244
- break;
245
- case AspectMode.AdjustWidth:
246
- obj.scale.x = aspect * obj.scale.y * worldAspect;
247
- break;
248
- }
249
- }
250
- yield;
251
- }
252
- }
253
238
  }
254
239
 
255
240
 
@@ -444,7 +429,8 @@ class PeerHandle extends EventDispatcher {
444
429
  }
445
430
 
446
431
  private onPeerConnect(id): void {
447
- console.log("Peer connected as", id);
432
+ if (debug)
433
+ console.log("Peer connected as", id);
448
434
  this.context.connection.send(PeerEvent.Connected, new PeerUserConnectedModel(this, id));
449
435
  }
450
436
 
@@ -539,6 +525,7 @@ class NetworkedVideo extends EventDispatcher {
539
525
  if (_steam) {
540
526
  const calls = this._sendingVideoStreams.get(_steam);
541
527
  if (calls) {
528
+ console.log("Closing calls", calls);
542
529
  for (const call of calls) {
543
530
  call.close();
544
531
  }