@realsee/dnalogel 2.0.0-alpha.13 → 2.0.0-alpha.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.
Files changed (62) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/README.md +78 -28
  3. package/dist/ModelItemLabelPlugin/index.js +72 -83
  4. package/dist/ModelItemLabelPlugin/typings.d.ts +2 -1
  5. package/dist/ModelRoomLabelPlugin/index.js +1 -0
  6. package/dist/ModelTVVideoPlugin/index.d.ts +4 -0
  7. package/dist/ModelTVVideoPlugin/index.js +326 -0
  8. package/dist/ModelTVVideoPlugin/typings.d.ts +25 -0
  9. package/dist/ModelTVVideoPlugin/utils/parseData.d.ts +2 -0
  10. package/dist/PanoMeasurePlugin/index.js +2 -2
  11. package/dist/index.d.ts +3 -0
  12. package/dist/index.es.js +349 -86
  13. package/dist/index.js +349 -85
  14. package/dist/index.umd.js +349 -85
  15. package/docs/assets/main.js +1 -1
  16. package/docs/assets/search.js +1 -1
  17. package/docs/classes/ModelRoomLabelController.html +6 -6
  18. package/docs/enums/FLOOR_PLAN_ATTACHED_TO.html +1 -1
  19. package/docs/enums/ModelFloorplanErrorType.html +1 -1
  20. package/docs/index.html +78 -33
  21. package/docs/interfaces/LineJson.html +1 -1
  22. package/docs/interfaces/ModelChassisCompassPluginData.html +1 -1
  23. package/docs/interfaces/ModelChassisCompassPluginExportType.html +1 -1
  24. package/docs/interfaces/ModelChassisCompassPluginParameterType.html +1 -1
  25. package/docs/interfaces/ModelEntryDoorGuidePluginData.html +1 -1
  26. package/docs/interfaces/ModelEntryDoorGuidePluginExportType.html +1 -1
  27. package/docs/interfaces/ModelFloorplanParameterType.html +1 -1
  28. package/docs/interfaces/ModelFloorplanViewEvent.html +2 -2
  29. package/docs/interfaces/ModelItemLabelPluginData.html +1 -0
  30. package/docs/interfaces/ModelItemLabelPluginExportReturnsType.html +1 -0
  31. package/docs/interfaces/ModelItemLabelPluginParametersType.html +1 -0
  32. package/docs/interfaces/ModelRoomLabelPluginData.html +1 -1
  33. package/docs/interfaces/ModelTVVideoPluginData.html +1 -0
  34. package/docs/interfaces/ModelTVVideoPluginExportType.html +1 -0
  35. package/docs/interfaces/ModelTVVideoPluginParameterType.html +1 -0
  36. package/docs/interfaces/PanoCompassPluginData.html +1 -1
  37. package/docs/interfaces/PanoCompassPluginParameterType.html +1 -1
  38. package/docs/interfaces/PanoCursorRaycasterPluginExportType.html +6 -6
  39. package/docs/interfaces/PanoRulerPluginExportType.html +1 -1
  40. package/docs/interfaces/PanoRulerPluginOptions.html +1 -1
  41. package/docs/interfaces/PanoRulerPluginParameterType.html +1 -1
  42. package/docs/interfaces/PanoSpatialTagPluginData.html +1 -1
  43. package/docs/interfaces/PanoSpatialTagPluginExportType.html +1 -1
  44. package/docs/interfaces/PanoSpatialTagPluginParameterType.html +1 -1
  45. package/docs/interfaces/PointJson.html +1 -1
  46. package/docs/interfaces/RoomLabel.html +7 -7
  47. package/docs/interfaces/TopviewFloorplanPluginParameterType.html +1 -1
  48. package/docs/modules.html +12 -12
  49. package/libs/ModelItemLabelPlugin/ItemLabelComponent.js +48 -61
  50. package/libs/ModelItemLabelPlugin/index.js +5 -57
  51. package/libs/ModelItemLabelPlugin/typings.d.ts +2 -1
  52. package/libs/ModelItemLabelPlugin/typings.js +1 -0
  53. package/libs/ModelRoomLabelPlugin/Controller.js +1 -0
  54. package/libs/ModelTVVideoPlugin/index.d.ts +4 -0
  55. package/libs/ModelTVVideoPlugin/index.js +263 -0
  56. package/libs/ModelTVVideoPlugin/typings.d.ts +25 -0
  57. package/libs/ModelTVVideoPlugin/typings.js +7 -0
  58. package/libs/ModelTVVideoPlugin/utils/parseData.d.ts +2 -0
  59. package/libs/ModelTVVideoPlugin/utils/parseData.js +13 -0
  60. package/libs/index.d.ts +3 -0
  61. package/libs/index.js +1 -1
  62. package/package.json +2 -1
@@ -17,6 +17,7 @@ import {
17
17
  mount_component,
18
18
  outro_and_destroy_block,
19
19
  safe_not_equal,
20
+ set_style,
20
21
  transition_in,
21
22
  transition_out,
22
23
  update_keyed_each
@@ -27,6 +28,7 @@ import { beforeUpdate, onDestroy, onMount } from "svelte";
27
28
  import * as THREE from 'three';
28
29
  import ItemLabelItem from './ItemLabelItem';
29
30
  import "./events.type";
31
+ import debounce from '../shared-utils/debounce';
30
32
 
31
33
  function add_css(target) {
32
34
  append_styles(target, "svelte-rmdeb", ".item-labels-container.svelte-rmdeb{width:100%;height:100%;position:relative}");
@@ -34,11 +36,11 @@ function add_css(target) {
34
36
 
35
37
  function get_each_context(ctx, list, i) {
36
38
  const child_ctx = ctx.slice();
37
- child_ctx[26] = list[i];
39
+ child_ctx[28] = list[i];
38
40
  return child_ctx;
39
41
  }
40
42
 
41
- // (179:1) {#each renderItemLabels as itemLabelItem (itemLabelItem.id)}
43
+ // (162:1) {#each renderItemLabels as itemLabelItem (itemLabelItem.id)}
42
44
  function create_each_block(key_1, ctx) {
43
45
  let first;
44
46
  let itemlabelitem;
@@ -46,7 +48,7 @@ function create_each_block(key_1, ctx) {
46
48
 
47
49
  itemlabelitem = new ItemLabelItem({
48
50
  props: {
49
- itemLabel: /*itemLabelItem*/ ctx[26],
51
+ itemLabel: /*itemLabelItem*/ ctx[28],
50
52
  hooks: /*hooks*/ ctx[0]
51
53
  }
52
54
  });
@@ -67,7 +69,7 @@ function create_each_block(key_1, ctx) {
67
69
  p(new_ctx, dirty) {
68
70
  ctx = new_ctx;
69
71
  const itemlabelitem_changes = {};
70
- if (dirty & /*renderItemLabels*/ 2) itemlabelitem_changes.itemLabel = /*itemLabelItem*/ ctx[26];
72
+ if (dirty & /*renderItemLabels*/ 2) itemlabelitem_changes.itemLabel = /*itemLabelItem*/ ctx[28];
71
73
  if (dirty & /*hooks*/ 1) itemlabelitem_changes.hooks = /*hooks*/ ctx[0];
72
74
  itemlabelitem.$set(itemlabelitem_changes);
73
75
  },
@@ -94,7 +96,7 @@ function create_fragment(ctx) {
94
96
  let div_resize_listener;
95
97
  let current;
96
98
  let each_value = /*renderItemLabels*/ ctx[1];
97
- const get_key = ctx => /*itemLabelItem*/ ctx[26].id;
99
+ const get_key = ctx => /*itemLabelItem*/ ctx[28].id;
98
100
 
99
101
  for (let i = 0; i < each_value.length; i += 1) {
100
102
  let child_ctx = get_each_context(ctx, each_value, i);
@@ -111,7 +113,8 @@ function create_fragment(ctx) {
111
113
  }
112
114
 
113
115
  attr(div, "class", "item-labels-container svelte-rmdeb");
114
- add_render_callback(() => /*div_elementresize_handler*/ ctx[9].call(div));
116
+ add_render_callback(() => /*div_elementresize_handler*/ ctx[10].call(div));
117
+ set_style(div, "opacity", /*itemsVisible*/ ctx[4] ? 1 : 0, false);
115
118
  },
116
119
  m(target, anchor) {
117
120
  insert(target, div, anchor);
@@ -120,7 +123,7 @@ function create_fragment(ctx) {
120
123
  each_blocks[i].m(div, null);
121
124
  }
122
125
 
123
- div_resize_listener = add_resize_listener(div, /*div_elementresize_handler*/ ctx[9].bind(div));
126
+ div_resize_listener = add_resize_listener(div, /*div_elementresize_handler*/ ctx[10].bind(div));
124
127
  current = true;
125
128
  },
126
129
  p(ctx, [dirty]) {
@@ -130,6 +133,10 @@ function create_fragment(ctx) {
130
133
  each_blocks = update_keyed_each(each_blocks, dirty, get_key, 1, ctx, each_value, each_1_lookup, div, outro_and_destroy_block, create_each_block, null, get_each_context);
131
134
  check_outros();
132
135
  }
136
+
137
+ if (dirty & /*itemsVisible*/ 16) {
138
+ set_style(div, "opacity", /*itemsVisible*/ ctx[4] ? 1 : 0, false);
139
+ }
133
140
  },
134
141
  i(local) {
135
142
  if (current) return;
@@ -179,46 +186,14 @@ function instance($$self, $$props, $$invalidate) {
179
186
  // 计算重叠
180
187
  let cssOffsetLists = [];
181
188
 
189
+ // 动画过程中 itemsVisible 为 false
190
+ let itemsVisible = true;
191
+
182
192
  /**
183
193
  * 可见性策略:
184
194
  * 1、当前楼层(假设先不考虑多楼层)
185
195
  * 2、模型未被遮挡(一个 box 有没有被遮挡的计算? TODO)
186
196
  * */
187
- // 画辅助线
188
- function addHelper(x, y, z, type, helper) {
189
- let geometry, materials, mesh;
190
-
191
- if (type === 'box') {
192
- geometry = new THREE.BoxGeometry(0.6, 2.5, 1.010696);
193
- let mats = [];
194
-
195
- for (let i = 0; i < geometry.faces.length; i++) {
196
- const material = new THREE.MeshBasicMaterial({
197
- color: new THREE.Color(Math.random() * 0xffffff)
198
- });
199
-
200
- mats.push(material);
201
- }
202
-
203
- materials = mats;
204
- } else if (type === 'ball') {
205
- geometry = new THREE.SphereGeometry(0.02, 0.02, 64);
206
- materials = new THREE.MeshBasicMaterial({ color: new THREE.Color(0xffffff) });
207
- }
208
-
209
- mesh = new THREE.Mesh(geometry, materials);
210
-
211
- // y 轴需要加一半的高度,因为position是从地面开始的,或者我应该算偏移更合理
212
- mesh.position.set(x, y, z);
213
-
214
- if (helper) {
215
- const helper = new THREE.AxesHelper(5);
216
- mesh.add(helper);
217
- }
218
-
219
- five.scene.add(mesh);
220
- }
221
-
222
197
  const getLabelVisible = (five, itemLabel) => {
223
198
  // 虚拟 VR 仅有一层,不考虑楼层信息
224
199
  const raycaster = new Raycaster();
@@ -272,6 +247,8 @@ function instance($$self, $$props, $$invalidate) {
272
247
  return Math.ceil(-38.9 * itemSpaceHeight + 130);
273
248
  case DISPLAY_STRATEGY_TYPE.LARGE:
274
249
  return Math.ceil(-44.44 * itemSpaceHeight + 140);
250
+ case DISPLAY_STRATEGY_TYPE.EXTRA_LARGE:
251
+ return Math.ceil(-92.59 * itemSpaceHeight + 300);
275
252
  }
276
253
  }
277
254
 
@@ -318,15 +295,27 @@ function instance($$self, $$props, $$invalidate) {
318
295
  addDataUpdateListener();
319
296
  });
320
297
 
298
+ const handleCameraUpdateCallback = () => {
299
+ $$invalidate(4, itemsVisible = false);
300
+ handleCameraUpdate();
301
+ };
302
+
321
303
  onMount(() => {
322
304
  $$invalidate(1, renderItemLabels = itemLabels);
323
305
  curItemLabels = itemLabels;
324
306
  onItemLabelUpdate();
325
-
326
- // five.on('cameraUpdate', onItemLabelUpdate)
327
307
  addResizeListener();
308
+ five.on('cameraUpdate', handleCameraUpdateCallback);
328
309
  });
329
310
 
311
+ const handleCameraUpdate = debounce(
312
+ () => {
313
+ $$invalidate(4, itemsVisible = true);
314
+ onItemLabelUpdate();
315
+ },
316
+ 300
317
+ );
318
+
330
319
  const addResizeListener = () => {
331
320
  wrapperSize = {
332
321
  width: wrapper.clientWidth,
@@ -340,7 +329,6 @@ function instance($$self, $$props, $$invalidate) {
340
329
  if (curItemLabels !== itemLabels) {
341
330
  $$invalidate(1, renderItemLabels = itemLabels);
342
331
  curItemLabels = itemLabels;
343
- console.log('--debug--update--');
344
332
  onItemLabelUpdate();
345
333
  }
346
334
  };
@@ -358,13 +346,11 @@ function instance($$self, $$props, $$invalidate) {
358
346
  });
359
347
 
360
348
  onDestroy(() => {
361
- // five.off('cameraUpdate', onItemLabelUpdate)
349
+ five.off('cameraUpdate', handleCameraUpdateCallback);
362
350
  resizeObserver.unobserve(wrapper);
363
-
364
- curItemLabels = null;
365
- $$invalidate(1, renderItemLabels = null);
366
- wrapperSize = { width: 0, height: 0 };
367
- });
351
+ }); // curItemLabels = null
352
+ // renderItemLabels = null
353
+ // wrapperSize = { width: 0, height: 0 }
368
354
 
369
355
  function div_elementresize_handler() {
370
356
  containerWidth = this.clientWidth;
@@ -374,12 +360,12 @@ function instance($$self, $$props, $$invalidate) {
374
360
  }
375
361
 
376
362
  $$self.$$set = $$props => {
377
- if ('five' in $$props) $$invalidate(4, five = $$props.five);
378
- if ('modelOcclusionEnable' in $$props) $$invalidate(5, modelOcclusionEnable = $$props.modelOcclusionEnable);
379
- if ('itemLabels' in $$props) $$invalidate(6, itemLabels = $$props.itemLabels);
380
- if ('wrapper' in $$props) $$invalidate(7, wrapper = $$props.wrapper);
363
+ if ('five' in $$props) $$invalidate(5, five = $$props.five);
364
+ if ('modelOcclusionEnable' in $$props) $$invalidate(6, modelOcclusionEnable = $$props.modelOcclusionEnable);
365
+ if ('itemLabels' in $$props) $$invalidate(7, itemLabels = $$props.itemLabels);
366
+ if ('wrapper' in $$props) $$invalidate(8, wrapper = $$props.wrapper);
381
367
  if ('hooks' in $$props) $$invalidate(0, hooks = $$props.hooks);
382
- if ('displayStrategyType' in $$props) $$invalidate(8, displayStrategyType = $$props.displayStrategyType);
368
+ if ('displayStrategyType' in $$props) $$invalidate(9, displayStrategyType = $$props.displayStrategyType);
383
369
  };
384
370
 
385
371
  return [
@@ -387,6 +373,7 @@ function instance($$self, $$props, $$invalidate) {
387
373
  renderItemLabels,
388
374
  containerWidth,
389
375
  containerHeight,
376
+ itemsVisible,
390
377
  five,
391
378
  modelOcclusionEnable,
392
379
  itemLabels,
@@ -407,12 +394,12 @@ class Component extends SvelteComponent {
407
394
  create_fragment,
408
395
  safe_not_equal,
409
396
  {
410
- five: 4,
411
- modelOcclusionEnable: 5,
412
- itemLabels: 6,
413
- wrapper: 7,
397
+ five: 5,
398
+ modelOcclusionEnable: 6,
399
+ itemLabels: 7,
400
+ wrapper: 8,
414
401
  hooks: 0,
415
- displayStrategyType: 8
402
+ displayStrategyType: 9
416
403
  },
417
404
  add_css
418
405
  );
@@ -14,7 +14,7 @@ export const ModelItemLabelPlugin = (five, params) => {
14
14
  app: undefined,
15
15
  hooks: new Subscribe(),
16
16
  modelOcclusionEnable: (_a = params === null || params === void 0 ? void 0 : params.modelOcclusionEnable) !== null && _a !== void 0 ? _a : true,
17
- displayStrategyType: (_b = params === null || params === void 0 ? void 0 : params.displayStrategyType) !== null && _b !== void 0 ? _b : DISPLAY_STRATEGY_TYPE.SMALL
17
+ displayStrategyType: (_b = params === null || params === void 0 ? void 0 : params.displayStrategyType) !== null && _b !== void 0 ? _b : DISPLAY_STRATEGY_TYPE.SMALL,
18
18
  };
19
19
  pluginState.container.setAttribute('class', 'model-item-label-plugin-container');
20
20
  pluginState.container.style.cssText = `
@@ -23,8 +23,9 @@ export const ModelItemLabelPlugin = (five, params) => {
23
23
  top: 0;
24
24
  width: 100%;
25
25
  height: 100%;
26
- pointerEvents: none;
26
+ z-index: 5;
27
27
  `;
28
+ pluginState.container.style.pointerEvents = 'none'; // 写到 cssText 里不生效
28
29
  const appendTo = (wrapper) => {
29
30
  pluginState.wrapper = wrapper;
30
31
  wrapper.appendChild(pluginState.container);
@@ -32,7 +33,6 @@ export const ModelItemLabelPlugin = (five, params) => {
32
33
  return true;
33
34
  };
34
35
  const load = (data) => {
35
- console.log('🐶--当前标签总条数: ', data.model_item_labels.length);
36
36
  pluginState.itemLabels = parseModelItemLabelPluginData(data);
37
37
  render();
38
38
  };
@@ -54,40 +54,6 @@ export const ModelItemLabelPlugin = (five, params) => {
54
54
  removeListener4Five();
55
55
  pluginState.container.remove();
56
56
  };
57
- const handleRerender = () => {
58
- if (!pluginState.enabled || !pluginState.fiveModeEnabled)
59
- return;
60
- disable();
61
- };
62
- const rerender = (a, b, c) => {
63
- if (!pluginState.fiveModeEnabled)
64
- return;
65
- if (!c)
66
- return;
67
- enable();
68
- render();
69
- };
70
- const handleInteriaPanRerender = (a, b) => {
71
- if (!pluginState.fiveModeEnabled)
72
- return;
73
- if (!b)
74
- return;
75
- enable();
76
- render();
77
- };
78
- const handleWantsPanGesture = (pose, final) => {
79
- if (pose) {
80
- handleRerender();
81
- }
82
- };
83
- const handlePanGesture = (pose, final) => {
84
- if (!pluginState.fiveModeEnabled)
85
- return;
86
- if (!final)
87
- return;
88
- enable();
89
- render();
90
- };
91
57
  const render = () => {
92
58
  var _a;
93
59
  if (!pluginState.wrapper)
@@ -123,32 +89,14 @@ export const ModelItemLabelPlugin = (five, params) => {
123
89
  const addListener4Five = () => {
124
90
  five.on('modeChange', onFiveModeChange);
125
91
  five.once('dispose', dispose);
126
- // five.on('wantsGesture', handleRerender)
127
- // five.on('gesture', rerender)
128
- five.on('wantsMouseWheel', handleRerender);
129
- five.on('mouseWheel', rerender);
130
- five.on('wantsInteriaPan', handleRerender);
131
- five.on('interiaPan', handleInteriaPanRerender);
132
- five.on('wantsPanGesture', handleWantsPanGesture);
133
- five.on('panGesture', handlePanGesture);
134
- five.on('wantsPinchGesture', handleRerender);
135
- five.on('pinchGesture', rerender);
136
92
  };
137
93
  // 取消 five 监听
138
94
  const removeListener4Five = () => {
139
95
  five.off('modeChange', onFiveModeChange);
140
- // five.off('wantsGesture', handleRerender)
141
- // five.off('gesture', rerender)
142
- five.off('wantsMouseWheel', handleRerender);
143
- five.off('mouseWheel', rerender);
144
- five.off('wantsInteriaPan', handleRerender);
145
- five.off('interiaPan', handleInteriaPanRerender);
146
- five.off('wantsPanGesture', handleWantsPanGesture);
147
- five.off('panGesture', handlePanGesture);
148
- five.off('wantsPinchGesture', handleRerender);
149
- five.off('pinchGesture', rerender);
150
96
  };
151
97
  const onFiveModeChange = (mode) => {
98
+ if (!pluginState.enabled)
99
+ return;
152
100
  if (mode !== Five.Mode.Floorplan) {
153
101
  pluginState.fiveModeEnabled = false;
154
102
  render();
@@ -4,7 +4,8 @@ import { PluginEvent } from "./events.type";
4
4
  export declare enum DISPLAY_STRATEGY_TYPE {
5
5
  SMALL = "small",
6
6
  MIDLLE = "middle",
7
- LARGE = "large"
7
+ LARGE = "large",
8
+ EXTRA_LARGE = "x-large"
8
9
  }
9
10
  export interface ModelItemLabelPluginParametersType {
10
11
  modelOcclusionEnable?: boolean;
@@ -3,4 +3,5 @@ export var DISPLAY_STRATEGY_TYPE;
3
3
  DISPLAY_STRATEGY_TYPE["SMALL"] = "small";
4
4
  DISPLAY_STRATEGY_TYPE["MIDLLE"] = "middle";
5
5
  DISPLAY_STRATEGY_TYPE["LARGE"] = "large";
6
+ DISPLAY_STRATEGY_TYPE["EXTRA_LARGE"] = "x-large";
6
7
  })(DISPLAY_STRATEGY_TYPE || (DISPLAY_STRATEGY_TYPE = {}));
@@ -74,6 +74,7 @@ export class ModelRoomLabelController {
74
74
  this.container.style.pointerEvents = 'none';
75
75
  this.container.style.left = '0';
76
76
  this.container.style.top = '0';
77
+ this.container.style.zIndex = '5';
77
78
  // five listener
78
79
  this.five.once('dispose', () => this.dispose());
79
80
  this.five.on('modeChange', this.onFiveModeChange);
@@ -0,0 +1,4 @@
1
+ import type { FivePlugin } from '@realsee/five';
2
+ import { ModelTVVideoPluginExportType, ModelTVVideoPluginParameterType } from "./typings";
3
+ export declare const ModelTVVideoPlugin: FivePlugin<ModelTVVideoPluginParameterType, ModelTVVideoPluginExportType>;
4
+ export default ModelTVVideoPlugin;
@@ -0,0 +1,263 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import * as THREE from 'three';
11
+ import { parseModelTVVideoPoints } from "./utils/parseData";
12
+ export const ModelTVVideoPlugin = (five, { videoElement }) => {
13
+ const state = {
14
+ videoMeshes: [],
15
+ videoTextureEnabled: false,
16
+ videoSource: '',
17
+ rectPoints: [],
18
+ enabled: false,
19
+ videoElement: videoElement,
20
+ };
21
+ const setMuted = (muted) => {
22
+ if (state.videoTexture) {
23
+ state.videoTexture.image.muted = muted;
24
+ state.videoTexture.image.play();
25
+ }
26
+ };
27
+ const getMuted = () => {
28
+ if (state.videoTexture)
29
+ return state.videoTexture.image.muted;
30
+ else
31
+ return true;
32
+ };
33
+ const enable = () => {
34
+ if (state.enabled)
35
+ return;
36
+ if (!state.videoTexture)
37
+ return;
38
+ state.enabled = true;
39
+ state.videoMeshes = createPanoVideoMeshes();
40
+ console.log('state.videoMeshes', state.videoMeshes);
41
+ state.videoMeshes.forEach((mesh) => five.scene.add(mesh));
42
+ const play = () => {
43
+ console.log('play', state.videoTexture);
44
+ if (!state.videoTexture)
45
+ return;
46
+ const timeupdate = () => {
47
+ var _a;
48
+ if (!state.videoTexture)
49
+ return;
50
+ (_a = state.videoTexture) === null || _a === void 0 ? void 0 : _a.image.removeEventListener('timeupdate', timeupdate);
51
+ state.videoTextureEnabled = true;
52
+ state.videoMeshes.forEach((mesh) => {
53
+ if (mesh.material.map !== state.videoTexture)
54
+ mesh.material.map = state.videoTexture;
55
+ });
56
+ five.needsRender = true;
57
+ };
58
+ state.videoTexture.image.addEventListener('timeupdate', timeupdate);
59
+ if (state.videoTexture && state.videoMeshes.length) {
60
+ state.videoTexture.image.play();
61
+ }
62
+ };
63
+ if (five.model.loaded)
64
+ play();
65
+ else {
66
+ return five.once('modelLoaded', () => play());
67
+ }
68
+ };
69
+ const disable = () => {
70
+ if (!state.enabled)
71
+ return;
72
+ state.enabled = false;
73
+ state.videoMeshes.forEach((mesh) => {
74
+ mesh.geometry.dispose();
75
+ mesh.material.dispose();
76
+ five.scene.remove(mesh);
77
+ if (state.videoTexture)
78
+ state.videoTexture.image.pause();
79
+ });
80
+ state.videoMeshes = [];
81
+ five.needsRender = true;
82
+ };
83
+ const createPanoVideoMeshes = () => {
84
+ return state.rectPoints.map((points, index) => {
85
+ const geometry = new THREE.BufferGeometry();
86
+ const segments = 128;
87
+ const verticesArray = [];
88
+ verticesArray.push(points[0].x, points[0].y, points[0].z);
89
+ for (let i = 1; i < segments; i++) {
90
+ verticesArray.push(points[0].x + ((points[1].x - points[0].x) * i) / segments, points[0].y + ((points[1].y - points[0].y) * i) / segments, points[0].z + ((points[1].z - points[0].z) * i) / segments);
91
+ }
92
+ verticesArray.push(points[1].x, points[1].y, points[1].z);
93
+ verticesArray.push(points[2].x, points[2].y, points[2].z);
94
+ for (let i = 1; i < segments; i++) {
95
+ verticesArray.push(points[2].x + ((points[3].x - points[2].x) * i) / segments, points[2].y + ((points[3].y - points[2].y) * i) / segments, points[2].z + ((points[3].z - points[2].z) * i) / segments);
96
+ }
97
+ verticesArray.push(points[3].x, points[3].y, points[3].z);
98
+ const uvArray = [];
99
+ uvArray.push(0, 1);
100
+ for (let i = 1; i < segments; i++) {
101
+ uvArray.push(0, 1 - i / segments);
102
+ }
103
+ uvArray.push(0, 0);
104
+ uvArray.push(1, 0);
105
+ for (let i = 1; i < segments; i++) {
106
+ uvArray.push(1, i / segments);
107
+ }
108
+ uvArray.push(1, 1);
109
+ const indicesArray = [];
110
+ for (let i = 0; i < segments; i++) {
111
+ indicesArray.push(i, i + 1, segments * 2 - i, i, segments * 2 - i, segments * 2 + 1 - i);
112
+ }
113
+ geometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array(verticesArray), 3));
114
+ geometry.setAttribute('uv', new THREE.BufferAttribute(new Float32Array(uvArray), 2));
115
+ geometry.setIndex(new THREE.BufferAttribute(new Uint32Array(indicesArray), 1));
116
+ const material = new THREE.MeshBasicMaterial({
117
+ map: state.videoTextureEnabled ? state.videoTexture : state.imageTexture,
118
+ side: THREE.DoubleSide,
119
+ });
120
+ const mesh = new THREE.Mesh(geometry, material);
121
+ mesh.renderOrder = 1;
122
+ mesh.name = `ModelTVVideoPlugin-${index}-${performance.now()}`;
123
+ return mesh;
124
+ });
125
+ };
126
+ const getImageTexture = (source) => {
127
+ const texture = new THREE.TextureLoader().load(source);
128
+ texture.minFilter = THREE.LinearFilter;
129
+ texture.magFilter = THREE.LinearFilter;
130
+ texture.format = THREE.RGBFormat;
131
+ return texture;
132
+ };
133
+ const getVideoTexture = (source, video) => {
134
+ return new Promise((resolve, reject) => {
135
+ const xhr = new XMLHttpRequest();
136
+ xhr.onreadystatechange = () => {
137
+ if (xhr.readyState == 4) {
138
+ if (xhr.status == 200) {
139
+ const url = window.URL || window.webkitURL;
140
+ video = video || document.createElement('video');
141
+ video.crossOrigin = 'anonymous';
142
+ video.autoplay = true;
143
+ video.muted = true;
144
+ video.loop = true;
145
+ video.playsInline = true;
146
+ video.src = url.createObjectURL(xhr.response);
147
+ const texture = new THREE.VideoTexture(video);
148
+ texture.minFilter = THREE.LinearFilter;
149
+ texture.magFilter = THREE.LinearFilter;
150
+ texture.format = THREE.RGBFormat;
151
+ resolve(Object.assign(texture, { videoSource: source }));
152
+ }
153
+ else {
154
+ reject(new Error('Video download Error: ' + xhr.status));
155
+ }
156
+ }
157
+ };
158
+ xhr.onerror = (error) => reject(error);
159
+ xhr.open('GET', source);
160
+ xhr.responseType = 'blob';
161
+ xhr.send();
162
+ });
163
+ };
164
+ const load = (data, videoElement) => __awaiter(void 0, void 0, void 0, function* () {
165
+ const { video_src, video_poster_src, points } = data;
166
+ state.videoSource = video_src;
167
+ // TODO 存疑,先处理多一次遍历 o(2n)
168
+ state.rectPoints = parseModelTVVideoPoints(points)
169
+ .map((items) => items.map(({ x, y, z }) => new THREE.Vector3(x, y, z)));
170
+ state.imageTexture = getImageTexture(video_poster_src);
171
+ if (videoElement) {
172
+ state.videoElement = videoElement;
173
+ }
174
+ state.videoTexture = yield getVideoTexture(state.videoSource, state.videoElement);
175
+ state.enabled = !!data.enable;
176
+ if (state.enabled)
177
+ enable();
178
+ });
179
+ const dispose = () => {
180
+ disable();
181
+ state.videoTexture = undefined;
182
+ five.off('modeChange', () => setMuted(true));
183
+ five.off('wantsTapGesture', (raycaster) => {
184
+ if (!state.enabled)
185
+ return;
186
+ const [intersect] = raycaster.intersectObjects(five.scene.children, true);
187
+ if (!!intersect && /^video/.test(intersect.object.name)) {
188
+ if (state.videoTexture)
189
+ setMuted(!state.videoTexture.image.muted);
190
+ return false;
191
+ }
192
+ });
193
+ five.off('panoArrived', () => {
194
+ if (!state.enabled)
195
+ return;
196
+ if (getMuted())
197
+ return;
198
+ const cameraPosition = five.camera.position;
199
+ const visible = state.rectPoints.find((points) => {
200
+ const centerVector = points.reduce((total, curr) => new THREE.Vector3(total.x + curr.x / points.length, total.y + curr.y / points.length, total.z + curr.z / points.length), new THREE.Vector3());
201
+ return (points.map((point) => new THREE.Vector3((point.x + centerVector.x) / 2, (point.y + centerVector.y) / 2, (point.z + centerVector.z) / 2)).filter((vector) => {
202
+ vector = vector.clone().sub(cameraPosition).normalize();
203
+ const raycaster = new THREE.Raycaster(cameraPosition, vector);
204
+ const [intersect] = raycaster.intersectObjects(five.scene.children, true);
205
+ return !!intersect && /^video/.test(intersect.object.name);
206
+ }).length >= 2);
207
+ });
208
+ if (!visible)
209
+ setMuted(true);
210
+ });
211
+ five.off('renderFrame', () => {
212
+ // console.log('renderFrame', state.videoMeshes)
213
+ state.videoMeshes.forEach((meshes) => {
214
+ if (meshes)
215
+ //@ts-ignore
216
+ meshes.needsRender = true;
217
+ });
218
+ });
219
+ };
220
+ five.on('modeChange', () => setMuted(true));
221
+ five.on('wantsTapGesture', (raycaster) => {
222
+ if (!state.enabled)
223
+ return;
224
+ const targetMesh = five.scene.children.find(item => /^ModelTVVideoPlugin/.test(item.name));
225
+ if (!targetMesh)
226
+ return;
227
+ const intersectObjects = raycaster.intersectObjects([targetMesh]);
228
+ if (intersectObjects.length === 0)
229
+ return;
230
+ if (state.videoTexture)
231
+ setMuted(!state.videoTexture.image.muted); // TODO toggle video play
232
+ return false;
233
+ });
234
+ five.on('panoArrived', () => {
235
+ if (!state.enabled)
236
+ return;
237
+ if (getMuted())
238
+ return;
239
+ const cameraPosition = five.camera.position;
240
+ const visible = state.rectPoints.find((points) => {
241
+ const centerVector = points.reduce((total, curr) => new THREE.Vector3(total.x + curr.x / points.length, total.y + curr.y / points.length, total.z + curr.z / points.length), new THREE.Vector3());
242
+ return (points.map((point) => new THREE.Vector3((point.x + centerVector.x) / 2, (point.y + centerVector.y) / 2, (point.z + centerVector.z) / 2)).filter((vector) => {
243
+ vector = vector.clone().sub(cameraPosition).normalize();
244
+ const raycaster = new THREE.Raycaster(cameraPosition, vector);
245
+ const [intersect] = raycaster.intersectObjects(five.scene.children, true);
246
+ return !!intersect && /^video/.test(intersect.object.name);
247
+ }).length >= 2);
248
+ });
249
+ if (!visible)
250
+ setMuted(true);
251
+ });
252
+ five.on('renderFrame', () => {
253
+ // console.log('renderFrame', state.videoMeshes)
254
+ state.videoMeshes.forEach((meshes) => {
255
+ if (meshes)
256
+ //@ts-ignore
257
+ meshes.needsRender = true;
258
+ });
259
+ });
260
+ Object.assign(window, { $state: state });
261
+ return { enable, disable, load, dispose };
262
+ };
263
+ export default ModelTVVideoPlugin;
@@ -0,0 +1,25 @@
1
+ export interface ModelTVVideoPluginParameterType {
2
+ videoElement?: HTMLVideoElement;
3
+ }
4
+ export declare enum DIRECTION {
5
+ TOP_LEFT = "topLeft",
6
+ TOP_RIGHT = "topRight",
7
+ BOTTOM_LEFT = "bottomLeft",
8
+ BOTTOM_RIGHT = "bottomRight"
9
+ }
10
+ export interface Point {
11
+ x: number;
12
+ y: number;
13
+ z: number;
14
+ }
15
+ export interface ModelTVVideoPluginData {
16
+ enable?: boolean;
17
+ video_src: string;
18
+ video_poster_src: string;
19
+ points: Point[][] | Record<DIRECTION, Point>[];
20
+ }
21
+ export interface ModelTVVideoPluginExportType {
22
+ enable: () => void;
23
+ disable: () => void;
24
+ load: (data: ModelTVVideoPluginData, videoElement?: HTMLVideoElement) => Promise<void>;
25
+ }
@@ -0,0 +1,7 @@
1
+ export var DIRECTION;
2
+ (function (DIRECTION) {
3
+ DIRECTION["TOP_LEFT"] = "topLeft";
4
+ DIRECTION["TOP_RIGHT"] = "topRight";
5
+ DIRECTION["BOTTOM_LEFT"] = "bottomLeft";
6
+ DIRECTION["BOTTOM_RIGHT"] = "bottomRight";
7
+ })(DIRECTION || (DIRECTION = {}));
@@ -0,0 +1,2 @@
1
+ import { DIRECTION, Point } from "../typings";
2
+ export declare function parseModelTVVideoPoints(points: Point[][] | Record<DIRECTION, Point>[]): Point[][];