architwin 1.0.14 → 1.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/architwin.ts +1656 -0
- package/package.json +5 -4
- package/types.ts +185 -0
package/architwin.ts
ADDED
|
@@ -0,0 +1,1656 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import type { MpSdk, ShowcaseBundleWindow, Scene } from "./bundle/sdk";
|
|
3
|
+
import type {
|
|
4
|
+
ITag,
|
|
5
|
+
IUser,
|
|
6
|
+
ISpace,
|
|
7
|
+
IShowcaseObject,
|
|
8
|
+
I3DObject,
|
|
9
|
+
IMPConfig,
|
|
10
|
+
ISweep,
|
|
11
|
+
IVideoObject,
|
|
12
|
+
ITagPublic,
|
|
13
|
+
NearbyPayload,
|
|
14
|
+
NearbyObjects,
|
|
15
|
+
ISlideshowObject,
|
|
16
|
+
IObjectData,
|
|
17
|
+
|
|
18
|
+
} from "./types";
|
|
19
|
+
import { distance} from "mathjs";
|
|
20
|
+
// @ts-ignore
|
|
21
|
+
// import * as _config from './config.json'
|
|
22
|
+
import JSZip from "jszip";
|
|
23
|
+
import JSZipUtils from "jszip-utils";
|
|
24
|
+
|
|
25
|
+
const _config = {
|
|
26
|
+
aws: {
|
|
27
|
+
region: "ap-northeast-1",
|
|
28
|
+
accessKeyId: "AKIAVVUXZ66KW7GBSW7A",
|
|
29
|
+
secretAccessKey: "fpJd3lBEERU1fWZ/TXhWz5muK1KI5GqLtljkNuK4'",
|
|
30
|
+
},
|
|
31
|
+
mp: {
|
|
32
|
+
sdkKey: "a3ae8341bd8f44899eba16df86307d7d",
|
|
33
|
+
urlParams: [
|
|
34
|
+
"help",
|
|
35
|
+
"play",
|
|
36
|
+
"nt",
|
|
37
|
+
"play",
|
|
38
|
+
"qs",
|
|
39
|
+
"brand",
|
|
40
|
+
"dh",
|
|
41
|
+
"tour",
|
|
42
|
+
"gt",
|
|
43
|
+
"hr",
|
|
44
|
+
"mls",
|
|
45
|
+
"mt",
|
|
46
|
+
"tagNav",
|
|
47
|
+
"pin",
|
|
48
|
+
"portal",
|
|
49
|
+
"f",
|
|
50
|
+
"fp",
|
|
51
|
+
"lang",
|
|
52
|
+
"kb",
|
|
53
|
+
"lp",
|
|
54
|
+
"st",
|
|
55
|
+
"title",
|
|
56
|
+
"tourcta",
|
|
57
|
+
"wts",
|
|
58
|
+
"ts",
|
|
59
|
+
"hl",
|
|
60
|
+
"vr",
|
|
61
|
+
"nozoom",
|
|
62
|
+
"search",
|
|
63
|
+
"wh",
|
|
64
|
+
],
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const apiURL = "http://localhost:5173/api/v1/";
|
|
69
|
+
const sdkKey = _config.mp.sdkKey;
|
|
70
|
+
const urlParams = _config.mp.urlParams;
|
|
71
|
+
|
|
72
|
+
console.log("apiURL...", apiURL);
|
|
73
|
+
// start here
|
|
74
|
+
let _api = {} as any;
|
|
75
|
+
let _space = {} as ISpace;
|
|
76
|
+
let _atwin = {} as MpSdk;
|
|
77
|
+
let _tags = [] as ITag[];
|
|
78
|
+
|
|
79
|
+
let tags = [] as ITagPublic[];
|
|
80
|
+
let sweeps = [] as any;
|
|
81
|
+
|
|
82
|
+
let _allSlideShow = [] as ISlideshowObject[]
|
|
83
|
+
let _slideShowImage = [] as any
|
|
84
|
+
let _currentSlideShowID = 0
|
|
85
|
+
let _currentSlideIndex = 0
|
|
86
|
+
let _currentSlideShow = null
|
|
87
|
+
var _timer = null
|
|
88
|
+
let _videos = [] as IVideoObject[]
|
|
89
|
+
let _currentViewMode = "" as MpSdk.Mode.Mode | null
|
|
90
|
+
let _3DXObject = [] as Scene.INode[]
|
|
91
|
+
let _unrenderedObjects = [] as IShowcaseObject[]
|
|
92
|
+
let _transformControlNode:Scene.INode
|
|
93
|
+
let _inputControlComponent = null
|
|
94
|
+
let _selectedObject = {} as IObjectData
|
|
95
|
+
let previousTimestamp:number = 0;
|
|
96
|
+
|
|
97
|
+
let _currentSweep = {} as ISweep;
|
|
98
|
+
let _currentCameraPose = {} as any;
|
|
99
|
+
let _sweeps = [] as any;
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
// end here
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
let _atwinSrc = `/bundle/showcase.html?m=''&applicationKey=${sdkKey}&newtags=1`;
|
|
107
|
+
|
|
108
|
+
// =========================================== INITIALIZATION ===========================================
|
|
109
|
+
|
|
110
|
+
// initialization: loginUser, iframe, space, tags, objects
|
|
111
|
+
async function connectSpace(
|
|
112
|
+
url: string,
|
|
113
|
+
auth: { apiKey: string; user: IUser },
|
|
114
|
+
config: IMPConfig
|
|
115
|
+
) {
|
|
116
|
+
console.log("connectSpace()");
|
|
117
|
+
|
|
118
|
+
console.log("__config", config);
|
|
119
|
+
|
|
120
|
+
const api = axios.create({
|
|
121
|
+
baseURL: apiURL,
|
|
122
|
+
headers: {
|
|
123
|
+
"Access-Control-Allow-Origin": "*",
|
|
124
|
+
"Content-Type": "application/json",
|
|
125
|
+
Authorization: auth.apiKey,
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
_api = api;
|
|
130
|
+
|
|
131
|
+
const showcase = document.getElementById(
|
|
132
|
+
config.iframeId
|
|
133
|
+
) as HTMLIFrameElement;
|
|
134
|
+
|
|
135
|
+
const spaceId = getSpaceId(url);
|
|
136
|
+
|
|
137
|
+
if (!spaceId) {
|
|
138
|
+
console.error("spaceId is undefined");
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
await loginUser(auth.user);
|
|
143
|
+
|
|
144
|
+
const space = await getSpace(spaceId);
|
|
145
|
+
|
|
146
|
+
if (!space) {
|
|
147
|
+
console.error("space is undefined");
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
setSpace(space);
|
|
152
|
+
|
|
153
|
+
_atwinSrc = getIframeSrc(config);
|
|
154
|
+
|
|
155
|
+
console.log("__atwinSrc", _atwinSrc);
|
|
156
|
+
|
|
157
|
+
showcase.src = _atwinSrc;
|
|
158
|
+
|
|
159
|
+
const tags = await getTags(space);
|
|
160
|
+
|
|
161
|
+
if (!tags) {
|
|
162
|
+
console.log("tags is undefined");
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
setTags(tags);
|
|
167
|
+
|
|
168
|
+
const objects = await get3DObjects(spaceId);
|
|
169
|
+
|
|
170
|
+
if (!objects) {
|
|
171
|
+
console.log("objects is undefined");
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
//setObjects(objects);
|
|
176
|
+
|
|
177
|
+
showcase.contentWindow?.location.reload(); // force reload iframe
|
|
178
|
+
|
|
179
|
+
const showcaseWindow = showcase.contentWindow as ShowcaseBundleWindow;
|
|
180
|
+
|
|
181
|
+
showcase.addEventListener("load", async () => {
|
|
182
|
+
console.log('iframe.addEventListener("load")');
|
|
183
|
+
|
|
184
|
+
console.log("showcaseWindow", showcaseWindow);
|
|
185
|
+
|
|
186
|
+
try {
|
|
187
|
+
_atwin = await showcaseWindow.MP_SDK.connect(showcaseWindow);
|
|
188
|
+
console.log("Hello Bundle SDK", _atwin);
|
|
189
|
+
onShowcaseConnect();
|
|
190
|
+
} catch (e) {
|
|
191
|
+
console.error(e);
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// start here
|
|
197
|
+
// After connectSpace it will render: tags, objects, and listeners
|
|
198
|
+
async function onShowcaseConnect(): Promise<void> {
|
|
199
|
+
console.log("onShowcaseConnect()");
|
|
200
|
+
try {
|
|
201
|
+
const modelData = await _atwin.Model.getData();
|
|
202
|
+
console.log("Model sid:" + modelData.sid);
|
|
203
|
+
|
|
204
|
+
_atwin.App.state.subscribe(async (appState) => {
|
|
205
|
+
console.log("appState", appState);
|
|
206
|
+
|
|
207
|
+
if (appState.phase === _atwin.App.Phase.LOADING) {
|
|
208
|
+
console.log("App is loading...");
|
|
209
|
+
await hideTags();
|
|
210
|
+
} else if (appState.phase === _atwin.App.Phase.STARTING) {
|
|
211
|
+
console.log("App is starting...");
|
|
212
|
+
await showTags(_tags);
|
|
213
|
+
} else if (appState.phase === _atwin.App.Phase.PLAYING) {
|
|
214
|
+
console.log("App is playing...");
|
|
215
|
+
await getSweeps();
|
|
216
|
+
await setLighting()
|
|
217
|
+
_atwin.Sweep.current.subscribe(function (currentSweep) {
|
|
218
|
+
// Change to the current sweep has occurred.
|
|
219
|
+
if (currentSweep.sid === "") {
|
|
220
|
+
console.log("Not currently stationed at a sweep position");
|
|
221
|
+
} else {
|
|
222
|
+
_currentSweep = currentSweep;
|
|
223
|
+
console.log("Currently at sweep", _currentSweep.id);
|
|
224
|
+
|
|
225
|
+
// check auto play
|
|
226
|
+
videoAutoPlay()
|
|
227
|
+
|
|
228
|
+
// console.log("_3DXObject "+_3DXObject.length)
|
|
229
|
+
// _3DXObject.forEach(obj => {
|
|
230
|
+
// console.log("3D Position "+ JSON.stringify(obj.position))
|
|
231
|
+
// })
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
_atwin.Camera.pose.subscribe(async function (pose) {
|
|
236
|
+
// Changes to the Camera pose have occurred.
|
|
237
|
+
_currentCameraPose = pose;
|
|
238
|
+
console.log("Current Camera Pose", _currentCameraPose);
|
|
239
|
+
const hasElapsed = hasTimeElapsed(300)
|
|
240
|
+
if (hasElapsed) {
|
|
241
|
+
console.log("_unrenderedObjects "+_unrenderedObjects.length)
|
|
242
|
+
await renderOnDemand()
|
|
243
|
+
}
|
|
244
|
+
if (hasTimeElapsed(1000)) {
|
|
245
|
+
getNearbyObjects({type: 'ALL',distance:2})
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
_atwin.Mode.current.subscribe((mode) => {
|
|
250
|
+
_currentViewMode = mode;
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
} catch (e) {
|
|
255
|
+
console.error(e);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// end here
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Super light weight alternative to lodash's debounce
|
|
264
|
+
* This function will allow you to debounce a function a set amoint in milliseconds
|
|
265
|
+
* Useful for improving performance if your function is inside a subscribe method
|
|
266
|
+
* @param maxTime milliseconds
|
|
267
|
+
* @returns {boolean}
|
|
268
|
+
*/
|
|
269
|
+
|
|
270
|
+
function hasTimeElapsed(maxTime:number):boolean {
|
|
271
|
+
const currentTimestamp = Date.now();
|
|
272
|
+
|
|
273
|
+
// Difference in milliseconds
|
|
274
|
+
const differenceInMilliseconds = (currentTimestamp - previousTimestamp) % 1000;
|
|
275
|
+
previousTimestamp = currentTimestamp
|
|
276
|
+
if (differenceInMilliseconds >= maxTime) {
|
|
277
|
+
console.log(`Elapsed more than ${maxTime}`);
|
|
278
|
+
return true
|
|
279
|
+
} else {
|
|
280
|
+
console.log(`Elapsed less than ${maxTime}`);
|
|
281
|
+
return false
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* This function detects and return objects fetched from the API that have NOT been rendered yet
|
|
287
|
+
* Not to be confused with getNearbyObjects which returns INodes that have already been rendered
|
|
288
|
+
* @constructor
|
|
289
|
+
* @param payload
|
|
290
|
+
* @type {NearbyPayload}
|
|
291
|
+
* @returns {Array<IShowcaseObject>}
|
|
292
|
+
*/
|
|
293
|
+
|
|
294
|
+
function getNearbyUnrenderedObjects(payload:NearbyPayload):Array<IShowcaseObject>{
|
|
295
|
+
let toBeRendered = [] as IShowcaseObject[]
|
|
296
|
+
const currentPose = {
|
|
297
|
+
x: _currentCameraPose.position.x,
|
|
298
|
+
y: _currentCameraPose.position.y
|
|
299
|
+
}
|
|
300
|
+
toBeRendered = _unrenderedObjects.filter(obj => {
|
|
301
|
+
const obj_pos = {
|
|
302
|
+
x: obj.object_position.x,
|
|
303
|
+
y: obj.object_position.y,
|
|
304
|
+
}
|
|
305
|
+
const distance = calculateDistance(currentPose,obj_pos)
|
|
306
|
+
return distance < payload.distance
|
|
307
|
+
})
|
|
308
|
+
|
|
309
|
+
const filtered = _unrenderedObjects.filter(obj=> {
|
|
310
|
+
return toBeRendered.indexOf(obj) === -1
|
|
311
|
+
})
|
|
312
|
+
_unrenderedObjects = filtered
|
|
313
|
+
console.log("render toBeRendered "+ toBeRendered.length)
|
|
314
|
+
return toBeRendered
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* This function returns an object that returns an array of 3D, video, and slideshow objects
|
|
319
|
+
* You can pass a object type as a object parameter to set what the contents of the returned objects will be
|
|
320
|
+
* @constructor
|
|
321
|
+
* @param payload
|
|
322
|
+
* @type {NearbyPayload}
|
|
323
|
+
* @returns {NearbyObjects}
|
|
324
|
+
*/
|
|
325
|
+
|
|
326
|
+
function getNearbyObjects(payload:NearbyPayload):NearbyObjects{
|
|
327
|
+
if (payload.type === undefined || payload.type === '') {
|
|
328
|
+
payload.type = 'ALL'
|
|
329
|
+
}
|
|
330
|
+
if (payload.distance === undefined) {
|
|
331
|
+
payload.distance = 2
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const pos1 = {
|
|
335
|
+
x: _currentCameraPose.position.x,
|
|
336
|
+
y: _currentCameraPose.position.y
|
|
337
|
+
}
|
|
338
|
+
let three_d = [] as Scene.INode[]
|
|
339
|
+
let videos = [] as IVideoObject[]
|
|
340
|
+
let slideshows = [] as ISlideshowObject[]
|
|
341
|
+
if (payload.type === 'ALL' || '3DX') {
|
|
342
|
+
three_d = _3DXObject.filter(obj => {
|
|
343
|
+
const pos2 = {
|
|
344
|
+
x: obj.position.x,
|
|
345
|
+
y: obj.position.y
|
|
346
|
+
}
|
|
347
|
+
const distance = calculateDistance(pos1,pos2)
|
|
348
|
+
console.log("3DX Distance: "+distance)
|
|
349
|
+
return distance < payload.distance
|
|
350
|
+
})
|
|
351
|
+
}
|
|
352
|
+
if (payload.type === 'ALL' || 'VIDEO') {
|
|
353
|
+
videos = _videos.filter(vid => {
|
|
354
|
+
const pos2 = {
|
|
355
|
+
x: vid.node.position.x,
|
|
356
|
+
y: vid.node.position.y
|
|
357
|
+
}
|
|
358
|
+
const distance = calculateDistance(pos1,pos2)
|
|
359
|
+
console.log("Video Distance: "+distance)
|
|
360
|
+
return distance < payload.distance
|
|
361
|
+
})
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
if(payload.type === 'ALL' || 'SLIDESHOW'){
|
|
365
|
+
|
|
366
|
+
slideshows = _allSlideShow.filter(slide => {
|
|
367
|
+
const pos2 = {
|
|
368
|
+
x: slide.node.position.x,
|
|
369
|
+
y: slide.node.position.y
|
|
370
|
+
}
|
|
371
|
+
const distance = calculateDistance(pos1,pos2)
|
|
372
|
+
console.log("Slideshow Distance: "+distance)
|
|
373
|
+
return distance < payload.distance
|
|
374
|
+
})
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
console.log("nearby3DXObjects "+three_d.length)
|
|
378
|
+
console.log("nearbyVideos "+videos.length)
|
|
379
|
+
console.log("nearbySlideshows "+slideshows.length)
|
|
380
|
+
return {
|
|
381
|
+
x3d: three_d,
|
|
382
|
+
videos: videos,
|
|
383
|
+
slideshows: slideshows
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
async function show3DObjects(object: IShowcaseObject): Promise < void> {
|
|
388
|
+
const [sceneObject] = await _atwin.Scene.createObjects(1);
|
|
389
|
+
const id = Math.floor(Math.random() * 20);
|
|
390
|
+
const modelNode = sceneObject.addNode(id.toString());
|
|
391
|
+
const component = modelNode.addComponent(getComponentLoader(object), {
|
|
392
|
+
url: object.object_data?.amazon_uri,
|
|
393
|
+
});
|
|
394
|
+
if (!component.inputs) {
|
|
395
|
+
console.error("component.inputs is undefined");
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
component.inputs.localScale = {
|
|
400
|
+
x: object.object_scale.x,
|
|
401
|
+
y: object.object_scale.y,
|
|
402
|
+
z: object.object_scale.z,
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
// @ts-ignore
|
|
406
|
+
modelNode.obj3D.position.set(
|
|
407
|
+
object.object_position.x,
|
|
408
|
+
object.object_position.y,
|
|
409
|
+
object.object_position.z
|
|
410
|
+
);
|
|
411
|
+
|
|
412
|
+
// @ts-ignore
|
|
413
|
+
modelNode.obj3D.rotation.set(
|
|
414
|
+
object.object_rotation.x,
|
|
415
|
+
object.object_rotation.y,
|
|
416
|
+
object.object_rotation.z
|
|
417
|
+
);
|
|
418
|
+
|
|
419
|
+
_3DXObject.push(modelNode)
|
|
420
|
+
console.log("_3DXObject "+_3DXObject.length)
|
|
421
|
+
modelNode.start();
|
|
422
|
+
|
|
423
|
+
//Spy on component
|
|
424
|
+
component.spyOnEvent(new ClickSpy(object,modelNode,component))
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Renders objects if within set range
|
|
429
|
+
* This function SHOULD be debounced for better performance
|
|
430
|
+
* @constructor
|
|
431
|
+
*/
|
|
432
|
+
async function renderOnDemand () {
|
|
433
|
+
if (_unrenderedObjects.length !== 0) {
|
|
434
|
+
console.log("renderOnDemand()")
|
|
435
|
+
const objects = getNearbyUnrenderedObjects({type:'',distance:2})
|
|
436
|
+
objects.forEach(async (obj) => {
|
|
437
|
+
if (
|
|
438
|
+
obj.object_data?.object_type === "FBX" ||
|
|
439
|
+
obj.object_data?.object_type === "GLB"
|
|
440
|
+
) {
|
|
441
|
+
await show3DObjects(obj);
|
|
442
|
+
}else if (obj.object_data?.object_type === "MP4") {
|
|
443
|
+
await showVideoObjects(obj);
|
|
444
|
+
} else if (obj.object_data?.object_type === "ZIP") {
|
|
445
|
+
await showSlideScreenModel(obj);
|
|
446
|
+
}
|
|
447
|
+
})
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
// get iframe src with configurations (mp url parameters)
|
|
453
|
+
function getIframeSrc(config: IMPConfig): string {
|
|
454
|
+
console.log("getIframeSrc()", config);
|
|
455
|
+
|
|
456
|
+
const modelId = _space.space_url.split("?m=")[1];
|
|
457
|
+
|
|
458
|
+
if (!modelId) {
|
|
459
|
+
console.error("modelId is undefined");
|
|
460
|
+
return "";
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
let src = ``;
|
|
464
|
+
src += `architwin/bundle/showcase.html?m=${modelId}&applicationKey=${sdkKey}&newtags=1`;
|
|
465
|
+
|
|
466
|
+
for (let param of urlParams) {
|
|
467
|
+
if (param in config) {
|
|
468
|
+
// @ts-ignore
|
|
469
|
+
src += `&${param}=${config[param]}`;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
return src;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// login user to fix cors error
|
|
477
|
+
async function loginUser(user: IUser) {
|
|
478
|
+
console.log("loginUser(user)", user);
|
|
479
|
+
|
|
480
|
+
const response = await _api.post(
|
|
481
|
+
"/cas/tickets?email=" + user.email + "&password=" + user.password
|
|
482
|
+
);
|
|
483
|
+
console.log("loginUser, response", response.status, response.data);
|
|
484
|
+
if (response.status == 200) {
|
|
485
|
+
return response.data;
|
|
486
|
+
} else {
|
|
487
|
+
return response;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// get showcase/space data in api
|
|
492
|
+
async function getSpace(spaceId: string): Promise<ISpace | null> {
|
|
493
|
+
console.log("getShowcase(spaceId: string)", spaceId);
|
|
494
|
+
|
|
495
|
+
const response = await _api.get(`showcases/id/${spaceId}`);
|
|
496
|
+
console.log("response", response);
|
|
497
|
+
|
|
498
|
+
if (response.status === 200) {
|
|
499
|
+
if (response.data.data.length === 0) {
|
|
500
|
+
console.error("No data");
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
return response.data.data[0];
|
|
504
|
+
} else {
|
|
505
|
+
console.error("Error in fetchShowcase()" + response);
|
|
506
|
+
}
|
|
507
|
+
return null;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
function getSpaceId(url: string) {
|
|
511
|
+
const urlArray = url.split(
|
|
512
|
+
/^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/
|
|
513
|
+
);
|
|
514
|
+
const path = urlArray[5];
|
|
515
|
+
const spaceId = path.split("/")[3];
|
|
516
|
+
|
|
517
|
+
console.log("urlArray", urlArray);
|
|
518
|
+
console.log("path", path);
|
|
519
|
+
|
|
520
|
+
if (!spaceId) {
|
|
521
|
+
console.error("spaceId is undefined");
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
return spaceId;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
function setSpace(space: ISpace): void {
|
|
528
|
+
console.log("setSpace(space: ISpace)", space);
|
|
529
|
+
_space = space;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// get tags from api
|
|
533
|
+
async function getTags(space: ISpace): Promise<ITag[] | null> {
|
|
534
|
+
console.log("getTags()");
|
|
535
|
+
|
|
536
|
+
try {
|
|
537
|
+
const response = await _api.get(`tags/showcase-id/${space.id}`);
|
|
538
|
+
console.log("response", response);
|
|
539
|
+
if (response.status === 200) {
|
|
540
|
+
const tags = response.data.data;
|
|
541
|
+
|
|
542
|
+
if (!tags) {
|
|
543
|
+
console.error("tags is undefined");
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
return tags;
|
|
547
|
+
} else {
|
|
548
|
+
console.error("Custom Error: Unable to fetch tags");
|
|
549
|
+
}
|
|
550
|
+
} catch (error) {
|
|
551
|
+
console.error(error);
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
return null;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
async function setTags(tags: ITag[]) {
|
|
558
|
+
console.log("setTags()", tags);
|
|
559
|
+
_tags = tags.map((tag) => {
|
|
560
|
+
//@ts-ignore
|
|
561
|
+
tag.json_data = JSON.parse(tag.json_data);
|
|
562
|
+
//@ts-ignore
|
|
563
|
+
tag.json_data.id = tag.json_data.sid;
|
|
564
|
+
return tag;
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
mapTags(_tags);
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
// get objects from api
|
|
571
|
+
async function get3DObjects(showcase_id: string): Promise<IShowcaseObject[]> {
|
|
572
|
+
//This function will return the 3d objects associated with each
|
|
573
|
+
if (showcase_id) {
|
|
574
|
+
try {
|
|
575
|
+
const id = parseInt(showcase_id);
|
|
576
|
+
|
|
577
|
+
console.log("showcase_id " + id);
|
|
578
|
+
|
|
579
|
+
const response = await _api.get(`showcase-objects/showcase-id/${id}`);
|
|
580
|
+
|
|
581
|
+
const showcase_objects: Array<IShowcaseObject> = response.data.data;
|
|
582
|
+
|
|
583
|
+
console.log("showcase_objects " + JSON.stringify(showcase_objects));
|
|
584
|
+
|
|
585
|
+
const getRequests = showcase_objects.map((obj) =>
|
|
586
|
+
_api.get(`objects/id/${obj.object_id}`)
|
|
587
|
+
);
|
|
588
|
+
try {
|
|
589
|
+
const response = await axios.all(getRequests);
|
|
590
|
+
|
|
591
|
+
const object_data: Array<I3DObject> = response.map((res) => {
|
|
592
|
+
return res.data.data[0];
|
|
593
|
+
});
|
|
594
|
+
console.log("object_data" + JSON.stringify(object_data));
|
|
595
|
+
const threed_objects: Array<IShowcaseObject> = showcase_objects.map(
|
|
596
|
+
(showcase) => {
|
|
597
|
+
const target = object_data.find(
|
|
598
|
+
(obj) => obj.id === showcase.object_id
|
|
599
|
+
);
|
|
600
|
+
if (target) {
|
|
601
|
+
showcase.object_data = target;
|
|
602
|
+
}
|
|
603
|
+
//parse string back to an object
|
|
604
|
+
if (showcase.object_position) {
|
|
605
|
+
showcase.object_position =
|
|
606
|
+
typeof showcase.object_position === "string"
|
|
607
|
+
? JSON.parse(showcase.object_position)
|
|
608
|
+
: showcase.object_position;
|
|
609
|
+
}
|
|
610
|
+
if (showcase.object_rotation) {
|
|
611
|
+
showcase.object_rotation =
|
|
612
|
+
typeof showcase.object_rotation === "string"
|
|
613
|
+
? JSON.parse(showcase.object_rotation)
|
|
614
|
+
: showcase.object_rotation;
|
|
615
|
+
}
|
|
616
|
+
if (showcase.object_scale) {
|
|
617
|
+
showcase.object_scale =
|
|
618
|
+
typeof showcase.object_scale === "string"
|
|
619
|
+
? JSON.parse(showcase.object_scale)
|
|
620
|
+
: showcase.object_scale;
|
|
621
|
+
}
|
|
622
|
+
return showcase;
|
|
623
|
+
}
|
|
624
|
+
);
|
|
625
|
+
|
|
626
|
+
console.log(
|
|
627
|
+
"get3DObjectsByShowcaseId " + JSON.stringify(threed_objects)
|
|
628
|
+
);
|
|
629
|
+
_unrenderedObjects = threed_objects
|
|
630
|
+
return threed_objects;
|
|
631
|
+
} catch (error) {
|
|
632
|
+
console.error("threed_objects " + error);
|
|
633
|
+
}
|
|
634
|
+
} catch (error) {
|
|
635
|
+
console.error("get3DObjectsByShowcaseId " + error);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
return [];
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
// function setObjects(objects: IShowcaseObject[]) {
|
|
642
|
+
// console.log("setObjects()", objects);
|
|
643
|
+
// _objects = objects;
|
|
644
|
+
// }
|
|
645
|
+
|
|
646
|
+
// =========================================== END INITIALIZATION ===========================================
|
|
647
|
+
|
|
648
|
+
// =========================================== TAGS FUNCTIONALITY ===========================================
|
|
649
|
+
|
|
650
|
+
// navigate to tag
|
|
651
|
+
async function gotoTag(tag_id: number): Promise<void> {
|
|
652
|
+
console.log("gotoTag(tag: tag_id)", tag_id);
|
|
653
|
+
if (!tag_id) {
|
|
654
|
+
console.error("tag is undefined");
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
657
|
+
try {
|
|
658
|
+
const tag = _tags.find((i) => i.id === tag_id);
|
|
659
|
+
|
|
660
|
+
if (!tag) {
|
|
661
|
+
console.error("tag is not found");
|
|
662
|
+
return;
|
|
663
|
+
}
|
|
664
|
+
await _atwin.Mattertag.navigateToTag(
|
|
665
|
+
tag.json_data.id,
|
|
666
|
+
_atwin.Mattertag.Transition.FLY
|
|
667
|
+
);
|
|
668
|
+
} catch (error) {
|
|
669
|
+
console.error(error);
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
// render tags to space
|
|
674
|
+
async function showTags(tags: ITag[]): Promise<void> {
|
|
675
|
+
tags.forEach(async (tag, indx) => {
|
|
676
|
+
const mpData = tag.json_data;
|
|
677
|
+
if (!mpData) {
|
|
678
|
+
console.error("tag.json_data/mpData is undefined");
|
|
679
|
+
return;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
const tagIds = await _atwin.Tag.add({
|
|
683
|
+
anchorPosition: mpData.anchorPosition,
|
|
684
|
+
color: mpData.color,
|
|
685
|
+
description: mpData.description,
|
|
686
|
+
//@ts-ignore
|
|
687
|
+
id: mpData.sid,
|
|
688
|
+
label: mpData.label,
|
|
689
|
+
stemVector: mpData.stemVector,
|
|
690
|
+
stemVisible: mpData.stemVisible,
|
|
691
|
+
});
|
|
692
|
+
// console.log('tagIds', tagIds)
|
|
693
|
+
if (tagIds) {
|
|
694
|
+
//@ts-ignore
|
|
695
|
+
tag.json_data.id = tagIds[0];
|
|
696
|
+
|
|
697
|
+
// register attachment
|
|
698
|
+
//@ts-ignore
|
|
699
|
+
if (
|
|
700
|
+
// @ts-ignore
|
|
701
|
+
tag.json_data.media &&
|
|
702
|
+
// @ts-ignore
|
|
703
|
+
// @ts-ignore
|
|
704
|
+
tag.json_data.media.src.trim() !== ""
|
|
705
|
+
) {
|
|
706
|
+
try {
|
|
707
|
+
console.log("Attaching media...");
|
|
708
|
+
//@ts-ignore
|
|
709
|
+
const [attachmentId1] = await _atwin.Tag.registerAttachment(
|
|
710
|
+
// @ts-ignore
|
|
711
|
+
tag.json_data.media.src
|
|
712
|
+
);
|
|
713
|
+
tag.json_data["attachments"] = [attachmentId1];
|
|
714
|
+
//@ts-ignore
|
|
715
|
+
await _atwin.Tag.attach(tag.json_data.sid, attachmentId1);
|
|
716
|
+
console.log("Media successfully attached");
|
|
717
|
+
} catch (error) {
|
|
718
|
+
console.warn(
|
|
719
|
+
"Custom warn: Media not attached: Invalid media src link: " + error
|
|
720
|
+
);
|
|
721
|
+
//@ts-ignore
|
|
722
|
+
console.warn(
|
|
723
|
+
// @ts-ignore
|
|
724
|
+
`mediaSrc: ${tag.json_data.media.src} | tag index: ${indx}`
|
|
725
|
+
);
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
// if(_space.tagpin_image){
|
|
730
|
+
// const iconLink = _baseUurl + _uploadDir + _space.tagpin_image
|
|
731
|
+
// const textureId = _space.tagpin_image
|
|
732
|
+
// await _atwin.Asset.registerTexture(textureId, iconLink)
|
|
733
|
+
// await _atwin.Tag.editIcon(tag.json_data.id, textureId)
|
|
734
|
+
// }
|
|
735
|
+
}
|
|
736
|
+
});
|
|
737
|
+
|
|
738
|
+
console.log("tags", _tags);
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
// remove built in tags
|
|
742
|
+
async function hideTags(): Promise<void> {
|
|
743
|
+
console.log("hideTags()");
|
|
744
|
+
const tags = await _atwin.Mattertag.getData();
|
|
745
|
+
if (tags) {
|
|
746
|
+
const tagIds = tags.map((i: MpSdk.Mattertag.MattertagData) => i.sid);
|
|
747
|
+
await _atwin.Mattertag.remove(tagIds);
|
|
748
|
+
console.log("Tags removed in space: ", tagIds);
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
// only expose tag id and name for the tags array
|
|
753
|
+
function mapTags($tags: ITag[]) {
|
|
754
|
+
console.log("mapTags()", $tags);
|
|
755
|
+
tags = $tags.map((i) => {
|
|
756
|
+
const x = {} as ITagPublic;
|
|
757
|
+
x.id = i.id;
|
|
758
|
+
x.name = i.json_data.label;
|
|
759
|
+
return x;
|
|
760
|
+
});
|
|
761
|
+
|
|
762
|
+
console.log("tags", tags);
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
// =========================================== END TAGS FUNCTIONALITY ===========================================
|
|
766
|
+
|
|
767
|
+
// =========================================== SWEEP FUNCTIONALITY ===========================================
|
|
768
|
+
|
|
769
|
+
// get explicit location for currentSweep
|
|
770
|
+
function getCurrentSweep() {
|
|
771
|
+
return _currentSweep;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
// get explicit location for currentPose
|
|
775
|
+
function getCurrentCameraPose() {
|
|
776
|
+
return _currentCameraPose;
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
// get explicit location for currentPose's position
|
|
780
|
+
function getCameraPosition() {
|
|
781
|
+
return _currentCameraPose.position;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
// get explicit location for currentSweep's position
|
|
785
|
+
function getCurrentSweepPosition() {
|
|
786
|
+
return _currentSweep.position;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
// gets all Sweeps in loaded Space
|
|
790
|
+
async function getSweeps(): Promise<ISweep | null> {
|
|
791
|
+
console.log("Getting Sweeps in Space");
|
|
792
|
+
|
|
793
|
+
// original implementation
|
|
794
|
+
_atwin.Sweep.data.subscribe({
|
|
795
|
+
onCollectionUpdated: function (collection) {
|
|
796
|
+
console.log("Sweeps In Space", collection);
|
|
797
|
+
if (!collection) {
|
|
798
|
+
console.log("No Sweeps in loaded Space");
|
|
799
|
+
}
|
|
800
|
+
_sweeps = collection;
|
|
801
|
+
|
|
802
|
+
const sw = Object.values(_sweeps);
|
|
803
|
+
|
|
804
|
+
sweeps = sw.map((item: any) => {
|
|
805
|
+
return {
|
|
806
|
+
id: item.uuid,
|
|
807
|
+
position: item.position,
|
|
808
|
+
neighbors: item.neighbors,
|
|
809
|
+
};
|
|
810
|
+
});
|
|
811
|
+
},
|
|
812
|
+
});
|
|
813
|
+
|
|
814
|
+
return null as any;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
|
|
818
|
+
// moves to selected Sweep
|
|
819
|
+
async function moveToSweep(sweepId: string) {
|
|
820
|
+
// this is sweepData to be used in the moveTo
|
|
821
|
+
// const position = sweep.position;
|
|
822
|
+
const transition = _atwin.Sweep.Transition.FLY;
|
|
823
|
+
const transitionTime = 2000; // in milliseconds
|
|
824
|
+
console.log("Sweep Move", sweepId);
|
|
825
|
+
|
|
826
|
+
_atwin.Sweep.moveTo(sweepId, {
|
|
827
|
+
// position: position,
|
|
828
|
+
transition: transition,
|
|
829
|
+
transitionTime: transitionTime,
|
|
830
|
+
})
|
|
831
|
+
.then(function () {
|
|
832
|
+
// Move successful.
|
|
833
|
+
console.log("Sweep Arrived at sweep " + sweepId);
|
|
834
|
+
// getNearbySweeps(sweepId);
|
|
835
|
+
})
|
|
836
|
+
.catch(function (error) {
|
|
837
|
+
// Error with moveTo command
|
|
838
|
+
console.log("Sweep Error on Arriving", error);
|
|
839
|
+
});
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
// returns nearby Sweeps
|
|
843
|
+
async function getNearbySweeps(sweepId: string) {
|
|
844
|
+
if (sweepId) {
|
|
845
|
+
const nearby = await sweeps.find((item: any) => item.id == sweepId)
|
|
846
|
+
.neighbors;
|
|
847
|
+
|
|
848
|
+
console.log("Nearby Sweeps", nearby);
|
|
849
|
+
return nearby;
|
|
850
|
+
} else {
|
|
851
|
+
console.log("No Nearby Sweeps");
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
|
|
856
|
+
// =========================================== END SWEEP FUNCTIONALITY ===========================================
|
|
857
|
+
|
|
858
|
+
// =========================================== OBJECTS FUNCTIONALITY ===========================================
|
|
859
|
+
|
|
860
|
+
function getComponentLoader(object: IShowcaseObject) {
|
|
861
|
+
if (object && object.object_data) {
|
|
862
|
+
let index = object.object_data.object_type as string;
|
|
863
|
+
|
|
864
|
+
let component = {
|
|
865
|
+
FBX: _atwin.Scene.Component.FBX_LOADER,
|
|
866
|
+
GLB: _atwin.Scene.Component.GLTF_LOADER,
|
|
867
|
+
MP4: "liveVideo",
|
|
868
|
+
ZIP: "slideScreenModel",
|
|
869
|
+
};
|
|
870
|
+
|
|
871
|
+
return component[index] || "";
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
return "";
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
function setSelectedObject(data:IShowcaseObject,node:Scene.INode,component:Scene.IComponent) {
|
|
878
|
+
_selectedObject = {
|
|
879
|
+
object:data,
|
|
880
|
+
component: component,
|
|
881
|
+
node:node
|
|
882
|
+
}
|
|
883
|
+
console.log("setSelectedObject()")
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
async function setLighting() {
|
|
887
|
+
const [sceneObject] = await _atwin.Scene.createObjects(1);
|
|
888
|
+
const lights = sceneObject.addNode();
|
|
889
|
+
|
|
890
|
+
lights.addComponent("mp.directionalLight", {
|
|
891
|
+
intensity: 0.6,
|
|
892
|
+
color: { r: 1.0, g: 1.0, b: 1.0 },
|
|
893
|
+
});
|
|
894
|
+
|
|
895
|
+
lights.addComponent("mp.ambientLight", {
|
|
896
|
+
intensity: 0.6,
|
|
897
|
+
color: { r: 1.0, g: 1.0, b: 1.0 },
|
|
898
|
+
});
|
|
899
|
+
|
|
900
|
+
lights.start();
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
function clearTransformControls() {
|
|
904
|
+
if (!_transformControlNode ) {
|
|
905
|
+
console.error('_transformControlNode is undefined')
|
|
906
|
+
return
|
|
907
|
+
}
|
|
908
|
+
if (_inputControlComponent == null || !_inputControlComponent) {
|
|
909
|
+
console.error('_inputControlComponent is undefined')
|
|
910
|
+
return
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
_transformControlNode.stop()
|
|
914
|
+
//_inputControlComponent.stop()
|
|
915
|
+
console.log('clearTransformControls()')
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
async function setTransformControls(selectedObject:IObjectData,mode:string = 'translate') {
|
|
919
|
+
console.log("Object to be transformed "+selectedObject.object.object_data.name)
|
|
920
|
+
|
|
921
|
+
clearTransformControls()
|
|
922
|
+
|
|
923
|
+
const [ sceneObject ] = await _atwin.Scene.createObjects(1);
|
|
924
|
+
const transformNode = sceneObject.addNode();
|
|
925
|
+
const transformComponent = transformNode.addComponent('mp.transformControls')
|
|
926
|
+
_transformControlNode = transformNode
|
|
927
|
+
|
|
928
|
+
const inputComponent = transformNode.addComponent('mp.input', {
|
|
929
|
+
eventsEnabled: true,
|
|
930
|
+
userNavigationEnabled: true,
|
|
931
|
+
})
|
|
932
|
+
|
|
933
|
+
_inputControlComponent = inputComponent.spyOnEvent(new ClickSpy(selectedObject.object,selectedObject.node,selectedObject.component))
|
|
934
|
+
transformNode.start()
|
|
935
|
+
transformComponent.inputs.selection = selectedObject.node
|
|
936
|
+
transformComponent.inputs.mode = mode
|
|
937
|
+
}
|
|
938
|
+
// async function testObject() {
|
|
939
|
+
// const [ sceneObject ] = await _atwin.Scene.createObjects(1);
|
|
940
|
+
// const modelNode = sceneObject.addNode();
|
|
941
|
+
// const component = modelNode.addComponent(_atwin.Scene.Component.FBX_LOADER, {
|
|
942
|
+
// url: "https://cdn.jsdelivr.net/gh/mrdoob/three.js@dev/examples/models/fbx/stanford-bunny.fbx",
|
|
943
|
+
// });
|
|
944
|
+
|
|
945
|
+
// // Create a scene node with a transform control component.
|
|
946
|
+
// const node = sceneObject.addNode();
|
|
947
|
+
// const myControl = node.addComponent('mp.transformControls');
|
|
948
|
+
|
|
949
|
+
// node.start();
|
|
950
|
+
// component.inputs.localScale = {
|
|
951
|
+
// x: 0.00002,
|
|
952
|
+
// y: 0.00002,
|
|
953
|
+
// z: 0.00002
|
|
954
|
+
// };
|
|
955
|
+
// modelNode.obj3D.position.set(0,-1,0);
|
|
956
|
+
// modelNode.start()
|
|
957
|
+
|
|
958
|
+
// // Attach the model to the transform control
|
|
959
|
+
// myControl.inputs.selection = modelNode;
|
|
960
|
+
// // Make the transform control visible so that the user can manipulate the control selection.
|
|
961
|
+
// myControl.inputs.visible = true;
|
|
962
|
+
|
|
963
|
+
// // set 'translate' mode to position the selection.
|
|
964
|
+
// myControl.inputs.mode = 'translate';
|
|
965
|
+
// }
|
|
966
|
+
function calculateDistance(
|
|
967
|
+
pos1: { x: number; y: number },
|
|
968
|
+
pos2: { x: number; y: number }
|
|
969
|
+
) {
|
|
970
|
+
const dx = pos2.x - pos1.x;
|
|
971
|
+
const dy = pos2.y - pos1.y;
|
|
972
|
+
return Math.sqrt(dx * dx + dy * dy);
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
async function showVideoObjects(object: IShowcaseObject) {
|
|
976
|
+
await createVideoComponent();
|
|
977
|
+
|
|
978
|
+
const [sceneObject] = await _atwin.Scene.createObjects(1);
|
|
979
|
+
const liveStreamNode = sceneObject.addNode();
|
|
980
|
+
var liveStreamComponent = await liveStreamNode.addComponent(
|
|
981
|
+
getComponentLoader(object),
|
|
982
|
+
{
|
|
983
|
+
url: object.object_data?.amazon_uri,
|
|
984
|
+
}
|
|
985
|
+
);
|
|
986
|
+
|
|
987
|
+
liveStreamNode.position.set(
|
|
988
|
+
object.object_position.x,
|
|
989
|
+
object.object_position.y,
|
|
990
|
+
object.object_position.z
|
|
991
|
+
);
|
|
992
|
+
// @ts-ignore
|
|
993
|
+
liveStreamNode.obj3D.rotation.set(
|
|
994
|
+
object.object_rotation.x,
|
|
995
|
+
object.object_rotation.y,
|
|
996
|
+
object.object_rotation.z
|
|
997
|
+
);
|
|
998
|
+
|
|
999
|
+
liveStreamNode.scale.set(
|
|
1000
|
+
object.object_scale.x,
|
|
1001
|
+
object.object_scale.y,
|
|
1002
|
+
object.object_scale.z
|
|
1003
|
+
);
|
|
1004
|
+
|
|
1005
|
+
if (liveStreamComponent.outputs.loadingState != "Error") {
|
|
1006
|
+
liveStreamNode.start();
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
liveStreamComponent.inputs.src = object.object_data?.amazon_uri;
|
|
1010
|
+
|
|
1011
|
+
_videos.push({
|
|
1012
|
+
data: object,
|
|
1013
|
+
component: liveStreamComponent,
|
|
1014
|
+
node: liveStreamNode,
|
|
1015
|
+
type: 'VIDEO',
|
|
1016
|
+
} as IVideoObject);
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
function playVideo(videoId: number) {
|
|
1020
|
+
let videoObject = _videos.find((object) => object.data.id == videoId);
|
|
1021
|
+
|
|
1022
|
+
|
|
1023
|
+
if (videoObject) {
|
|
1024
|
+
// @ts-ignore
|
|
1025
|
+
videoObject.component.video.play();
|
|
1026
|
+
} else {
|
|
1027
|
+
console.error("Unable to find video object using that id");
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
function pauseVideo(videoId: number) {
|
|
1032
|
+
let videoObject = _videos.find((object) => object.data.id == videoId);
|
|
1033
|
+
|
|
1034
|
+
if (videoObject) {
|
|
1035
|
+
// @ts-ignore
|
|
1036
|
+
videoObject.component.video.pause();
|
|
1037
|
+
} else {
|
|
1038
|
+
console.error("Unable to find video object using that id");
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
function videoAutoPlay() {
|
|
1043
|
+
async function nearestVideo(videoObject: IVideoObject) {
|
|
1044
|
+
let videoPosition = videoObject.data.object_position;
|
|
1045
|
+
let cameraPosition = getCurrentSweepPosition();
|
|
1046
|
+
|
|
1047
|
+
const computedDistance = distance(
|
|
1048
|
+
[cameraPosition.x, cameraPosition.y, cameraPosition.z],
|
|
1049
|
+
[videoPosition.x, videoPosition.y, videoPosition.z]
|
|
1050
|
+
);
|
|
1051
|
+
|
|
1052
|
+
try {
|
|
1053
|
+
if (
|
|
1054
|
+
videoObject.data.autoplay &&
|
|
1055
|
+
videoObject.data.autoplay_distance &&
|
|
1056
|
+
computedDistance < videoObject.data.autoplay_distance
|
|
1057
|
+
) {
|
|
1058
|
+
// @ts-ignore
|
|
1059
|
+
videoObject.component.video.play();
|
|
1060
|
+
} else {
|
|
1061
|
+
// @ts-ignore
|
|
1062
|
+
videoObject.component.video.pause();
|
|
1063
|
+
}
|
|
1064
|
+
} catch (e) {
|
|
1065
|
+
console.error("Unable to play or stop video");
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
_videos.forEach((videoObject: IVideoObject) => nearestVideo(videoObject));
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
function createVideoComponent() {
|
|
1073
|
+
function videoRenderer() {
|
|
1074
|
+
this.inputs = {
|
|
1075
|
+
visible: true,
|
|
1076
|
+
userNavigationEnabled: true,
|
|
1077
|
+
eventsEnabled: true,
|
|
1078
|
+
colliderEnabled: true,
|
|
1079
|
+
src: null,
|
|
1080
|
+
};
|
|
1081
|
+
|
|
1082
|
+
this.outputs = {
|
|
1083
|
+
texture: null,
|
|
1084
|
+
video: null,
|
|
1085
|
+
aspect: 720 / 480,
|
|
1086
|
+
};
|
|
1087
|
+
|
|
1088
|
+
this.onInit = function () {
|
|
1089
|
+
this.video;
|
|
1090
|
+
this.texture;
|
|
1091
|
+
};
|
|
1092
|
+
|
|
1093
|
+
this.onEvent = function (eventType, eventData) {};
|
|
1094
|
+
|
|
1095
|
+
this.onInputsUpdated = function (previous) {
|
|
1096
|
+
this.releaseTexture();
|
|
1097
|
+
|
|
1098
|
+
const THREE = this.context.three;
|
|
1099
|
+
if (!this.inputs.src) {
|
|
1100
|
+
this.video.src = "";
|
|
1101
|
+
return;
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
if (this.inputs.src instanceof HTMLVideoElement) {
|
|
1105
|
+
this.video = this.inputs.src;
|
|
1106
|
+
} else {
|
|
1107
|
+
this.video = this.createVideoElement();
|
|
1108
|
+
|
|
1109
|
+
if (typeof this.inputs.src === "string") {
|
|
1110
|
+
this.video.src = this.inputs.src;
|
|
1111
|
+
} else {
|
|
1112
|
+
this.video.srcObject = this.inputs.src;
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
this.video.load();
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
this.texture = new THREE.VideoTexture(this.video);
|
|
1119
|
+
this.texture.minFilter = THREE.LinearFilter;
|
|
1120
|
+
this.texture.magFilter = THREE.LinearFilter;
|
|
1121
|
+
this.texture.format = THREE.RGBFormat;
|
|
1122
|
+
|
|
1123
|
+
var geometry = new THREE.PlaneGeometry(1, 1);
|
|
1124
|
+
geometry.scale(1, 480 / 720, 1);
|
|
1125
|
+
geometry.translate(0, 0.33, 0);
|
|
1126
|
+
this.material = new THREE.MeshBasicMaterial({
|
|
1127
|
+
map: this.texture,
|
|
1128
|
+
side: THREE.DoubleSide,
|
|
1129
|
+
});
|
|
1130
|
+
var mesh = new THREE.Mesh(geometry, this.material);
|
|
1131
|
+
this.outputs.objectRoot = mesh;
|
|
1132
|
+
this.outputs.collider = mesh;
|
|
1133
|
+
mesh.visible = this.inputs.visible;
|
|
1134
|
+
|
|
1135
|
+
this.outputs.texture = this.texture;
|
|
1136
|
+
this.outputs.video = this.video;
|
|
1137
|
+
};
|
|
1138
|
+
|
|
1139
|
+
this.releaseTexture = function () {
|
|
1140
|
+
if (this.texture) {
|
|
1141
|
+
this.outputs.texture = null;
|
|
1142
|
+
this.texture.dispose();
|
|
1143
|
+
}
|
|
1144
|
+
};
|
|
1145
|
+
|
|
1146
|
+
this.createVideoElement = function () {
|
|
1147
|
+
const video = document.createElement("video");
|
|
1148
|
+
video.setAttribute("id", "htmlLivestreamVideo");
|
|
1149
|
+
video.crossOrigin = "anonymous";
|
|
1150
|
+
video.setAttribute("height", "480");
|
|
1151
|
+
video.setAttribute("width", "720");
|
|
1152
|
+
video.setAttribute("webkit-playsinline", "webkit-playsinline");
|
|
1153
|
+
video.setAttribute("controls", "controls");
|
|
1154
|
+
video.muted = false;
|
|
1155
|
+
video.loop = true;
|
|
1156
|
+
video.volume = 1;
|
|
1157
|
+
return video;
|
|
1158
|
+
};
|
|
1159
|
+
|
|
1160
|
+
this.onTick = function (tickDelta) {};
|
|
1161
|
+
|
|
1162
|
+
this.onDestroy = function () {
|
|
1163
|
+
this.material.dispose();
|
|
1164
|
+
};
|
|
1165
|
+
|
|
1166
|
+
this.spyOnEvent = function (payload) {
|
|
1167
|
+
console.log("payload", payload);
|
|
1168
|
+
};
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
function makeVideoRender() {
|
|
1172
|
+
return new videoRenderer();
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
_atwin.Scene.register("liveVideo", makeVideoRender);
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
//This function is for registering the custom component
|
|
1179
|
+
function createSlideScreenModel() {
|
|
1180
|
+
function SlideScreenModelRenderer() {
|
|
1181
|
+
this.inputs = {
|
|
1182
|
+
src: null,
|
|
1183
|
+
userNavigationEnabled: true,
|
|
1184
|
+
eventsEnabled: true,
|
|
1185
|
+
colliderEnabled: true,
|
|
1186
|
+
visible: true,
|
|
1187
|
+
};
|
|
1188
|
+
this.outputs = {
|
|
1189
|
+
texture: null,
|
|
1190
|
+
t_image: null,
|
|
1191
|
+
};
|
|
1192
|
+
this.onInit = function () {
|
|
1193
|
+
this.texture;
|
|
1194
|
+
this.t_image;
|
|
1195
|
+
this.material;
|
|
1196
|
+
this.mesh;
|
|
1197
|
+
};
|
|
1198
|
+
|
|
1199
|
+
//This is commented out since the slideshow must not be clicked yet
|
|
1200
|
+
|
|
1201
|
+
this.events = {
|
|
1202
|
+
"INTERACTION.CLICK": true,
|
|
1203
|
+
};
|
|
1204
|
+
|
|
1205
|
+
this.onEvent = async function (eventType, eventData) {
|
|
1206
|
+
console.log("onEventSlideShow", eventType, eventData);
|
|
1207
|
+
if (eventType == "INTERACTION.CLICK") {
|
|
1208
|
+
if (_allSlideShow.length > 0) {
|
|
1209
|
+
let slideshow = _allSlideShow.filter(
|
|
1210
|
+
(l) =>
|
|
1211
|
+
l.collider?.collider?.uuid == eventData.collider?.uuid ?? false
|
|
1212
|
+
)[0];
|
|
1213
|
+
console.log("slideShow eventData", slideshow);
|
|
1214
|
+
if (slideshow != null) {
|
|
1215
|
+
console.log("slideShow eventData", slideshow);
|
|
1216
|
+
|
|
1217
|
+
// This is the UNIQUE properties of this plane
|
|
1218
|
+
var mousePosition = await getMousePosition();
|
|
1219
|
+
|
|
1220
|
+
// @ts-ignore
|
|
1221
|
+
var planePosition = slideshow.node.obj3D.position;
|
|
1222
|
+
|
|
1223
|
+
console.log("MOUSE POSITION", mousePosition);
|
|
1224
|
+
console.log("PLANE POSITION", planePosition);
|
|
1225
|
+
|
|
1226
|
+
if (slideshow.object.object_type == "ZIP") {
|
|
1227
|
+
if (slideshow.id != _currentSlideShowID) {
|
|
1228
|
+
_currentSlideIndex = _slideShowImage.find(
|
|
1229
|
+
(elem) => elem.id === slideshow.id
|
|
1230
|
+
).idx;
|
|
1231
|
+
console.log("CURRENT INDEX", _currentSlideIndex);
|
|
1232
|
+
_currentSlideShow = _slideShowImage.find(
|
|
1233
|
+
(elem) => elem.id === slideshow.id
|
|
1234
|
+
).images;
|
|
1235
|
+
console.log("CURRENT SLIDE", _currentSlideShow);
|
|
1236
|
+
}
|
|
1237
|
+
if (slideshow.id == _currentSlideShowID) {
|
|
1238
|
+
console.log("ChangeImage", slideshow, _currentSlideShowID);
|
|
1239
|
+
if (planePosition.x < mousePosition.x) {
|
|
1240
|
+
console.log("LEFT SIDE");
|
|
1241
|
+
clearInterval(_timer);
|
|
1242
|
+
// model.pause()
|
|
1243
|
+
this.inputs.src = imageStream("prev");
|
|
1244
|
+
} else if (planePosition.y < mousePosition.y) {
|
|
1245
|
+
console.log("RIGHT SIDE");
|
|
1246
|
+
// model.pause()
|
|
1247
|
+
clearInterval(_timer);
|
|
1248
|
+
this.inputs.src = imageStream("next");
|
|
1249
|
+
} else if (planePosition.y > mousePosition.y) {
|
|
1250
|
+
console.log("AUTOPLAY");
|
|
1251
|
+
_timer = setInterval(() => {
|
|
1252
|
+
_currentSlideIndex =
|
|
1253
|
+
(_currentSlideIndex + 1) % _currentSlideShow.length;
|
|
1254
|
+
console.log("CURRENT INDEX", _currentSlideIndex);
|
|
1255
|
+
this.inputs.src = imageStream("next");
|
|
1256
|
+
}, 2000);
|
|
1257
|
+
}
|
|
1258
|
+
} else {
|
|
1259
|
+
_currentSlideShowID = slideshow.id;
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
};
|
|
1266
|
+
|
|
1267
|
+
this.onInputsUpdated = function (previous) {
|
|
1268
|
+
this.releaseTexture();
|
|
1269
|
+
const three = this.context.three;
|
|
1270
|
+
// Create IMAGE
|
|
1271
|
+
this.t_image = document.createElement("IMG");
|
|
1272
|
+
this.t_image.src = this.inputs.src;
|
|
1273
|
+
this.t_image.setAttribute("width", "auto");
|
|
1274
|
+
this.t_image.setAttribute("height", "auto");
|
|
1275
|
+
this.t_image.crossOrigin = "anonymous";
|
|
1276
|
+
const planeGeo = new three.PlaneGeometry(1.5, 1.5);
|
|
1277
|
+
// planeGeo.scale(1, 480/720, 1);
|
|
1278
|
+
planeGeo.translate(0, 0.33, 0);
|
|
1279
|
+
|
|
1280
|
+
// this.texture = new THREE.Texture(this.t_image);
|
|
1281
|
+
|
|
1282
|
+
// used texture loader for onLoad callback
|
|
1283
|
+
if (previous.src != this.inputs.src) {
|
|
1284
|
+
console.log("this.inputs.src", this.inputs.src);
|
|
1285
|
+
this.texture = new three.TextureLoader().load(
|
|
1286
|
+
this.t_image.src,
|
|
1287
|
+
(tex = this.texture) => {
|
|
1288
|
+
// onLoad callback function
|
|
1289
|
+
tex.minFilter = three.LinearFilter;
|
|
1290
|
+
tex.magFilter = three.LinearFilter;
|
|
1291
|
+
tex.format = three.RGBAFormat;
|
|
1292
|
+
tex.needsUpdate = true; //This should be true in order to update the texture
|
|
1293
|
+
tex.onUpdate = this.textureUpdated;
|
|
1294
|
+
this.material = new three.MeshBasicMaterial({
|
|
1295
|
+
map: this.texture,
|
|
1296
|
+
side: three.DoubleSide,
|
|
1297
|
+
transparent: true,
|
|
1298
|
+
// color: 0xFF0000,
|
|
1299
|
+
alphaTest: 0.5,
|
|
1300
|
+
});
|
|
1301
|
+
this.mesh = new three.Mesh(planeGeo, this.material); //Create a mesh for the Plane
|
|
1302
|
+
this.mesh.scale.set(
|
|
1303
|
+
1.0,
|
|
1304
|
+
this.texture.image.height / this.texture.image.width,
|
|
1305
|
+
1.0
|
|
1306
|
+
); // setting the aspect ratio of image as the scale of mesh
|
|
1307
|
+
|
|
1308
|
+
this.outputs.objectRoot = this.mesh;
|
|
1309
|
+
this.outputs.collider = this.mesh;
|
|
1310
|
+
this.outputs.texture = this.texture;
|
|
1311
|
+
this.outputs.t_image = this.t_image;
|
|
1312
|
+
}
|
|
1313
|
+
);
|
|
1314
|
+
}
|
|
1315
|
+
if (this.mesh != null) {
|
|
1316
|
+
this.mesh.visible = this.inputs.visible;
|
|
1317
|
+
}
|
|
1318
|
+
};
|
|
1319
|
+
|
|
1320
|
+
this.textureUpdated = function (params) {
|
|
1321
|
+
console.log("Update Callback", params);
|
|
1322
|
+
};
|
|
1323
|
+
|
|
1324
|
+
this.releaseTexture = function () {
|
|
1325
|
+
if (this.texture) {
|
|
1326
|
+
this.outputs.texture = null;
|
|
1327
|
+
}
|
|
1328
|
+
};
|
|
1329
|
+
|
|
1330
|
+
this.onTick = function (tickDelta) {};
|
|
1331
|
+
|
|
1332
|
+
this.onDestroy = function () {
|
|
1333
|
+
this.material.dispose();
|
|
1334
|
+
this.texture.dispose();
|
|
1335
|
+
};
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
function makeSlideScreenModelRenderer() {
|
|
1339
|
+
return new SlideScreenModelRenderer();
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
// Registering the component with the sdk
|
|
1343
|
+
_atwin.Scene.register("slideScreenModel", makeSlideScreenModelRenderer);
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
//This is the function for rendering the slidescreen model
|
|
1347
|
+
async function showSlideScreenModel(object: IShowcaseObject) {
|
|
1348
|
+
createSlideScreenModel();
|
|
1349
|
+
|
|
1350
|
+
const [sceneObject] = await _atwin.Scene.createObjects(1);
|
|
1351
|
+
const modelNode = sceneObject.addNode();
|
|
1352
|
+
const component = modelNode.addComponent("slideScreenModel");
|
|
1353
|
+
console.log("COMPONENT", component);
|
|
1354
|
+
|
|
1355
|
+
modelNode.scale.set = {
|
|
1356
|
+
x: object.object_scale.x,
|
|
1357
|
+
y: object.object_scale.y,
|
|
1358
|
+
z: object.object_scale.z,
|
|
1359
|
+
};
|
|
1360
|
+
|
|
1361
|
+
//@ts-ignore
|
|
1362
|
+
modelNode.obj3D.position.set(
|
|
1363
|
+
object.object_position.x,
|
|
1364
|
+
object.object_position.y,
|
|
1365
|
+
object.object_position.z
|
|
1366
|
+
);
|
|
1367
|
+
|
|
1368
|
+
//@ts-ignore
|
|
1369
|
+
modelNode.obj3D.rotation.set(
|
|
1370
|
+
object.object_rotation.x,
|
|
1371
|
+
object.object_rotation.y,
|
|
1372
|
+
object.object_rotation.z
|
|
1373
|
+
);
|
|
1374
|
+
|
|
1375
|
+
// modelNode.scale.set(1, 1, 1)
|
|
1376
|
+
// modelNode.position.set(-3.32,0.01 ,3.33)
|
|
1377
|
+
modelNode.start();
|
|
1378
|
+
|
|
1379
|
+
await addImageToSlideShow(object.object_data, component, object.id);
|
|
1380
|
+
console.log("SLIDE OUTPUT", component.outputs);
|
|
1381
|
+
|
|
1382
|
+
var slideShow = {
|
|
1383
|
+
id: object.id,
|
|
1384
|
+
collider: component.outputs,
|
|
1385
|
+
object: object.object_data,
|
|
1386
|
+
node: modelNode,
|
|
1387
|
+
component: component,
|
|
1388
|
+
type: 'ZIP'
|
|
1389
|
+
} as ISlideshowObject
|
|
1390
|
+
|
|
1391
|
+
_allSlideShow.push(slideShow);
|
|
1392
|
+
console.log("ALL SLIDESHOW", _allSlideShow);
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
//This function is accessing the zip files and place it inside the slidescreen
|
|
1396
|
+
async function addImageToSlideShow(data, component, objectID) {
|
|
1397
|
+
var dataList = [];
|
|
1398
|
+
await new JSZip.external.Promise(function (resolve, reject) {
|
|
1399
|
+
JSZipUtils.getBinaryContent(data.amazon_uri, function (err, data) {
|
|
1400
|
+
if (err) {
|
|
1401
|
+
console.log("getBI error", err);
|
|
1402
|
+
reject(err);
|
|
1403
|
+
} else {
|
|
1404
|
+
console.log("getBI success", data);
|
|
1405
|
+
resolve(data);
|
|
1406
|
+
}
|
|
1407
|
+
});
|
|
1408
|
+
})
|
|
1409
|
+
.then(function (data) {
|
|
1410
|
+
console.log("data from getBI", data);
|
|
1411
|
+
//@ts-ignore
|
|
1412
|
+
return JSZip.loadAsync(data);
|
|
1413
|
+
})
|
|
1414
|
+
.then((data) => {
|
|
1415
|
+
console.log("data from loadAsync", data);
|
|
1416
|
+
for (let key in data.files) {
|
|
1417
|
+
// determine whether it is a directory
|
|
1418
|
+
if (key.includes("__MACOSX")) continue;
|
|
1419
|
+
if (key.includes(".DS_Store")) continue;
|
|
1420
|
+
if (data.files[key].dir) continue;
|
|
1421
|
+
let base = data.file(data.files[key].name).async("base64"); // will be converted to base64 format picture
|
|
1422
|
+
base.then((res) => {
|
|
1423
|
+
dataList.push({
|
|
1424
|
+
name: data.files[key].name,
|
|
1425
|
+
path: `data:image/png;base64,${res}`,
|
|
1426
|
+
});
|
|
1427
|
+
if (dataList.length == 1) {
|
|
1428
|
+
component.inputs.src = `data:image/png;base64,${res}`;
|
|
1429
|
+
}
|
|
1430
|
+
});
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
_slideShowImage.push({
|
|
1434
|
+
id: objectID,
|
|
1435
|
+
images: dataList,
|
|
1436
|
+
idx: 0,
|
|
1437
|
+
playID: null,
|
|
1438
|
+
play: false,
|
|
1439
|
+
comp: component,
|
|
1440
|
+
});
|
|
1441
|
+
});
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
async function getMousePosition() {
|
|
1445
|
+
var planePosition;
|
|
1446
|
+
await _atwin.Pointer.intersection.subscribe(function (intersection) {
|
|
1447
|
+
planePosition = intersection.position;
|
|
1448
|
+
// console.log("This is intersection mouse position", planePosition);
|
|
1449
|
+
});
|
|
1450
|
+
// pointer.cancel();
|
|
1451
|
+
return planePosition;
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
//This is the function to change the image in the slideshow
|
|
1455
|
+
function imageStream(direction) {
|
|
1456
|
+
let diff = direction === "prev" ? -1 : 1;
|
|
1457
|
+
let length = _currentSlideShow.length;
|
|
1458
|
+
let index = _currentSlideIndex + diff;
|
|
1459
|
+
if (index === -1) {
|
|
1460
|
+
index = length - 1;
|
|
1461
|
+
}
|
|
1462
|
+
if (index === length) {
|
|
1463
|
+
index = 0;
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
_currentSlideIndex = index;
|
|
1467
|
+
_slideShowImage.find((elem) => elem.id === _currentSlideShowID).idx = index;
|
|
1468
|
+
return _currentSlideShow[_currentSlideIndex].path;
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
// ===== END OBJECTS FUNCTIONALITY =====
|
|
1472
|
+
|
|
1473
|
+
// ===== NAVIGATION AND CAMERA CONTROL =====
|
|
1474
|
+
async function cameraRotate(x: number, y: number, speed: number) {
|
|
1475
|
+
// x and y is rotation in horizontal and vertical rotation in degrees
|
|
1476
|
+
// speed, speed of rotation
|
|
1477
|
+
await _atwin.Camera.rotate(x, y, { speed: speed })
|
|
1478
|
+
.then(() => {
|
|
1479
|
+
console.log("camera rotate success.", x, y);
|
|
1480
|
+
})
|
|
1481
|
+
.catch((error) => {
|
|
1482
|
+
console.log("camera rotate error.", error);
|
|
1483
|
+
});
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
async function cameraPan(x: number, z: number) {
|
|
1487
|
+
let mode = getViewMode();
|
|
1488
|
+
|
|
1489
|
+
if (
|
|
1490
|
+
mode == _atwin.Mode.Mode.FLOORPLAN ||
|
|
1491
|
+
mode == _atwin.Mode.Mode.DOLLHOUSE
|
|
1492
|
+
) {
|
|
1493
|
+
// x is pan (look sideways left and right) in degrees
|
|
1494
|
+
// z is tilt (look up and down) in degrees
|
|
1495
|
+
await _atwin.Camera.pan({ x: x, z: z })
|
|
1496
|
+
.then(() => {
|
|
1497
|
+
console.log("camera pan success.");
|
|
1498
|
+
})
|
|
1499
|
+
.catch((error) => {
|
|
1500
|
+
console.log("camera rotate error:", error);
|
|
1501
|
+
});
|
|
1502
|
+
} else {
|
|
1503
|
+
console.error("Incorrect view mode.");
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
async function cameraLookAt(x: number, y: number) {
|
|
1508
|
+
// set camera view at coordinate in SCREEN
|
|
1509
|
+
// x and y is PIXEL from upper left corner
|
|
1510
|
+
await _atwin.Camera.lookAtScreenCoords(x, y)
|
|
1511
|
+
.then(function () {
|
|
1512
|
+
console.log("camera looking at...", x, y);
|
|
1513
|
+
})
|
|
1514
|
+
.catch(function (error) {
|
|
1515
|
+
console.log("camera looking at error:", error);
|
|
1516
|
+
});
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
/**
|
|
1520
|
+
* Moves the camera position in the direction of the argument provided.
|
|
1521
|
+
* @constructor
|
|
1522
|
+
* @param direction FORWARD,BACKWARD,LEFT,RIGHT,UP,DOWN
|
|
1523
|
+
* @type {string}
|
|
1524
|
+
*/
|
|
1525
|
+
|
|
1526
|
+
async function moveInDirection(direction: string) {
|
|
1527
|
+
// move in DIRECTION, can be LEFT, RIGTH, UP, DOWN, FORWARD, BACKWARD
|
|
1528
|
+
let nextDirection = _atwin.Camera.Direction.LEFT;
|
|
1529
|
+
switch (direction.toUpperCase()) {
|
|
1530
|
+
case "LEFT":
|
|
1531
|
+
nextDirection = _atwin.Camera.Direction.LEFT;
|
|
1532
|
+
|
|
1533
|
+
break;
|
|
1534
|
+
case "RIGHT":
|
|
1535
|
+
nextDirection = _atwin.Camera.Direction.RIGHT;
|
|
1536
|
+
|
|
1537
|
+
break;
|
|
1538
|
+
|
|
1539
|
+
case "UP":
|
|
1540
|
+
nextDirection = _atwin.Camera.Direction.UP;
|
|
1541
|
+
|
|
1542
|
+
break;
|
|
1543
|
+
case "DOWN":
|
|
1544
|
+
nextDirection = _atwin.Camera.Direction.DOWN;
|
|
1545
|
+
|
|
1546
|
+
break;
|
|
1547
|
+
|
|
1548
|
+
case "BACK":
|
|
1549
|
+
nextDirection = _atwin.Camera.Direction.BACK;
|
|
1550
|
+
|
|
1551
|
+
break;
|
|
1552
|
+
|
|
1553
|
+
case "FORWARD":
|
|
1554
|
+
nextDirection = _atwin.Camera.Direction.FORWARD;
|
|
1555
|
+
|
|
1556
|
+
break;
|
|
1557
|
+
|
|
1558
|
+
default:
|
|
1559
|
+
nextDirection = _atwin.Camera.Direction.LEFT;
|
|
1560
|
+
|
|
1561
|
+
break;
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
await _atwin.Camera.moveInDirection(nextDirection)
|
|
1565
|
+
.then(function () {
|
|
1566
|
+
console.log("Move to...", nextDirection);
|
|
1567
|
+
})
|
|
1568
|
+
.catch(function () {
|
|
1569
|
+
console.warn(
|
|
1570
|
+
"An error occured while moving in that direction.",
|
|
1571
|
+
nextDirection
|
|
1572
|
+
);
|
|
1573
|
+
});
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1576
|
+
// =========================================== END NAVIGATION AND CAMERA CONTROL ===================================
|
|
1577
|
+
|
|
1578
|
+
// =========================================== MODE FUNCTIONALITY ==================================================
|
|
1579
|
+
function getViewMode() {
|
|
1580
|
+
return _currentViewMode;
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1583
|
+
async function setViewMode(mode: string) {
|
|
1584
|
+
console.log("=== get Mode Type ===", _getModeType(mode));
|
|
1585
|
+
let modeType = _getModeType(mode);
|
|
1586
|
+
|
|
1587
|
+
await _atwin.Mode.moveTo(modeType)
|
|
1588
|
+
.then(function (nextMode) {
|
|
1589
|
+
// Move successful.
|
|
1590
|
+
console.log("Arrived at new view mode " + nextMode);
|
|
1591
|
+
})
|
|
1592
|
+
.catch(function (error) {
|
|
1593
|
+
// Error with moveTo command
|
|
1594
|
+
console.error("Error occur on:", error);
|
|
1595
|
+
});
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1598
|
+
function _getModeType(mode: string) {
|
|
1599
|
+
let modes = {
|
|
1600
|
+
DOLLHOUSE: "mode.dollhouse",
|
|
1601
|
+
FLOORPLAN: "mode.floorplan",
|
|
1602
|
+
INSIDE: "mode.inside",
|
|
1603
|
+
OUTSIDE: "mode.outside",
|
|
1604
|
+
TRANSITIONING: "mode.transitioning",
|
|
1605
|
+
};
|
|
1606
|
+
|
|
1607
|
+
return modes[mode.toUpperCase()] || "";
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
// ================== INPUT EVENT CLASSES ======================= //
|
|
1611
|
+
|
|
1612
|
+
class ClickSpy {
|
|
1613
|
+
object_data: IShowcaseObject;
|
|
1614
|
+
node: Scene.INode;
|
|
1615
|
+
component: Scene.IComponent;
|
|
1616
|
+
|
|
1617
|
+
constructor(data:IShowcaseObject,node:Scene.INode,component:Scene.IComponent){
|
|
1618
|
+
this.object_data = data
|
|
1619
|
+
this.node = node
|
|
1620
|
+
this.component = component
|
|
1621
|
+
}
|
|
1622
|
+
public eventType = 'INTERACTION.CLICK';
|
|
1623
|
+
public async onEvent(payload: unknown) {
|
|
1624
|
+
console.log('INTERACTION.CLICK', payload);
|
|
1625
|
+
const selectedObj:IObjectData = {
|
|
1626
|
+
object: this.object_data,
|
|
1627
|
+
node: this.node,
|
|
1628
|
+
component: this.component
|
|
1629
|
+
}
|
|
1630
|
+
|
|
1631
|
+
setSelectedObject(this.object_data,this.node,this.component)
|
|
1632
|
+
await setTransformControls(selectedObj)
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1636
|
+
export {
|
|
1637
|
+
tags,
|
|
1638
|
+
sweeps,
|
|
1639
|
+
connectSpace,
|
|
1640
|
+
gotoTag,
|
|
1641
|
+
getCurrentSweep,
|
|
1642
|
+
getCurrentSweepPosition,
|
|
1643
|
+
moveToSweep,
|
|
1644
|
+
getNearbySweeps,
|
|
1645
|
+
pauseVideo,
|
|
1646
|
+
playVideo,
|
|
1647
|
+
getCurrentCameraPose,
|
|
1648
|
+
getCameraPosition,
|
|
1649
|
+
moveInDirection,
|
|
1650
|
+
cameraLookAt,
|
|
1651
|
+
cameraPan,
|
|
1652
|
+
cameraRotate,
|
|
1653
|
+
getViewMode,
|
|
1654
|
+
setViewMode,
|
|
1655
|
+
};
|
|
1656
|
+
|