@@ -1,5 +1,5 @@
|
|
1
1
|
import { TypeStore } from "./../engine_typestore"
|
2
|
-
|
2
|
+
|
3
3
|
// Import types
|
4
4
|
import { __Ignore } from "../../engine-components/codegen/components";
|
5
5
|
import { ActionBuilder } from "../../engine-components/export/usdz/extensions/behavior/BehavioursBuilder";
|
@@ -215,7 +215,7 @@
|
|
215
215
|
import { XRGrabRendering } from "../../engine-components/webxr/WebXRGrabRendering";
|
216
216
|
import { XRRig } from "../../engine-components/webxr/WebXRRig";
|
217
217
|
import { XRState } from "../../engine-components/XRFlag";
|
218
|
-
|
218
|
+
|
219
219
|
// Register types
|
220
220
|
TypeStore.add("__Ignore", __Ignore);
|
221
221
|
TypeStore.add("ActionBuilder", ActionBuilder);
|
@@ -208,7 +208,7 @@
|
|
208
208
|
}
|
209
209
|
|
210
210
|
onAfterRenderRoutine = () => {
|
211
|
-
if ((this.screenspace || this.
|
211
|
+
if ((this.screenspace || this.renderOnTop) && this.previousParent && this.context.mainCamera) {
|
212
212
|
if (this.screenspace) {
|
213
213
|
const camObj = this.context.mainCamera;
|
214
214
|
camObj?.add(this.gameObject);
|
@@ -1,8 +1,9 @@
|
|
1
1
|
import { Graphic } from "./Graphic";
|
2
2
|
import { FrameEvent } from "../../engine/engine_setup";
|
3
3
|
import { Behaviour, GameObject } from "../Component";
|
4
|
-
import { ICanvasGroup } from "./Interfaces";
|
4
|
+
import { ICanvasGroup, IHasAlphaFactor } from "./Interfaces";
|
5
5
|
import { serializable } from "../../engine/engine_serialization_decorator";
|
6
|
+
import { BaseUIComponent } from "./BaseUIComponent";
|
6
7
|
|
7
8
|
|
8
9
|
export class CanvasGroup extends Behaviour implements ICanvasGroup {
|
@@ -40,8 +41,10 @@
|
|
40
41
|
|
41
42
|
private _buffer: Graphic[] = [];
|
42
43
|
private applyChangesNow() {
|
43
|
-
for (const ch of GameObject.getComponentsInChildren(this.gameObject,
|
44
|
-
ch
|
44
|
+
for (const ch of GameObject.getComponentsInChildren(this.gameObject, BaseUIComponent, this._buffer)) {
|
45
|
+
const hasAlphaFactor = ch as any as IHasAlphaFactor;
|
46
|
+
if (hasAlphaFactor.setAlphaFactor)
|
47
|
+
hasAlphaFactor.setAlphaFactor(this._alpha);
|
45
48
|
}
|
46
49
|
}
|
47
50
|
}
|
@@ -212,7 +212,6 @@
|
|
212
212
|
|
213
213
|
private async onInstantiate(parent?: Object3D | InstantiateOptions, networked: boolean = false, saveOnServer?: boolean) {
|
214
214
|
const context = Context.Current;
|
215
|
-
if (!parent) parent = context.scene;
|
216
215
|
if (this.mustLoad) {
|
217
216
|
await this.loadAssetAsync();
|
218
217
|
}
|
@@ -235,6 +234,7 @@
|
|
235
234
|
Object.assign(options, parent);
|
236
235
|
}
|
237
236
|
}
|
237
|
+
if (!options.parent) options.parent = context.scene;
|
238
238
|
|
239
239
|
let count = AssetReference.currentlyInstantiating.get(this.uri);
|
240
240
|
// allow up to 10000 instantiations of the same prefab in the same frame
|
@@ -1,8 +1,6 @@
|
|
1
|
-
|
1
|
+
const defaultNetworkingBackendUrlProvider = "https://urls.needle.tools/default-networking-backend/index";
|
2
|
+
let serverUrl : string | undefined = "wss://needle-tiny-starter.glitch.me/socket";
|
2
3
|
|
3
|
-
// const serverUrl = 'wss://tiny-server-1-r26roub2hq-ew.a.run.app/';
|
4
|
-
let serverUrl = 'wss://needle-tiny-starter.glitch.me/socket';
|
5
|
-
|
6
4
|
import { Websocket, WebsocketBuilder } from 'websocket-ts';
|
7
5
|
// import { Networking } from '../engine-components/Networking';
|
8
6
|
import { Context } from './engine_setup';
|
@@ -85,6 +83,8 @@
|
|
85
83
|
value: boolean;
|
86
84
|
}
|
87
85
|
|
86
|
+
declare type WebsocketSendType = IModel | object | boolean | null | string | number;
|
87
|
+
|
88
88
|
export class OwnershipModel {
|
89
89
|
|
90
90
|
public guid: string;
|
@@ -308,8 +308,9 @@
|
|
308
308
|
this.send(RoomEvents.Leave, { room: room });
|
309
309
|
}
|
310
310
|
|
311
|
-
public send(key: string | OwnershipEvent, data:
|
311
|
+
public send<T extends WebsocketSendType>(key: string | OwnershipEvent, data: T | null = null, queue: SendQueue = SendQueue.Queued) {
|
312
312
|
|
313
|
+
//@ts-ignore
|
313
314
|
if (data === null) data = {};
|
314
315
|
|
315
316
|
if (queue === SendQueue.Queued) {
|
@@ -331,7 +332,7 @@
|
|
331
332
|
delete this._state[guid];
|
332
333
|
}
|
333
334
|
|
334
|
-
public sendDeleteRemoteStateAll(){
|
335
|
+
public sendDeleteRemoteStateAll() {
|
335
336
|
this.send("delete-all-state");
|
336
337
|
this._state = {};
|
337
338
|
}
|
@@ -404,23 +405,25 @@
|
|
404
405
|
this.netWebSocketUrlProvider = prov;
|
405
406
|
}
|
406
407
|
|
407
|
-
public connect() {
|
408
|
-
if (this.connected) return;
|
408
|
+
public async connect() {
|
409
|
+
if (this.connected) return Promise.resolve(true);
|
409
410
|
if (debugNet)
|
410
411
|
console.log("connecting");
|
411
|
-
// this.channel = geckos({ port: 9208, url: 'http://127.0.0.1' });
|
412
|
-
// this.channel.onConnect(this.onConnectGeckosIo.bind(this));
|
413
|
-
// const networking = GameObject.findObjectOfType(Networking, this.context, false);
|
414
412
|
const overrideUrl = this.netWebSocketUrlProvider?.getWebsocketUrl();
|
415
413
|
if (overrideUrl) {
|
416
414
|
serverUrl = overrideUrl;
|
417
415
|
}
|
418
|
-
else if(isHostedOnGlitch()) {
|
416
|
+
else if (isHostedOnGlitch()) {
|
419
417
|
serverUrl = "wss://" + window.location.host + "/socket";
|
420
418
|
}
|
421
|
-
this.connectWebsocket();
|
419
|
+
return this.connectWebsocket();
|
422
420
|
};
|
423
421
|
|
422
|
+
public disconnect() {
|
423
|
+
this._ws?.close();
|
424
|
+
this._ws = undefined;
|
425
|
+
}
|
426
|
+
|
424
427
|
private _listeners: { [key: string]: Function[] } = {};
|
425
428
|
private _listenersBinary: { [key: string]: BinaryCallback[] } = {};
|
426
429
|
private connected: boolean = false;
|
@@ -428,7 +431,6 @@
|
|
428
431
|
private _connectionId: string | null = null;
|
429
432
|
|
430
433
|
// Websocket ------------------------------------------------------------
|
431
|
-
private _isConnectingToWebsocket: boolean = false;
|
432
434
|
private _ws: Websocket | undefined;
|
433
435
|
private _waitingForSocket: { [key: string]: Array<Function> } = {};
|
434
436
|
private _isInRoom: boolean = false;
|
@@ -439,26 +441,55 @@
|
|
439
441
|
private _state: { [key: string]: any } = {};
|
440
442
|
private _currentDelay: number = -1;
|
441
443
|
|
444
|
+
private _connectingToWebsocketPromise: Promise<boolean> | null = null;
|
445
|
+
|
442
446
|
private connectWebsocket() {
|
443
|
-
if (this.
|
444
|
-
this.
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
}
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
447
|
+
if (this._connectingToWebsocketPromise) return this._connectingToWebsocketPromise;
|
448
|
+
return this._connectingToWebsocketPromise = new Promise(async (res, _) => {
|
449
|
+
let didResolve = false;
|
450
|
+
const resolve = (val: boolean) => {
|
451
|
+
if (didResolve) return;
|
452
|
+
didResolve = true;
|
453
|
+
res(val);
|
454
|
+
}
|
455
|
+
if(serverUrl === undefined){
|
456
|
+
console.log("Fetch default backend url: " + defaultNetworkingBackendUrlProvider);
|
457
|
+
let failed = false;
|
458
|
+
const defaultUrlResponse = await fetch(defaultNetworkingBackendUrlProvider);
|
459
|
+
serverUrl = await defaultUrlResponse.text();
|
460
|
+
if(failed) return;
|
461
|
+
}
|
462
|
+
|
463
|
+
if(serverUrl === undefined){
|
464
|
+
resolve(false);
|
465
|
+
return;
|
466
|
+
}
|
467
|
+
|
468
|
+
console.log("⊡ Connecting to networking backend on\n" + serverUrl)
|
469
|
+
const ws = new WebsocketBuilder(serverUrl)
|
470
|
+
.onOpen(() => {
|
471
|
+
this._connectingToWebsocketPromise = null;
|
472
|
+
this._ws = ws;
|
473
|
+
this.connected = true;
|
474
|
+
console.log("⊞ Connected to networking backend\n" + serverUrl);
|
475
|
+
resolve(true);
|
476
|
+
this.onSendQueued(SendQueue.OnConnection);
|
477
|
+
})
|
478
|
+
.onClose((_evt) => {
|
479
|
+
this._connectingToWebsocketPromise = null;
|
480
|
+
this.connected = false;
|
481
|
+
this._isInRoom = false;
|
482
|
+
resolve(false);
|
483
|
+
})
|
484
|
+
.onError((i, ev) => {
|
485
|
+
console.error("⊠ Websocket error", i, ev);
|
486
|
+
resolve(false);
|
487
|
+
})
|
488
|
+
.onMessage(this.onMessage.bind(this))
|
489
|
+
.onRetry(() => { console.log("Retry connecting to networking websocket") })
|
490
|
+
.build();
|
491
|
+
|
492
|
+
});
|
462
493
|
}
|
463
494
|
|
464
495
|
private onMessage(_, ev) {
|
@@ -575,7 +606,7 @@
|
|
575
606
|
break;
|
576
607
|
|
577
608
|
case "all-room-state-deleted":
|
578
|
-
if(debugNet) console.log("RECEIVED all-room-state-deleted");
|
609
|
+
if (debugNet) console.log("RECEIVED all-room-state-deleted");
|
579
610
|
this._state = {};
|
580
611
|
break;
|
581
612
|
|
@@ -40,6 +40,9 @@
|
|
40
40
|
this._alphaFactor = factor;
|
41
41
|
this.onColorChanged();
|
42
42
|
}
|
43
|
+
get alphaFactor() {
|
44
|
+
return this._alphaFactor;
|
45
|
+
}
|
43
46
|
|
44
47
|
protected onColorChanged() {
|
45
48
|
if (this.uiObject) {
|
@@ -14,11 +14,11 @@
|
|
14
14
|
interactable: boolean;
|
15
15
|
}
|
16
16
|
|
17
|
-
export interface
|
17
|
+
export interface IHasAlphaFactor {
|
18
18
|
setAlphaFactor(val: number);
|
19
19
|
}
|
20
20
|
|
21
|
-
export interface IGraphic extends IComponent,
|
21
|
+
export interface IGraphic extends IComponent, IHasAlphaFactor {
|
22
22
|
get isGraphic(): boolean;
|
23
23
|
raycastTarget: boolean;
|
24
24
|
}
|
@@ -2,12 +2,11 @@
|
|
2
2
|
import { BaseUIComponent } from "./BaseUIComponent";
|
3
3
|
import { DocumentedOptions as ThreeMeshUIEveryOptions } from "three-mesh-ui/build/types/core/elements/MeshUIBaseElement";
|
4
4
|
import { serializable } from "../../engine/engine_serialization_decorator";
|
5
|
-
import {
|
6
|
-
import { EventSystem } from "./EventSystem";
|
5
|
+
import { Matrix4, Object3D, Quaternion, Vector2, Vector3 } from "three";
|
7
6
|
import { getParam } from "../../engine/engine_utils";
|
8
7
|
import { onChange } from "./Utils";
|
9
8
|
import { foreachComponentEnumerator } from "../../engine/engine_gameobject";
|
10
|
-
import { ICanvas, IRectTransform, IRectTransformChangedReceiver
|
9
|
+
import { ICanvas, IRectTransform, IRectTransformChangedReceiver } from "./Interfaces";
|
11
10
|
import { GameObject } from '../Component';
|
12
11
|
|
13
12
|
const debug = getParam("debugui");
|
@@ -43,14 +42,14 @@
|
|
43
42
|
get rotation() { return this.gameObject.quaternion; }
|
44
43
|
get scale(): Vector3 { return this.gameObject.scale; }
|
45
44
|
|
46
|
-
private _anchoredPosition!:
|
45
|
+
private _anchoredPosition!: Vector2;
|
47
46
|
|
48
|
-
@serializable(
|
47
|
+
@serializable(Vector2)
|
49
48
|
get anchoredPosition() {
|
50
|
-
if (!this._anchoredPosition) this._anchoredPosition = new
|
49
|
+
if (!this._anchoredPosition) this._anchoredPosition = new Vector2();
|
51
50
|
return this._anchoredPosition;
|
52
51
|
}
|
53
|
-
private set anchoredPosition(value:
|
52
|
+
private set anchoredPosition(value: Vector2) {
|
54
53
|
this._anchoredPosition = value;
|
55
54
|
}
|
56
55
|
|
@@ -99,17 +98,24 @@
|
|
99
98
|
private lastMatrix!: Matrix4;
|
100
99
|
private rectBlock!: Object3D;
|
101
100
|
private _transformNeedsUpdate: boolean = false;
|
101
|
+
private _initialPosition!: Vector3;
|
102
102
|
|
103
103
|
awake() {
|
104
104
|
super.awake();
|
105
|
+
// this is required if an animator animated the transform anchoring
|
106
|
+
if (!this._anchoredPosition)
|
107
|
+
this._anchoredPosition = new Vector2();
|
108
|
+
|
105
109
|
this.lastMatrix = new Matrix4();
|
106
110
|
this.rectBlock = new Object3D();
|
107
111
|
// Is this legacy? Not sure if this is still needed
|
108
112
|
this.rectBlock.position.z = .1;
|
109
113
|
this.rectBlock.name = this.name;
|
110
114
|
|
111
|
-
//
|
112
|
-
|
115
|
+
// TODO: get rid of the initial position
|
116
|
+
this._initialPosition = this.gameObject.position.clone();
|
117
|
+
this._initialPosition.z = 0;
|
118
|
+
this.onApplyTransform("RectTransform awake");
|
113
119
|
|
114
120
|
// TODO: we need to replace this with the watch that e.g. Rigibody is using (or the one in utils?)
|
115
121
|
// perhaps we can also just manually check the few properties in the update loops?
|
@@ -119,20 +125,6 @@
|
|
119
125
|
onChange(this, "pivot", () => { this.markDirty(); });
|
120
126
|
onChange(this, "anchorMin", () => { this.markDirty(); });
|
121
127
|
onChange(this, "anchorMax", () => { this.markDirty(); });
|
122
|
-
|
123
|
-
// When exported with an anchored position offset we remove it here
|
124
|
-
// because it would otherwise be applied twice when the anchoring is animated
|
125
|
-
// Maybe we can get rid of this workaround if we just set the mesh ui position from the
|
126
|
-
// anchored position value but then we would have to make sure if a user/the engine updates
|
127
|
-
// "position" the change would also land in anchoredPosition
|
128
|
-
// Another solution would perhaps be to get rid of the extra "anchoredPosition" vector3
|
129
|
-
// and instead use the same vector3 instance on both "position" and "anchoredPosition"
|
130
|
-
// But I'm also not sure if this will not cause issues elsewhere later / be confusing?
|
131
|
-
// (that being said we can make anchoredPosition hidden)
|
132
|
-
if (!this.isRoot()) {
|
133
|
-
this.gameObject.position.x += this.anchoredPosition.x;
|
134
|
-
this.gameObject.position.y -= this.anchoredPosition.y;
|
135
|
-
}
|
136
128
|
}
|
137
129
|
|
138
130
|
onEnable() {
|
@@ -155,7 +147,7 @@
|
|
155
147
|
}
|
156
148
|
|
157
149
|
get isDirty() {
|
158
|
-
if(!this._transformNeedsUpdate) this._transformNeedsUpdate = !this.lastMatrix.equals(this.gameObject.matrix);
|
150
|
+
if (!this._transformNeedsUpdate) this._transformNeedsUpdate = !this.lastMatrix.equals(this.gameObject.matrix);
|
159
151
|
return this._transformNeedsUpdate;
|
160
152
|
}
|
161
153
|
|
@@ -199,7 +191,6 @@
|
|
199
191
|
else
|
200
192
|
this._parentRectTransform = undefined;
|
201
193
|
this._transformNeedsUpdate = false;
|
202
|
-
this.lastMatrix.copy(this.gameObject.matrix);
|
203
194
|
|
204
195
|
if (debugLayout) console.warn("RectTransform → ApplyTransform", this.name + " because " + reason);
|
205
196
|
|
@@ -223,7 +214,8 @@
|
|
223
214
|
tempVec.set(0, 0, 0);
|
224
215
|
this.applyAnchoring(tempVec);
|
225
216
|
tempVec.z += this.offset;
|
226
|
-
tempVec.z -= this.gameObject.position.z;
|
217
|
+
// tempVec.z -= this.gameObject.position.z;
|
218
|
+
|
227
219
|
tempMatrix.identity();
|
228
220
|
tempMatrix.setPosition(tempVec.x, tempVec.y, tempVec.z);
|
229
221
|
uiobject.matrix.premultiply(tempMatrix);
|
@@ -236,6 +228,8 @@
|
|
236
228
|
if (!canvas.screenspace) uiobject.rotation.y = Math.PI;
|
237
229
|
}
|
238
230
|
|
231
|
+
this.lastMatrix.copy(this.gameObject.matrix);
|
232
|
+
|
239
233
|
// iterate other components on this object that might need to know about the transform change
|
240
234
|
// e.g. Graphic components should update their width and height
|
241
235
|
const includeChildren = true;
|
@@ -259,11 +253,21 @@
|
|
259
253
|
// }
|
260
254
|
// }
|
261
255
|
|
256
|
+
private _lastAnchoring!: Vector2;
|
257
|
+
|
262
258
|
/** applies the position offset to the passed in vector */
|
263
259
|
private applyAnchoring(pos: Vector3) {
|
264
|
-
pos.x += this.anchoredPosition.x;
|
265
|
-
pos.y += this.anchoredPosition.y;
|
266
260
|
|
261
|
+
if (!this._lastAnchoring) this._lastAnchoring = new Vector2();
|
262
|
+
const diff = this._lastAnchoring.sub(this._anchoredPosition)
|
263
|
+
this.gameObject.position.x += diff.x;
|
264
|
+
this.gameObject.position.y += diff.y;
|
265
|
+
this._lastAnchoring.copy(this._anchoredPosition);
|
266
|
+
|
267
|
+
pos.x += (this._initialPosition.x - this.gameObject.position.x);
|
268
|
+
pos.y += (this._initialPosition.y - this.gameObject.position.y);
|
269
|
+
pos.z += (this._initialPosition.z - this.gameObject.position.z);
|
270
|
+
|
267
271
|
const parent = this._parentRectTransform;
|
268
272
|
if (parent) {
|
269
273
|
// Calculate vertical offset
|
@@ -1,6 +1,7 @@
|
|
1
1
|
import { Behaviour } from "./Component";
|
2
2
|
import * as utils from "../engine/engine_utils"
|
3
3
|
import { serializable } from "../engine/engine_serialization_decorator";
|
4
|
+
import { getParam } from "../engine/engine_utils";
|
4
5
|
|
5
6
|
const viewParamName = "view";
|
6
7
|
const debug = utils.getParam("debugsyncedroom");
|
@@ -40,7 +41,9 @@
|
|
40
41
|
this.context.connection.joinRoom(viewId, true);
|
41
42
|
return;
|
42
43
|
}
|
43
|
-
|
44
|
+
// If setup to join a random room
|
45
|
+
if (this.joinRandomRoom || getParam(this.urlParameterName))
|
46
|
+
this.tryJoinRoom();
|
44
47
|
}
|
45
48
|
|
46
49
|
onDisable(): void {
|
@@ -48,6 +51,13 @@
|
|
48
51
|
this.context.connection.leaveRoom(this.roomName);
|
49
52
|
}
|
50
53
|
|
54
|
+
/** Will generate a random room name, set it as an URL parameter and attempt to join the room */
|
55
|
+
tryJoinRandomRoom() {
|
56
|
+
this.setRandomRoomUrlParameter();
|
57
|
+
this.tryJoinRoom();
|
58
|
+
}
|
59
|
+
|
60
|
+
/** Try to join the currently set roomName */
|
51
61
|
tryJoinRoom(call: number = 0): boolean {
|
52
62
|
if (call === undefined) call = 0;
|
53
63
|
let hasRoomParameter = false;
|
@@ -78,12 +88,6 @@
|
|
78
88
|
return false;
|
79
89
|
}
|
80
90
|
|
81
|
-
if (!this.roomName || this.roomName.length <= 0) {
|
82
|
-
if (debug)
|
83
|
-
console.error("Missing room name on \"" + this.name + "\". Make sure this is correctly configured in Unity", this.context.connection.isDebugEnabled ? this : "");
|
84
|
-
return false;
|
85
|
-
}
|
86
|
-
|
87
91
|
if (!this.context.connection.isConnected) {
|
88
92
|
this.context.connection.connect();
|
89
93
|
}
|
@@ -157,8 +161,6 @@
|
|
157
161
|
urlParams.set(viewParamName, this.context.connection.currentRoomViewId);
|
158
162
|
return window.location.origin + window.location.pathname + "?" + urlParams.toString();
|
159
163
|
}
|
160
|
-
|
161
|
-
|
162
164
|
return null;
|
163
165
|
}
|
164
166
|
}
|
@@ -6,7 +6,7 @@
|
|
6
6
|
import { Canvas } from './Canvas';
|
7
7
|
import { serializable } from '../../engine/engine_serialization_decorator';
|
8
8
|
import { getParam, resolveUrl } from '../../engine/engine_utils';
|
9
|
-
import { ICanvas } from './Interfaces';
|
9
|
+
import { ICanvas, IHasAlphaFactor } from './Interfaces';
|
10
10
|
|
11
11
|
const debug = getParam("debugtext");
|
12
12
|
|
@@ -38,7 +38,7 @@
|
|
38
38
|
BoldAndItalic = 3,
|
39
39
|
}
|
40
40
|
|
41
|
-
export class Text extends Graphic {
|
41
|
+
export class Text extends Graphic implements IHasAlphaFactor {
|
42
42
|
|
43
43
|
@serializable()
|
44
44
|
alignment: TextAnchor = TextAnchor.UpperLeft;
|
@@ -55,6 +55,13 @@
|
|
55
55
|
@serializable()
|
56
56
|
fontStyle: FontStyle = FontStyle.Normal;
|
57
57
|
|
58
|
+
// private _alphaFactor : number = 1;
|
59
|
+
setAlphaFactor(factor: number): void {
|
60
|
+
super.setAlphaFactor(factor);
|
61
|
+
this.uiObject?.set({ fontOpacity: this.color.alpha * this.alphaFactor });
|
62
|
+
this.markDirty();
|
63
|
+
}
|
64
|
+
|
58
65
|
@serializable()
|
59
66
|
get text(): string {
|
60
67
|
return this._text;
|
@@ -78,11 +85,9 @@
|
|
78
85
|
}
|
79
86
|
|
80
87
|
set fontSize(val: number) {
|
81
|
-
|
82
88
|
// Setting that kind of property in a parent, would cascade to each 'non-overrided' children.
|
83
89
|
this._fontSize = val;
|
84
90
|
this.uiObject?.set({ fontSize: val });
|
85
|
-
|
86
91
|
}
|
87
92
|
|
88
93
|
|
@@ -661,12 +661,6 @@
|
|
661
661
|
|
662
662
|
}
|
663
663
|
|
664
|
-
if ( ! ( material.uuid in context.materials ) ) {
|
665
|
-
|
666
|
-
context.materials[ material.uuid ] = material;
|
667
|
-
|
668
|
-
}
|
669
|
-
|
670
664
|
} else {
|
671
665
|
|
672
666
|
console.warn( 'THREE.USDZExporter: Unsupported material type (USDZ only supports MeshStandardMaterial)', name );
|
@@ -675,6 +669,15 @@
|
|
675
669
|
|
676
670
|
}
|
677
671
|
|
672
|
+
if( material ) {
|
673
|
+
|
674
|
+
if ( ! ( material.uuid in context.materials ) ) {
|
675
|
+
|
676
|
+
context.materials[ material.uuid ] = material;
|
677
|
+
|
678
|
+
}
|
679
|
+
}
|
680
|
+
|
678
681
|
for ( const ch of object.children ) {
|
679
682
|
|
680
683
|
addResources( ch, context );
|
@@ -15,6 +15,7 @@
|
|
15
15
|
import { hasProLicense } from "../../../engine/engine_license";
|
16
16
|
import { BehaviorExtension } from "./extensions/behavior/Behaviour";
|
17
17
|
import { AudioExtension } from "./extensions/behavior/AudioExtension";
|
18
|
+
import { TextExtension } from "./extensions/USDZText";
|
18
19
|
|
19
20
|
const debug = getParam("debugusdz");
|
20
21
|
|
@@ -96,6 +97,7 @@
|
|
96
97
|
if (this.interactive) {
|
97
98
|
this.extensions.push(new BehaviorExtension());
|
98
99
|
this.extensions.push(new AudioExtension());
|
100
|
+
this.extensions.push(new TextExtension());
|
99
101
|
}
|
100
102
|
}
|
101
103
|
|
@@ -1,5 +1,11 @@
|
|
1
|
+
import { IUSDExporterExtension } from "../Extension";
|
1
2
|
import { IBehaviorElement } from "../extensions/behavior/BehavioursBuilder";
|
2
|
-
import { USDDocument, USDWriter } from "../ThreeUSDZExporter";
|
3
|
+
import { USDDocument, USDObject, USDWriter, USDZExporterContext } from "../ThreeUSDZExporter";
|
4
|
+
import { GameObject } from "../../../Component";
|
5
|
+
import { Text } from "../../../ui/Text"
|
6
|
+
import { RectTransform } from "../../../ui/RectTransform";
|
7
|
+
import { Color, Material, Matrix4, MeshStandardMaterial, Object3D, Vector3 } from "three";
|
8
|
+
import { TextAnchor } from "../../../ui/Text";
|
3
9
|
|
4
10
|
|
5
11
|
export enum TextWrapMode {
|
@@ -41,6 +47,8 @@
|
|
41
47
|
horizontalAlignment?: HorizontalAlignment;
|
42
48
|
verticalAlignment?: VerticalAlignment;
|
43
49
|
|
50
|
+
material?: Material;
|
51
|
+
|
44
52
|
setDepth(depth: number): USDZText {
|
45
53
|
this.depth = depth;
|
46
54
|
return this;
|
@@ -94,6 +102,10 @@
|
|
94
102
|
if (this.verticalAlignment)
|
95
103
|
writer.appendLine(`token verticalAlignment = "${this.verticalAlignment}"`);
|
96
104
|
|
105
|
+
if (this.material !== undefined) {
|
106
|
+
writer.appendLine(`rel material:binding = </Materials/Material_${this.material.id}>`)
|
107
|
+
}
|
108
|
+
|
97
109
|
writer.closeBlock();
|
98
110
|
|
99
111
|
}
|
@@ -120,23 +132,99 @@
|
|
120
132
|
text.height = height;
|
121
133
|
text.horizontalAlignment = horizontal;
|
122
134
|
text.verticalAlignment = vertical;
|
123
|
-
if (wrapMode)
|
135
|
+
if (wrapMode !== undefined)
|
124
136
|
text.wrapMode = wrapMode;
|
125
137
|
return text;
|
126
138
|
}
|
127
139
|
}
|
128
140
|
|
141
|
+
const rotateYAxisMatrix = new Matrix4().makeRotationY(Math.PI);
|
129
142
|
|
130
|
-
export class TextExtension {
|
131
|
-
|
132
|
-
|
133
|
-
const text = TextBuilder.multiLine("Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.",
|
134
|
-
1, 1, HorizontalAlignment.justified, VerticalAlignment.top);
|
135
|
-
text.pointSize = 300;
|
136
|
-
text.depth = .01;
|
137
|
-
text.writeTo(undefined, writer);
|
138
|
-
});
|
143
|
+
export class TextExtension implements IUSDExporterExtension {
|
144
|
+
get extensionName(): string {
|
145
|
+
return "text";
|
139
146
|
}
|
147
|
+
|
148
|
+
onExportObject(object: Object3D, model: USDObject, _context: USDZExporterContext) {
|
149
|
+
|
150
|
+
const text = GameObject.getComponent(object, Text);
|
151
|
+
if (text) {
|
152
|
+
const rt = GameObject.getComponent(object, RectTransform);
|
153
|
+
let width = 100;
|
154
|
+
let height = 100;
|
155
|
+
if (rt) {
|
156
|
+
width = rt.width;
|
157
|
+
height = rt.height;
|
158
|
+
}
|
159
|
+
const newModel = model.clone();
|
160
|
+
newModel.matrix = rotateYAxisMatrix.clone();
|
161
|
+
const color = new Color().copySRGBToLinear(text.color);
|
162
|
+
newModel.material = new MeshStandardMaterial({ color: color, emissive: color });
|
163
|
+
model.add(newModel);
|
164
|
+
|
165
|
+
// model.matrix.scale(new Vector3(100, 100, 100));
|
166
|
+
newModel.addEventListener("serialize", (writer: USDWriter, _context: USDZExporterContext) => {
|
167
|
+
const textObj = TextBuilder.multiLine(text.text, width, height, HorizontalAlignment.center, VerticalAlignment.bottom, TextWrapMode.flowing);
|
168
|
+
this.setTextAlignment(textObj, text.alignment);
|
169
|
+
this.setOverflow(textObj, text);
|
170
|
+
if (newModel.material)
|
171
|
+
textObj.material = newModel.material;
|
172
|
+
textObj.pointSize = this.convertToTextSize(text.fontSize);
|
173
|
+
textObj.depth = .001;
|
174
|
+
textObj.writeTo(undefined, writer);
|
175
|
+
});
|
176
|
+
}
|
177
|
+
}
|
178
|
+
|
179
|
+
private convertToTextSize(pixel: number) {
|
180
|
+
return 1 / 0.0502 * 144 * pixel;
|
181
|
+
}
|
182
|
+
|
183
|
+
private setOverflow(textObj: USDZText, text: Text) {
|
184
|
+
if (text.horizontalOverflow) {
|
185
|
+
textObj.wrapMode = TextWrapMode.singleLine;
|
186
|
+
}
|
187
|
+
else {
|
188
|
+
textObj.wrapMode = TextWrapMode.flowing;
|
189
|
+
}
|
190
|
+
}
|
191
|
+
|
192
|
+
private setTextAlignment(text: USDZText, alignment: TextAnchor) {
|
193
|
+
switch (alignment) {
|
194
|
+
case TextAnchor.LowerLeft:
|
195
|
+
case TextAnchor.MiddleLeft:
|
196
|
+
case TextAnchor.UpperLeft:
|
197
|
+
text.horizontalAlignment = HorizontalAlignment.left;
|
198
|
+
break;
|
199
|
+
case TextAnchor.LowerCenter:
|
200
|
+
case TextAnchor.MiddleCenter:
|
201
|
+
case TextAnchor.UpperCenter:
|
202
|
+
text.horizontalAlignment = HorizontalAlignment.center;
|
203
|
+
break;
|
204
|
+
case TextAnchor.LowerRight:
|
205
|
+
case TextAnchor.MiddleRight:
|
206
|
+
case TextAnchor.UpperRight:
|
207
|
+
text.horizontalAlignment = HorizontalAlignment.right;
|
208
|
+
break;
|
209
|
+
}
|
210
|
+
switch (alignment) {
|
211
|
+
case TextAnchor.LowerLeft:
|
212
|
+
case TextAnchor.LowerCenter:
|
213
|
+
case TextAnchor.LowerRight:
|
214
|
+
text.verticalAlignment = VerticalAlignment.bottom;
|
215
|
+
break;
|
216
|
+
case TextAnchor.MiddleLeft:
|
217
|
+
case TextAnchor.MiddleCenter:
|
218
|
+
case TextAnchor.MiddleRight:
|
219
|
+
text.verticalAlignment = VerticalAlignment.middle;
|
220
|
+
break;
|
221
|
+
case TextAnchor.UpperLeft:
|
222
|
+
case TextAnchor.UpperCenter:
|
223
|
+
case TextAnchor.UpperRight:
|
224
|
+
text.verticalAlignment = VerticalAlignment.top;
|
225
|
+
break;
|
226
|
+
}
|
227
|
+
}
|
140
228
|
}
|
141
229
|
|
142
230
|
|
@@ -241,7 +241,7 @@
|
|
241
241
|
this.context.domElement.append(buttonsContainer);
|
242
242
|
|
243
243
|
// AR support
|
244
|
-
|
244
|
+
if (this.enableAR && this.createARButton && arSupported)
|
245
245
|
{
|
246
246
|
arButton = WebXR.createARButton(this);
|
247
247
|
this._arButton = arButton;
|