@@ -40,7 +40,7 @@
|
|
40
40
|
}
|
41
41
|
@serializable()
|
42
42
|
set fieldOfView(val: number | undefined) {
|
43
|
-
const changed = this.
|
43
|
+
const changed = this.fieldOfView != val;
|
44
44
|
this._fov = val;
|
45
45
|
if (changed && this._cam) {
|
46
46
|
if (this._cam instanceof PerspectiveCamera) {
|
@@ -300,8 +300,11 @@
|
|
300
300
|
if (debug) console.log("Camera does not exist (apply clear flags)")
|
301
301
|
return;
|
302
302
|
}
|
303
|
-
|
304
|
-
|
303
|
+
|
304
|
+
// restore previous fov (e.g. when user was in VR or AR and the camera's fov has changed)
|
305
|
+
this.fieldOfView = this._fov;
|
306
|
+
|
307
|
+
if (debug) showBalloonMessage("apply Camera clear flags: " + this._clearFlags);
|
305
308
|
switch (this._clearFlags) {
|
306
309
|
case ClearFlags.Skybox:
|
307
310
|
if (Camera.backgroundShouldBeTransparent(this.context)) {
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import { Context } from "./engine_setup";
|
2
2
|
import { getParam, isMobileDevice, isMozillaXR } from "./engine_utils";
|
3
3
|
|
4
|
-
const debug = getParam("
|
4
|
+
const debug = getParam("debugoverlay");
|
5
5
|
export const arContainerClassName = "ar";
|
6
6
|
export const quitARClassName = "quit-ar";
|
7
7
|
|
@@ -148,12 +148,19 @@
|
|
148
148
|
svg.classList.add("quit-ar-button");
|
149
149
|
svg.setAttribute('width', "38px");
|
150
150
|
svg.setAttribute('height', "38px");
|
151
|
+
svg.style.cssText = `
|
152
|
+
background: radial-gradient(circle, rgba(0,0,0,.05) 70%, rgba(0,0,0,0) 100%);
|
153
|
+
border-radius: 50%;
|
154
|
+
`;
|
151
155
|
fixedButtonContainer.appendChild(svg);
|
152
156
|
|
153
157
|
var path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
154
158
|
path.setAttribute('d', 'M 12,12 L 28,28 M 28,12 12,28');
|
155
|
-
path.setAttribute('stroke', '#
|
159
|
+
path.setAttribute('stroke', '#fff');
|
156
160
|
path.setAttribute('stroke-width', "3px");
|
161
|
+
path.style.cssText = `
|
162
|
+
filter: drop-shadow(0 0px 1.2px rgba(0,0,0,.7));
|
163
|
+
`
|
157
164
|
svg.appendChild(path);
|
158
165
|
if (debug) console.log("Created fallback close button", svg, element);
|
159
166
|
}
|
@@ -13,7 +13,7 @@
|
|
13
13
|
const DEFAULT_KTX2_TRANSCODER_LOCATION ='https://www.gstatic.com/basis-universal/versioned/2021-04-15-ba1c3e4/';
|
14
14
|
|
15
15
|
let dracoLoader: DRACOLoader;
|
16
|
-
let meshoptDecoder: MeshoptDecoder;
|
16
|
+
let meshoptDecoder: typeof MeshoptDecoder;
|
17
17
|
let ktx2Loader: KTX2Loader;
|
18
18
|
|
19
19
|
export function setDracoDecoderPath(path: string | undefined) {
|
@@ -75,7 +75,7 @@
|
|
75
75
|
|
76
76
|
if (!ParticleSystemRenderer._havePatchedQuarkShaders) {
|
77
77
|
ParticleSystemRenderer._havePatchedQuarkShaders = true;
|
78
|
-
|
78
|
+
|
79
79
|
// HACK patch three.quarks fo three152+, see https://github.com/Alchemist0823/three.quarks/issues/56#issuecomment-1560825038
|
80
80
|
const _rebuild = TrailBatch.prototype.rebuildMaterial;
|
81
81
|
TrailBatch.prototype.rebuildMaterial = function () {
|
@@ -130,13 +130,17 @@
|
|
130
130
|
class MinMaxCurveFunction implements FunctionValueGenerator {
|
131
131
|
|
132
132
|
private _curve: MinMaxCurve;
|
133
|
+
private _factor: number;
|
133
134
|
|
134
|
-
constructor(curve: MinMaxCurve
|
135
|
+
constructor(curve: MinMaxCurve, factor: number = 1) {
|
136
|
+
this._curve = curve;
|
137
|
+
this._factor = factor;
|
138
|
+
}
|
135
139
|
|
136
140
|
type: "function" = "function";
|
137
141
|
|
138
142
|
genValue(t: number): number {
|
139
|
-
return this._curve.evaluate(t, Math.random());
|
143
|
+
return this._curve.evaluate(t, Math.random()) * this._factor;
|
140
144
|
}
|
141
145
|
toJSON(): FunctionJSON {
|
142
146
|
throw new Error("Method not implemented.");
|
@@ -416,7 +420,7 @@
|
|
416
420
|
initialize(particle: Particle): void {
|
417
421
|
const simulationSpeed = this.system.main.simulationSpeed;
|
418
422
|
|
419
|
-
const factor = 1
|
423
|
+
const factor = 1;
|
420
424
|
particle.startSpeed = this.system.main.startSpeed.evaluate(Math.random(), Math.random()) * factor;
|
421
425
|
particle.velocity.copy(this.system.shape.getDirection(particle.position)).multiplyScalar(particle.startSpeed);
|
422
426
|
if (this.system.inheritVelocity?.enabled) {
|
@@ -425,16 +429,16 @@
|
|
425
429
|
if (!particle[$startVelocity]) particle[$startVelocity] = particle.velocity.clone();
|
426
430
|
else particle[$startVelocity].copy(particle.velocity);
|
427
431
|
|
428
|
-
const gravityFactor = this.system.main.gravityModifier.evaluate(Math.random(), Math.random())
|
432
|
+
const gravityFactor = this.system.main.gravityModifier.evaluate(Math.random(), Math.random());
|
429
433
|
particle[$gravityFactor] = gravityFactor * simulationSpeed;
|
430
|
-
particle[$gravitySpeed] =
|
434
|
+
particle[$gravitySpeed] = gravityFactor * simulationSpeed * .5
|
431
435
|
|
432
436
|
particle[$velocityLerpFactor] = Math.random();
|
433
437
|
this.system.velocityOverLifetime?.init(particle);
|
434
438
|
|
435
439
|
this._gravityDirection.set(0, -1, 0);
|
436
440
|
if (this.system.main.simulationSpace === ParticleSystemSimulationSpace.Local)
|
437
|
-
this._gravityDirection.applyQuaternion(this.system.worldQuaternionInverted);
|
441
|
+
this._gravityDirection.applyQuaternion(this.system.worldQuaternionInverted).normalize();
|
438
442
|
}
|
439
443
|
|
440
444
|
update(particle: Particle, delta: number): void {
|
@@ -444,10 +448,9 @@
|
|
444
448
|
const baseVelocity = particle[$startVelocity];
|
445
449
|
let gravityFactor = particle[$gravityFactor];
|
446
450
|
if (gravityFactor !== 0) {
|
447
|
-
|
448
|
-
temp3.copy(this._gravityDirection).multiplyScalar(
|
449
|
-
particle[$gravitySpeed] += delta;
|
450
|
-
if (debug) Gizmos.DrawDirection(particle.position, temp3, 0x0000ff, 0, false, 10);
|
451
|
+
const factor = gravityFactor * particle[$gravitySpeed];
|
452
|
+
temp3.copy(this._gravityDirection).multiplyScalar(factor);
|
453
|
+
particle[$gravitySpeed] += delta * .05;
|
451
454
|
baseVelocity.add(temp3);
|
452
455
|
}
|
453
456
|
particle.velocity.copy(baseVelocity);
|
@@ -931,10 +934,10 @@
|
|
931
934
|
const duration = this.main.duration;
|
932
935
|
const lifetime = this.main.startLifetime.getMax();
|
933
936
|
const maxDurationToPrewarm = 1000;
|
934
|
-
const timeToSimulate = Math.min(duration, lifetime, maxDurationToPrewarm);
|
937
|
+
const timeToSimulate = Math.min(Math.max(duration, lifetime) / Math.max(.01, this.main.simulationSpeed), maxDurationToPrewarm);
|
935
938
|
const framesToSimulate = Math.ceil(timeToSimulate / dt);
|
936
939
|
const startTime = Date.now();
|
937
|
-
if (debug)
|
940
|
+
if (debug || this.name === "Snow")
|
938
941
|
console.log(`Particles ${this.name} - Prewarm for ${framesToSimulate} frames (${timeToSimulate} sec). Duration: ${duration}, Lifetime: ${lifetime}`);
|
939
942
|
for (let i = 0; i < framesToSimulate; i++) {
|
940
943
|
if (this.currentParticles >= this.maxParticles) break;
|
@@ -38,7 +38,14 @@
|
|
38
38
|
@serializable()
|
39
39
|
queryParameterName: string = "scene";
|
40
40
|
|
41
|
+
/**
|
42
|
+
* when enabled the scene name will be used as the query parameter (otherwise the scene index will be used)
|
43
|
+
* Needs `queryParameterName` set
|
44
|
+
* */
|
41
45
|
@serializable()
|
46
|
+
useSceneName: boolean = true;
|
47
|
+
|
48
|
+
@serializable()
|
42
49
|
clamp: boolean = true;
|
43
50
|
|
44
51
|
/** when enabled the new scene is pushed to the browser navigation history, only works with a valid query parameter set */
|
@@ -281,9 +288,15 @@
|
|
281
288
|
if (this.useSceneLighting)
|
282
289
|
this.context.sceneLighting.enable(scene)
|
283
290
|
if (this.useHistory && index >= 0) {
|
291
|
+
// take the index as the query parameter value
|
292
|
+
let queryParameterValue = index.toString();
|
293
|
+
// unless the user defines that he wants to use the scene name
|
294
|
+
if (this.useSceneName) {
|
295
|
+
queryParameterValue = sceneUriToName(scene.uri);
|
296
|
+
}
|
284
297
|
// save the loaded scene as an url parameter
|
285
298
|
if (this.queryParameterName?.length)
|
286
|
-
setParamWithoutReload(this.queryParameterName,
|
299
|
+
setParamWithoutReload(this.queryParameterName, queryParameterValue, this.useHistory);
|
287
300
|
// or set the history state without updating the url parameter
|
288
301
|
else {
|
289
302
|
const lastState = history.state;
|
@@ -328,9 +341,11 @@
|
|
328
341
|
}
|
329
342
|
else {
|
330
343
|
// Try to find a scene with a matching name
|
344
|
+
// we don't care about casing. e.g. Scene1 and scene1 should both match
|
345
|
+
const lowerCaseValue = value.toLowerCase();
|
331
346
|
for (let i = 0; i < this.scenes.length; i++) {
|
332
347
|
const scene = this.scenes[i];
|
333
|
-
if (scene.uri.toLowerCase().includes(
|
348
|
+
if (sceneUriToName(scene.uri).toLowerCase().includes(lowerCaseValue)) {
|
334
349
|
return this.select(i);;
|
335
350
|
}
|
336
351
|
}
|
@@ -349,8 +364,15 @@
|
|
349
364
|
}
|
350
365
|
|
351
366
|
|
367
|
+
function sceneUriToName(uri: string): string {
|
368
|
+
const name = uri.split("/").pop();
|
369
|
+
let value = name?.split(".").shift();
|
370
|
+
if (value?.length) return value;
|
371
|
+
return uri;
|
372
|
+
}
|
352
373
|
|
353
374
|
|
375
|
+
|
354
376
|
class PreLoadScheduler {
|
355
377
|
maxLoadAhead: number;
|
356
378
|
maxLoadBehind: number;
|
@@ -21,7 +21,7 @@
|
|
21
21
|
const debugWebXR = getParam("debugwebxr");
|
22
22
|
|
23
23
|
export async function detectARSupport() {
|
24
|
-
if(isMozillaXR()) return true;
|
24
|
+
if (isMozillaXR()) return true;
|
25
25
|
if ("xr" in navigator) {
|
26
26
|
//@ts-ignore
|
27
27
|
return (await navigator["xr"].isSessionSupported('immersive-ar')) === true;
|
@@ -136,7 +136,7 @@
|
|
136
136
|
return arButton;
|
137
137
|
}
|
138
138
|
|
139
|
-
private static onModifyAROptions(options){
|
139
|
+
private static onModifyAROptions(options) {
|
140
140
|
WebXR.dispatchEvent(WebXREvent.ModifyAROptions, options);
|
141
141
|
}
|
142
142
|
|
@@ -255,11 +255,10 @@
|
|
255
255
|
this.context.appendHTMLElement(buttonsContainer);
|
256
256
|
|
257
257
|
const forceButtons = debugWebXR;
|
258
|
-
if(debugWebXR) console.log("ARSupported?", arSupported, "VRSupported?", vrSupported);
|
258
|
+
if (debugWebXR) console.log("ARSupported?", arSupported, "VRSupported?", vrSupported);
|
259
259
|
|
260
260
|
// AR support
|
261
|
-
if (forceButtons || (this.createARButton && this.enableAR && arSupported))
|
262
|
-
{
|
261
|
+
if (forceButtons || (this.createARButton && this.enableAR && arSupported)) {
|
263
262
|
arButton = WebXR.createARButton(this);
|
264
263
|
this._arButton = arButton;
|
265
264
|
buttonsContainer.appendChild(arButton);
|
@@ -271,7 +270,7 @@
|
|
271
270
|
this._vrButton = vrButton;
|
272
271
|
buttonsContainer.appendChild(vrButton);
|
273
272
|
}
|
274
|
-
|
273
|
+
|
275
274
|
setTimeout(() => {
|
276
275
|
WebXR.resetButtonStyles(vrButton);
|
277
276
|
WebXR.resetButtonStyles(arButton);
|
@@ -289,11 +288,11 @@
|
|
289
288
|
// TODO: figure out why screen is black if we enable the code written here
|
290
289
|
// const referenceSpace = renderer.xr.getReferenceSpace();
|
291
290
|
const session = this.context.renderer.xr.getSession();
|
292
|
-
|
293
291
|
|
292
|
+
|
294
293
|
if (session) {
|
295
294
|
const pose = frame.getViewerPose(this.context.renderer.xr.getReferenceSpace());
|
296
|
-
if(!pose) return;
|
295
|
+
if (!pose) return;
|
297
296
|
this._currentHeadPose = pose;
|
298
297
|
const transform: XRRigidTransform = pose?.transform;
|
299
298
|
if (transform) {
|
@@ -356,7 +355,7 @@
|
|
356
355
|
this._originalCameraRotation.copy(getWorldQuaternion(this.context.mainCamera));
|
357
356
|
this._originalCameraParent = this.context.mainCamera.parent;
|
358
357
|
}
|
359
|
-
if(this.Rig){
|
358
|
+
if (this.Rig) {
|
360
359
|
this._originalXRRigParent = this.Rig.parent;
|
361
360
|
this._originalXRRigPosition.copy(this.Rig.position);
|
362
361
|
this._originalXRRigRotation.copy(this.Rig.quaternion);
|
@@ -415,9 +414,9 @@
|
|
415
414
|
const xr = this.context.renderer.xr;
|
416
415
|
if (this.context.mainCamera) {
|
417
416
|
const cam = xr.getCamera() as WebXRArrayCamera;
|
418
|
-
if(debugWebXR) console.log("WebXRCamera", cam);
|
417
|
+
if (debugWebXR) console.log("WebXRCamera", cam);
|
419
418
|
const cull = this.context.mainCameraComponent?.cullingMask;
|
420
|
-
if(cam && cull !== undefined){
|
419
|
+
if (cam && cull !== undefined) {
|
421
420
|
for (const c of cam.cameras) {
|
422
421
|
c.layers.mask = cull;
|
423
422
|
}
|
@@ -467,8 +466,14 @@
|
|
467
466
|
|
468
467
|
const wasInAR = this._isInAR;
|
469
468
|
|
470
|
-
if (
|
471
|
-
this.
|
469
|
+
if (session) {
|
470
|
+
if (this._isInAR) {
|
471
|
+
this.webAR?.onEnd(session);
|
472
|
+
}
|
473
|
+
else {
|
474
|
+
// if in VR we want to restore the FOV
|
475
|
+
this.context.mainCameraComponent?.applyClearFlagsIfIsActiveCamera();
|
476
|
+
}
|
472
477
|
}
|
473
478
|
|
474
479
|
this._isInAR = false;
|
@@ -491,7 +496,7 @@
|
|
491
496
|
this.context.mainCamera.scale.set(1, 1, 1);
|
492
497
|
}
|
493
498
|
|
494
|
-
if(wasInAR){
|
499
|
+
if (wasInAR) {
|
495
500
|
this._originalXRRigParent?.add(this.rig);
|
496
501
|
this.rig.position.copy(this._originalXRRigPosition);
|
497
502
|
this.rig.quaternion.copy(this._originalXRRigRotation);
|