@needle-tools/engine 4.4.0-alpha.4 → 4.4.0-beta

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 (67) hide show
  1. package/CHANGELOG.md +19 -1
  2. package/dist/needle-engine.bundle.js +5067 -4828
  3. package/dist/needle-engine.bundle.light.js +5062 -4823
  4. package/dist/needle-engine.bundle.light.min.js +129 -116
  5. package/dist/needle-engine.bundle.light.umd.cjs +125 -112
  6. package/dist/needle-engine.bundle.min.js +129 -116
  7. package/dist/needle-engine.bundle.umd.cjs +127 -114
  8. package/dist/needle-engine.d.ts +9 -9
  9. package/dist/needle-engine.js +91 -90
  10. package/dist/needle-engine.light.d.ts +9 -9
  11. package/dist/needle-engine.light.js +91 -90
  12. package/dist/needle-engine.light.min.js +1 -1
  13. package/dist/needle-engine.light.umd.cjs +1 -1
  14. package/dist/needle-engine.min.js +1 -1
  15. package/dist/needle-engine.umd.cjs +1 -1
  16. package/dist/three.js +1 -0
  17. package/dist/three.light.js +1 -0
  18. package/dist/three.light.min.js +16 -16
  19. package/dist/three.light.umd.cjs +15 -15
  20. package/dist/three.min.js +16 -16
  21. package/dist/three.umd.cjs +15 -15
  22. package/lib/engine/engine_input.d.ts +7 -0
  23. package/lib/engine/engine_input.js +12 -0
  24. package/lib/engine/engine_input.js.map +1 -1
  25. package/lib/engine/engine_serialization_decorator.js +4 -0
  26. package/lib/engine/engine_serialization_decorator.js.map +1 -1
  27. package/lib/engine/engine_three_utils.d.ts +26 -1
  28. package/lib/engine/engine_three_utils.js +43 -0
  29. package/lib/engine/engine_three_utils.js.map +1 -1
  30. package/lib/engine/engine_utils_format.js +11 -5
  31. package/lib/engine/engine_utils_format.js.map +1 -1
  32. package/lib/engine/webcomponents/needle menu/needle-menu.d.ts +1 -0
  33. package/lib/engine/webcomponents/needle menu/needle-menu.js +3 -3
  34. package/lib/engine/webcomponents/needle menu/needle-menu.js.map +1 -1
  35. package/lib/engine-components/Camera.d.ts +2 -2
  36. package/lib/engine-components/Camera.js +4 -6
  37. package/lib/engine-components/Camera.js.map +1 -1
  38. package/lib/engine-components/CameraUtils.js +32 -13
  39. package/lib/engine-components/CameraUtils.js.map +1 -1
  40. package/lib/engine-components/Component.d.ts +4 -1
  41. package/lib/engine-components/Component.js +4 -1
  42. package/lib/engine-components/Component.js.map +1 -1
  43. package/lib/engine-components/ContactShadows.js +8 -2
  44. package/lib/engine-components/ContactShadows.js.map +1 -1
  45. package/lib/engine-components/OrbitControls.d.ts +3 -0
  46. package/lib/engine-components/OrbitControls.js +34 -10
  47. package/lib/engine-components/OrbitControls.js.map +1 -1
  48. package/lib/engine-components/SyncedTransform.d.ts +3 -2
  49. package/lib/engine-components/SyncedTransform.js +28 -18
  50. package/lib/engine-components/SyncedTransform.js.map +1 -1
  51. package/lib/engine-components/utils/EnvironmentScene.d.ts +5 -0
  52. package/lib/engine-components/utils/EnvironmentScene.js +206 -0
  53. package/lib/engine-components/utils/EnvironmentScene.js.map +1 -0
  54. package/package.json +1 -1
  55. package/src/engine/engine_input.ts +13 -0
  56. package/src/engine/engine_serialization_decorator.ts +4 -0
  57. package/src/engine/engine_three_utils.ts +52 -1
  58. package/src/engine/engine_utils_format.ts +13 -5
  59. package/src/engine/webcomponents/needle menu/needle-menu.ts +3 -3
  60. package/src/engine-components/Camera.ts +9 -6
  61. package/src/engine-components/CameraUtils.ts +35 -13
  62. package/src/engine-components/Component.ts +5 -2
  63. package/src/engine-components/ContactShadows.ts +8 -2
  64. package/src/engine-components/OrbitControls.ts +35 -10
  65. package/src/engine-components/SyncedTransform.ts +37 -25
  66. package/src/engine-components/utils/EnvironmentScene.ts +246 -0
  67. package/src/engine/webcomponents/needle menu/dist/needle-menu.js +0 -662
@@ -59,13 +59,21 @@ export async function tryDetermineFileTypeFromURL(url: string, useExtension: boo
59
59
  }
60
60
  }
61
61
 
62
+
62
63
  // If the URL doesnt contain a filetype we need to check the header
63
64
  // This is the case for example if we load a file from a data url
64
- const newUrl = new URL(url);
65
- // Adding a URL parameter to avoid the brower to bust the full cache
66
- // If we don't do this the file that might already be disc cached will be deleted from the cache
67
- newUrl.searchParams.append("range", "true");
68
- const header = await fetch(newUrl, {
65
+
66
+ if(url.startsWith("blob:")) {
67
+ // We can't modify the blob URL
68
+ }
69
+ else {
70
+ const newUrl = new URL(url);
71
+ // Adding a URL parameter to avoid the brower to bust the full cache
72
+ // If we don't do this the file that might already be disc cached will be deleted from the cache
73
+ newUrl.searchParams.append("range", "true");
74
+ url = newUrl.toString();
75
+ }
76
+ const header = await fetch(url, {
69
77
  method: "GET",
70
78
  headers: {
71
79
  "range": "bytes=0-32"
@@ -697,7 +697,7 @@ export class NeedleMenuElement extends HTMLElement {
697
697
  if (res == true && hasCommercialLicense() && !debugNonCommercial) {
698
698
  let visible = this._userRequestedLogoVisible;
699
699
  if (visible === undefined) visible = false;
700
- this.#onSetLogoVisible(visible);
700
+ this.___onSetLogoVisible(visible);
701
701
  }
702
702
  }));
703
703
  } catch (e) {
@@ -827,10 +827,10 @@ export class NeedleMenuElement extends HTMLElement {
827
827
  if (!localNetwork) return;
828
828
  }
829
829
  }
830
- this.#onSetLogoVisible(visible);
830
+ this.___onSetLogoVisible(visible);
831
831
  }
832
832
 
833
- #onSetLogoVisible(visible: boolean) {
833
+ private ___onSetLogoVisible(visible: boolean) {
834
834
  this.logoContainer.style.display = "";
835
835
  this.logoContainer.style.opacity = "1";
836
836
  this.logoContainer.style.visibility = "visible";
@@ -1,4 +1,4 @@
1
- import { EquirectangularReflectionMapping, Euler, Frustum, Matrix4, OrthographicCamera, PerspectiveCamera, Ray, Vector3 } from "three";
1
+ import { Color, EquirectangularReflectionMapping, Euler, Frustum, Matrix4, OrthographicCamera, PerspectiveCamera, Ray, Vector3 } from "three";
2
2
  import { Texture } from "three";
3
3
 
4
4
  import { showBalloonMessage } from "../engine/debug/index.js";
@@ -289,15 +289,18 @@ export class Camera extends Behaviour implements ICamera {
289
289
  public get backgroundColor(): RGBAColor | null {
290
290
  return this._backgroundColor ?? null;
291
291
  }
292
- public set backgroundColor(val: RGBAColor | null) {
292
+ public set backgroundColor(val: RGBAColor | Color | null) {
293
293
  if (!val) return;
294
294
  if (!this._backgroundColor) {
295
- if (!val.clone) return;
296
- this._backgroundColor = val.clone();
295
+ this._backgroundColor = new RGBAColor(1, 1, 1, 1);
297
296
  }
298
- else this._backgroundColor.copy(val);
297
+
298
+ this._backgroundColor.copy(val);
299
+
299
300
  // set background color to solid if provided color doesnt have any alpha channel
300
- if (val.alpha === undefined) this._backgroundColor.alpha = 1;
301
+ if ((!("alpha" in val) || val.alpha === undefined)) {
302
+ this._backgroundColor.alpha = 1;
303
+ }
301
304
  this.applyClearFlagsIfIsActiveCamera();
302
305
  }
303
306
 
@@ -1,15 +1,17 @@
1
- import { PerspectiveCamera } from "three";
1
+ import { Color, PerspectiveCamera, PMREMGenerator } from "three";
2
2
 
3
3
  import { getCameraController } from "../engine/engine_camera.js";
4
4
  import { addNewComponent, getOrAddComponent } from "../engine/engine_components.js";
5
5
  import { Context } from "../engine/engine_context.js";
6
6
  import { ContextEvent, ContextRegistry } from "../engine/engine_context_registry.js";
7
7
  import { NeedleEngineHTMLElement } from "../engine/engine_element.js";
8
+ import { createFlatTexture, createTrilightTexture } from "../engine/engine_shaders.js";
8
9
  import type { ICamera, IContext } from "../engine/engine_types.js";
9
10
  import { getParam } from "../engine/engine_utils.js";
10
11
  import { RGBAColor } from "../engine/js-extensions/index.js";
11
12
  import { Camera, ClearFlags } from "./Camera.js";
12
13
  import { OrbitControls } from "./OrbitControls.js";
14
+ import EnvironmentScene from "./utils/EnvironmentScene.js";
13
15
 
14
16
  const debug = getParam("debugmissingcamera");
15
17
 
@@ -30,27 +32,47 @@ ContextRegistry.registerCallback(ContextEvent.MissingCamera, (evt) => {
30
32
 
31
33
  const camInstance = new Camera();
32
34
  camInstance.sourceId = evt.files?.[0]?.src ?? "unknown"
35
+ camInstance.fieldOfView = 35;
33
36
 
37
+ const transparentAttribute = evt.context.domElement.getAttribute("transparent");
38
+ if (transparentAttribute != undefined) {
39
+ camInstance.clearFlags = ClearFlags.Uninitialized;
40
+ }
34
41
  // Set the clearFlags to a skybox if we have one OR if the user set a skybox image attribute
35
- if (evt.context.domElement.getAttribute("skybox-image")?.length || evt.context.domElement.getAttribute("background-image")?.length || (evt.context as Context).lightmaps.tryGetSkybox(camInstance.sourceId)) {
42
+ else if (evt.context.domElement.getAttribute("skybox-image")?.length || evt.context.domElement.getAttribute("background-image")?.length || (evt.context as Context).lightmaps.tryGetSkybox(camInstance.sourceId)) {
36
43
  camInstance.clearFlags = ClearFlags.Skybox;
44
+ // TODO: can we store the backgroundBlurriness in the gltf file somewhere except inside the camera?
45
+ // e.g. when we export a scene from blender without a camera in the scene
46
+ camInstance.backgroundBlurriness = .2; // same as in blender 0.5
37
47
  }
38
- else
39
- // TODO provide a nice default skybox
48
+ else {
40
49
  camInstance.clearFlags = ClearFlags.SolidColor;
41
- camInstance.backgroundColor = new RGBAColor(0.5, 0.5, 0.5, 1);
42
- camInstance.fieldOfView = 35;
43
50
 
44
- const transparentAttribute = evt.context.domElement.getAttribute("transparent");
45
- if (transparentAttribute != undefined) {
46
- camInstance.clearFlags = ClearFlags.Uninitialized;
51
+ let backgroundColor = "#efefef";
52
+ if(typeof window !== undefined && (window.matchMedia('(prefers-color-scheme: dark)').matches)) {
53
+ backgroundColor = "#1f1f1f";
54
+ }
55
+ scene.background = new Color(backgroundColor); // dont set it on the camera because this might be controlled from "background-color" attribute which is set on the scene directly. If the camera has a background color, it will override the scene's background color
56
+
57
+ // Generate a default environment map if none is set
58
+ if (!scene.environment) {
59
+ // const backgroundColorAttribute = evt.context.domElement.getAttribute("background-color") ?? "#fff";
60
+ // const backgroundColor = new Color(backgroundColorAttribute);
61
+ const pmremGenerator = new PMREMGenerator(evt.context.renderer);
62
+ const env = new EnvironmentScene("neutral");
63
+ // const background = scene.background;
64
+ // const col0 = new Color(.1, .1, .1);
65
+ // const col1 = new Color(.3, .3, .3);
66
+ // const col2 = new Color(1, 1, 1);
67
+ // const envmap = createTrilightTexture(col0, col1, col2, 32, 32);
68
+ // scene.background = envmap;
69
+ scene.environment = pmremGenerator.fromScene(env, .025).texture;
70
+ // scene.background = background;
71
+ }
47
72
  }
48
73
 
49
- // TODO: can we store the backgroundBlurriness in the gltf file somewhere except inside the camera?
50
- // e.g. when we export a scene from blender without a camera in the scene
51
- camInstance.backgroundBlurriness = .2; // same as in blender 0.5
52
- const cam = addNewComponent(cameraObject, camInstance, true) as ICamera;
53
74
 
75
+ const cam = addNewComponent(cameraObject, camInstance, true) as ICamera;
54
76
  cameraObject.position.x = 0;
55
77
  cameraObject.position.y = 1;
56
78
  cameraObject.position.z = 2;
@@ -1128,7 +1128,8 @@ export abstract class Component implements IComponent, EventTarget,
1128
1128
  }
1129
1129
 
1130
1130
  /**
1131
- * Gets the position of this component's GameObject in world space
1131
+ * Gets the position of this component's GameObject in world space.
1132
+ * Note: This is equivalent to calling `this.gameObject.worldPosition`
1132
1133
  */
1133
1134
  get worldPosition(): Vector3 {
1134
1135
  return threeutils.getWorldPosition(this.gameObject);
@@ -1154,6 +1155,7 @@ export abstract class Component implements IComponent, EventTarget,
1154
1155
 
1155
1156
  /**
1156
1157
  * Gets the rotation of this component's GameObject in world space as a quaternion
1158
+ * Note: This is equivalent to calling `this.gameObject.worldQuaternion`
1157
1159
  */
1158
1160
  get worldQuaternion(): Quaternion {
1159
1161
  return threeutils.getWorldQuaternion(this.gameObject);
@@ -1194,7 +1196,8 @@ export abstract class Component implements IComponent, EventTarget,
1194
1196
  }
1195
1197
 
1196
1198
  /**
1197
- * Gets the rotation of this component's GameObject in world space as Euler angles (in degrees)
1199
+ * Gets the rotation of this component's GameObject in world space as Euler angles (in degrees)
1200
+ * Note: This is equivalent to calling `this.gameObject.worldRotation`
1198
1201
  */
1199
1202
  get worldRotation(): Vector3 {
1200
1203
  return this.gameObject.worldRotation;
@@ -17,9 +17,15 @@ import { Behaviour, GameObject } from "./Component.js";
17
17
  const debug = getParam("debugcontactshadows");
18
18
 
19
19
  onStart(ctx => {
20
- const val = ctx.domElement.getAttribute("contactshadows");
20
+ const val = ctx.domElement.getAttribute("contactshadows") || ctx.domElement.getAttribute("contact-shadows");
21
21
  if (val != undefined && val != "0" && val != "false") {
22
- ContactShadows.auto(ctx);
22
+ console.debug("Auto-creating ContactShadows because of `contactshadows` attribute");
23
+ const shadows = ContactShadows.auto(ctx);
24
+ const intensity = parseFloat(val);
25
+ if (!isNaN(intensity)) {
26
+ shadows.opacity = intensity;
27
+ shadows.darkness = intensity;
28
+ }
23
29
  }
24
30
  })
25
31
 
@@ -316,6 +316,7 @@ export class OrbitControls extends Behaviour implements ICameraController {
316
316
  if (DeviceUtilities.isMobileDevice()) this.doubleClickToFocus = true;
317
317
  }
318
318
  this._controls.addEventListener("start", this.onControlsChangeStarted);
319
+ this._controls.addEventListener("end", this.onControlsChangeEnded);
319
320
 
320
321
  if (!this._startedListeningToKeyEvents && this.enableKeys) {
321
322
  this._startedListeningToKeyEvents = true;
@@ -346,6 +347,7 @@ export class OrbitControls extends Behaviour implements ICameraController {
346
347
  this._controls.enabled = false;
347
348
  this._controls.autoRotate = false;
348
349
  this._controls.removeEventListener("start", this.onControlsChangeStarted);
350
+ this._controls.removeEventListener("end", this.onControlsChangeEnded);
349
351
  try {
350
352
  this._controls.stopListenToKeyEvents();
351
353
  } catch { /** this fails if we never listened to key events... */ }
@@ -366,7 +368,7 @@ export class OrbitControls extends Behaviour implements ICameraController {
366
368
  this._activePointerEvents.push(_evt);
367
369
  }
368
370
  private _onPointerDownLate = (evt: NEPointerEvent) => {
369
- if(evt.used && this._controls) {
371
+ if (evt.used && this._controls) {
370
372
  // Disabling orbit controls here because otherwise we get a slight movement when e.g. using DragControls
371
373
  this._controls.enabled = false;
372
374
  }
@@ -413,22 +415,45 @@ export class OrbitControls extends Behaviour implements ICameraController {
413
415
  this.setTargetFromRaycast();
414
416
  }
415
417
  // Automatically update the camera focus
416
- else if (!evt.used && this.autoTarget) {
417
- const ray = new Ray(this._cameraObject?.worldPosition, this._cameraObject?.worldForward.multiplyScalar(-1));
418
- const hits = this.context.physics.raycastFromRay(ray);
419
- const hit = hits.length > 0 ? hits[0] : undefined;
420
- if(hit && hit.distance > this.minZoom && hit.distance < this.maxZoom) {
421
- if(debug) Gizmos.DrawWireSphere(hit.point, 0.1, 0xff0000, 2);
422
- this._controls?.target.copy(hits[0].point);
423
- }
424
- }
418
+ // else if (!evt.used && this.autoTarget) {
419
+ // this.updateTargetNow();
420
+ // }
425
421
  };
426
422
 
423
+ private updateTargetNow() {
424
+ const ray = new Ray(this._cameraObject?.worldPosition, this._cameraObject?.worldForward.multiplyScalar(-1));
425
+ const hits = this.context.physics.raycastFromRay(ray);
426
+ const hit = hits.length > 0 ? hits[0] : undefined;
427
+ if (hit && hit.distance > this.minZoom && hit.distance < this.maxZoom) {
428
+ if (debug) Gizmos.DrawWireSphere(hit.point, 0.1, 0xff0000, 2);
429
+ this._controls?.target.copy(hits[0].point);
430
+ }
431
+ }
432
+
433
+ private _orbitStartAngle: number = 0;
427
434
  private onControlsChangeStarted = () => {
435
+ if (this._controls) {
436
+ this._orbitStartAngle = this._controls.getAzimuthalAngle() + this._controls.getPolarAngle();
437
+ }
428
438
  if (this._syncedTransform) {
429
439
  this._syncedTransform.requestOwnership();
430
440
  }
431
441
  }
442
+ private onControlsChangeEnded = () => {
443
+
444
+ if (this._controls) {
445
+ if (this.autoTarget) {
446
+ const newAngle = this._controls.getAzimuthalAngle() + this._controls.getPolarAngle();
447
+ const delta = newAngle - this._orbitStartAngle;
448
+ if (Math.abs(delta) < .01) {
449
+ if (debug) console.debug("OrbitControls: No movement detected, updating target now");
450
+ this.updateTargetNow();
451
+ }
452
+ else if(debug) console.debug("OrbitControls: Movement detected", delta);
453
+ }
454
+ }
455
+
456
+ }
432
457
 
433
458
  private _shouldDisable: boolean = false;
434
459
  private afterHandleInput(evt: CustomEvent<AfterHandleInputEvent>) {
@@ -51,7 +51,7 @@ onUpdate((ctx) => {
51
51
  const threshold = isRunningOnGlitch ? 10 : 40;
52
52
  FAST_INTERVAL = Math.floor(FAST_ACTIVE_SYNCTRANSFORMS / threshold);
53
53
  FAST_ACTIVE_SYNCTRANSFORMS = 0;
54
- if(debug && FAST_INTERVAL > 0) console.log("Sync Transform Fast Interval", FAST_INTERVAL);
54
+ if (debug && FAST_INTERVAL > 0) console.log("Sync Transform Fast Interval", FAST_INTERVAL);
55
55
  })
56
56
 
57
57
  /**
@@ -62,20 +62,20 @@ onUpdate((ctx) => {
62
62
  */
63
63
  export class SyncedTransform extends Behaviour {
64
64
 
65
-
65
+
66
66
  // public autoOwnership: boolean = true;
67
67
  /** When true, overrides physics behavior when this object is owned by the local user */
68
68
  public overridePhysics: boolean = true
69
-
69
+
70
70
  /** Whether to smoothly interpolate position changes when receiving updates */
71
71
  public interpolatePosition: boolean = true;
72
-
72
+
73
73
  /** Whether to smoothly interpolate rotation changes when receiving updates */
74
74
  public interpolateRotation: boolean = true;
75
-
75
+
76
76
  /** When true, sends updates at a higher frequency, useful for fast-moving objects */
77
77
  public fastMode: boolean = false;
78
-
78
+
79
79
  /** When true, notifies other clients when this object is destroyed */
80
80
  public syncDestroy: boolean = false;
81
81
 
@@ -135,8 +135,9 @@ export class SyncedTransform extends Behaviour {
135
135
  this._targetRotation = new Quaternion();
136
136
 
137
137
  // sync instantiate issue was because they shared the same last pos vector!
138
- this.lastWorldPos = new Vector3();
139
- this.lastWorldRotation = new Quaternion();
138
+ this.lastPosition = new Vector3();
139
+ this.lastRotation = new Quaternion();
140
+ this.lastScale = new Vector3();
140
141
 
141
142
  this.rb = GameObject.getComponentInChildren(this.gameObject, Rigidbody);
142
143
  if (this.rb) {
@@ -207,6 +208,11 @@ export class SyncedTransform extends Behaviour {
207
208
  if (!this.interpolateRotation || !this._receivedDataBefore)
208
209
  setWorldEuler(this.gameObject, this.tempEuler);
209
210
  }
211
+
212
+ const scale = transform.scale();
213
+ if (scale) {
214
+ this.gameObject.scale.set(scale.x(), scale.y(), scale.z());
215
+ }
210
216
  }
211
217
  this._receivedDataBefore = true;
212
218
 
@@ -221,8 +227,9 @@ export class SyncedTransform extends Behaviour {
221
227
  * Initializes tracking of position and rotation when component is enabled
222
228
  */
223
229
  onEnable(): void {
224
- this.lastWorldPos.copy(this.worldPosition);
225
- this.lastWorldRotation.copy(this.worldQuaternion);
230
+ this.lastPosition.copy(this.worldPosition);
231
+ this.lastRotation.copy(this.worldQuaternion);
232
+ this.lastScale.copy(this.gameObject.scale);
226
233
  this._needsUpdate = true;
227
234
  // console.log("ENABLE", this.guid, this.gameObject.guid, this.lastWorldPos);
228
235
  if (this._model) {
@@ -241,8 +248,9 @@ export class SyncedTransform extends Behaviour {
241
248
 
242
249
 
243
250
  private receivedUpdate = false;
244
- private lastWorldPos!: Vector3;
245
- private lastWorldRotation!: Quaternion;
251
+ private lastPosition!: Vector3;
252
+ private lastRotation!: Quaternion;
253
+ private lastScale!: Vector3;
246
254
 
247
255
  /**
248
256
  * @internal
@@ -264,24 +272,27 @@ export class SyncedTransform extends Behaviour {
264
272
  this._model.requestOwnership();
265
273
  }
266
274
 
267
- const wp = this.worldPosition;
268
- const wr = this.worldQuaternion;
275
+ const pos = this.worldPosition;
276
+ const rot = this.worldQuaternion;
277
+ const scale = this.gameObject.scale;
269
278
  if (this._model.isOwned && !this.receivedUpdate) {
270
- const worlddiff = wp.distanceTo(this.lastWorldPos);
271
- const worldRot = wr.angleTo(this.lastWorldRotation);
272
279
  const threshold = this._model.hasOwnership || this.fastMode ? .0001 : .001;
273
- if (worlddiff > threshold || worldRot > threshold) {
280
+ if (pos.distanceTo(this.lastPosition) > threshold ||
281
+ rot.angleTo(this.lastRotation) > threshold ||
282
+ scale.distanceTo(this.lastScale) > threshold) {
274
283
  // console.log(worlddiff, worldRot);
275
284
  if (!this._model.hasOwnership) {
276
285
 
277
286
  if (debug)
278
- console.log(this.guid, "reset because not owned but", this.gameObject.name, this.lastWorldPos);
287
+ console.log(this.guid, "reset because not owned but", this.gameObject.name, this.lastPosition);
279
288
 
280
- this.worldPosition = this.lastWorldPos;
281
- wp.copy(this.lastWorldPos);
289
+ this.worldPosition = this.lastPosition;
290
+ pos.copy(this.lastPosition);
282
291
 
283
- this.worldQuaternion = this.lastWorldRotation;
284
- wr.copy(this.lastWorldRotation);
292
+ this.worldQuaternion = this.lastRotation;
293
+ rot.copy(this.lastRotation);
294
+
295
+ this.gameObject.scale.copy(this.lastScale);
285
296
 
286
297
  InstancingUtil.markDirty(this.gameObject, true);
287
298
  this._needsUpdate = false;
@@ -323,12 +334,13 @@ export class SyncedTransform extends Behaviour {
323
334
 
324
335
 
325
336
  this.receivedUpdate = false;
326
- this.lastWorldPos.copy(wp);
327
- this.lastWorldRotation.copy(wr);
337
+ this.lastPosition.copy(pos);
338
+ this.lastRotation.copy(rot);
339
+ this.lastScale.copy(scale);
328
340
 
329
341
 
330
342
  if (!this._model) return;
331
-
343
+
332
344
  if (!this._model || this._model.hasOwnership === undefined || !this._model.hasOwnership) {
333
345
  // if we're not the owner of this synced transform then don't send any data
334
346
  return;
@@ -0,0 +1,246 @@
1
+ // https://github.com/google/model-viewer/blob/c61dd1459b59cd74f539eb31658f943fd13c702e/packages/model-viewer/src/three-components/EnvironmentScene.ts#L198
2
+
3
+ /* @license
4
+ * Copyright 2021 Google LLC. All Rights Reserved.
5
+ * Licensed under the Apache License, Version 2.0 (the 'License');
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an 'AS IS' BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ import {BackSide, BoxGeometry, Mesh, MeshBasicMaterial, MeshStandardMaterial, PointLight, Scene} from 'three';
19
+
20
+ interface Box {
21
+ position: [number, number, number];
22
+ rotation: number;
23
+ scale: [number, number, number];
24
+ }
25
+
26
+ interface Light {
27
+ intensity: number;
28
+ position: [number, number, number];
29
+ scale: [number, number, number];
30
+ }
31
+
32
+ interface Env {
33
+ topLight: {
34
+ intensity: number,
35
+ position: [number, number, number],
36
+ };
37
+ room: {
38
+ position: [number, number, number],
39
+ scale: [number, number, number],
40
+ };
41
+ boxes: Box[];
42
+ lights: Light[];
43
+ }
44
+
45
+ const legacy = {
46
+ topLight: {
47
+ intensity: 500,
48
+ position: [0.418, 16.199, 0.300],
49
+ },
50
+ room: {
51
+ position: [-0.757, 13.219, 0.717],
52
+ scale: [31.713, 28.305, 28.591],
53
+ },
54
+ boxes: [
55
+ {
56
+ position: [-10.906, 2.009, 1.846],
57
+ rotation: -0.195,
58
+ scale: [2.328, 7.905, 4.651],
59
+ },
60
+ {
61
+ position: [-5.607, -0.754, -0.758],
62
+ rotation: 0.994,
63
+ scale: [1.970, 1.534, 3.955],
64
+ },
65
+ {
66
+ position: [6.167, 0.857, 7.803],
67
+ rotation: 0.561,
68
+ scale: [3.927, 6.285, 3.687],
69
+ },
70
+ {
71
+ position: [-2.017, 0.018, 6.124],
72
+ rotation: 0.333,
73
+ scale: [2.002, 4.566, 2.064],
74
+ },
75
+ {
76
+ position: [2.291, -0.756, -2.621],
77
+ rotation: -0.286,
78
+ scale: [1.546, 1.552, 1.496],
79
+ },
80
+ {
81
+ position: [-2.193, -0.369, -5.547],
82
+ rotation: 0.516,
83
+ scale: [3.875, 3.487, 2.986],
84
+ },
85
+ ],
86
+ lights: [
87
+ {
88
+ intensity: 50,
89
+ position: [-16.116, 14.37, 8.208],
90
+ scale: [0.1, 2.428, 2.739],
91
+ },
92
+ {
93
+ intensity: 50,
94
+ position: [-16.109, 18.021, -8.207],
95
+ scale: [0.1, 2.425, 2.751],
96
+ },
97
+ {
98
+ intensity: 17,
99
+ position: [14.904, 12.198, -1.832],
100
+ scale: [0.15, 4.265, 6.331],
101
+ },
102
+ {
103
+ intensity: 43,
104
+ position: [-0.462, 8.89, 14.520],
105
+ scale: [4.38, 5.441, 0.088],
106
+ },
107
+ {
108
+ intensity: 20,
109
+ position: [3.235, 11.486, -12.541],
110
+ scale: [2.5, 2.0, 0.1],
111
+ },
112
+ {
113
+ intensity: 100,
114
+ position: [0.0, 20.0, 0.0],
115
+ scale: [1.0, 0.1, 1.0],
116
+ },
117
+ ]
118
+ } as Env;
119
+
120
+ const neutral = {
121
+ topLight: {
122
+ intensity: 400,
123
+ position: [0.5, 14.0, 0.5],
124
+ },
125
+ room: {
126
+ position: [0.0, 13.2, 0.0],
127
+ scale: [31.5, 28.5, 31.5],
128
+ },
129
+ boxes: [
130
+ {
131
+ position: [-10.906, -1.0, 1.846],
132
+ rotation: -0.195,
133
+ scale: [2.328, 7.905, 4.651],
134
+ },
135
+ {
136
+ position: [-5.607, -0.754, -0.758],
137
+ rotation: 0.994,
138
+ scale: [1.970, 1.534, 3.955],
139
+ },
140
+ {
141
+ position: [6.167, -0.16, 7.803],
142
+ rotation: 0.561,
143
+ scale: [3.927, 6.285, 3.687],
144
+ },
145
+ {
146
+ position: [-2.017, 0.018, 6.124],
147
+ rotation: 0.333,
148
+ scale: [2.002, 4.566, 2.064],
149
+ },
150
+ {
151
+ position: [2.291, -0.756, -2.621],
152
+ rotation: -0.286,
153
+ scale: [1.546, 1.552, 1.496],
154
+ },
155
+ {
156
+ position: [-2.193, -0.369, -5.547],
157
+ rotation: 0.516,
158
+ scale: [3.875, 3.487, 2.986],
159
+ },
160
+ ],
161
+ lights: [
162
+ {
163
+ intensity: 80,
164
+ position: [-14.0, 10.0, 8.0],
165
+ scale: [0.1, 2.5, 2.5],
166
+ },
167
+ {
168
+ intensity: 80,
169
+ position: [-14.0, 14.0, -4.0],
170
+ scale: [0.1, 2.5, 2.5],
171
+ },
172
+ {
173
+ intensity: 23,
174
+ position: [14.0, 12.0, 0.0],
175
+ scale: [0.1, 5.0, 5.0],
176
+ },
177
+ {
178
+ intensity: 16,
179
+ position: [0.0, 9.0, 14.0],
180
+ scale: [5.0, 5.0, 0.1],
181
+ },
182
+ {
183
+ intensity: 80,
184
+ position: [7.0, 8.0, -14.0],
185
+ scale: [2.5, 2.5, 0.1],
186
+ },
187
+ {
188
+ intensity: 80,
189
+ position: [-7.0, 16.0, -14.0],
190
+ scale: [2.5, 2.5, 0.1],
191
+ },
192
+ {
193
+ intensity: 1,
194
+ position: [0.0, 20.0, 0.0],
195
+ scale: [0.1, 0.1, 0.1],
196
+ },
197
+ ]
198
+ } as Env;
199
+
200
+ export default class EnvironmentScene extends Scene {
201
+ constructor(name: 'legacy'|'neutral') {
202
+ super();
203
+
204
+ this.position.y = -3.5;
205
+
206
+ const geometry = new BoxGeometry();
207
+ geometry.deleteAttribute('uv');
208
+
209
+ const roomMaterial =
210
+ new MeshStandardMaterial({metalness: 0, side: BackSide});
211
+ const boxMaterial = new MeshStandardMaterial({metalness: 0});
212
+
213
+ const data = name == 'legacy' ? legacy : neutral;
214
+
215
+ const mainLight = new PointLight(0xffffff, data.topLight.intensity, 28, 2);
216
+ mainLight.position.set(...data.topLight.position);
217
+ this.add(mainLight);
218
+
219
+ const room = new Mesh(geometry, roomMaterial);
220
+ room.position.set(...data.room.position);
221
+ room.scale.set(...data.room.scale);
222
+ this.add(room);
223
+
224
+ for (const box of data.boxes) {
225
+ const box1 = new Mesh(geometry, boxMaterial);
226
+ box1.position.set(...box.position);
227
+ box1.rotation.set(0, box.rotation, 0);
228
+ box1.scale.set(...box.scale);
229
+ this.add(box1);
230
+ }
231
+
232
+ for (const light of data.lights) {
233
+ const light1 =
234
+ new Mesh(geometry, this.createAreaLightMaterial(light.intensity));
235
+ light1.position.set(...light.position);
236
+ light1.scale.set(...light.scale);
237
+ this.add(light1);
238
+ }
239
+ }
240
+
241
+ createAreaLightMaterial(intensity: number): MeshBasicMaterial {
242
+ const material = new MeshBasicMaterial();
243
+ material.color.setScalar(intensity);
244
+ return material;
245
+ }
246
+ }