@needle-tools/engine 3.1.0-alpha.2 → 3.2.0-alpha

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 (88) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/needle-engine.js +9024 -8814
  3. package/dist/needle-engine.min.js +264 -264
  4. package/dist/needle-engine.umd.cjs +266 -266
  5. package/lib/engine/codegen/register_types.js +2 -0
  6. package/lib/engine/codegen/register_types.js.map +1 -1
  7. package/lib/engine/debug/debug_overlay.js +2 -0
  8. package/lib/engine/debug/debug_overlay.js.map +1 -1
  9. package/lib/engine/engine_context_registry.d.ts +2 -0
  10. package/lib/engine/engine_context_registry.js +3 -0
  11. package/lib/engine/engine_context_registry.js.map +1 -1
  12. package/lib/engine/engine_gltf_builtin_components.js +2 -0
  13. package/lib/engine/engine_gltf_builtin_components.js.map +1 -1
  14. package/lib/engine/engine_license.js +1 -1
  15. package/lib/engine/engine_license.js.map +1 -1
  16. package/lib/engine/engine_lightdata.js +1 -1
  17. package/lib/engine/engine_lightdata.js.map +1 -1
  18. package/lib/engine/engine_networking_auto.d.ts +3 -3
  19. package/lib/engine/engine_networking_auto.js +7 -3
  20. package/lib/engine/engine_networking_auto.js.map +1 -1
  21. package/lib/engine/engine_rendererdata.d.ts +1 -0
  22. package/lib/engine/engine_rendererdata.js +39 -15
  23. package/lib/engine/engine_rendererdata.js.map +1 -1
  24. package/lib/engine/engine_serialization_builtin_serializer.js +10 -1
  25. package/lib/engine/engine_serialization_builtin_serializer.js.map +1 -1
  26. package/lib/engine/extensions/NEEDLE_lighting_settings.js +14 -11
  27. package/lib/engine/extensions/NEEDLE_lighting_settings.js.map +1 -1
  28. package/lib/engine/extensions/NEEDLE_lightmaps.js.map +1 -1
  29. package/lib/engine/extensions/NEEDLE_progressive.js +11 -4
  30. package/lib/engine/extensions/NEEDLE_progressive.js.map +1 -1
  31. package/lib/engine/extensions/extensions.js +4 -0
  32. package/lib/engine/extensions/extensions.js.map +1 -1
  33. package/lib/engine-components/AudioSource.d.ts +1 -0
  34. package/lib/engine-components/AudioSource.js +6 -3
  35. package/lib/engine-components/AudioSource.js.map +1 -1
  36. package/lib/engine-components/Camera.js +1 -1
  37. package/lib/engine-components/Camera.js.map +1 -1
  38. package/lib/engine-components/Component.js.map +1 -1
  39. package/lib/engine-components/Renderer.d.ts +5 -0
  40. package/lib/engine-components/Renderer.js +64 -17
  41. package/lib/engine-components/Renderer.js.map +1 -1
  42. package/lib/engine-components/RendererLightmap.d.ts +1 -2
  43. package/lib/engine-components/RendererLightmap.js +8 -10
  44. package/lib/engine-components/RendererLightmap.js.map +1 -1
  45. package/lib/engine-components/SceneSwitcher.d.ts +32 -0
  46. package/lib/engine-components/SceneSwitcher.js +228 -0
  47. package/lib/engine-components/SceneSwitcher.js.map +1 -0
  48. package/lib/engine-components/ScreenCapture.js +1 -0
  49. package/lib/engine-components/ScreenCapture.js.map +1 -1
  50. package/lib/engine-components/VideoPlayer.d.ts +2 -0
  51. package/lib/engine-components/VideoPlayer.js +7 -3
  52. package/lib/engine-components/VideoPlayer.js.map +1 -1
  53. package/lib/engine-components/XRFlag.js +3 -0
  54. package/lib/engine-components/XRFlag.js.map +1 -1
  55. package/lib/engine-components/codegen/components.d.ts +1 -0
  56. package/lib/engine-components/codegen/components.js +1 -0
  57. package/lib/engine-components/codegen/components.js.map +1 -1
  58. package/lib/engine-components/timeline/PlayableDirector.js +0 -2
  59. package/lib/engine-components/timeline/PlayableDirector.js.map +1 -1
  60. package/lib/engine-components/timeline/TimelineTracks.js +9 -3
  61. package/lib/engine-components/timeline/TimelineTracks.js.map +1 -1
  62. package/lib/tsconfig.tsbuildinfo +1 -1
  63. package/package.json +1 -1
  64. package/src/engine/codegen/register_types.js +2 -0
  65. package/src/engine/debug/debug_overlay.ts +1 -0
  66. package/src/engine/engine_context_registry.ts +3 -0
  67. package/src/engine/engine_gltf_builtin_components.ts +3 -0
  68. package/src/engine/engine_license.ts +1 -1
  69. package/src/engine/engine_lightdata.ts +1 -1
  70. package/src/engine/engine_networking_auto.ts +13 -6
  71. package/src/engine/engine_rendererdata.ts +35 -16
  72. package/src/engine/engine_serialization_builtin_serializer.ts +10 -1
  73. package/src/engine/extensions/NEEDLE_lighting_settings.ts +15 -13
  74. package/src/engine/extensions/NEEDLE_lightmaps.ts +3 -2
  75. package/src/engine/extensions/NEEDLE_progressive.ts +9 -3
  76. package/src/engine/extensions/extensions.ts +6 -0
  77. package/src/engine-components/AudioSource.ts +6 -3
  78. package/src/engine-components/Camera.ts +1 -1
  79. package/src/engine-components/Component.ts +0 -1
  80. package/src/engine-components/Renderer.ts +69 -20
  81. package/src/engine-components/RendererLightmap.ts +7 -11
  82. package/src/engine-components/SceneSwitcher.ts +224 -0
  83. package/src/engine-components/ScreenCapture.ts +1 -0
  84. package/src/engine-components/VideoPlayer.ts +16 -11
  85. package/src/engine-components/XRFlag.ts +3 -0
  86. package/src/engine-components/codegen/components.ts +1 -0
  87. package/src/engine-components/timeline/PlayableDirector.ts +0 -1
  88. package/src/engine-components/timeline/TimelineTracks.ts +9 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@needle-tools/engine",
3
- "version": "3.1.0-alpha.2",
3
+ "version": "3.2.0-alpha",
4
4
  "description": "Needle Engine is a web-based runtime for 3D apps. It runs on your machine for development with great integrations into editors like Unity or Blender - and can be deployed onto any device! It is flexible, extensible and networking and XR are built-in",
5
5
  "main": "dist/needle-engine.umd.cjs",
6
6
  "type": "module",
@@ -119,6 +119,7 @@ import { RGBAColor } from "../../engine-components/js-extensions/RGBAColor";
119
119
  import { Rigidbody } from "../../engine-components/RigidBody";
120
120
  import { RotationBySpeedModule } from "../../engine-components/ParticleSystemModules";
121
121
  import { RotationOverLifetimeModule } from "../../engine-components/ParticleSystemModules";
122
+ import { SceneSwitcher } from "../../engine-components/SceneSwitcher";
122
123
  import { ScreenCapture } from "../../engine-components/ScreenCapture";
123
124
  import { ScreenSpaceAmbientOcclusion } from "../../engine-components/postprocessing/Effects/ScreenspaceAmbientOcclusion";
124
125
  import { ShadowCatcher } from "../../engine-components/ShadowCatcher";
@@ -303,6 +304,7 @@ TypeStore.add("RGBAColor", RGBAColor);
303
304
  TypeStore.add("Rigidbody", Rigidbody);
304
305
  TypeStore.add("RotationBySpeedModule", RotationBySpeedModule);
305
306
  TypeStore.add("RotationOverLifetimeModule", RotationOverLifetimeModule);
307
+ TypeStore.add("SceneSwitcher", SceneSwitcher);
306
308
  TypeStore.add("ScreenCapture", ScreenCapture);
307
309
  TypeStore.add("ScreenSpaceAmbientOcclusion", ScreenSpaceAmbientOcclusion);
308
310
  TypeStore.add("ShadowCatcher", ShadowCatcher);
@@ -24,6 +24,7 @@ export function getErrorCount() {
24
24
  export function makeErrorsVisibleForDevelopment() {
25
25
  if (hide) return;
26
26
  const isLocal = isLocalNetwork();
27
+ if(debug) console.log("Is this a local network?", isLocal);
27
28
  if (isLocal) {
28
29
  if (debug)
29
30
  console.log(window.location.hostname);
@@ -1,6 +1,8 @@
1
1
  import { IContext } from "./engine_types";
2
2
 
3
3
  export enum ContextEvent {
4
+ /** called when the context is registered to the registry, the context is not fully initialized at this point */
5
+ ContextRegistered = "ContextRegistered",
4
6
  ContextCreated = "ContextCreated",
5
7
  ContextDestroyed = "ContextDestroyed",
6
8
  MissingCamera = "MissingCamera",
@@ -26,6 +28,7 @@ export class ContextRegistry {
26
28
 
27
29
  static register(ctx: IContext) {
28
30
  this.Registered.push(ctx);
31
+ this.dispatchCallback(ContextEvent.ContextRegistered, ctx);
29
32
  }
30
33
 
31
34
  static unregister(ctx: IContext) {
@@ -50,6 +50,9 @@ export async function createBuiltinComponents(context: Context, gltfId: SourceId
50
50
  idProvider = new InstantiateIdProvider(seed as number);
51
51
  }
52
52
 
53
+ const idEnd = gltfId.indexOf("?");
54
+ gltfId = idEnd === -1 ? gltfId : gltfId.substring(0, idEnd);
55
+
53
56
  const serializationContext = new SerializationContext(gltf.scene);
54
57
  serializationContext.gltfId = gltfId;
55
58
  serializationContext.context = context;
@@ -14,7 +14,7 @@ export function hasProLicense() {
14
14
  }
15
15
 
16
16
 
17
- ContextRegistry.registerCallback(ContextEvent.ContextCreated, evt => {
17
+ ContextRegistry.registerCallback(ContextEvent.ContextRegistered, evt => {
18
18
  showLicenseInfo(evt.context);
19
19
  });
20
20
 
@@ -33,7 +33,7 @@ export class LightDataRegistry implements ILightDataRegistry {
33
33
  }
34
34
 
35
35
  registerTexture(sourceId: SourceIdentifier, type: LightmapType, tex: Texture, index: number) {
36
- if (debugLightmap) console.log("Registering ", LightmapType[type], tex, sourceId);
36
+ if (debugLightmap) console.log("Registering ", LightmapType[type] + " \"" + sourceId + "\"", tex);
37
37
  if (!this._lightmaps.has(sourceId))
38
38
  this._lightmaps.set(sourceId, new Map());
39
39
  const map = this._lightmaps.get(sourceId);
@@ -200,21 +200,28 @@ export declare type SyncFieldOptions = {
200
200
  onPropertyChanged: Function,
201
201
  };
202
202
 
203
+ export declare type FieldChangedCallbackFn = (newValue: any, previousValue: any) => void | boolean;
204
+
203
205
  /**
204
206
  * Decorate a field to be automatically networked synced
205
- * @param onFieldChanged name of a callback function that will be called when the field is changed.
206
- * This function may return false to prevent notifyChanged from being called
207
+ * @param onFieldChanged name of a callback function that will be called when the field is changed.
208
+ * You can also pass in a function like so: syncField(myClass.prototype.myFunctionToBeCalled)
209
+ * This function may return false to prevent notifyChanged from being called
207
210
  * (for example a networked color is sent as a number and may be converted to a color in the receiver again)
208
- *
209
211
  * Parameters: (newValue, previousValue)
210
- * @returns
211
212
  */
212
- export const syncField = function (onFieldChanged?: string) {
213
+ export const syncField = function(onFieldChanged: string | FieldChangedCallbackFn) {
213
214
 
214
215
  return function (target: any, propertyKey: string) {
215
216
 
216
217
  let syncer: ComponentPropertiesSyncer | null = null;
217
- const fn = onFieldChanged ? target[onFieldChanged] : undefined;
218
+
219
+ let fn: Function | undefined = undefined;
220
+ if (typeof onFieldChanged === "string")
221
+ fn = target[onFieldChanged];
222
+ else if (typeof onFieldChanged === "function") {
223
+ fn = onFieldChanged;
224
+ }
218
225
 
219
226
  const t = target;
220
227
  const internalAwake = t.__internalAwake;
@@ -36,7 +36,8 @@ export class RendererData {
36
36
  this.context.pre_update_callbacks.push(this.preUpdate.bind(this))
37
37
  }
38
38
 
39
- private sceneLightSettings?: SceneLightSettings;
39
+ private _currentReflectionId?: SourceIdentifier;
40
+ private sceneLightSettings?: Map<SourceIdentifier, SceneLightSettings>;
40
41
 
41
42
  private preUpdate() {
42
43
  const time = this.context.time;
@@ -53,14 +54,26 @@ export class RendererData {
53
54
 
54
55
  get environmentIntensity(): number {
55
56
  if (!this.sceneLightSettings) return 1;
56
- return this.sceneLightSettings.ambientIntensity;// * Math.PI * .5;
57
+ if (!this._currentReflectionId) return 1;
58
+ const settings = this.sceneLightSettings.get(this._currentReflectionId);
59
+ if(settings)
60
+ return settings.ambientIntensity;// * Math.PI * .5;
61
+ return 1;
57
62
  }
58
63
 
59
64
  registerSceneLightSettings(sceneLightSettings: SceneLightSettings) {
60
- this.sceneLightSettings = sceneLightSettings;
65
+ const sourceId = sceneLightSettings.sourceId;
66
+ if(!sourceId){
67
+ console.error("Missing source id for scene light settings, can not register:", sceneLightSettings);
68
+ return;
69
+ }
70
+ if (debug) console.log("Register lighting settings", sceneLightSettings?.sourceId, sceneLightSettings);
71
+ if (!this.sceneLightSettings) this.sceneLightSettings = new Map();
72
+ this.sceneLightSettings.set(sourceId, sceneLightSettings);
61
73
  }
62
74
 
63
75
  registerReflection(sourceId: SourceIdentifier, reflectionTexture: Texture) {
76
+ if (debug) console.log("Register reflection", sourceId, reflectionTexture);
64
77
  const h = new LightData(this.context, reflectionTexture, 1);
65
78
  this._lighting[sourceId] = h;
66
79
  }
@@ -70,16 +83,21 @@ export class RendererData {
70
83
  }
71
84
 
72
85
  enableReflection(sourceId: SourceIdentifier) {
86
+ const previousId = this._currentReflectionId;
87
+ this._currentReflectionId = sourceId;
88
+ const settings = this.sceneLightSettings?.get(sourceId);
89
+
73
90
  if (debug) {
74
- console.log(this.sceneLightSettings ? AmbientMode[this.sceneLightSettings.ambientMode] : "Unknown ambient mode");
91
+ console.log("Enable reflection", sourceId, settings ? AmbientMode[settings.ambientMode] : "Unknown ambient mode");
75
92
  }
76
93
 
77
- switch (this.sceneLightSettings?.ambientMode) {
94
+ switch (settings?.ambientMode) {
78
95
  case AmbientMode.Skybox:
79
96
  case AmbientMode.Custom:
80
97
  // only set environment reflection when ambient mode is skybox or custom
81
98
  const existing = this.getReflection(sourceId);
82
99
  if (existing && existing.Source) {
100
+ if (debug) console.log("Setting environment reflection", existing.Source);
83
101
  const scene = this.context.scene;
84
102
  const tex = existing.Source;
85
103
  tex.encoding = sRGBEncoding;
@@ -87,29 +105,30 @@ export class RendererData {
87
105
  scene.environment = tex;
88
106
  return;
89
107
  }
108
+ else if (debug) console.warn("Could not find reflection for source", sourceId);
90
109
  break;
91
110
  }
92
111
 
93
- if (this.sceneLightSettings?.environmentReflectionSource === DefaultReflectionMode.Custom) {
94
- switch (this.sceneLightSettings?.ambientMode) {
112
+ if (settings?.environmentReflectionSource === DefaultReflectionMode.Custom) {
113
+ switch (settings?.ambientMode) {
95
114
  case AmbientMode.Trilight:
96
- if (this.sceneLightSettings.ambientTrilight) {
97
- const colors = this.sceneLightSettings.ambientTrilight;
115
+ if (settings.ambientTrilight) {
116
+ const colors = settings.ambientTrilight;
98
117
  const tex = createTrilightTexture(colors[0], colors[1], colors[2], 64, 64);
99
118
  tex.encoding = sRGBEncoding;
100
119
  tex.mapping = EquirectangularReflectionMapping;
101
120
  this.context.scene.environment = tex;
102
121
  }
103
- else console.error("Missing ambient trilight", this.sceneLightSettings.sourceId);
122
+ else console.error("Missing ambient trilight", settings.sourceId);
104
123
  return;
105
124
  case AmbientMode.Flat:
106
- if (this.sceneLightSettings.ambientLight) {
107
- const tex = createFlatTexture(this.sceneLightSettings.ambientLight, 64);
125
+ if (settings.ambientLight) {
126
+ const tex = createFlatTexture(settings.ambientLight, 64);
108
127
  tex.encoding = sRGBEncoding;
109
128
  tex.mapping = EquirectangularReflectionMapping;
110
129
  this.context.scene.environment = tex;
111
130
  }
112
- else console.error("Missing ambientlight", this.sceneLightSettings.sourceId);
131
+ else console.error("Missing ambientlight", settings.sourceId);
113
132
  return;
114
133
  default:
115
134
  return;
@@ -124,7 +143,7 @@ export class RendererData {
124
143
 
125
144
  async getSceneLightingData(sourceId: SourceIdentifier): Promise<SphericalHarmonicsData> {
126
145
  if (debug)
127
- console.log("GET SCENE LIGHT DATA");
146
+ console.log("GET SCENE LIGHT DATA", sourceId);
128
147
 
129
148
  // const existing = this.getReflection(sourceId);
130
149
  // const sh = existing?.getSphericalHarmonicsArray(this.sceneLightSettings?.ambientIntensity ?? 1);
@@ -140,7 +159,7 @@ export class RendererData {
140
159
  const ex = this.getReflection(sourceId);
141
160
  if (ex) {
142
161
  clearInterval(interval);
143
- res(ex.getSphericalHarmonicsArray(this.sceneLightSettings?.ambientIntensity ?? 1)!);
162
+ res(ex.getSphericalHarmonicsArray(this.environmentIntensity ?? 1)!);
144
163
  }
145
164
  }, 10);
146
165
  });
@@ -181,7 +200,7 @@ export class LightData {
181
200
  const reflection = this._source;
182
201
  let rt: THREE.WebGLCubeRenderTarget | null = null;
183
202
  if (reflection) {
184
- if (debug) console.log("GENERATING LIGHT PROBE", reflection);
203
+ if (debug) console.log("GENERATING LIGHT PROBE", reflection, this.Source);
185
204
  const size = Math.min(reflection.image.width, 512);
186
205
  const target = new WebGLCubeRenderTarget(size);
187
206
  rt = target.fromEquirectangularTexture(this._context.renderer, reflection);
@@ -240,7 +240,16 @@ class EventListSerializer extends TypeSerializer {
240
240
  }
241
241
  const hasMethod = call.method?.length > 0;
242
242
  if (target && hasMethod) {
243
- const printWarningMethodNotFound = () => console.warn(`Could not find method ${call.method} on object ${target.name}`, target, typeof target[call.method]);
243
+ const printWarningMethodNotFound = () => {
244
+ const uppercaseMethodName = call.method[0].toLowerCase() + call.method.slice(1);
245
+ if (typeof target[uppercaseMethodName] === "function") {
246
+ console.warn(`Could not find method ${call.method} on object ${target.name}. Please rename ${call.method} to ${uppercaseMethodName}?`, target, typeof target[call.method]);
247
+ return;
248
+ }
249
+ else {
250
+ console.warn(`Could not find method ${call.method} on object ${target.name}`, target, typeof target[call.method]);
251
+ }
252
+ }
244
253
  const method = target[call.method];
245
254
  if (typeof method !== "function") {
246
255
  let foundMethod = false;
@@ -1,4 +1,4 @@
1
- import { AmbientLight, Color, HemisphereLight } from "three";
1
+ import { AmbientLight, Color, HemisphereLight, Object3D } from "three";
2
2
  import { GLTF, GLTFLoaderPlugin, GLTFParser } from "three/examples/jsm/loaders/GLTFLoader";
3
3
  import { SourceIdentifier } from "../engine_types";
4
4
  import { Behaviour, GameObject } from "../../engine-components/Component";
@@ -42,9 +42,12 @@ export class NEEDLE_lighting_settings implements GLTFLoaderPlugin {
42
42
  const ext: LightingSettings = extensions[EXTENSION_NAME];
43
43
  if (ext) {
44
44
  if (debug)
45
- console.log(ext);
45
+ console.log("Apply \"" + this.name + "\", src: \"" + this.sourceId + "\"", ext);
46
46
  // add a component to the root of the scene
47
- const settings = GameObject.addNewComponent(_result.scene, SceneLightSettings, false);
47
+ const lightSettings = new Object3D();
48
+ lightSettings.name = "Needle LightSettings";
49
+ _result.scene.add(lightSettings);
50
+ const settings = GameObject.addNewComponent(lightSettings, SceneLightSettings, false);
48
51
  settings.sourceId = this.sourceId;
49
52
  settings.ambientIntensity = ext.ambientIntensity;
50
53
  settings.ambientLight = new Color().fromArray(ext.ambientLight);
@@ -98,12 +101,11 @@ export class SceneLightSettings extends Behaviour {
98
101
 
99
102
  onEnable() {
100
103
  const isActive = this.context.mainCameraComponent?.sourceId === this.sourceId;
101
- if (debug)
102
- console.log("Enable scene lighting", this.sourceId, isActive, this, this.context.mainCameraComponent?.sourceId);
104
+ if (debug) console.log("Enable scene lighting", this.sourceId, isActive, this, this.context.mainCameraComponent?.sourceId);
103
105
  if (!isActive) {
104
- if(debug) console.warn("This is no active?!", this.context.mainCameraComponent?.sourceId)
105
- // this.enabled = false;
106
- // return;
106
+ if(debug) console.warn("This environment light is not active??!", this.context.mainCameraComponent?.sourceId)
107
+ this.enabled = false;
108
+ return;
107
109
  }
108
110
  if (this.ambientMode == AmbientMode.Flat) {
109
111
  if (this.ambientLight && !this._ambientLightObj) {
@@ -130,19 +132,19 @@ export class SceneLightSettings extends Behaviour {
130
132
  if (!this._lightProbeObj) {
131
133
  if (this.sourceId) {
132
134
  this.context.rendererData.getSceneLightingData(this.sourceId).then(data => {
133
- if (debug)
134
- console.log(data);
135
135
  if (!data) return;
136
136
  this._lightProbeObj = data.lightProbe;
137
137
  if (this.enabled && !this.destroyed && this._lightProbeObj) {
138
- this.scene.add(this._lightProbeObj);
138
+ if (debug)
139
+ console.log("Add", this.sourceId, data);
140
+ this.gameObject.add(this._lightProbeObj);
139
141
  }
140
142
  });
141
143
  }
142
144
  }
143
145
  else {
144
146
  if (this.enabled && this.destroyed && this._lightProbeObj) {
145
- this.scene.add(this._lightProbeObj);
147
+ this.gameObject.add(this._lightProbeObj);
146
148
  }
147
149
  }
148
150
  }
@@ -154,7 +156,7 @@ export class SceneLightSettings extends Behaviour {
154
156
 
155
157
  onDisable() {
156
158
  if (debug)
157
- console.log("disable", this.sourceId, this);
159
+ console.log("Disable envlight:", this.sourceId, this);
158
160
  if (this._lightProbeObj) this._lightProbeObj.removeFromParent();
159
161
  if(this._ambientLightObj) this._ambientLightObj.removeFromParent();
160
162
  if (this.sourceId)
@@ -58,6 +58,7 @@ export class NEEDLE_lightmaps implements GLTFLoaderPlugin {
58
58
  console.log(ext);
59
59
 
60
60
  return new Promise(async (res, _rej) => {
61
+
61
62
  const dependencies: Array<Promise<any>> = [];
62
63
  for (const entry of arr) {
63
64
  if (entry.pointer) {
@@ -70,10 +71,10 @@ export class NEEDLE_lightmaps implements GLTFLoaderPlugin {
70
71
  // TODO this is most likely wrong for floating point textures
71
72
  if (entry.type !== LightmapType.Lightmap)
72
73
  tex.encoding = sRGBEncoding;
73
- else
74
+ else
74
75
  tex.encoding = LinearEncoding;
75
76
 
76
-
77
+
77
78
  // Dont flip skybox textures anymore - previously we exported them flipped when baking in Unity but now we allow to pass through export without re-baking exisitng skybox textures if they use default values. So we expect textures to be NOT flipped anymore
78
79
  // if (entry.type === LightmapType.Skybox) {
79
80
  // if (tex.type == FloatType || tex.type == HalfFloatType)
@@ -138,10 +138,16 @@ export class NEEDLE_progressive implements GLTFLoaderPlugin {
138
138
  }
139
139
  const resolveKey = uri + "_" + progressiveInfo.guid;
140
140
  if (this.resolved[resolveKey]) {
141
- if (debug) console.log("Texture has already been loaded: " + resolveKey, material.name, slot, current.name);
142
141
  let res = this.resolved[resolveKey];
143
- res = this.copySettings(current, res);
144
- return res;
142
+ // check if the texture has been disposed or not
143
+ if (res.image && res.image.data) {
144
+ if (debug) console.log("Texture has already been loaded: " + resolveKey, material.name, slot, current.name, res);
145
+ res = this.copySettings(current, res);
146
+ return res;
147
+ }
148
+ else if (res) {
149
+ if(debug) console.log("Texture has been disposed, will load again: " + resolveKey, material.name, slot, current.name, res);
150
+ }
145
151
  }
146
152
 
147
153
  const info = this.onProgressiveLoadStart(context, source, uri, material, slot);
@@ -51,6 +51,12 @@ class PointerResolver {
51
51
  }
52
52
 
53
53
  export function registerExtensions(loader: GLTFLoader, context: Context, sourceId: SourceIdentifier) {
54
+
55
+ // Make sure to remove any url parameters from the sourceId (because the source id in the renderer does not have a ?v=xxx so it will not be able to register the resolved lightmap otherwise)
56
+ const idEnd = sourceId.lastIndexOf("?");
57
+ if (idEnd >= 0) sourceId = sourceId.substring(0, idEnd);
58
+
59
+
54
60
  loader.register(p => new NEEDLE_gameobject_data(p));
55
61
  loader.register(p => new NEEDLE_persistent_assets(p));
56
62
  loader.register(p => new NEEDLE_lightmaps(p, context.lightmaps, sourceId));
@@ -143,6 +143,7 @@ export class AudioSource extends Behaviour {
143
143
  @serializable()
144
144
  rollOffMode: AudioRolloffMode = 0;
145
145
 
146
+ playInBackground: boolean = true;
146
147
 
147
148
  private _loop: boolean = false;
148
149
  private sound: THREE.PositionalAudio | null = null;
@@ -196,9 +197,11 @@ export class AudioSource extends Behaviour {
196
197
  private onVisibilityChanged = () => {
197
198
  switch (document.visibilityState) {
198
199
  case "hidden":
199
- this.wasPlaying = this.isPlaying;
200
- if (this.isPlaying) {
201
- this.pause();
200
+ if (this.playInBackground === false) {
201
+ this.wasPlaying = this.isPlaying;
202
+ if (this.isPlaying) {
203
+ this.pause();
204
+ }
202
205
  }
203
206
  break;
204
207
  case "visible":
@@ -390,7 +390,7 @@ class CameraSkybox {
390
390
  enable() {
391
391
  this._skybox = this.context.lightmaps.tryGetSkybox(this._camera.sourceId) as THREE.Texture;
392
392
  if (!this._skybox) {
393
- console.warn("Failed to load/find skybox texture", this);
393
+ console.warn("Failed to load/find skybox texture", this._camera.sourceId, this.context.lightmaps);
394
394
  }
395
395
  else if (this.context.scene.background !== this._skybox) {
396
396
  if (debug)
@@ -9,7 +9,6 @@ import { syncDestroy, syncInstantiate } from "../engine/engine_networking_instan
9
9
  import { ConstructorConcrete, SourceIdentifier, IComponent, IGameObject, Constructor, GuidsMap, UIDProvider, Collision, ICollider } from "../engine/engine_types";
10
10
  import { addNewComponent, destroyComponentInstance, findObjectOfType, findObjectsOfType, getComponent, getComponentInChildren, getComponentInParent, getComponents, getComponentsInChildren, getComponentsInParent, getOrAddComponent, moveComponentInstance, removeComponent } from "../engine/engine_components";
11
11
  import { findByGuid, destroy, InstantiateOptions, instantiate, HideFlags, foreachComponent, markAsInstancedRendered, isActiveInHierarchy, isActiveSelf, isUsingInstancing, setActive, isDestroyed } from "../engine/engine_gameobject";
12
- import { resolveUrl } from "../engine/engine_utils";
13
12
 
14
13
 
15
14
  // export interface ISerializationCallbackReceiver {
@@ -59,6 +59,18 @@ class SharedMaterialArray implements ISharedMaterials {
59
59
  private _indexMapMaxIndex?: number;
60
60
  private _indexMap?: Map<number, number>;
61
61
 
62
+ private _changed: boolean = false;
63
+ get changed(): boolean {
64
+ return this._changed;
65
+ }
66
+ set changed(value: boolean) {
67
+ if (value === true) {
68
+ if (debugRenderer)
69
+ console.warn("SharedMaterials have changed: " + this._renderer.name, this);
70
+ }
71
+ this._changed = value;
72
+ }
73
+
62
74
  is(renderer: Renderer) {
63
75
  return this._renderer === renderer;
64
76
  }
@@ -131,7 +143,12 @@ class SharedMaterialArray implements ISharedMaterials {
131
143
  if (typeof key === "string")
132
144
  setMaterial(value, Number.parseInt(key));
133
145
  // console.log(target, key, value);
134
- return Reflect.set(target, key, value);
146
+ if (Reflect.set(target, key, value)) {
147
+ if (value instanceof Material)
148
+ target.changed = true;
149
+ return true;
150
+ }
151
+ return false;
135
152
  }
136
153
  });
137
154
  }
@@ -141,6 +158,13 @@ class SharedMaterialArray implements ISharedMaterials {
141
158
  return this._targets.length;
142
159
  }
143
160
 
161
+ // iterator to support: for(const mat of sharedMaterials)
162
+ *[Symbol.iterator]() {
163
+ for (let i = 0; i < this.length; i++) {
164
+ yield this.getMaterial(i);
165
+ }
166
+ }
167
+
144
168
  private resolveIndex(index: number): number {
145
169
  const map = this._indexMap;
146
170
  // if we have a index map it means that some materials were missing
@@ -157,6 +181,7 @@ class SharedMaterialArray implements ISharedMaterials {
157
181
  const target = this._targets[index];
158
182
  if (!target || target["material"] === undefined) return;
159
183
  target["material"] = mat;
184
+ this.changed = true;
160
185
  }
161
186
 
162
187
  private getMaterial(index: number) {
@@ -231,7 +256,10 @@ export class Renderer extends Behaviour implements IRenderer {
231
256
  }
232
257
 
233
258
  set sharedMaterial(mat: THREE.Material) {
259
+ const cur = this.sharedMaterials[0];
260
+ if (cur === mat) return;
234
261
  this.sharedMaterials[0] = mat;
262
+ this.applyLightmapping();
235
263
  }
236
264
 
237
265
  /**@deprecated please use sharedMaterial */
@@ -241,7 +269,7 @@ export class Renderer extends Behaviour implements IRenderer {
241
269
 
242
270
  /**@deprecated please use sharedMaterial */
243
271
  set material(mat: THREE.Material) {
244
- this.sharedMaterials[0] = mat;
272
+ this.sharedMaterial = mat;
245
273
  }
246
274
 
247
275
  private _sharedMaterials!: SharedMaterialArray;
@@ -323,7 +351,6 @@ export class Renderer extends Behaviour implements IRenderer {
323
351
 
324
352
  this._reflectionProbe = null;
325
353
 
326
- const type = this.gameObject.type;
327
354
  if (this.isMultiMaterialObject(this.gameObject)) {
328
355
  for (const child of this.gameObject.children) {
329
356
  this.context.addBeforeRenderListener(child, this.onBeforeRenderThree.bind(this));
@@ -359,20 +386,39 @@ export class Renderer extends Behaviour implements IRenderer {
359
386
  this.context.addBeforeRenderListener(this.gameObject, this.onBeforeRenderThree.bind(this));
360
387
  }
361
388
 
389
+ this.applyLightmapping();
390
+
391
+ if (showWireframe) {
392
+ for (let i = 0; i < this.sharedMaterials.length; i++) {
393
+ const mat: any = this.sharedMaterials[i];
394
+ if (mat) {
395
+ mat.wireframe = true;
396
+ }
397
+ }
398
+ }
399
+
400
+ }
401
+
402
+ private applyLightmapping() {
362
403
  if (this.lightmapIndex >= 0) {
404
+ const type = this.gameObject.type;
405
+
363
406
  // use the override lightmap if its not undefined
364
407
  const tex = this._lightmapTextureOverride !== undefined
365
408
  ? this._lightmapTextureOverride
366
409
  : this.context.lightmaps.tryGetLightmap(this.sourceId, this.lightmapIndex);
367
410
  if (tex) {
368
- // tex.encoding = THREE.LinearEncoding;
369
- this._lightmaps = [];
411
+ if (!this._lightmaps)
412
+ this._lightmaps = [];
370
413
 
371
414
  if (type === "Mesh") {
372
415
  const mat = this.gameObject["material"];
373
416
  if (!mat?.isMeshBasicMaterial) {
374
- const rm = new RendererLightmap(this.gameObject, this.context);// GameObject.addNewComponent(this.gameObject, RendererLightmap);
375
- this._lightmaps.push(rm);
417
+ if (this._lightmaps.length <= 0) {
418
+ const rm = new RendererLightmap(this.gameObject, this.context);
419
+ this._lightmaps.push(rm);
420
+ }
421
+ const rm = this._lightmaps[0];
376
422
  rm.init(this.lightmapIndex, this.lightmapScaleOffset, tex, debugLightmap);
377
423
  }
378
424
  else {
@@ -383,10 +429,16 @@ export class Renderer extends Behaviour implements IRenderer {
383
429
  // for multi materials we need to loop through children
384
430
  // and then we add a lightmap renderer component to each of them
385
431
  else if (this.isMultiMaterialObject(this.gameObject) && this.sharedMaterials.length > 0) {
386
- for (const child of this.gameObject.children) {
432
+ for (let i = 0; i < this.gameObject.children.length; i++) {
433
+ const child = this.gameObject.children[i];
387
434
  if (!child["material"]?.isMeshBasicMaterial) {
388
- const rm = new RendererLightmap(child as GameObject, this.context);
389
- this._lightmaps.push(rm);
435
+ let rm: RendererLightmap | undefined = undefined;
436
+ if (i >= this._lightmaps.length) {
437
+ rm = new RendererLightmap(child as GameObject, this.context);
438
+ this._lightmaps.push(rm);
439
+ }
440
+ else
441
+ rm = this._lightmaps[i];
390
442
  rm.init(this.lightmapIndex, this.lightmapScaleOffset, tex, debugLightmap);
391
443
  // onBeforeRender is not called when the renderer is on a group
392
444
  // this is an issue we probably also need to handle for custom shaders
@@ -396,16 +448,8 @@ export class Renderer extends Behaviour implements IRenderer {
396
448
  }
397
449
  }
398
450
  }
399
- }
400
-
401
-
402
-
403
- if (showWireframe) {
404
- for (let i = 0; i < this.sharedMaterials.length; i++) {
405
- const mat: any = this.sharedMaterials[i];
406
- if (mat) {
407
- mat.wireframe = true;
408
- }
451
+ else {
452
+ if (debug) console.warn("Lightmap not found", this.sourceId, this.lightmapIndex);
409
453
  }
410
454
  }
411
455
 
@@ -518,6 +562,11 @@ export class Renderer extends Behaviour implements IRenderer {
518
562
  else {
519
563
  this.applySettings(this.gameObject);
520
564
  }
565
+
566
+ if (this.sharedMaterials.changed) {
567
+ this.sharedMaterials.changed = false;
568
+ this.applyLightmapping();
569
+ }
521
570
 
522
571
  if (this.handles?.length) {
523
572
  // if (this.name === "Darbouka")