@@ -226,7 +226,6 @@
|
|
226
226
|
onDisable(): void {
|
227
227
|
if (this.mixer) {
|
228
228
|
this.mixer.stopAllAction();
|
229
|
-
this.mixer = undefined;
|
230
229
|
}
|
231
230
|
}
|
232
231
|
/** @internal */
|
@@ -405,7 +405,7 @@
|
|
405
405
|
const dur = state.motion.clip!.duration;
|
406
406
|
const normalizedTime = dur <= 0 ? 1 : Math.abs(action.time / dur);
|
407
407
|
let exitTime = transition.exitTime;
|
408
|
-
|
408
|
+
|
409
409
|
// When the animation is playing backwards we need to check exit time inverted
|
410
410
|
if (action.timeScale < 0) {
|
411
411
|
exitTime = 1 - exitTime;
|
@@ -752,13 +752,18 @@
|
|
752
752
|
for (const behaviour of state.behaviours) {
|
753
753
|
if (!behaviour?.typeName) continue;
|
754
754
|
const type = TypeStore.get(behaviour.typeName);
|
755
|
-
|
756
|
-
|
757
|
-
instance.
|
758
|
-
|
759
|
-
|
755
|
+
if (type) {
|
756
|
+
const instance: StateMachineBehaviour = new type() as StateMachineBehaviour;
|
757
|
+
if (instance.isStateMachineBehaviour) {
|
758
|
+
instance._context = this.context ?? undefined;
|
759
|
+
assign(instance, behaviour.properties);
|
760
|
+
behaviour.instance = instance;
|
761
|
+
}
|
762
|
+
if (debug) console.log("Created animator controller behaviour", state.name, behaviour.typeName, behaviour.properties, instance);
|
760
763
|
}
|
761
|
-
|
764
|
+
else {
|
765
|
+
if (debug || isDevEnvironment()) console.warn("Could not find AnimatorBehaviour type: " + behaviour.typeName);
|
766
|
+
}
|
762
767
|
}
|
763
768
|
}
|
764
769
|
}
|
@@ -128,9 +128,9 @@
|
|
128
128
|
* Call to fit the shadows to the scene.
|
129
129
|
*/
|
130
130
|
fitShadows() {
|
131
|
-
if (debug) console.
|
132
|
-
setAutoFitEnabled(this.
|
133
|
-
const box = getBoundingBox(this.context.scene.children, [this.
|
131
|
+
if (debug) console.warn("Fitting shadows to scene");
|
132
|
+
setAutoFitEnabled(this.shadowsRoot, false);
|
133
|
+
const box = getBoundingBox(this.context.scene.children, [this.shadowsRoot]);
|
134
134
|
// expand box in all directions (except below ground)
|
135
135
|
// 0.75 expands by 75% in each direction
|
136
136
|
// The "32" is pretty much heuristically determined – adjusting the value until we don't get a visible border anymore.
|
@@ -154,13 +154,14 @@
|
|
154
154
|
this.shadowsRoot.scale.set(box.max.x - min.x, box.max.y - min.y, box.max.z - min.z);
|
155
155
|
this.applyMinSize();
|
156
156
|
this.shadowsRoot.matrixWorldNeedsUpdate = true;
|
157
|
+
if (debug) console.log("Fitted shadows to scene", this.shadowsRoot.scale.clone());
|
157
158
|
}
|
158
159
|
|
159
160
|
|
160
161
|
/** @internal */
|
161
162
|
awake() {
|
162
163
|
// ignore self for autofitting
|
163
|
-
setAutoFitEnabled(this.
|
164
|
+
setAutoFitEnabled(this.shadowsRoot, false);
|
164
165
|
}
|
165
166
|
|
166
167
|
/** @internal */
|
@@ -194,6 +194,7 @@
|
|
194
194
|
div[data-needle_engine_debug_overlay] {
|
195
195
|
font-family: 'Roboto Flex', sans-serif;
|
196
196
|
font-weight: 400;
|
197
|
+
font-size: 16px;
|
197
198
|
}
|
198
199
|
|
199
200
|
div[data-needle_engine_debug_overlay] strong {
|
@@ -38,7 +38,10 @@
|
|
38
38
|
* Unregister an animation mixer instance.
|
39
39
|
*/
|
40
40
|
unregisterAnimationMixer(mixer: AnimationMixer | null | undefined): void {
|
41
|
-
if (!mixer)
|
41
|
+
if (!mixer) {
|
42
|
+
console.warn("AnimationsRegistry.unregisterAnimationMixer called with null or undefined mixer")
|
43
|
+
return;
|
44
|
+
}
|
42
45
|
const index = this.mixers.indexOf(mixer);
|
43
46
|
if (index === -1) return;
|
44
47
|
this.mixers.splice(index, 1);
|
@@ -87,11 +90,17 @@
|
|
87
90
|
* @param file The GLTF file to assign the animations from
|
88
91
|
*/
|
89
92
|
static assignAnimationsFromFile(file: Pick<GLTF, "animations" | "scene">, opts?: { createAnimationComponent(obj: Object3D, animation: AnimationClip): IAnimationComponent }) {
|
90
|
-
if (!file || !file.animations)
|
93
|
+
if (!file || !file.animations) {
|
94
|
+
console.debug("No animations found in file");
|
95
|
+
return;
|
96
|
+
}
|
91
97
|
|
92
98
|
for (let i = 0; i < file.animations.length; i++) {
|
93
99
|
const animation = file.animations[i];
|
94
|
-
if (!animation.tracks || animation.tracks.length <= 0)
|
100
|
+
if (!animation.tracks || animation.tracks.length <= 0) {
|
101
|
+
console.warn("Animation has no tracks");
|
102
|
+
continue;
|
103
|
+
}
|
95
104
|
for (const t in animation.tracks) {
|
96
105
|
const track = animation.tracks[t];
|
97
106
|
const parsedPath = PropertyBinding.parseTrackName(track.name);
|
@@ -47,9 +47,21 @@
|
|
47
47
|
}
|
48
48
|
|
49
49
|
type SkyboxAttributes = {
|
50
|
-
/** URL to .exr, .hdr, .png, .jpg to be used as skybox */
|
50
|
+
/** use background-image instead - URL to .exr, .hdr, .png, .jpg to be used as skybox */
|
51
51
|
"skybox-image"?: string,
|
52
52
|
/** URL to .exr, .hdr, .png, .jpg to be used as skybox */
|
53
|
+
"background-image"?: string,
|
54
|
+
/** Blurs the background image (if any) - value between 0 and 1
|
55
|
+
* 0: no blur
|
56
|
+
* 1: full blur
|
57
|
+
*/
|
58
|
+
"background-blurriness"?: number,
|
59
|
+
/**
|
60
|
+
* Background color to be used if no skybox or background image is provided.
|
61
|
+
* For example: "skybox-color='#ff0000'" will set the background color to red.
|
62
|
+
*/
|
63
|
+
"background-color"?: string,
|
64
|
+
/** URL to .exr, .hdr, .png, .jpg to be used for lighting */
|
53
65
|
"environment-image"?: string,
|
54
66
|
}
|
55
67
|
|
@@ -19,6 +19,7 @@
|
|
19
19
|
private contentElement: HTMLElement | null = null;
|
20
20
|
private originalDomOverlayParent: ParentNode | null = null;
|
21
21
|
|
22
|
+
|
22
23
|
requestEndAR = () => {
|
23
24
|
this.onRequestedEndAR();
|
24
25
|
}
|
@@ -151,20 +152,27 @@
|
|
151
152
|
|
152
153
|
var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
153
154
|
svg.classList.add("quit-ar-button");
|
154
|
-
svg.setAttribute('width', "
|
155
|
-
svg.setAttribute('height', "
|
155
|
+
svg.setAttribute('width', "40px");
|
156
|
+
svg.setAttribute('height', "40px");
|
156
157
|
svg.style.cssText = `
|
157
|
-
background:
|
158
|
+
background: rgba(255, 255, 255, .4);
|
159
|
+
-webkit-backdrop-filter: blur(8px);
|
160
|
+
backdrop-filter: blur(8px);
|
158
161
|
border-radius: 50%;
|
162
|
+
box-shadow: 0 0 5px rgba(0,0,0,.3);
|
163
|
+
outline: 1px solid rgba(255, 255, 255, .6);
|
164
|
+
display: flex;
|
165
|
+
justify-content: center;
|
166
|
+
align-items: center;
|
159
167
|
`;
|
160
168
|
fixedButtonContainer.appendChild(svg);
|
161
169
|
|
162
170
|
var path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
163
171
|
path.setAttribute('d', 'M 12,12 L 28,28 M 28,12 12,28');
|
164
|
-
path.setAttribute('stroke', '#
|
165
|
-
path.setAttribute('stroke-width', "
|
172
|
+
path.setAttribute('stroke', '#000000');
|
173
|
+
path.setAttribute('stroke-width', "2px");
|
166
174
|
path.style.cssText = `
|
167
|
-
filter: drop-shadow(0 0px 1.2px rgba(0,0,0,.7))
|
175
|
+
/**filter: drop-shadow(0 0px 1.2px rgba(0,0,0,.7));**/
|
168
176
|
`
|
169
177
|
svg.appendChild(path);
|
170
178
|
if (debug) console.log("Created fallback close button", svg, element);
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { AgXToneMapping, LinearToneMapping, NeutralToneMapping, NoToneMapping } from "three";
|
1
|
+
import { AgXToneMapping, Color, LinearToneMapping, NeutralToneMapping, NoToneMapping } from "three";
|
2
2
|
|
3
3
|
import { getLoader, registerLoader } from "../engine/engine_gltf.js";
|
4
4
|
import { GameObject } from "../engine-components/Component.js";
|
@@ -40,6 +40,8 @@
|
|
40
40
|
"ktx2DecoderPath",
|
41
41
|
"tone-mapping",
|
42
42
|
"tone-mapping-exposure",
|
43
|
+
"background-blurriness",
|
44
|
+
"background-color",
|
43
45
|
]
|
44
46
|
|
45
47
|
// https://developers.google.com/web/fundamentals/web-components/customelements
|
@@ -308,12 +310,24 @@
|
|
308
310
|
setKtx2TranscoderPath(newValue);
|
309
311
|
break;
|
310
312
|
case "tone-mapping": {
|
311
|
-
this.
|
313
|
+
this.applyAttributes();
|
312
314
|
break;
|
313
315
|
}
|
314
316
|
case "tone-mapping-exposure": {
|
315
|
-
this.
|
317
|
+
this.applyAttributes();
|
318
|
+
break;
|
316
319
|
}
|
320
|
+
case "background-blurriness": {
|
321
|
+
const value = parseFloat(newValue);
|
322
|
+
if (value != undefined && this._context) {
|
323
|
+
this._context.scene.backgroundBlurriness = value;
|
324
|
+
}
|
325
|
+
break;
|
326
|
+
}
|
327
|
+
case "background-color": {
|
328
|
+
this.applyAttributes();
|
329
|
+
break;
|
330
|
+
}
|
317
331
|
}
|
318
332
|
}
|
319
333
|
|
@@ -453,7 +467,7 @@
|
|
453
467
|
this._context.alias = alias;
|
454
468
|
this._createContextPromise = this._context.create(args);
|
455
469
|
const success = await this._createContextPromise;
|
456
|
-
this.
|
470
|
+
this.applyAttributes();
|
457
471
|
|
458
472
|
if (debug) console.warn("--------------\nNeedle Engine: finished loading " + loadId + "\n", filesToLoad, `Aborted? ${controller.signal.aborted}`);
|
459
473
|
if (this._loadId !== loadId || controller.signal.aborted) {
|
@@ -477,36 +491,57 @@
|
|
477
491
|
}));
|
478
492
|
}
|
479
493
|
|
480
|
-
private
|
481
|
-
if
|
494
|
+
private applyAttributes() {
|
495
|
+
// set tonemapping if configured
|
496
|
+
if (this._context.renderer) {
|
497
|
+
const attribute = this.getAttribute("tonemapping") || this.getAttribute("tone-mapping") as TonemappingAttributeOptions | null | undefined;
|
498
|
+
switch (attribute?.toLowerCase()) {
|
499
|
+
case "none":
|
500
|
+
this._context.renderer.toneMapping = NoToneMapping;
|
501
|
+
break;
|
502
|
+
case "linear":
|
503
|
+
this._context.renderer.toneMapping = LinearToneMapping;
|
504
|
+
break;
|
505
|
+
case "neutral":
|
506
|
+
this._context.renderer.toneMapping = NeutralToneMapping;
|
507
|
+
break;
|
508
|
+
case "agx":
|
509
|
+
this._context.renderer.toneMapping = AgXToneMapping;
|
510
|
+
break;
|
511
|
+
default:
|
512
|
+
if (attribute !== null && attribute !== undefined) {
|
513
|
+
console.warn("Invalid tone-mapping attribute: " + attribute);
|
514
|
+
}
|
515
|
+
}
|
482
516
|
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
case "linear":
|
490
|
-
this._context.renderer.toneMapping = LinearToneMapping;
|
491
|
-
break;
|
492
|
-
case "neutral":
|
493
|
-
this._context.renderer.toneMapping = NeutralToneMapping;
|
494
|
-
break;
|
495
|
-
case "agx":
|
496
|
-
this._context.renderer.toneMapping = AgXToneMapping;
|
497
|
-
break;
|
498
|
-
default:
|
499
|
-
if (attribute !== null && attribute !== undefined) {
|
500
|
-
console.warn("Invalid tone-mapping attribute: " + attribute);
|
501
|
-
}
|
517
|
+
const exposure = this.getAttribute("tone-mapping-exposure");
|
518
|
+
if (exposure !== null && exposure !== undefined) {
|
519
|
+
const value = parseFloat(exposure);
|
520
|
+
if (!isNaN(value))
|
521
|
+
this._context.renderer.toneMappingExposure = value;
|
522
|
+
}
|
502
523
|
}
|
503
524
|
|
504
|
-
const
|
505
|
-
if (
|
506
|
-
const value = parseFloat(
|
507
|
-
if (
|
508
|
-
this._context.
|
525
|
+
const backgroundBlurriness = this.getAttribute("background-blurriness");
|
526
|
+
if (backgroundBlurriness !== null && backgroundBlurriness !== undefined) {
|
527
|
+
const value = parseFloat(backgroundBlurriness);
|
528
|
+
if (value !== undefined && this._context) {
|
529
|
+
this._context.scene.backgroundBlurriness = value;
|
530
|
+
}
|
509
531
|
}
|
532
|
+
|
533
|
+
const backgroundColor = this.getAttribute("background-color");
|
534
|
+
if (this._context) {
|
535
|
+
if (typeof backgroundColor === "string" && backgroundColor.length > 0) {
|
536
|
+
const currentBackground = this._context.scene.background;
|
537
|
+
if (currentBackground instanceof Color) {
|
538
|
+
currentBackground.set(backgroundColor);
|
539
|
+
}
|
540
|
+
else {
|
541
|
+
this._context.scene.background = new Color(backgroundColor);
|
542
|
+
}
|
543
|
+
}
|
544
|
+
}
|
510
545
|
}
|
511
546
|
|
512
547
|
private onXRSessionStarted = () => {
|
@@ -774,7 +809,7 @@
|
|
774
809
|
const isIgnored = ignoredCharacters.includes(c);
|
775
810
|
if (isIgnored) continue;
|
776
811
|
const isFirstCharacter = displayName.length === 0;
|
777
|
-
|
812
|
+
|
778
813
|
if (isFirstCharacter) {
|
779
814
|
c = c.toUpperCase();
|
780
815
|
}
|
@@ -784,7 +819,7 @@
|
|
784
819
|
if (lastCharacterWasSpace) {
|
785
820
|
c = c.toUpperCase();
|
786
821
|
}
|
787
|
-
|
822
|
+
|
788
823
|
lastCharacterWasSpace = false;
|
789
824
|
displayName += c;
|
790
825
|
|
@@ -2,20 +2,24 @@
|
|
2
2
|
|
3
3
|
const debug = getParam("debugtypes");
|
4
4
|
|
5
|
+
declare type Type = new (...args: any[]) => any;
|
5
6
|
|
6
7
|
class _TypeStore {
|
7
8
|
|
8
|
-
private _types =
|
9
|
+
private _types: Map<string, Type> = new Map();
|
9
10
|
|
10
11
|
constructor() {
|
11
12
|
if (debug) console.warn("TypeStore: Created", this);
|
12
13
|
}
|
13
14
|
|
14
|
-
|
15
|
+
/**
|
16
|
+
* add a type to the store
|
17
|
+
*/
|
18
|
+
public add(key: string, type: Type) {
|
15
19
|
if (debug) console.warn("ADD TYPE", key);
|
16
|
-
const existing = this._types
|
17
|
-
if (existing
|
18
|
-
this._types
|
20
|
+
const existing = this._types.get(key);
|
21
|
+
if (!existing)
|
22
|
+
this._types.set(key, type);
|
19
23
|
else {
|
20
24
|
if (debug) {
|
21
25
|
if (existing !== type) {
|
@@ -25,9 +29,23 @@
|
|
25
29
|
}
|
26
30
|
}
|
27
31
|
|
28
|
-
|
29
|
-
|
32
|
+
/**
|
33
|
+
* @returns the type for the given key if registered
|
34
|
+
*/
|
35
|
+
public get(key: string): Type | null {
|
36
|
+
return this._types.get(key) || null;
|
30
37
|
}
|
38
|
+
|
39
|
+
/**
|
40
|
+
* @returns the key/name for the given type if registered
|
41
|
+
*/
|
42
|
+
public getKey(type: Type): string | null {
|
43
|
+
for (const [key, value] of this._types) {
|
44
|
+
if (value === type)
|
45
|
+
return key;
|
46
|
+
}
|
47
|
+
return null;
|
48
|
+
}
|
31
49
|
}
|
32
50
|
|
33
51
|
export const $BuiltInTypeFlag = Symbol("BuiltInType");
|
@@ -40,7 +58,7 @@
|
|
40
58
|
*
|
41
59
|
* `export class MyType extends Behaviour { ... }`
|
42
60
|
*/
|
43
|
-
export const registerType = function (constructor:
|
61
|
+
export const registerType = function (constructor: Type) {
|
44
62
|
if (!TypeStore.get(constructor.name))
|
45
63
|
TypeStore.add(constructor.name, constructor);
|
46
64
|
}
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import { isDevEnvironment } from "./debug/debug.js";
|
1
2
|
import { getParam } from "./engine_utils.js";
|
2
3
|
|
3
4
|
const debug = getParam("debugfileformat");
|
@@ -135,9 +136,25 @@
|
|
135
136
|
console.debug("OBJ detected");
|
136
137
|
return "obj";
|
137
138
|
}
|
139
|
+
// Check if it starts with "v " (vertex) or "f " (face) indicating that it's an OBJ file
|
140
|
+
// If the OBJ file does lack a header altogether and no other check matched
|
141
|
+
else if ((bytes[0] == 118 && bytes[1] == 32) || (bytes[0] == 102 && bytes[1] == 32)) {
|
142
|
+
console.debug("OBJ detected (the file has no header and starts with vertex or face)");
|
143
|
+
return "obj";
|
144
|
+
}
|
145
|
+
// Check if the file starts with "# File exported by ZBrush"
|
146
|
+
else if(bytes[0] == 35 && bytes[1] == 32 && bytes[2] == 70 && bytes[3] == 105 && bytes[4] == 108 && bytes[5] == 101 && bytes[6] == 32 && bytes[7] == 101 && bytes[8] == 120 && bytes[9] == 112 && bytes[10] == 111 && bytes[11] == 114 && bytes[12] == 116 && bytes[13] == 101 && bytes[14] == 100 && bytes[15] == 32 && bytes[16] == 98 && bytes[17] == 121 && bytes[18] == 32 && bytes[19] == 90 && bytes[20] == 66 && bytes[21] == 114 && bytes[22] == 117 && bytes[23] == 115 && bytes[24] == 104) {
|
147
|
+
console.debug("OBJ detected (exported by ZBrush)");
|
148
|
+
return "obj";
|
149
|
+
}
|
138
150
|
else {
|
139
|
-
|
140
|
-
|
151
|
+
if (isDevEnvironment() || debug) {
|
152
|
+
const text = new TextDecoder().decode(data.slice(0, 16));
|
153
|
+
console.debug("Could not determine file type from binary data: \"" + text + "...\"", bytes);
|
154
|
+
}
|
155
|
+
else {
|
156
|
+
console.debug("Could not determine file type from binary data", bytes);
|
157
|
+
}
|
141
158
|
}
|
142
159
|
|
143
160
|
// const text = new TextDecoder().decode(data);
|
@@ -44,6 +44,7 @@
|
|
44
44
|
priority?: number;
|
45
45
|
/** Experimental. Allows to put two buttons in one row for the compact layout */
|
46
46
|
class?: "row2";
|
47
|
+
title?: string;
|
47
48
|
}
|
48
49
|
|
49
50
|
/**
|
@@ -302,6 +303,9 @@
|
|
302
303
|
top: auto;
|
303
304
|
bottom: min(30px, 10vh);
|
304
305
|
}
|
306
|
+
#root.top {
|
307
|
+
top: calc(.7rem + env(safe-area-inset-top));
|
308
|
+
}
|
305
309
|
|
306
310
|
.wrapper {
|
307
311
|
position: relative;
|
@@ -367,8 +371,8 @@
|
|
367
371
|
flex-direction: row;
|
368
372
|
}
|
369
373
|
|
370
|
-
.options >
|
371
|
-
height: 2.25rem;
|
374
|
+
.options > *, ::slotted(*) {
|
375
|
+
max-height: 2.25rem;
|
372
376
|
padding: .4rem .5rem;
|
373
377
|
}
|
374
378
|
|
@@ -563,6 +567,7 @@
|
|
563
567
|
.compact .options > * {
|
564
568
|
font-size: 1.2rem;
|
565
569
|
padding: .6rem .5rem;
|
570
|
+
width: 100%;
|
566
571
|
}
|
567
572
|
.compact.has-options .logo {
|
568
573
|
border: none;
|
@@ -858,6 +863,9 @@
|
|
858
863
|
button.textContent = node.label;
|
859
864
|
button.onclick = node.onClick;
|
860
865
|
button.setAttribute("priority", node.priority?.toString() ?? "0");
|
866
|
+
if (node.title) {
|
867
|
+
button.title = node.title;
|
868
|
+
}
|
861
869
|
if (node.icon) {
|
862
870
|
const icon = getIconElement(node.icon);
|
863
871
|
if (node.iconSide === "right") {
|
@@ -988,7 +996,7 @@
|
|
988
996
|
if (!this._domElement) return;
|
989
997
|
|
990
998
|
const width = this._domElement.clientWidth;
|
991
|
-
if (width <
|
999
|
+
if (width < 100) {
|
992
1000
|
clearTimeout(this._timeoutHandle!);
|
993
1001
|
this.root.classList.add("compact");
|
994
1002
|
this.foldout.classList.add("floating-panel-style");
|
@@ -39,7 +39,7 @@
|
|
39
39
|
|
40
40
|
ContextRegistry.addContextCreatedCallback((args) => {
|
41
41
|
const context = args.context;
|
42
|
-
const skyboxImage = context.domElement.getAttribute("skybox-image");
|
42
|
+
const skyboxImage = context.domElement.getAttribute("skybox-image") || context.domElement.getAttribute("background-image");
|
43
43
|
const environmentImage = context.domElement.getAttribute("environment-image");
|
44
44
|
const promises = new Array<Promise<any>>();
|
45
45
|
if (skyboxImage) {
|
@@ -48,7 +48,7 @@
|
|
48
48
|
// if the user is loading a GLB without a camera then the CameraUtils (which creates the default camera)
|
49
49
|
// checks if we have this attribute set and then sets the skybox clearflags accordingly
|
50
50
|
// if the user has a GLB with a camera but set to solid color then the skybox image is not visible -> we will just warn then and not override the camera settings
|
51
|
-
if (context.mainCameraComponent?.clearFlags !== ClearFlags.Skybox) console.warn("\"skybox-image\" attribute has no effect: camera clearflags are not set to \"Skybox\"");
|
51
|
+
if (context.mainCameraComponent?.clearFlags !== ClearFlags.Skybox) console.warn("\"skybox-image\"/\"background-image\" attribute has no effect: camera clearflags are not set to \"Skybox\"");
|
52
52
|
const promise = createRemoteSkyboxComponent(context, skyboxImage, true, false, "skybox-image");
|
53
53
|
promises.push(promise);
|
54
54
|
}
|
@@ -69,6 +69,18 @@
|
|
69
69
|
y!: number;
|
70
70
|
}
|
71
71
|
|
72
|
+
function updateTextureIfNecessary(tex: Texture) {
|
73
|
+
if (!tex) return;
|
74
|
+
if (tex.colorSpace != SRGBColorSpace) {
|
75
|
+
tex.colorSpace = SRGBColorSpace;
|
76
|
+
tex.needsUpdate = true;
|
77
|
+
}
|
78
|
+
if (tex.minFilter == NearestFilter && tex.magFilter == NearestFilter) {
|
79
|
+
tex.anisotropy = 1;
|
80
|
+
tex.needsUpdate = true;
|
81
|
+
}
|
82
|
+
}
|
83
|
+
|
72
84
|
/**
|
73
85
|
* A sprite is a mesh that represents a 2D image
|
74
86
|
*/
|
@@ -115,10 +127,7 @@
|
|
115
127
|
get material() {
|
116
128
|
if (!this._material) {
|
117
129
|
if (this.texture) {
|
118
|
-
this.texture
|
119
|
-
if (this.texture.minFilter == NearestFilter && this.texture.magFilter == NearestFilter)
|
120
|
-
this.texture.anisotropy = 1;
|
121
|
-
this.texture.needsUpdate = true;
|
130
|
+
updateTextureIfNecessary(this.texture);
|
122
131
|
}
|
123
132
|
this._material = new MeshBasicMaterial({
|
124
133
|
map: this.texture,
|
@@ -204,10 +213,7 @@
|
|
204
213
|
const sprite = this.spriteSheet.sprites[index];
|
205
214
|
const tex = sprite?.texture;
|
206
215
|
if (!tex) return;
|
207
|
-
tex
|
208
|
-
if (tex.minFilter == NearestFilter && tex.magFilter == NearestFilter)
|
209
|
-
tex.anisotropy = 1;
|
210
|
-
tex.needsUpdate = true;
|
216
|
+
updateTextureIfNecessary(tex);
|
211
217
|
|
212
218
|
if (!sprite["__hasLoadedProgressive"]) {
|
213
219
|
sprite["__hasLoadedProgressive"] = true;
|
@@ -318,10 +324,12 @@
|
|
318
324
|
if (!this._spriteSheet) {
|
319
325
|
this._spriteSheet = SpriteData.create();
|
320
326
|
}
|
321
|
-
this._spriteSheet.sprite
|
322
|
-
|
327
|
+
if (this._spriteSheet.sprite != value) {
|
328
|
+
this._spriteSheet.sprite = value;
|
329
|
+
this.updateSprite();
|
330
|
+
}
|
323
331
|
}
|
324
|
-
else {
|
332
|
+
else if (value != this._spriteSheet) {
|
325
333
|
this._spriteSheet = value;
|
326
334
|
this.updateSprite();
|
327
335
|
}
|
@@ -680,6 +680,14 @@
|
|
680
680
|
private _didMultitouch: boolean = false;
|
681
681
|
|
682
682
|
private touchStart = (evt: TouchEvent) => {
|
683
|
+
if (evt.defaultPrevented) return;
|
684
|
+
|
685
|
+
// let isValidTouch = true;
|
686
|
+
// isValidTouch = evt.target === this.context.domElement || evt.target === this.context.renderer.domElement;
|
687
|
+
// if (!isValidTouch) {
|
688
|
+
// return;
|
689
|
+
// }
|
690
|
+
|
683
691
|
for (let i = 0; i < evt.changedTouches.length; i++) {
|
684
692
|
const touch = evt.changedTouches[i];
|
685
693
|
// if a user starts swiping in the top area of the screen
|