@@ -5,6 +5,7 @@
|
|
5
5
|
import { serializable } from "../engine/engine_serialization_decorator.js";
|
6
6
|
import { ApplicationEvents } from "../engine/engine_application.js";
|
7
7
|
import { Audio, AudioContext, AudioLoader, PositionalAudio } from "three";
|
8
|
+
import { isDevEnvironment } from "../engine/debug/index.js";
|
8
9
|
|
9
10
|
|
10
11
|
const debug = utils.getParam("debugaudio");
|
@@ -327,8 +328,10 @@
|
|
327
328
|
// We only support strings and media stream
|
328
329
|
// TODO: maybe we should return here if an invalid value is passed in
|
329
330
|
if (clip !== undefined && typeof clip !== "string" && !(clip instanceof MediaStream)) {
|
330
|
-
|
331
|
-
|
331
|
+
if (isDevEnvironment())
|
332
|
+
console.warn("Called play on AudioSource with unknown argument type:", clip + "\nUsing the assigned clip instead:", this.clip)
|
333
|
+
// e.g. when a AudioSource.Play is called from SpatialTrigger onEnter this event is called with the TriggerReceiver... to still make this work we *re-use* our already assigned clip. Because otherwise calling `play` would not play the clip...
|
334
|
+
clip = this.clip;
|
332
335
|
}
|
333
336
|
|
334
337
|
// Check if we need to call load first
|
@@ -14,7 +14,16 @@
|
|
14
14
|
import { AudioSource } from "../../../../AudioSource.js";
|
15
15
|
import { NEEDLE_progressive } from "../../../../../engine/extensions/NEEDLE_progressive.js";
|
16
16
|
import { isDevEnvironment } from "../../../../../engine/debug/index.js";
|
17
|
+
import { Raycaster, ObjectRaycaster } from "../../../../ui/Raycaster.js";
|
17
18
|
|
19
|
+
function ensureRaycaster(obj: GameObject) {
|
20
|
+
if (!obj) return;
|
21
|
+
if (!obj.getComponentInParent(Raycaster)) {
|
22
|
+
if (isDevEnvironment()) console.warn("Create Raycaster on " + obj.name + " because no raycaster was found in the hierarchy")
|
23
|
+
obj.addNewComponent(ObjectRaycaster);
|
24
|
+
}
|
25
|
+
}
|
26
|
+
|
18
27
|
export class ChangeTransformOnClick extends Behaviour implements IPointerClickHandler, UsdzBehaviour {
|
19
28
|
|
20
29
|
@serializable(Object3D)
|
@@ -35,6 +44,19 @@
|
|
35
44
|
private targetRot = new Quaternion();
|
36
45
|
private targetScale = new Vector3();
|
37
46
|
|
47
|
+
start(): void {
|
48
|
+
ensureRaycaster(this.gameObject);
|
49
|
+
}
|
50
|
+
|
51
|
+
onPointerClick(args: PointerEventData) {
|
52
|
+
args.use();
|
53
|
+
if (this.coroutine) this.stopCoroutine(this.coroutine);
|
54
|
+
if (!this.relativeMotion)
|
55
|
+
this.coroutine = this.startCoroutine(this.moveToTarget());
|
56
|
+
else
|
57
|
+
this.coroutine = this.startCoroutine(this.moveRelative());
|
58
|
+
}
|
59
|
+
|
38
60
|
private *moveToTarget() {
|
39
61
|
|
40
62
|
if (!this.target || !this.object) return;
|
@@ -125,15 +147,6 @@
|
|
125
147
|
this.coroutine = null;
|
126
148
|
}
|
127
149
|
|
128
|
-
onPointerClick(args: PointerEventData) {
|
129
|
-
args.use();
|
130
|
-
if (this.coroutine) this.stopCoroutine(this.coroutine);
|
131
|
-
if (!this.relativeMotion)
|
132
|
-
this.coroutine = this.startCoroutine(this.moveToTarget());
|
133
|
-
else
|
134
|
-
this.coroutine = this.startCoroutine(this.moveRelative());
|
135
|
-
}
|
136
|
-
|
137
150
|
beforeCreateDocument(ext) {
|
138
151
|
if (this.target && this.object && this.gameObject) {
|
139
152
|
const moveForward = new BehaviorModel("Move to " + this.target?.name,
|
@@ -154,7 +167,7 @@
|
|
154
167
|
variantMaterial?: Material;
|
155
168
|
|
156
169
|
@serializable()
|
157
|
-
fadeDuration
|
170
|
+
fadeDuration: number = 0;
|
158
171
|
|
159
172
|
private _objectsWithThisMaterial: Mesh[] = [];
|
160
173
|
|
@@ -182,6 +195,10 @@
|
|
182
195
|
}
|
183
196
|
}
|
184
197
|
|
198
|
+
start(): void {
|
199
|
+
ensureRaycaster(this.gameObject);
|
200
|
+
}
|
201
|
+
|
185
202
|
onPointerClick(args: PointerEventData) {
|
186
203
|
args.use();
|
187
204
|
if (!this.variantMaterial) return;
|
@@ -206,7 +223,7 @@
|
|
206
223
|
if (this.materialToSwitch) {
|
207
224
|
await NEEDLE_progressive.assignTextureLOD(this.context, this.sourceId, this.materialToSwitch, 0);
|
208
225
|
}
|
209
|
-
if(this.variantMaterial) {
|
226
|
+
if (this.variantMaterial) {
|
210
227
|
await NEEDLE_progressive.assignTextureLOD(this.context, this.sourceId, this.variantMaterial, 0);
|
211
228
|
}
|
212
229
|
}
|
@@ -233,17 +250,17 @@
|
|
233
250
|
if (!this.materialToSwitch) return;
|
234
251
|
const handlers = ChangeMaterialOnClick._materialTriggersPerId[this.materialToSwitch.uuid];
|
235
252
|
if (handlers) {
|
236
|
-
const variants: { [key: string]: USDObject
|
253
|
+
const variants: { [key: string]: Array<USDObject> } = {}
|
237
254
|
for (const handler of handlers) {
|
238
255
|
const createdVariants = handler.createVariants();
|
239
256
|
if (createdVariants && createdVariants.length > 0)
|
240
257
|
variants[handler.selfModel.uuid] = createdVariants;
|
241
258
|
}
|
242
|
-
const otherVariants: any[] = [];
|
243
259
|
for (const handler of handlers) {
|
260
|
+
const otherVariants: Array<USDObject> = [];
|
244
261
|
for (const key in variants) {
|
245
262
|
if (key !== handler.selfModel.uuid) {
|
246
|
-
otherVariants.push(variants[key]);
|
263
|
+
otherVariants.push(...variants[key]);
|
247
264
|
}
|
248
265
|
}
|
249
266
|
handler.createAndAttachBehaviors(ext, variants[handler.selfModel.uuid], otherVariants);
|
@@ -252,30 +269,21 @@
|
|
252
269
|
delete ChangeMaterialOnClick._materialTriggersPerId[this.materialToSwitch.uuid];
|
253
270
|
}
|
254
271
|
|
255
|
-
private createAndAttachBehaviors(ext: BehaviorExtension, myVariants
|
272
|
+
private createAndAttachBehaviors(ext: BehaviorExtension, myVariants: Array<USDObject>, otherVariants: Array<USDObject>) {
|
256
273
|
const start: ActionModel[] = [];
|
257
274
|
const select: ActionModel[] = [];
|
258
275
|
|
259
276
|
const fadeDuration = Math.max(0, this.fadeDuration);
|
260
277
|
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
select.push(hideOriginal);
|
265
|
-
}
|
266
|
-
for (const v of otherVariants) {
|
267
|
-
select.push(ActionBuilder.fadeAction(v, fadeDuration, false));
|
268
|
-
}
|
269
|
-
for (const v of myVariants) {
|
270
|
-
start.push(ActionBuilder.fadeAction(v, fadeDuration, false));
|
271
|
-
select.push(ActionBuilder.fadeAction(v, fadeDuration, true));
|
272
|
-
}
|
278
|
+
select.push(ActionBuilder.fadeAction([...this.targetModels, ...otherVariants], fadeDuration, false));
|
279
|
+
start.push(ActionBuilder.fadeAction(myVariants, fadeDuration, false));
|
280
|
+
select.push(ActionBuilder.fadeAction(myVariants, fadeDuration, true));
|
273
281
|
|
274
|
-
ext.addBehavior(new BehaviorModel("
|
282
|
+
ext.addBehavior(new BehaviorModel("Select_" + this.selfModel.name,
|
275
283
|
TriggerBuilder.tapTrigger(this.selfModel),
|
276
284
|
ActionBuilder.parallel(...select))
|
277
285
|
);
|
278
|
-
ext.addBehavior(new BehaviorModel("
|
286
|
+
ext.addBehavior(new BehaviorModel("StartHidden_" + this.selfModel.name,
|
279
287
|
TriggerBuilder.sceneStartTrigger(),
|
280
288
|
ActionBuilder.parallel(...start))
|
281
289
|
);
|
@@ -318,6 +326,10 @@
|
|
318
326
|
@serializable()
|
319
327
|
hideSelf: boolean = true;
|
320
328
|
|
329
|
+
start(): void {
|
330
|
+
ensureRaycaster(this.gameObject);
|
331
|
+
}
|
332
|
+
|
321
333
|
onPointerClick(args: PointerEventData) {
|
322
334
|
args.use();
|
323
335
|
if (!this.toggleOnClick && this.hideSelf)
|
@@ -460,12 +472,16 @@
|
|
460
472
|
@serializable()
|
461
473
|
toggleOnClick: boolean = false;
|
462
474
|
|
475
|
+
start(): void {
|
476
|
+
ensureRaycaster(this.gameObject);
|
477
|
+
}
|
478
|
+
|
463
479
|
onPointerClick(args: PointerEventData) {
|
464
480
|
args.use();
|
465
481
|
if (!this.target && !this.clip) return;
|
466
482
|
|
467
483
|
if (!this.target) {
|
468
|
-
|
484
|
+
|
469
485
|
const newAudioSource = this.gameObject.addNewComponent(AudioSource);
|
470
486
|
if (newAudioSource) {
|
471
487
|
newAudioSource.spatialBlend = 1;
|
@@ -474,9 +490,9 @@
|
|
474
490
|
this.target = newAudioSource;
|
475
491
|
}
|
476
492
|
}
|
477
|
-
|
493
|
+
|
478
494
|
if (this.target) {
|
479
|
-
|
495
|
+
|
480
496
|
if (this.target.isPlaying && this.toggleOnClick) {
|
481
497
|
this.target.stop();
|
482
498
|
}
|
@@ -489,14 +505,14 @@
|
|
489
505
|
}
|
490
506
|
}
|
491
507
|
}
|
492
|
-
|
508
|
+
|
493
509
|
createBehaviours(ext, model, _context) {
|
494
510
|
if (!this.target && !this.clip) return;
|
495
511
|
if (model.uuid === this.gameObject.uuid) {
|
496
512
|
|
497
513
|
const clipUrl = this.clip ? this.clip : this.target ? this.target.clip : undefined;
|
498
514
|
if (!clipUrl) return;
|
499
|
-
if(typeof clipUrl !== "string") return;
|
515
|
+
if (typeof clipUrl !== "string") return;
|
500
516
|
|
501
517
|
const playbackTarget = this.target ? this.target.gameObject : this.gameObject;
|
502
518
|
const clipName = clipUrl.split("/").pop();
|
@@ -514,7 +530,7 @@
|
|
514
530
|
if (!this.target && !this.clip) return;
|
515
531
|
const clipUrl = this.clip ? this.clip : this.target ? this.target.clip : undefined;
|
516
532
|
if (!clipUrl) return;
|
517
|
-
if(typeof clipUrl !== "string") return;
|
533
|
+
if (typeof clipUrl !== "string") return;
|
518
534
|
const clipName = clipUrl.split("/").pop();
|
519
535
|
|
520
536
|
const audio = await fetch(this.clip);
|
@@ -529,9 +545,6 @@
|
|
529
545
|
|
530
546
|
export class PlayAnimationOnClick extends Behaviour implements IPointerClickHandler, UsdzBehaviour, UsdzAnimation {
|
531
547
|
|
532
|
-
@serializable(Object3D)
|
533
|
-
target?: Object3D;
|
534
|
-
|
535
548
|
@serializable(Animator)
|
536
549
|
animator?: Animator;
|
537
550
|
|
@@ -544,6 +557,12 @@
|
|
544
557
|
@serializable()
|
545
558
|
loopAfterPlaying: boolean = false;
|
546
559
|
|
560
|
+
private get target() { return this.animator?.gameObject }
|
561
|
+
|
562
|
+
start(): void {
|
563
|
+
ensureRaycaster(this.gameObject);
|
564
|
+
}
|
565
|
+
|
547
566
|
onPointerClick(args: PointerEventData) {
|
548
567
|
args.use();
|
549
568
|
if (!this.target) return;
|
@@ -100,9 +100,6 @@
|
|
100
100
|
result = (targetObject as any).getPath?.call(targetObject) as string;
|
101
101
|
}
|
102
102
|
|
103
|
-
// in three there's now a new "Scenes" parent and "Scene" xform that's injected;
|
104
|
-
// we need to add this to our path here so that we have full paths
|
105
|
-
result = result.replace(document.name, document.name + "/Scenes/Scene");
|
106
103
|
return result;
|
107
104
|
}
|
108
105
|
|
@@ -51,7 +51,7 @@
|
|
51
51
|
const intersects = this.box?.intersectsBox(BoxHelperComponent.testBox);
|
52
52
|
if (intersects) {
|
53
53
|
if (debug)
|
54
|
-
Gizmos.
|
54
|
+
Gizmos.DrawWireBox3(BoxHelperComponent.testBox, 0xff0000, 5);
|
55
55
|
}
|
56
56
|
return intersects;
|
57
57
|
}
|
@@ -131,7 +131,7 @@
|
|
131
131
|
else {
|
132
132
|
const group = this.sharedMesh as any as Group;
|
133
133
|
if (group?.isGroup) {
|
134
|
-
console.warn(`MeshCollider mesh is a group \"${this.sharedMesh?.name}\", adding all children as colliders. This is currently not fully supported (colliders can not be removed from world again)`, this)
|
134
|
+
console.warn(`MeshCollider mesh is a group \"${this.sharedMesh?.name || this.gameObject.name}\", adding all children as colliders. This is currently not fully supported (colliders can not be removed from world again)`, this);
|
135
135
|
for (const ch in group.children) {
|
136
136
|
const child = group.children[ch] as Mesh;
|
137
137
|
if (child.isMesh) {
|
@@ -94,7 +94,7 @@
|
|
94
94
|
obj.material["depthWrite"] = false;
|
95
95
|
}
|
96
96
|
|
97
|
-
static
|
97
|
+
static DrawWireBox(center: Vec3, size: Vec3, color: ColorRepresentation = defaultColor, duration: number = 0, depthTest: boolean = true) {
|
98
98
|
const obj = Internal.getBox(duration);
|
99
99
|
obj.position.set(center.x, center.y, center.z);
|
100
100
|
obj.scale.set(size.x, size.y, size.z);
|
@@ -104,7 +104,7 @@
|
|
104
104
|
obj.material["depthWrite"] = false;
|
105
105
|
}
|
106
106
|
|
107
|
-
static
|
107
|
+
static DrawWireBox3(box: Box3, color: ColorRepresentation = defaultColor, duration: number = 0, depthTest: boolean = true) {
|
108
108
|
const obj = Internal.getBox(duration);
|
109
109
|
obj.position.copy(box.getCenter(_tmp));
|
110
110
|
obj.scale.copy(box.getSize(_tmp));
|
@@ -416,7 +416,7 @@
|
|
416
416
|
}
|
417
417
|
}
|
418
418
|
|
419
|
-
private onUserLeft(_: UserJoinedOrLeftRoomModel) {
|
419
|
+
private onUserLeft = (_: UserJoinedOrLeftRoomModel) => {
|
420
420
|
this.stopCallsToUsersThatAreNotInTheRoomAnymore();
|
421
421
|
}
|
422
422
|
|
@@ -10,6 +10,7 @@
|
|
10
10
|
import { PeerNetworking } from './engine_networking_peer.js';
|
11
11
|
import { type IModel, type INetworkConnection, SendQueue } from './engine_networking_types.js';
|
12
12
|
import { isHostedOnGlitch } from './engine_networking_utils.js';
|
13
|
+
import { isDevEnvironment } from './debug/debug.js';
|
13
14
|
|
14
15
|
export const debugNet = utils.getParam("debugnet") ? true : false;
|
15
16
|
export const debugOwner = debugNet || utils.getParam("debugowner") ? true : false;
|
@@ -568,8 +569,9 @@
|
|
568
569
|
else this.handleIncomingStringMessage(message);
|
569
570
|
return;
|
570
571
|
}
|
571
|
-
catch {
|
572
|
+
catch (err) {
|
572
573
|
if (debugNet && msg === "pong") console.log("<<", msg);
|
574
|
+
else if (isDevEnvironment()) console.error("Failed to parse message", err);
|
573
575
|
}
|
574
576
|
}
|
575
577
|
|
@@ -685,10 +687,17 @@
|
|
685
687
|
}
|
686
688
|
}
|
687
689
|
|
688
|
-
|
690
|
+
let listeners = this._listeners[message.key];
|
689
691
|
if (listeners) {
|
692
|
+
// Copy listeners array in case a listener is removed while iterating
|
693
|
+
listeners = [...listeners];
|
690
694
|
for (const listener of listeners) {
|
691
|
-
|
695
|
+
try {
|
696
|
+
listener(message.data);
|
697
|
+
}
|
698
|
+
catch (err) {
|
699
|
+
console.error("Error invoking callback for \"" + message.key + "\"", err);
|
700
|
+
}
|
692
701
|
}
|
693
702
|
}
|
694
703
|
|
@@ -65,6 +65,7 @@
|
|
65
65
|
|
66
66
|
export class RapierPhysics implements IPhysicsEngine {
|
67
67
|
|
68
|
+
/** Enable to draw collider shapes */
|
68
69
|
debugRenderColliders: boolean = false;
|
69
70
|
|
70
71
|
removeBody(obj: IComponent) {
|
@@ -559,7 +560,11 @@
|
|
559
560
|
// Prevent negative scales
|
560
561
|
scale.x = Math.abs(scale.x);
|
561
562
|
scale.y = Math.abs(scale.y);
|
562
|
-
const
|
563
|
+
const finalRadius = radius * scale.x;
|
564
|
+
// half height = distance between capsule origin and top sphere origin (not the top end of the capsule)
|
565
|
+
height = Math.max(height, finalRadius * 2);
|
566
|
+
const hh = (height * .5 * scale.y) - (radius * scale.x);
|
567
|
+
const desc = ColliderDesc.capsule(hh, finalRadius);
|
563
568
|
this.createCollider(collider, desc, center);
|
564
569
|
}
|
565
570
|
|
@@ -594,7 +599,7 @@
|
|
594
599
|
positions = this._meshCache.get(key)!;
|
595
600
|
}
|
596
601
|
else {
|
597
|
-
console.warn(`Your MeshCollider \"${collider.name}\" is scaled\nthis is not optimal for performance since this isn't supported by the Rapier physics engine yet. Consider applying the scale to the collider mesh`);
|
602
|
+
console.warn(`Your MeshCollider \"${collider.name}\" is scaled (${scale.x}, ${scale.y}, ${scale.z})\nthis is not optimal for performance since this isn't supported by the Rapier physics engine yet. Consider applying the scale to the collider mesh`);
|
598
603
|
// showBalloonWarning("Your model is using scaled mesh colliders which is not optimal for performance: " + mesh.name + ", consider using unscaled objects");
|
599
604
|
const scaledPositions = new Float32Array(positions.length);
|
600
605
|
for (let i = 0; i < positions.length; i += 3) {
|
@@ -608,7 +613,7 @@
|
|
608
613
|
}
|
609
614
|
const desc = convex ? ColliderDesc.convexHull(positions) : ColliderDesc.trimesh(positions, indices);
|
610
615
|
if (desc) {
|
611
|
-
|
616
|
+
this.createCollider(collider, desc);
|
612
617
|
// col.setMassProperties(1, { x: 0, y: 0, z: 0 }, { x: 0, y: 0, z: 0 }, { x: 0, y: 0, z: 0, w: 1 });
|
613
618
|
// rb?.setTranslation({ x: 0, y: 2, z: 0 });
|
614
619
|
// col.setTranslationWrtParent(new Vector3(0,2,0));
|
@@ -586,9 +586,15 @@
|
|
586
586
|
return null;
|
587
587
|
}
|
588
588
|
}
|
589
|
-
|
590
|
-
|
591
|
-
|
589
|
+
try {
|
590
|
+
// the fallback - this assumes that the type has a constructor that accepts the serialized arguments
|
591
|
+
// made originally with THREE.Vector3 in mind but SHOULD actually not be used/called anymore
|
592
|
+
instance = new type(...setBuffer(data));
|
593
|
+
}
|
594
|
+
catch (err) {
|
595
|
+
console.error("Error creating " + context.path, context.target, err)
|
596
|
+
return;
|
597
|
+
}
|
592
598
|
}
|
593
599
|
|
594
600
|
// recurse if the deserialized member also implements Iserializable
|
@@ -470,5 +470,6 @@
|
|
470
470
|
addFixedJoint(body1: IRigidbody, body2: IRigidbody);
|
471
471
|
addHingeJoint(body1: IRigidbody, body2: IRigidbody, anchor: Vec3, axis: Vec3);
|
472
472
|
|
473
|
+
/** Enable to render collider shapes */
|
473
474
|
debugRenderColliders: boolean;
|
474
475
|
}
|
@@ -609,7 +609,7 @@
|
|
609
609
|
if (this.enabled) {
|
610
610
|
switch (this.shapeType) {
|
611
611
|
case ParticleSystemShapeType.Box:
|
612
|
-
if (debug) Gizmos.
|
612
|
+
if (debug) Gizmos.DrawWireBox(this.position, this.scale, 0xdddddd, 1);
|
613
613
|
this._vector.x = Math.random() * this.scale.x - this.scale.x / 2;
|
614
614
|
this._vector.y = Math.random() * this.scale.y - this.scale.y / 2;
|
615
615
|
this._vector.z = Math.random() * this.scale.z - this.scale.z / 2;
|
@@ -165,7 +165,6 @@
|
|
165
165
|
// console.warn(this.gameObject.guid, this.guid, this.owner, this.isLocalPlayer, PlayerState.isLocalPlayer(this));
|
166
166
|
const evt = new CustomEvent("local-owner-changed", { detail: detail });
|
167
167
|
this.dispatchEvent(evt);
|
168
|
-
PlayerState.dispatchEvent(PlayerStateEvent.OwnerChanged, evt);
|
169
168
|
}
|
170
169
|
const evt = new CustomEvent("owner-changed", { detail: detail });
|
171
170
|
this.dispatchEvent(evt);
|
@@ -173,24 +172,28 @@
|
|
173
172
|
}
|
174
173
|
|
175
174
|
awake(): void {
|
176
|
-
PlayerState.all.push(this);
|
177
|
-
if(debug) console.log("Registered new PlayerState", this, PlayerState.all.length-1, PlayerState.all)
|
175
|
+
PlayerState.all.push(this);
|
176
|
+
if (debug) console.log("Registered new PlayerState", this.guid, PlayerState.all.length - 1, PlayerState.all)
|
178
177
|
|
179
|
-
this.context.connection.beginListen(RoomEvents.UserLeftRoom,
|
180
|
-
// console.log("USER LEFT", model.userId)
|
181
|
-
if (model.userId === this.owner) {
|
182
|
-
// console.log("LEFT", this.owner)
|
183
|
-
this.doDestroy();
|
184
|
-
return;
|
185
|
-
}
|
186
|
-
});
|
178
|
+
this.context.connection.beginListen(RoomEvents.UserLeftRoom, this.onUserLeftRoom);
|
187
179
|
}
|
188
180
|
|
181
|
+
private onUserLeftRoom = (model: { userId: string }) => {
|
182
|
+
if (model.userId === this.owner) {
|
183
|
+
if (debug)
|
184
|
+
console.log("PLAYERSYNC LEFT", this.owner)
|
185
|
+
this.doDestroy();
|
186
|
+
return;
|
187
|
+
}
|
188
|
+
}
|
189
|
+
|
190
|
+
|
189
191
|
start() {
|
190
192
|
// If a player is spawned but not in the room anymore we want to destroy it
|
191
193
|
// this might happen in a case where all users get disconnected at once and the server
|
192
194
|
// still has the syncInstantiate messages that are sent to all clients
|
193
195
|
if (this.owner && !this.context.connection.userIsInRoom(this.owner)) {
|
196
|
+
if (debug) console.log("PlayerSync.start → doDestroy because user is not in room anymore...", this)
|
194
197
|
this.doDestroy();
|
195
198
|
return;
|
196
199
|
}
|
@@ -203,6 +206,7 @@
|
|
203
206
|
}
|
204
207
|
|
205
208
|
onDestroy() {
|
209
|
+
this.context.connection.stopListen(RoomEvents.UserLeftRoom, this.onUserLeftRoom);
|
206
210
|
PlayerState.all.splice(PlayerState.all.indexOf(this), 1);
|
207
211
|
|
208
212
|
if (this.isLocalPlayer) {
|
@@ -121,7 +121,11 @@
|
|
121
121
|
let path = this.name;
|
122
122
|
while ( current ) {
|
123
123
|
|
124
|
-
path
|
124
|
+
// StageRoot has a special path right now since there's additional Xforms for encapsulation.
|
125
|
+
// Better would be to actually model them as part of our object graph, but they're written separately,
|
126
|
+
// so currently we don't and instead work around that here.
|
127
|
+
const currentName = current.parent ? current.name : (current.name + "/Scenes/Scene");
|
128
|
+
path = currentName + '/' + path;
|
125
129
|
current = current.parent;
|
126
130
|
|
127
131
|
}
|
@@ -295,6 +299,7 @@
|
|
295
299
|
}
|
296
300
|
|
297
301
|
const newLine = '\n';
|
302
|
+
const materialRoot = '</StageRoot/Materials';
|
298
303
|
|
299
304
|
class USDWriter {
|
300
305
|
str: string;
|
@@ -452,11 +457,16 @@
|
|
452
457
|
|
453
458
|
await invokeAll( context, 'onAfterBuildDocument' );
|
454
459
|
|
455
|
-
parseDocument( context )
|
460
|
+
parseDocument( context, () => {
|
461
|
+
// injected after stageRoot.
|
462
|
+
// TODO property use context/writer instead of string concat
|
463
|
+
return buildMaterials( materials, textures, options.quickLookCompatible );
|
464
|
+
} );
|
456
465
|
|
457
466
|
await invokeAll( context, 'onAfterSerialize' );
|
458
467
|
|
459
|
-
|
468
|
+
// Moved into parseDocument callback for proper defaultPrim encapsulation
|
469
|
+
// context.output += buildMaterials( materials, textures, options.quickLookCompatible );
|
460
470
|
|
461
471
|
const header = context.document.buildHeader();
|
462
472
|
const final = header + '\n' + context.output;
|
@@ -613,7 +623,7 @@
|
|
613
623
|
|
614
624
|
}
|
615
625
|
|
616
|
-
async function parseDocument( context: USDZExporterContext ) {
|
626
|
+
async function parseDocument( context: USDZExporterContext, afterStageRoot: () => string ) {
|
617
627
|
|
618
628
|
for ( const child of context.document.children ) {
|
619
629
|
|
@@ -656,6 +666,7 @@
|
|
656
666
|
|
657
667
|
writer.closeBlock();
|
658
668
|
writer.closeBlock();
|
669
|
+
writer.appendLine(afterStageRoot());
|
659
670
|
writer.closeBlock();
|
660
671
|
|
661
672
|
context.output += writer.toString();
|
@@ -927,8 +938,8 @@
|
|
927
938
|
|
928
939
|
if ( geometry ) {
|
929
940
|
writer.beginBlock( `def Xform "${name}" (
|
930
|
-
prepend references = @./geometries/Geometry_${geometry.id}.usd@</Geometry>
|
931
|
-
prepend apiSchemas = ["MaterialBindingAPI"]
|
941
|
+
prepend references = @./geometries/Geometry_${geometry.id}.usd@</Geometry>
|
942
|
+
${material ? 'prepend apiSchemas = ["MaterialBindingAPI"]' : ''}
|
932
943
|
)` );
|
933
944
|
}
|
934
945
|
else if ( camera )
|
@@ -936,8 +947,8 @@
|
|
936
947
|
else
|
937
948
|
writer.beginBlock( `def Xform "${name}"` );
|
938
949
|
|
939
|
-
if ( material )
|
940
|
-
writer.appendLine( `rel material:binding =
|
950
|
+
if ( geometry && material )
|
951
|
+
writer.appendLine( `rel material:binding = ${materialRoot}/Material_${material.id}>` );
|
941
952
|
writer.appendLine( `matrix4d xformOp:transform = ${transform}` );
|
942
953
|
writer.appendLine( 'uniform token[] xformOpOrder = ["xformOp:transform"]' );
|
943
954
|
|
@@ -1214,7 +1225,7 @@
|
|
1214
1225
|
}
|
1215
1226
|
|
1216
1227
|
const needsTextureTransform = ( repeat.x != 1 || repeat.y != 1 || offset.x != 0 || offset.y != 0 || rotation != 0 );
|
1217
|
-
const textureTransformInput =
|
1228
|
+
const textureTransformInput = `${materialRoot}/Material_${material.id}/${'uvReader_' + uv}.outputs:result>`; const textureTransformOutput = `${materialRoot}/Material_${material.id}/Transform2d_${mapType}.outputs:result>`;
|
1218
1229
|
const needsTextureScale = mapType !== 'normal' && (color && (color.r !== 1 || color.g !== 1 || color.b !== 1 || opacity !== 1)) || false;
|
1219
1230
|
|
1220
1231
|
const needsNormalScaleAndBias = mapType === 'normal';
|
@@ -1279,15 +1290,15 @@
|
|
1279
1290
|
|
1280
1291
|
if ( material.map !== null ) {
|
1281
1292
|
|
1282
|
-
inputs.push( `${pad}color3f inputs:diffuseColor.connect =
|
1293
|
+
inputs.push( `${pad}color3f inputs:diffuseColor.connect = ${materialRoot}/Material_${material.id}/Texture_${material.map.id}_diffuse.outputs:rgb>` );
|
1283
1294
|
|
1284
1295
|
if ( material.transparent ) {
|
1285
1296
|
|
1286
|
-
inputs.push( `${pad}float inputs:opacity.connect =
|
1297
|
+
inputs.push( `${pad}float inputs:opacity.connect = ${materialRoot}/Material_${material.id}/Texture_${material.map.id}_diffuse.outputs:a>` );
|
1287
1298
|
|
1288
1299
|
} else if ( material.alphaTest > 0.0 ) {
|
1289
1300
|
|
1290
|
-
inputs.push( `${pad}float inputs:opacity.connect =
|
1301
|
+
inputs.push( `${pad}float inputs:opacity.connect = ${materialRoot}/Material_${material.id}/Texture_${material.map.id}_diffuse.outputs:a>` );
|
1291
1302
|
inputs.push( `${pad}float inputs:opacityThreshold = ${material.alphaTest}` );
|
1292
1303
|
|
1293
1304
|
}
|
@@ -1302,7 +1313,7 @@
|
|
1302
1313
|
|
1303
1314
|
if ( material.aoMap ) {
|
1304
1315
|
|
1305
|
-
inputs.push( `${pad}float inputs:occlusion.connect =
|
1316
|
+
inputs.push( `${pad}float inputs:occlusion.connect = ${materialRoot}/Material_${material.id}/Texture_${material.aoMap.id}_occlusion.outputs:r>` );
|
1306
1317
|
|
1307
1318
|
samplers.push( buildTexture( material.aoMap, 'occlusion' ) );
|
1308
1319
|
|
@@ -1310,7 +1321,7 @@
|
|
1310
1321
|
|
1311
1322
|
if ( material.alphaMap ) {
|
1312
1323
|
|
1313
|
-
inputs.push( `${pad}float inputs:opacity.connect =
|
1324
|
+
inputs.push( `${pad}float inputs:opacity.connect = ${materialRoot}/Material_${material.id}/Texture_${material.alphaMap.id}_opacity.outputs:r>` );
|
1314
1325
|
inputs.push( `${pad}float inputs:opacityThreshold = 0.0001` );
|
1315
1326
|
|
1316
1327
|
samplers.push( buildTexture( material.alphaMap, 'opacity' ) );
|
@@ -1331,7 +1342,7 @@
|
|
1331
1342
|
|
1332
1343
|
if ( material.emissiveMap ) {
|
1333
1344
|
|
1334
|
-
inputs.push( `${pad}color3f inputs:emissiveColor.connect =
|
1345
|
+
inputs.push( `${pad}color3f inputs:emissiveColor.connect = ${materialRoot}/Material_${material.id}/Texture_${material.emissiveMap.id}_emissive.outputs:rgb>` );
|
1335
1346
|
|
1336
1347
|
samplers.push( buildTexture( material.emissiveMap, 'emissive' ) );
|
1337
1348
|
|
@@ -1347,7 +1358,7 @@
|
|
1347
1358
|
|
1348
1359
|
if ( material.normalMap ) {
|
1349
1360
|
|
1350
|
-
inputs.push( `${pad}normal3f inputs:normal.connect =
|
1361
|
+
inputs.push( `${pad}normal3f inputs:normal.connect = ${materialRoot}/Material_${material.id}/Texture_${material.normalMap.id}_normal.outputs:rgb>` );
|
1351
1362
|
|
1352
1363
|
samplers.push( buildTexture( material.normalMap, 'normal' ) );
|
1353
1364
|
|
@@ -1355,7 +1366,7 @@
|
|
1355
1366
|
|
1356
1367
|
if ( material.roughnessMap && material.roughness === 1 ) {
|
1357
1368
|
|
1358
|
-
inputs.push( `${pad}float inputs:roughness.connect =
|
1369
|
+
inputs.push( `${pad}float inputs:roughness.connect = ${materialRoot}/Material_${material.id}/Texture_${material.roughnessMap.id}_roughness.outputs:g>` );
|
1359
1370
|
|
1360
1371
|
samplers.push( buildTexture( material.roughnessMap, 'roughness' ) );
|
1361
1372
|
|
@@ -1367,7 +1378,7 @@
|
|
1367
1378
|
|
1368
1379
|
if ( material.metalnessMap && material.metalness === 1 ) {
|
1369
1380
|
|
1370
|
-
inputs.push( `${pad}float inputs:metallic.connect =
|
1381
|
+
inputs.push( `${pad}float inputs:metallic.connect = ${materialRoot}/Material_${material.id}/Texture_${material.metalnessMap.id}_metallic.outputs:b>` );
|
1371
1382
|
|
1372
1383
|
samplers.push( buildTexture( material.metalnessMap, 'metallic' ) );
|
1373
1384
|
|
@@ -1387,7 +1398,7 @@
|
|
1387
1398
|
|
1388
1399
|
if ( !material.transparent && ! (material.alphaTest > 0.0) && material.transmissionMap) {
|
1389
1400
|
|
1390
|
-
inputs.push( `${pad}float inputs:opacity.connect =
|
1401
|
+
inputs.push( `${pad}float inputs:opacity.connect = ${materialRoot}/Material_${material.id}/Texture_${material.transmissionMap.id}_transmission.outputs:r>` );
|
1391
1402
|
|
1392
1403
|
samplers.push( buildTexture( material.transmissionMap, 'transmission' ) );
|
1393
1404
|
}
|
@@ -1405,7 +1416,7 @@
|
|
1405
1416
|
token outputs:surface
|
1406
1417
|
}
|
1407
1418
|
|
1408
|
-
token outputs:surface.connect =
|
1419
|
+
token outputs:surface.connect = ${materialRoot}/Material_${material.id}/PreviewSurface.outputs:surface>
|
1409
1420
|
|
1410
1421
|
def Shader "uvReader_st"
|
1411
1422
|
{
|
@@ -75,9 +75,10 @@
|
|
75
75
|
|
76
76
|
writeTo(_document: USDDocument | undefined, writer: USDWriter) {
|
77
77
|
|
78
|
+
writer.beginBlock( `def Preliminary_Text "${this.id}" (
|
79
|
+
prepend apiSchemas = ["MaterialBindingAPI"]
|
80
|
+
)` );
|
78
81
|
|
79
|
-
writer.beginBlock(`def Preliminary_Text "${this.id}"`);
|
80
|
-
|
81
82
|
if (this.content)
|
82
83
|
writer.appendLine(`string content = "${this.content}"`);
|
83
84
|
|
@@ -103,7 +104,7 @@
|
|
103
104
|
writer.appendLine(`token verticalAlignment = "${this.verticalAlignment}"`);
|
104
105
|
|
105
106
|
if (this.material !== undefined) {
|
106
|
-
writer.appendLine(`rel material:binding = </Materials/Material_${this.material.id}>`)
|
107
|
+
writer.appendLine(`rel material:binding = </StageRoot/Materials/Material_${this.material.id}>`)
|
107
108
|
}
|
108
109
|
|
109
110
|
writer.closeBlock();
|
@@ -211,11 +212,14 @@
|
|
211
212
|
if (rt) // Not ideal but works for now:
|
212
213
|
newModel.matrix.premultiply(invertX);
|
213
214
|
|
214
|
-
const color =
|
215
|
+
const color = text.color.clone();
|
215
216
|
newModel.material = new MeshStandardMaterial({ color: color, emissive: color });
|
216
217
|
|
217
218
|
newModel.addEventListener("serialize", (writer: USDWriter, _context: USDZExporterContext) => {
|
218
219
|
let txt = text.text;
|
220
|
+
// Some texts use \r\n for newlines, we remove the \r here.
|
221
|
+
// Also encountered a single text ending with \r which broke the output.
|
222
|
+
txt = txt.replace(/\r/g, "");
|
219
223
|
txt = txt.replace(/\n/g, "\\n");
|
220
224
|
const textObj = TextBuilder.multiLine(txt, width, height, HorizontalAlignment.center, VerticalAlignment.bottom, TextWrapMode.flowing);
|
221
225
|
this.setTextAlignment(textObj, text.alignment);
|