Needle Engine

Changes between version 3.7.7-beta and 3.8.0-alpha
Files changed (20) hide show
  1. src/engine-components/ui/BaseUIComponent.ts +5 -0
  2. src/engine-components/Camera.ts +3 -3
  3. src/engine-components/ui/Canvas.ts +7 -1
  4. src/engine/engine_context.ts +20 -13
  5. src/engine/engine_lightdata.ts +6 -8
  6. src/engine/engine_networking_utils.ts +1 -1
  7. src/engine/engine_scenelighting.ts +5 -5
  8. src/engine/extensions/extensions.ts +18 -3
  9. src/engine-components/GroundProjection.ts +1 -1
  10. src/engine-components/Light.ts +58 -53
  11. src/engine/extensions/NEEDLE_lightmaps.ts +3 -3
  12. src/engine/extensions/NEEDLE_render_objects.ts +10 -8
  13. src/engine/extensions/NEEDLE_techniques_webgl.ts +5 -1
  14. src/engine-components/OrbitControls.ts +4 -3
  15. src/engine-components/ParticleSystem.ts +14 -0
  16. src/engine-components/ReflectionProbe.ts +3 -3
  17. src/engine-components/Renderer.ts +2 -3
  18. src/engine-components/RendererLightmap.ts +21 -20
  19. src/engine-components/webxr/WebARCameraBackground.ts +3 -3
  20. src/engine-components/webxr/WebXR.ts +6 -6
src/engine-components/ui/BaseUIComponent.ts CHANGED
@@ -6,8 +6,11 @@
6
6
  import { AxesHelper, Object3D } from 'three';
7
7
  import { ICanvas, IGraphic } from './Interfaces';
8
8
  import { ShadowCastingMode } from '../Renderer';
9
+ import { getParam } from '../../engine/engine_utils';
9
10
  export const includesDir = "./include";
10
11
 
12
+ const debug = getParam("debugshadowcomponents");
13
+
11
14
  // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy
12
15
 
13
16
 
@@ -128,6 +131,8 @@
128
131
  // otherwise it will fail when object are enabled at runtime
129
132
  if (needsUpdate)
130
133
  ThreeMeshUI.update();
134
+
135
+ if(debug) console.log(this.shadowComponent)
131
136
  }
132
137
 
133
138
 
src/engine-components/Camera.ts CHANGED
@@ -8,7 +8,7 @@
8
8
  import { getWorldPosition, Graphics } from "../engine/engine_three_utils";
9
9
  import { Gizmos } from "../engine/engine_gizmos";
10
10
 
11
- import { EquirectangularReflectionMapping, OrthographicCamera, PerspectiveCamera, Ray, sRGBEncoding, Vector3 } from "three";
11
+ import { EquirectangularReflectionMapping, OrthographicCamera, PerspectiveCamera, Ray, SRGBColorSpace, sRGBEncoding, Vector3 } from "three";
12
12
  import { OrbitControls } from "./OrbitControls";
13
13
  import { RenderTexture } from "../engine/engine_texture";
14
14
 
@@ -261,7 +261,7 @@
261
261
  else
262
262
  cam = this.gameObject.children[0] as THREE.PerspectiveCamera | THREE.OrthographicCamera | null;
263
263
  if (cam && cam.isCamera) {
264
- if (cam.type === "PerspectiveCamera") {
264
+ if (cam instanceof PerspectiveCamera) {
265
265
  if (this._fov)
266
266
  cam.fov = this._fov;
267
267
  cam.near = this._nearClipPlane;
@@ -391,7 +391,7 @@
391
391
  else if (this.context.scene.background !== this._skybox) {
392
392
  if (debug)
393
393
  console.log("Set skybox", this._camera, this._skybox);
394
- this._skybox.encoding = sRGBEncoding;
394
+ this._skybox.colorSpace = SRGBColorSpace;
395
395
  this._skybox.mapping = EquirectangularReflectionMapping;
396
396
  this.context.scene.background = this._skybox;
397
397
  }
src/engine-components/ui/Canvas.ts CHANGED
@@ -190,6 +190,7 @@
190
190
  }
191
191
 
192
192
  onBeforeRenderRoutine = () => {
193
+ if(this.context.isInVR) return;
193
194
  this.previousParent = this.gameObject.parent;
194
195
  // console.log(this.previousParent?.name + "/" + this.gameObject.name);
195
196
 
@@ -208,6 +209,7 @@
208
209
  }
209
210
 
210
211
  onAfterRenderRoutine = () => {
212
+ if(this.context.isInVR) return;
211
213
  if ((this.screenspace || this.renderOnTop) && this.previousParent && this.context.mainCamera) {
212
214
  if (this.screenspace) {
213
215
  const camObj = this.context.mainCamera;
@@ -215,7 +217,10 @@
215
217
  } else {
216
218
  this.previousParent.add(this.gameObject);
217
219
  }
220
+ const prevAutoClearDepth = this.context.renderer.autoClear;
221
+ const prevAutoClearColor = this.context.renderer.autoClearColor;
218
222
  this.context.renderer.autoClear = false;
223
+ this.context.renderer.autoClearColor = false;
219
224
  this.context.renderer.clearDepth();
220
225
  this.onUpdateRenderMode(true);
221
226
  this.handleLayoutUpdates();
@@ -223,7 +228,8 @@
223
228
  // this.handleLayoutUpdates();
224
229
  EventSystem.ensureUpdateMeshUI(ThreeMeshUI, this.context);
225
230
  this.context.renderer.render(this.gameObject, this.context.mainCamera);
226
- this.context.renderer.autoClear = true;
231
+ this.context.renderer.autoClear = prevAutoClearDepth;
232
+ this.context.renderer.autoClearColor = prevAutoClearColor;
227
233
  this.previousParent.add(this.gameObject);
228
234
  }
229
235
  this._lastMatrixWorld?.copy(this.gameObject.matrixWorld);
src/engine/engine_context.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  BufferGeometry, Cache, Camera, Clock, Color, DepthTexture, Group,
3
3
  Material, NearestFilter, NoToneMapping, Object3D, PCFSoftShadowMap,
4
- PerspectiveCamera, RGBAFormat, Scene, sRGBEncoding,
5
- Texture, WebGLRenderer, WebGLRendererParameters, WebGLRenderTarget
4
+ PerspectiveCamera, RGBAFormat, Scene, SRGBColorSpace,
5
+ Texture, WebGLRenderer, WebGLRendererParameters, WebGLRenderTarget, WebXRArrayCamera
6
6
  } from 'three'
7
7
  import { Input } from './engine_input';
8
8
  import { Physics } from './engine_physics';
@@ -28,7 +28,7 @@
28
28
  import { CoroutineData, GLTF, ICamera, IComponent, IContext, ILight } from "./engine_types"
29
29
  import { destroy, foreachComponent } from './engine_gameobject';
30
30
  import { ContextEvent, ContextRegistry } from './engine_context_registry';
31
- import { delay } from './engine_utils';
31
+ import { delay, getParam } from './engine_utils';
32
32
  import { VERSION } from './engine_constants';
33
33
  import { isDevEnvironment, LogType, showBalloonMessage } from './debug';
34
34
 
@@ -202,6 +202,7 @@
202
202
  get isInAR() { return this.xrSessionMode === XRSessionMode.ImmersiveAR; }
203
203
  get xrSession() { return this.renderer.xr?.getSession(); }
204
204
  get xrFrame() { return this._xrFrame }
205
+ get xrCamera() : WebXRArrayCamera | undefined { return this.renderer.xr?.getCamera(); }
205
206
  private _xrFrame: XRFrame | null = null;
206
207
  get arOverlayElement(): HTMLElement {
207
208
  const el = this.domElement as any;
@@ -276,7 +277,7 @@
276
277
  private _isCreating: boolean = false;
277
278
  private _isVisible: boolean = false;
278
279
 
279
- private _stats: Stats.default | null = stats ? Stats.default() : null;
280
+ private _stats = stats ? new Stats.default() : null;
280
281
 
281
282
  constructor(args?: ContextArgs) {
282
283
  this.name = args?.name || "";
@@ -323,9 +324,7 @@
323
324
  private createRenderer() {
324
325
  this.renderer?.dispose();
325
326
 
326
- const params: WebGLRendererParameters = {
327
- antialias: true,
328
- };
327
+ const params = this.getWebGLRendererParameters();
329
328
 
330
329
  // get canvas already configured in the Needle Engine Web Component
331
330
  const canvas = this.domElement?.shadowRoot?.querySelector("canvas");
@@ -333,23 +332,31 @@
333
332
 
334
333
  this.renderer = new WebGLRenderer(params);
335
334
 
335
+ this.renderer.debug.checkShaderErrors = isDevEnvironment() || getParam("checkshadererrors") === true;
336
+
336
337
  // some tonemapping other than "NONE" is required for adjusting exposure with EXR environments
337
338
  this.renderer.toneMappingExposure = 1; // range [0...inf] instead of the usual -15..15
338
339
  this.renderer.toneMapping = NoToneMapping; // could also set to LinearToneMapping, ACESFilmicToneMapping
339
340
 
340
341
  this.renderer.setClearColor(new Color('lightgrey'), 0);
341
- // @ts-ignore
342
- this.renderer.antialias = true;
343
- // @ts-ignore
344
- this.renderer.alpha = false;
342
+ // // @ts-ignore
343
+ // this.renderer.alpha = false;
345
344
  this.renderer.shadowMap.enabled = true;
346
345
  this.renderer.shadowMap.type = PCFSoftShadowMap;
347
346
  this.renderer.setSize(this.domWidth, this.domHeight);
348
- this.renderer.outputEncoding = sRGBEncoding;
349
- this.renderer.physicallyCorrectLights = true;
347
+ this.renderer.outputColorSpace = SRGBColorSpace;
348
+ // https://github.com/mrdoob/three.js/pull/25556
349
+ this.renderer.useLegacyLights = false;
350
350
  }
351
351
 
352
+ private getWebGLRendererParameters(): WebGLRendererParameters {
353
+ return {
354
+ antialias: true,
355
+ alpha: false,
356
+ };
357
+ }
352
358
 
359
+
353
360
  private _intersectionObserver: IntersectionObserver | null = null;
354
361
  private internalOnUpdateVisible() {
355
362
  this._intersectionObserver?.disconnect();
src/engine/engine_lightdata.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { LightmapType } from "./extensions/NEEDLE_lightmaps";
2
- import { Texture } from "three";
2
+ import { Texture, ShaderChunk, UniformsLib, Vector4 } from "three";
3
3
  import { Context } from "./engine_setup";
4
- import * as THREE from "three";
5
4
  import { getParam } from "./engine_utils";
6
5
  import { SourceIdentifier } from "./engine_types";
7
6
 
@@ -78,9 +77,8 @@
78
77
  // all the chunks we can patch
79
78
  // console.log(THREE.ShaderChunk);
80
79
  // Unity: ambientOrLightmapUV.xy = v.uv1.xy * unity_LightmapST.xy + unity_LightmapST.zw; ambientOrLightmapUV.zw = 0;
81
- THREE.ShaderChunk.lights_fragment_maps = THREE.ShaderChunk.lights_fragment_maps.replace("vec4 lightMapTexel = texture2D( lightMap, vUv2 );", `
82
-
83
- vec2 lUv = vUv2.xy * lightmapScaleOffset.xy + vec2(lightmapScaleOffset.z, (1. - (lightmapScaleOffset.y + lightmapScaleOffset.w)));
80
+ ShaderChunk.lights_fragment_maps = ShaderChunk.lights_fragment_maps.replace("vec4 lightMapTexel = texture2D( lightMap, vLightMapUv );", `
81
+ vec2 lUv = vLightMapUv.xy * lightmapScaleOffset.xy + vec2(lightmapScaleOffset.z, (1. - (lightmapScaleOffset.y + lightmapScaleOffset.w)));
84
82
  vec4 lightMapTexel = texture2D( lightMap, lUv);
85
83
  // The range of RGBM lightmaps goes from 0 to 34.49 (5^2.2) in linear space, and from 0 to 5 in gamma space.
86
84
  lightMapTexel.rgb *= lightMapTexel.a * 8.; // no idea where that "8" comes from... heuristically derived
@@ -88,7 +86,7 @@
88
86
  lightMapTexel = conv_sRGBToLinear(lightMapTexel);
89
87
  `);
90
88
 
91
- THREE.ShaderChunk.lightmap_pars_fragment = `
89
+ ShaderChunk.lightmap_pars_fragment = `
92
90
  #ifdef USE_LIGHTMAP
93
91
  uniform sampler2D lightMap;
94
92
  uniform float lightMapIntensity;
@@ -105,7 +103,7 @@
105
103
  // so they shouldn't receive light probe lighting.
106
104
  // TODO: this gets difficult if there are additional real-time lightprobes added; we would need to exclude
107
105
  // exactly those that were active when lighting was baked... that's complicated!
108
- THREE.ShaderChunk.lights_fragment_begin = THREE.ShaderChunk.lights_fragment_begin.replace(
106
+ ShaderChunk.lights_fragment_begin = ShaderChunk.lights_fragment_begin.replace(
109
107
  "irradiance += getLightProbeIrradiance( lightProbe, geometry.normal );", `
110
108
  #if defined(USE_LIGHTMAP)
111
109
  irradiance += 0.;
@@ -113,4 +111,4 @@
113
111
  irradiance += getLightProbeIrradiance( lightProbe, geometry.normal );
114
112
  #endif`);
115
113
 
116
- THREE.UniformsLib.lightmap["lightmapScaleOffset"] = { value: new THREE.Vector4(1, 1, 0, 0) };
114
+ UniformsLib.lightmap["lightmapScaleOffset"] = { value: new Vector4(1, 1, 0, 0) };
src/engine/engine_networking_utils.ts CHANGED
@@ -7,7 +7,7 @@
7
7
  const localNetworkResults = new Map<string, boolean>();
8
8
 
9
9
  export function isLocalNetwork(hostname = window.location.hostname) {
10
- if(localNetworkResults.has(hostname)) return localNetworkResults.get(hostname);
10
+ if(localNetworkResults.has(hostname)) return localNetworkResults.get(hostname)!;
11
11
  const isLocalNetwork = new RegExp("[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}|localhost", "gm").test(hostname);
12
12
  localNetworkResults.set(hostname, isLocalNetwork);
13
13
  if (isLocalNetwork === true) return true;
src/engine/engine_scenelighting.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Vector4, EquirectangularReflectionMapping, sRGBEncoding, WebGLCubeRenderTarget, Texture, LightProbe, Color, SphericalHarmonics3 } from "three";
1
+ import { Vector4, EquirectangularReflectionMapping, sRGBEncoding, WebGLCubeRenderTarget, Texture, LightProbe, Color, SphericalHarmonics3, SRGBColorSpace } from "three";
2
2
  import { LightProbeGenerator } from "three/examples/jsm/lights/LightProbeGenerator.js"
3
3
  import { Context } from "./engine_setup";
4
4
  import { SceneLightSettings } from "./extensions/NEEDLE_lighting_settings";
@@ -164,7 +164,7 @@
164
164
  if (debug) console.log("Setting environment reflection", existing);
165
165
  const scene = this.context.scene;
166
166
  const tex = existing.Source;
167
- tex.encoding = sRGBEncoding;
167
+ tex.colorSpace = SRGBColorSpace;
168
168
  tex.mapping = EquirectangularReflectionMapping;
169
169
  scene.environment = tex;
170
170
  return;
@@ -179,7 +179,7 @@
179
179
  if (settings.ambientTrilight) {
180
180
  const colors = settings.ambientTrilight;
181
181
  const tex = createTrilightTexture(colors[0], colors[1], colors[2], 64, 64);
182
- tex.encoding = sRGBEncoding;
182
+ tex.colorSpace = SRGBColorSpace;
183
183
  tex.mapping = EquirectangularReflectionMapping;
184
184
  this.context.scene.environment = tex;
185
185
  }
@@ -188,7 +188,7 @@
188
188
  case AmbientMode.Flat:
189
189
  if (settings.ambientLight) {
190
190
  const tex = createFlatTexture(settings.ambientLight, 64);
191
- tex.encoding = sRGBEncoding;
191
+ tex.colorSpace = SRGBColorSpace;
192
192
  tex.mapping = EquirectangularReflectionMapping;
193
193
  this.context.scene.environment = tex;
194
194
  }
@@ -259,7 +259,7 @@
259
259
  this._source = tex;
260
260
  this._ambientScale = ambientScale;
261
261
  tex.mapping = EquirectangularReflectionMapping;
262
- tex.encoding = sRGBEncoding;
262
+ tex.colorSpace = SRGBColorSpace;
263
263
  }
264
264
 
265
265
  getSphericalHarmonicsArray(intensityFactor: number = 1): SphericalHarmonicsData | null {
src/engine/extensions/extensions.ts CHANGED
@@ -16,7 +16,17 @@
16
16
  import { InternalUsageTrackerPlugin } from "./usage_tracker";
17
17
  import { isUsageTrackingEnabled } from "../engine_assetdatabase";
18
18
  import { GLTFLoaderPlugin } from "three/examples/jsm/loaders/GLTFLoader";
19
+ // import { GLTFAnimationPointerExtension } from "three/examples/jsm/loaders/GLTFLoaderAnimationPointer";
19
20
 
21
+ // lazily import the GLTFAnimationPointerExtension in case it doesnt exist (e.g. using vanilla three)
22
+ let GLTFAnimationPointerExtension : any;
23
+ import("three/examples/jsm/loaders/GLTFLoaderAnimationPointer").then(mod => {
24
+ GLTFAnimationPointerExtension = mod.GLTFAnimationPointerExtension;
25
+ return GLTFAnimationPointerExtension;
26
+ }).catch(e => {
27
+ console.warn("Failed to import GLTFLoaderAnimationPointer. Please use @needle-tools/three for full KHR_animation support", e);
28
+ });
29
+
20
30
  const _addedCustomExtension = new Array<ConstructorConcrete<GLTFLoaderPlugin>>();
21
31
 
22
32
  export function addCustomExtension(ext: ConstructorConcrete<GLTFLoaderPlugin>) {
@@ -70,8 +80,13 @@
70
80
  for (const ext of _addedCustomExtension)
71
81
  loader.register(p => new ext(p));
72
82
 
73
- const setPointerResolverFunction = loader["setAnimationPointerResolver"];
74
- if (typeof setPointerResolverFunction === "function")
75
- setPointerResolverFunction.bind(loader)(new PointerResolver());
83
+ loader.register(p => {
84
+ if (GLTFAnimationPointerExtension) {
85
+ const ext = new GLTFAnimationPointerExtension(p);
86
+ const setPointerResolverFunction = ext.setAnimationPointerResolver;
87
+ setPointerResolverFunction.bind(ext)(new PointerResolver());
88
+ return ext;
89
+ }
90
+ });
76
91
 
77
92
  }
src/engine-components/GroundProjection.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Behaviour, GameObject } from "./Component";
2
- import { GroundProjectedEnv as GroundProjection } from 'three/examples/jsm/objects/GroundProjectedEnv.js';
2
+ import { GroundProjectedSkybox as GroundProjection } from 'three/examples/jsm/objects/GroundProjectedSkybox.js';
3
3
  import { serializable } from "../engine/engine_serialization_decorator";
4
4
  import { Watch as Watch } from "../engine/engine_utils";
5
5
  import { Texture } from "three";
src/engine-components/Light.ts CHANGED
@@ -173,6 +173,7 @@
173
173
  }
174
174
  this.light.intensity = val * factor;
175
175
  }
176
+ if (debug) console.log("Set light intensity to " + this._intensity, val, this)
176
177
  }
177
178
  get intensity(): number { return this._intensity; }
178
179
  private _intensity: number = -1;
@@ -180,7 +181,7 @@
180
181
  @serializable()
181
182
  get shadowDistance(): number {
182
183
  const light = this.light;
183
- if (light) {
184
+ if (light?.shadow) {
184
185
  const cam = light.shadow.camera as OrthographicCamera;
185
186
  return cam.far;
186
187
  }
@@ -189,7 +190,7 @@
189
190
  set shadowDistance(val: number) {
190
191
  this._shadowDistance = val;
191
192
  const light = this.light;
192
- if (light) {
193
+ if (light?.shadow) {
193
194
  const cam = light.shadow.camera as OrthographicCamera;
194
195
  cam.far = val;
195
196
  cam.updateProjectionMatrix();
@@ -203,7 +204,7 @@
203
204
  @serializable()
204
205
  get shadowResolution(): number {
205
206
  const light = this.light;
206
- if (light) {
207
+ if (light?.shadow) {
207
208
  return light.shadow.mapSize.x;
208
209
  }
209
210
  return -1;
@@ -212,7 +213,7 @@
212
213
  if (val === this._shadowResolution) return;
213
214
  this._shadowResolution = val;
214
215
  const light = this.light;
215
- if (light) {
216
+ if (light?.shadow) {
216
217
  light.shadow.mapSize.set(val, val);
217
218
  light.shadow.needsUpdate = true;
218
219
  }
@@ -256,12 +257,13 @@
256
257
  }
257
258
 
258
259
  onEnable(): void {
259
- if(debug) console.log("ENABLE LIGHT", this.name);
260
+ if (debug) console.log("ENABLE LIGHT", this.name);
260
261
  this.createLight();
261
262
  if (this.isBaked) return;
262
263
  else if (this.light) {
263
264
  this.light.visible = true;
264
265
  this.light.intensity = this._intensity;
266
+ if (debug) console.log("Set light intensity to " + this.light.intensity, this.name)
265
267
  if (this.selfIsLight) {
266
268
  // nothing to do
267
269
  }
@@ -275,7 +277,7 @@
275
277
  }
276
278
 
277
279
  onDisable() {
278
- if(debug) console.log("DISABLE LIGHT", this.name);
280
+ if (debug) console.log("DISABLE LIGHT", this.name);
279
281
  if (this.light) {
280
282
  if (this.selfIsLight)
281
283
  this.light.intensity = 0;
@@ -376,62 +378,64 @@
376
378
  }
377
379
  else this.light.castShadow = false;
378
380
 
379
- // shadow intensity is currently not a thing: https://github.com/mrdoob/three.js/pull/14087
380
- if (this._shadowResolution !== undefined && this._shadowResolution > 4) {
381
- this.light.shadow.mapSize.width = this._shadowResolution;
382
- this.light.shadow.mapSize.height = this._shadowResolution;
383
- }
384
- else {
385
- this.light.shadow.mapSize.width = 2048;
386
- this.light.shadow.mapSize.height = 2048;
387
- }
388
- // this.light.shadow.needsUpdate = true;
389
- // console.log(this.light.shadow.mapSize);
390
- // return;
381
+ if (this.light.shadow) {
382
+ // shadow intensity is currently not a thing: https://github.com/mrdoob/three.js/pull/14087
383
+ if (this._shadowResolution !== undefined && this._shadowResolution > 4) {
384
+ this.light.shadow.mapSize.width = this._shadowResolution;
385
+ this.light.shadow.mapSize.height = this._shadowResolution;
386
+ }
387
+ else {
388
+ this.light.shadow.mapSize.width = 2048;
389
+ this.light.shadow.mapSize.height = 2048;
390
+ }
391
+ // this.light.shadow.needsUpdate = true;
392
+ // console.log(this.light.shadow.mapSize);
393
+ // return;
391
394
 
392
- if (debug)
393
- console.log("Override shadow bias?", this._overrideShadowBiasSettings, this.shadowBias, this.shadowNormalBias);
395
+ if (debug)
396
+ console.log("Override shadow bias?", this._overrideShadowBiasSettings, this.shadowBias, this.shadowNormalBias);
394
397
 
395
- this.light.shadow.bias = this.shadowBias;
396
- this.light.shadow.normalBias = this.shadowNormalBias;
398
+ this.light.shadow.bias = this.shadowBias;
399
+ this.light.shadow.normalBias = this.shadowNormalBias;
397
400
 
398
- this.updateShadowSoftHard();
401
+ this.updateShadowSoftHard();
399
402
 
400
- const cam = this.light.shadow.camera as THREE.OrthographicCamera;
401
- cam.near = this.shadowNearPlane;
402
- // use shadow distance that was set explictly (if any)
403
- if (this._shadowDistance !== undefined && typeof this._shadowDistance === "number")
404
- cam.far = this._shadowDistance;
405
- else // otherwise fallback to object scale and max distance
406
- cam.far = shadowMaxDistance * Math.abs(this.gameObject.scale.z);
403
+ const cam = this.light.shadow.camera as THREE.OrthographicCamera;
404
+ cam.near = this.shadowNearPlane;
405
+ // use shadow distance that was set explictly (if any)
406
+ if (this._shadowDistance !== undefined && typeof this._shadowDistance === "number")
407
+ cam.far = this._shadowDistance;
408
+ else // otherwise fallback to object scale and max distance
409
+ cam.far = shadowMaxDistance * Math.abs(this.gameObject.scale.z);
407
410
 
408
- // width and height
409
- this.gameObject.scale.set(1, 1, 1);
410
- if (this.shadowWidth !== undefined) {
411
- cam.left = -this.shadowWidth / 2;
412
- cam.right = this.shadowWidth / 2;
411
+ // width and height
412
+ this.gameObject.scale.set(1, 1, 1);
413
+ if (this.shadowWidth !== undefined) {
414
+ cam.left = -this.shadowWidth / 2;
415
+ cam.right = this.shadowWidth / 2;
416
+ }
417
+ else {
418
+ const sx = this.gameObject.scale.x;
419
+ cam.left *= sx;
420
+ cam.right *= sx;
421
+ }
422
+ if (this.shadowHeight !== undefined) {
423
+ cam.top = this.shadowHeight / 2;
424
+ cam.bottom = -this.shadowHeight / 2;
425
+ }
426
+ else {
427
+ const sy = this.gameObject.scale.y;
428
+ cam.top *= sy;
429
+ cam.bottom *= sy;
430
+ }
431
+ this.light.shadow.needsUpdate = true;
432
+
433
+ if (debug)
434
+ this.context.scene.add(new THREE.CameraHelper(cam));
413
435
  }
414
- else {
415
- const sx = this.gameObject.scale.x;
416
- cam.left *= sx;
417
- cam.right *= sx;
418
- }
419
- if (this.shadowHeight !== undefined) {
420
- cam.top = this.shadowHeight / 2;
421
- cam.bottom = -this.shadowHeight / 2;
422
- }
423
- else {
424
- const sy = this.gameObject.scale.y;
425
- cam.top *= sy;
426
- cam.bottom *= sy;
427
- }
428
436
 
429
437
 
430
- this.light.shadow.needsUpdate = true;
431
- if (debug)
432
- this.context.scene.add(new THREE.CameraHelper(cam));
433
438
 
434
-
435
439
  if (this.isBaked) {
436
440
  this.light.removeFromParent();
437
441
  }
@@ -457,6 +461,7 @@
457
461
 
458
462
  private updateShadowSoftHard() {
459
463
  if (!this.light) return;
464
+ if (!this.light.shadow) return;
460
465
  if (this.shadows === LightShadows.Soft) {
461
466
  // const radius = this.light.shadow.mapSize.width / 1024 * 5;
462
467
  // const samples = Mathf.clamp(Math.round(radius), 2, 10);
src/engine/extensions/NEEDLE_lightmaps.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { ILightDataRegistry } from "../engine_lightdata";
2
- import { FloatType, HalfFloatType, LinearEncoding, sRGBEncoding, Texture } from "three";
2
+ import { FloatType, HalfFloatType, LinearEncoding, LinearSRGBColorSpace, SRGBColorSpace, sRGBEncoding, Texture } from "three";
3
3
  import { GLTF, GLTFLoaderPlugin, GLTFParser } from "three/examples/jsm/loaders/GLTFLoader";
4
4
  import { SourceIdentifier } from "../engine_types";
5
5
  import { resolveReferences } from "./extension_utils";
@@ -70,9 +70,9 @@
70
70
  else {
71
71
  // TODO this is most likely wrong for floating point textures
72
72
  if (entry.type !== LightmapType.Lightmap)
73
- tex.encoding = sRGBEncoding;
73
+ tex.colorSpace = SRGBColorSpace;
74
74
  else
75
- tex.encoding = LinearEncoding;
75
+ tex.colorSpace = LinearSRGBColorSpace;
76
76
 
77
77
 
78
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
src/engine/extensions/NEEDLE_render_objects.ts CHANGED
@@ -22,6 +22,8 @@
22
22
  IncrementWrapStencilOp,
23
23
  DecrementWrapStencilOp,
24
24
  InvertStencilOp,
25
+ StencilFunc,
26
+ StencilOp as ThreeStencilOp,
25
27
  } from "three";
26
28
  import { getParam, isDebugMode } from "../engine_utils";
27
29
  import { showBalloonWarning } from "../debug";
@@ -81,10 +83,10 @@
81
83
  mat.stencilWriteMask = 255;
82
84
  mat.stencilFuncMask = 255;
83
85
  mat.stencilRef = stencil.value;
84
- mat.stencilFunc = stencil.compareFunc;
85
- mat.stencilZPass = stencil.passOp;
86
- mat.stencilFail = stencil.failOp;
87
- mat.stencilZFail = stencil.zFailOp;
86
+ mat.stencilFunc = stencil.compareFunc as StencilFunc;
87
+ mat.stencilZPass = stencil.passOp as ThreeStencilOp;
88
+ mat.stencilFail = stencil.failOp as ThreeStencilOp;
89
+ mat.stencilZFail = stencil.zFailOp as ThreeStencilOp;
88
90
  obj.sharedMaterials[i] = mat;
89
91
  }
90
92
  }
@@ -116,7 +118,7 @@
116
118
  if (stencils && Array.isArray(stencils)) {
117
119
  for (const stencil of stencils) {
118
120
  const obj: StencilSettingsModel = { ...stencil };
119
- obj.compareFunc = ToThreeCompareFunction(obj.compareFunc);
121
+ obj.compareFunc = ToThreeCompareFunction(obj.compareFunc as number);
120
122
  obj.passOp = ToThreeStencilOp(obj.passOp);
121
123
  obj.failOp = ToThreeStencilOp(obj.failOp);
122
124
  obj.zFailOp = ToThreeStencilOp(obj.zFailOp);
@@ -161,7 +163,7 @@
161
163
  Always,
162
164
  }
163
165
 
164
- function ToThreeStencilOp(op: StencilOp): number {
166
+ function ToThreeStencilOp(op: StencilOp): ThreeStencilOp {
165
167
  switch (op) {
166
168
  case StencilOp.Keep:
167
169
  return KeepStencilOp;
@@ -183,7 +185,7 @@
183
185
  return 0;
184
186
  }
185
187
 
186
- function ToThreeCompareFunction(func: CompareFunction): number {
188
+ function ToThreeCompareFunction(func: CompareFunction): StencilFunc {
187
189
  switch (func) {
188
190
  case CompareFunction.Never:
189
191
  return NeverStencilFunc;
@@ -202,7 +204,7 @@
202
204
  case CompareFunction.Always:
203
205
  return AlwaysStencilFunc;
204
206
  }
205
- return 0;
207
+ return NeverStencilFunc;
206
208
  }
207
209
 
208
210
 
src/engine/extensions/NEEDLE_techniques_webgl.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { GLTFLoaderPlugin, GLTFParser } from "three/examples/jsm/loaders/GLTFLoader";
2
2
  import { FindShaderTechniques, whiteDefaultTexture, ToUnityMatrixArray, SetUnitySphericalHarmonics } from '../engine_shaders';
3
- import { AlwaysDepth, BackSide, Camera, DoubleSide, EqualDepth, FrontSide, GreaterDepth, GreaterEqualDepth, IUniform, LessDepth, LessEqualDepth, LinearEncoding, Material, Matrix4, NotEqualDepth, Object3D, RawShaderMaterial, Vector3, Vector4 } from 'three';
3
+ import { AlwaysDepth, BackSide, Camera, DoubleSide, EqualDepth, FrontSide, GLSL3, GreaterDepth, GreaterEqualDepth, IUniform, LessDepth, LessEqualDepth, LinearEncoding, Material, Matrix4, NotEqualDepth, Object3D, RawShaderMaterial, Vector3, Vector4 } from 'three';
4
4
  import { Context } from '../engine_setup';
5
5
  import { getParam } from "../engine_utils";
6
6
  import * as SHADERDATA from "../shaders/shaderData"
@@ -455,7 +455,11 @@
455
455
  // "USE_SHADOWMAP" : true
456
456
  // },
457
457
  });
458
+ material.glslVersion = GLSL3;
459
+ material.vertexShader = material.vertexShader.replace("#version 300 es", "");
460
+ material.fragmentShader = material.fragmentShader.replace("#version 300 es", "");
458
461
 
462
+
459
463
  const culling = uniforms["_Cull"]?.value;
460
464
  switch (culling) {
461
465
  case CullMode.Off:
src/engine-components/OrbitControls.ts CHANGED
@@ -272,10 +272,10 @@
272
272
  if (!freeCam && this.lookAtConstraint?.locked) this.setFromTargetPosition(0, this.lookAtConstraint01);
273
273
 
274
274
 
275
- if (this._controls && !this.context.isInXR) {
275
+ if (this._controls) {
276
276
  if (this.debugLog)
277
277
  this._controls.domElement = this.context.renderer.domElement;
278
- this._controls.enabled = !this._shouldDisable && this._camera === this.context.mainCameraComponent;
278
+ this._controls.enabled = !this._shouldDisable && this._camera === this.context.mainCameraComponent && !this.context.isInXR;
279
279
  this._controls.enableDamping = this.enableDamping;
280
280
  this._controls.keys = this.enableKeys ? defaultKeys : disabledKeys;
281
281
  this._controls.autoRotate = this.autoRotate;
@@ -283,7 +283,8 @@
283
283
  this._controls.enableZoom = this.enableZoom;
284
284
  this._controls.dampingFactor = this.dampingFactor;
285
285
  this._controls.enablePan = this.enablePan;
286
- this._controls.update();
286
+ if (!this.context.isInXR)
287
+ this._controls.update();
287
288
  }
288
289
  }
289
290
 
src/engine-components/ParticleSystem.ts CHANGED
@@ -69,7 +69,21 @@
69
69
  return res;
70
70
  }
71
71
 
72
+ private static _havePatchedQuarkShaders = false;
73
+
72
74
  getMaterial(trailEnabled: boolean = false) {
75
+
76
+ if (!ParticleSystemRenderer._havePatchedQuarkShaders) {
77
+ ParticleSystemRenderer._havePatchedQuarkShaders = true;
78
+
79
+ // HACK patch three.quarks fo three152+, see https://github.com/Alchemist0823/three.quarks/issues/56#issuecomment-1560825038
80
+ const _rebuild = TrailBatch.prototype.rebuildMaterial;
81
+ TrailBatch.prototype.rebuildMaterial = function () {
82
+ _rebuild.call(this);
83
+ this.material.defines.MAP_UV = "uv";
84
+ }
85
+ }
86
+
73
87
  const material = (trailEnabled === true && this.trailMaterial) ? this.trailMaterial : this.particleMaterial;
74
88
 
75
89
  // progressive load on start
src/engine-components/ReflectionProbe.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Behaviour } from "./Component";
2
- import { Box3, Color, EquirectangularReflectionMapping, LinearEncoding, LineBasicMaterial, Material, MeshStandardMaterial, Object3D, SrcAlphaFactor, sRGBEncoding, Texture, Vector3, WebGLCubeRenderTarget, WebGLRenderTarget } from "three";
2
+ import { Box3, Color, EquirectangularReflectionMapping, LinearEncoding, LineBasicMaterial, Material, MeshStandardMaterial, Object3D, SrcAlphaFactor, SRGBColorSpace, sRGBEncoding, Texture, Vector3, WebGLCubeRenderTarget, WebGLRenderTarget } from "three";
3
3
  import { serializable } from "../engine/engine_serialization";
4
4
  import { Context } from "../engine/engine_setup";
5
5
  import { getWorldPosition, getWorldScale } from "../engine/engine_three_utils";
@@ -54,7 +54,7 @@
54
54
  this._texture = tex;
55
55
  if (tex) {
56
56
  tex.mapping = EquirectangularReflectionMapping;
57
- tex.encoding = sRGBEncoding;
57
+ tex.colorSpace = SRGBColorSpace;
58
58
  tex.needsUpdate = true;
59
59
  }
60
60
  }
@@ -89,7 +89,7 @@
89
89
 
90
90
  if (this.texture) {
91
91
  this.texture.mapping = EquirectangularReflectionMapping;
92
- this.texture.encoding = sRGBEncoding;
92
+ this.texture.colorSpace = SRGBColorSpace;
93
93
  this.texture.needsUpdate = true;
94
94
  }
95
95
  }
src/engine-components/Renderer.ts CHANGED
@@ -20,7 +20,6 @@
20
20
 
21
21
  const debugRenderer = getParam("debugrenderer");
22
22
  const suppressInstancing = getParam("noInstancing");
23
- const debugLightmap = getParam("debuglightmaps") ? true : false;
24
23
  const debugInstancing = getParam("debuginstancing");
25
24
  const debugProgressiveLoading = getParam("debugprogressive");
26
25
  const suppressProgressiveLoading = getParam("noprogressive");
@@ -420,7 +419,7 @@
420
419
  this._lightmaps.push(rm);
421
420
  }
422
421
  const rm = this._lightmaps[0];
423
- rm.init(this.lightmapIndex, this.lightmapScaleOffset, tex, debugLightmap);
422
+ rm.init(this.lightmapIndex, this.lightmapScaleOffset, tex);
424
423
  }
425
424
  else {
426
425
  if (mat)
@@ -440,7 +439,7 @@
440
439
  }
441
440
  else
442
441
  rm = this._lightmaps[i];
443
- rm.init(this.lightmapIndex, this.lightmapScaleOffset, tex, debugLightmap);
442
+ rm.init(this.lightmapIndex, this.lightmapScaleOffset, tex);
444
443
  // onBeforeRender is not called when the renderer is on a group
445
444
  // this is an issue we probably also need to handle for custom shaders
446
445
  // and need a better solution, but for now this fixes lightmaps for multimaterial objects
src/engine-components/RendererLightmap.ts CHANGED
@@ -1,8 +1,10 @@
1
1
  import { Behaviour, GameObject } from "./Component";
2
- import * as THREE from "three";
3
- import { Texture } from "three";
2
+ import { Material, Mesh, ShaderMaterial, Texture, Vector4 } from "three";
4
3
  import { Context, OnRenderCallback } from "../engine/engine_setup";
4
+ import { getParam } from "../engine/engine_utils";
5
5
 
6
+ const debug = getParam("debuglightmaps");
7
+
6
8
  // this component is automatically added by the Renderer if the object has lightmap uvs AND we have a lightmap
7
9
  // for multimaterial objects GLTF exports a "Group" with the renderer component
8
10
  // and every child mesh is a material from unity
@@ -19,20 +21,20 @@
19
21
  }
20
22
 
21
23
  lightmapIndex: number = -1;
22
- lightmapScaleOffset: THREE.Vector4 = new THREE.Vector4(1, 1, 0, 0);
24
+ lightmapScaleOffset: THREE.Vector4 = new Vector4(1, 1, 0, 0);
23
25
 
24
26
  private context: Context;
25
27
  private gameObject: GameObject;
26
- private lightmapTexture: THREE.Texture | null = null;
27
- private lightmapScaleOffsetUniform = { value: new THREE.Vector4(1, 1, 0, 0) };
28
- private lightmapUniform: { value: THREE.Texture | null } = { value: null };
28
+ private lightmapTexture: Texture | null = null;
29
+ private lightmapScaleOffsetUniform = { value: new Vector4(1, 1, 0, 0) };
30
+ private lightmapUniform: { value: Texture | null } = { value: null };
29
31
 
30
32
  constructor(gameObject: GameObject, context: Context) {
31
33
  this.gameObject = gameObject;
32
34
  this.context = context;
33
35
  }
34
36
 
35
- init(lightmapIndex: number, lightmapScaleOffset: THREE.Vector4, lightmapTexture: THREE.Texture, debug: boolean = false) {
37
+ init(lightmapIndex: number, lightmapScaleOffset: Vector4, lightmapTexture: Texture) {
36
38
  console.assert(this.gameObject !== undefined && this.gameObject !== null, "Missing gameobject", this);
37
39
 
38
40
  this.lightmapIndex = lightmapIndex;
@@ -40,8 +42,7 @@
40
42
  this.lightmapScaleOffset = lightmapScaleOffset;
41
43
  this.lightmapTexture = lightmapTexture;
42
44
 
43
- const debugLightmaps = debug;
44
- if (debugLightmaps) this.setLightmapDebugMaterial();
45
+ if (debug) this.setLightmapDebugMaterial();
45
46
  this.applyLightmap();
46
47
  }
47
48
 
@@ -68,15 +69,16 @@
68
69
 
69
70
  console.assert(this.gameObject.type === "Mesh", "Lightmap only works on meshes", this);
70
71
 
71
- const mesh = this.gameObject as unknown as THREE.Mesh;
72
+ const mesh = this.gameObject as unknown as Mesh;
72
73
  // TODO: ensure uv2 exists
73
- if (!mesh.geometry.getAttribute("uv2"))
74
- mesh.geometry.setAttribute("uv2", mesh.geometry.getAttribute("uv"));
74
+ if (!mesh.geometry.getAttribute("uv1"))
75
+ mesh.geometry.setAttribute("uv1", mesh.geometry.getAttribute("uv"));
75
76
 
76
- const mat = this.gameObject["material"].clone();
77
+ const mat: Material = this.gameObject["material"].clone();
77
78
  this.gameObject["material"] = mat;
78
79
 
79
80
  this.gameObject["material"].onBeforeCompile = (shader, _) => {
81
+ shader.lightMapUv = "uv1";
80
82
  shader.uniforms.lightmap = this.lightmapUniform;
81
83
  shader.uniforms.lightmapScaleOffset = this.lightmapScaleOffsetUniform;
82
84
  };
@@ -107,13 +109,12 @@
107
109
  private setLightmapDebugMaterial() {
108
110
 
109
111
  // debug lightmaps
110
- this.gameObject["material"] = new THREE.ShaderMaterial({
112
+ this.gameObject["material"] = new ShaderMaterial({
111
113
  vertexShader: `
112
- attribute vec2 uv2;
113
- varying vec2 vUv2;
114
+ varying vec2 vUv1;
114
115
  void main()
115
116
  {
116
- vUv2 = uv2;
117
+ vUv1 = uv1;
117
118
  gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
118
119
  }
119
120
  `,
@@ -121,7 +122,7 @@
121
122
  uniform sampler2D lightmap;
122
123
  uniform float lightMapIntensity;
123
124
  uniform vec4 lightmapScaleOffset;
124
- varying vec2 vUv2;
125
+ varying vec2 vUv1;
125
126
 
126
127
  // took from threejs 05fc79cd52b79e8c3e8dec1e7dca72c5c39983a4
127
128
  vec4 conv_sRGBToLinear( in vec4 value ) {
@@ -129,7 +130,7 @@
129
130
  }
130
131
 
131
132
  void main() {
132
- vec2 lUv = vUv2.xy * lightmapScaleOffset.xy + vec2(lightmapScaleOffset.z, (1. - (lightmapScaleOffset.y + lightmapScaleOffset.w)));
133
+ vec2 lUv = vUv1.xy * lightmapScaleOffset.xy + vec2(lightmapScaleOffset.z, (1. - (lightmapScaleOffset.y + lightmapScaleOffset.w)));
133
134
 
134
135
  vec4 lightMapTexel = texture2D( lightmap, lUv);
135
136
  // The range of RGBM lightmaps goes from 0 to 34.49 (5^2.2) in linear space, and from 0 to 5 in gamma space.
@@ -138,7 +139,7 @@
138
139
  //lightMapTexel = conv_sRGBToLinear(lightMapTexel);
139
140
  // lightMapTexel.rgb = vec3(1.);
140
141
 
141
- // gl_FragColor = vec4(vUv2.xy, 0, 1);
142
+ // gl_FragColor = vec4(vUv1.xy, 0, 1);
142
143
  gl_FragColor = lightMapTexel;
143
144
  }
144
145
  `,
src/engine-components/webxr/WebARCameraBackground.ts CHANGED
@@ -3,7 +3,6 @@
3
3
  import { RGBAColor } from "../js-extensions/RGBAColor"
4
4
  import { WebXR } from "./WebXR";
5
5
  import {
6
- Camera as ThreeCamera,
7
6
  Scene,
8
7
  Texture,
9
8
  Mesh, MeshBasicMaterial,
@@ -11,7 +10,8 @@
11
10
  PlaneGeometry,
12
11
  ShaderLib,
13
12
  ShaderMaterial,
14
- DoubleSide
13
+ DoubleSide,
14
+ PerspectiveCamera
15
15
  } from "three";
16
16
 
17
17
  export class WebARCameraBackground extends Behaviour {
@@ -53,7 +53,7 @@
53
53
  const geometry = new PlaneGeometry();
54
54
  const scene = new Scene();
55
55
  scene.add(new Mesh(geometry, material));
56
- const camera = new ThreeCamera();
56
+ const camera = new PerspectiveCamera();
57
57
 
58
58
  return function forceTextureInitialization(renderer, texture) {
59
59
  material.map = texture;
src/engine-components/webxr/WebXR.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ArrayCamera, Color, Euler, EventDispatcher, Group, Matrix4, Mesh, MeshBasicMaterial, Object3D, Quaternion, RingGeometry, Texture, Vector3 } from 'three';
1
+ import { ArrayCamera, Color, Euler, EventDispatcher, Group, Matrix4, Mesh, MeshBasicMaterial, Object3D, Quaternion, RingGeometry, Texture, Vector3, WebXRArrayCamera } from 'three';
2
2
  import { ARButton } from '../../include/three/ARButton.js';
3
3
  import { VRButton } from '../../include/three/VRButton.js';
4
4
 
@@ -393,7 +393,7 @@
393
393
  private _originalXRRigRotation: Quaternion = new Quaternion();
394
394
 
395
395
  private onEnterXR(session: XRSession, frame: XRFrame) {
396
- console.log("[XR] session begin", session);
396
+ console.log("[XR] session begin", session, frame);
397
397
  WebXR._isInXr = true;
398
398
 
399
399
  this.ensureRig();
@@ -414,16 +414,16 @@
414
414
  // we set layers to sync raycasting and have a similar behaviour to unity
415
415
  const xr = this.context.renderer.xr;
416
416
  if (this.context.mainCamera) {
417
- //@ts-ignore
418
- const cam = xr.getCamera(this.context.mainCamera) as ArrayCamera;
417
+ const cam = xr.getCamera() as WebXRArrayCamera;
418
+ if(debugWebXR) console.log("WebXRCamera", cam);
419
419
  const cull = this.context.mainCameraComponent?.cullingMask;
420
- if(cull !== undefined){
420
+ if(cam && cull !== undefined){
421
421
  for (const c of cam.cameras) {
422
422
  c.layers.mask = cull;
423
423
  }
424
424
  cam.layers.mask = cull;
425
425
  }
426
- else {
426
+ else if (cam) {
427
427
  for (const c of cam.cameras) {
428
428
  c.layers.enableAll();
429
429
  }