fl-web-component 1.4.7 → 1.4.9-beta.0

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 (38) hide show
  1. package/README.md +1 -28
  2. package/dist/fl-web-component.common.1.js +2 -2
  3. package/dist/fl-web-component.common.1.js.map +1 -1
  4. package/dist/fl-web-component.common.2.js.map +1 -1
  5. package/dist/fl-web-component.common.js +77420 -47296
  6. package/dist/fl-web-component.common.js.map +1 -1
  7. package/dist/fl-web-component.css +1 -1
  8. package/package.json +12 -4
  9. package/packages/components/com-flcanvas/components/entityFormatting.js +9 -1
  10. package/packages/components/com-graphics/box.json +77 -0
  11. package/packages/components/com-graphics/component/ann-tool.vue +465 -0
  12. package/packages/components/com-graphics/index copy.vue +1679 -0
  13. package/packages/components/com-graphics/index.vue +3890 -301
  14. package/packages/components/com-graphics/pid.vue +210 -44
  15. package/packages/components/com-graphics/test.html +127 -0
  16. package/packages/components/com-tiles/index.vue +187 -0
  17. package/packages/utils/StreamLoader.js +1498 -0
  18. package/packages/utils/StreamLoaderParser.worker.js +595 -0
  19. package/patches/camera-controls+2.9.0.patch +63 -63
  20. package/src/main.js +9 -1
  21. package/src/static/ann-img/mark_circle@2x.png +0 -0
  22. package/src/static/ann-img/mark_clear@2x.png +0 -0
  23. package/src/static/ann-img/mark_cloud@2x.png +0 -0
  24. package/src/static/ann-img/mark_color@2x.png +0 -0
  25. package/src/static/ann-img/mark_eraser@2x.png +0 -0
  26. package/src/static/ann-img/mark_exit@2x.png +0 -0
  27. package/src/static/ann-img/mark_finish@2x.png +0 -0
  28. package/src/static/ann-img/mark_font@2x.png +0 -0
  29. package/src/static/ann-img/mark_polyline@2x.png +0 -0
  30. package/src/static/ann-img/mark_rectangle@2x.png +0 -0
  31. package/src/static/ann-img/mark_zoomin@2x.png +0 -0
  32. package/src/static/ann-img/mark_zoomout@2x.png +0 -0
  33. package/src/utils/cloud.js +110 -0
  34. package/src/utils/cursor.js +10 -0
  35. package/src/utils/flgltf-parser.js +245 -193
  36. package/src/utils/instance-parser.js +718 -170
  37. package/dist/fl-web-component.common.3.js +0 -7740
  38. package/dist/fl-web-component.common.3.js.map +0 -1
@@ -1,8 +1,17 @@
1
1
  <template>
2
- <embed type="image/svg+xml" id="svg-tigger" :src="currentSvg" />
2
+ <div id="svg-component" ref="svgComponent">
3
+ <!-- 批注工具条 -->
4
+ <div v-if="toolbarShow" id="toolbar-show">
5
+ <AnnTool ref="AnnToolbar" @closeAnn="closeAnnMode" :src="currentSvg"></AnnTool>
6
+ </div>
7
+ <embed v-show="canvasShow" type="image/svg+xml" id="svg-tigger" :src="currentSvg" />
8
+ </div>
9
+
3
10
  </template>
4
11
  <script>
5
12
  import svgPanZoom from 'svg-pan-zoom';
13
+ import html2canvas from 'html2canvas';
14
+ import AnnTool from './component/ann-tool.vue'
6
15
  var svgCon = {},
7
16
  svgTigger = {},
8
17
  svgEmbed = {};
@@ -25,46 +34,61 @@
25
34
  this.currentSvg = newVal
26
35
  }
27
36
  },
37
+ components: {
38
+ AnnTool
39
+ },
28
40
  data() {
29
41
  return {
30
42
  currentSvg: '',
43
+ activeObj: null,
44
+ canvasShow: true,
45
+ toolbarShow: false,
46
+ svgInitStyle: {}
31
47
  };
32
48
  },
33
49
  created() {
34
50
  this.currentSvg = this.src
35
51
  },
36
52
  mounted() {
37
- svgEmbed = document.getElementById('svg-tigger');
38
- svgEmbed.addEventListener('resize', () => {
39
- this.onWindowResize;
40
- });
41
- svgEmbed.addEventListener('load', () => {
42
- preTargetCon.splice(0)
43
- preTargetStyle.splice(0)
44
- svgTigger = svgPanZoom('#svg-tigger', {
45
- viewportSelector: '.svg-pan-zoom_viewport',
46
- panEnabled: true,
47
- zoomEnabled: true,
48
- preventMouseEventsDefault: false,
53
+ this.$nextTick(() => {
54
+ svgEmbed = document.getElementById('svg-tigger');
55
+ svgEmbed.style.width = this.$refs.svgComponent.clientWidth + 'px'
56
+ svgEmbed.style.height = this.$refs.svgComponent.clientHeight + 'px'
57
+ svgEmbed.addEventListener('resize', () => {
58
+ this.onWindowResize;
49
59
  });
50
- svgCon = svgEmbed.getSVGDocument().querySelector('svg');
51
- svgCon.style = 'cursor: pointer;user-select: none;';
52
- svgCon.setAttribute('viewBox', '0 0 ' + svgEmbed.offsetWidth + ' ' + svgEmbed.offsetHeight);
53
- inspectionRect = svgCon.createSVGRect();
54
- inspectionRect.width = 10;
55
- inspectionRect.height = 10;
56
- pointerRect = svgCon.createSVGPoint();
57
- this.onWindowResize();
58
- // 禁止右键菜单栏
59
- svgCon.addEventListener('contextmenu', e => {
60
- e.preventDefault();
60
+ svgEmbed.addEventListener('load', () => {
61
+ preTargetCon.splice(0)
62
+ preTargetStyle.splice(0)
63
+ svgTigger = svgPanZoom('#svg-tigger', {
64
+ viewportSelector: '.svg-pan-zoom_viewport',
65
+ panEnabled: true,
66
+ zoomEnabled: true,
67
+ preventMouseEventsDefault: false,
68
+ fit: true,
69
+ center: true
70
+ });
71
+ svgCon = svgEmbed.getSVGDocument().querySelector('svg');
72
+ svgCon.style = 'cursor: pointer;user-select: none;';
73
+ this.svgInitStyle = svgEmbed.getSVGDocument().getElementsByClassName("svg-pan-zoom_viewport")[0].getBoundingClientRect()
74
+ svgCon.setAttribute('viewBox', '0 0 ' + svgEmbed.offsetWidth + ' ' + svgEmbed.offsetHeight);
75
+ inspectionRect = svgCon.createSVGRect();
76
+ inspectionRect.width = 10;
77
+ inspectionRect.height = 10;
78
+ pointerRect = svgCon.createSVGPoint();
79
+ this.onWindowResize();
80
+ // 禁止右键菜单栏
81
+ svgCon.addEventListener('contextmenu', e => {
82
+ e.preventDefault();
83
+ });
84
+ // 鼠标的按下与抬起事件
85
+ svgCon.addEventListener('mousedown', this.mousedown);
86
+ svgCon.addEventListener('mouseup', this.mouseup);
87
+ // 加载完成后会通知
88
+ this.$emit('loaded');
61
89
  });
62
- // 鼠标的按下与抬起事件
63
- svgCon.addEventListener('mousedown', this.mousedown);
64
- svgCon.addEventListener('mouseup', this.mouseup);
65
- // 加载完成后会通知
66
- this.$emit('loaded');
67
- });
90
+ })
91
+
68
92
  },
69
93
  methods: {
70
94
  changeSvg(url) {
@@ -83,6 +107,7 @@
83
107
  },
84
108
  // 鼠标抬起
85
109
  mouseup(evt) {
110
+ evt.stopPropagation()
86
111
  lastTime = new Date().getTime();
87
112
  let targetElement = null;
88
113
  // 判断鼠标按下到抬起的时间间隔是否超过300ms 没有超过 则判断为点击
@@ -99,13 +124,30 @@
99
124
  targetElement =
100
125
  intersectionList.length > 0 ? intersectionList[intersectionList.length - 1] : null;
101
126
  }
127
+ if (targetElement && !targetElement.id) {
128
+ this.searchSvgId(targetElement.parentNode)
129
+ } else {
130
+ this.activeObj = targetElement
131
+ }
102
132
  if (evt.button === 0) {
103
- this.$emit('leftClick', targetElement);
133
+ this.$emit('leftClick', this.activeObj);
104
134
  } else if (evt.button === 2) {
105
- this.$emit('rightClick', targetElement);
135
+ console.log('rightClick')
136
+ this.$emit('rightClick', {
137
+ event: evt,
138
+ targetObj: this.activeObj
139
+ });
106
140
  }
107
141
  }
108
142
  },
143
+ // 查找对象的id
144
+ searchSvgId(dom) {
145
+ if (!dom.id) {
146
+ this.searchSvgId(dom.parentNode)
147
+ } else if (dom.id) {
148
+ this.activeObj = dom
149
+ }
150
+ },
109
151
  // 高亮目标元素
110
152
  /*
111
153
  参数:svgIds: [], 需要高亮的id的集合, flag: true / false, 是否要定位目标元素, color: '', 高亮的颜色
@@ -117,6 +159,7 @@
117
159
  for (let index = 0; index < svgIds.length; index++) {
118
160
  let element = svgIds[index];
119
161
  let targetCon = svgCon.getElementById(element.svgId);
162
+ console.log(targetCon)
120
163
  if (!targetCon) return;
121
164
  this.depthTraversal(targetCon.children, color);
122
165
  }
@@ -124,7 +167,7 @@
124
167
  svgTigger.reset();
125
168
  let firstCon = svgCon.getElementById(svgIds[0].svgId);
126
169
  setTimeout(() => {
127
- svgTigger.zoomAtPoint(2, {
170
+ svgTigger.zoomAtPoint(4, {
128
171
  x: firstCon.getBoundingClientRect().x,
129
172
  y: firstCon.getBoundingClientRect().y,
130
173
  });
@@ -134,32 +177,110 @@
134
177
  },
135
178
  // pid组高亮 深度遍历
136
179
  depthTraversal(svgCon, color) {
180
+
137
181
  for (let i = 0; i < svgCon.length; i++) {
138
182
  if (svgCon[i].children.length > 0) {
139
183
  this.depthTraversal(svgCon[i].children, color);
140
184
  } else {
141
185
  preTargetCon.push(svgCon[i]);
142
186
  preTargetStyle.push({
143
- fillOpacity: svgCon[i].style.fillOpacity,
187
+ // fillOpacity: svgCon[i].style.fillOpacity,
144
188
  stroke: svgCon[i].style.stroke,
145
- strokeWidth: svgCon[i].style.strokeWidth,
189
+ // strokeWidth: svgCon[i].style.strokeWidth,
190
+ fill: svgCon[i].style.fill
146
191
  });
147
- svgCon[i].style.fillOpacity = '0';
192
+ // svgCon[i].style.fillOpacity = '0';
148
193
  svgCon[i].style.stroke = color;
149
- svgCon[i].style.strokeWidth = 0.5;
194
+ if (svgCon[i].nodeName === 'text' || (svgCon[i].style.fill && svgCon[i].style.fill!== 'none')) {
195
+ (svgCon[i].style.fill = color)
196
+ }
197
+ // svgCon[i].style.strokeWidth = 0.5;
150
198
  }
151
199
  }
152
200
  },
153
201
  // 清除上一次的高亮
154
202
  resetHightLight() {
155
203
  for (let i = 0; i < preTargetCon.length; i++) {
156
- preTargetCon[i].style.fillOpacity = preTargetStyle[i].fillOpacity;
204
+ // preTargetCon[i].style.fillOpacity = preTargetStyle[i].fillOpacity;
157
205
  preTargetCon[i].style.stroke = preTargetStyle[i].stroke;
158
- preTargetCon[i].style.strokeWidth = preTargetStyle[i].strokeWidth;
206
+ // preTargetCon[i].style.strokeWidth = preTargetStyle[i].strokeWidth;
207
+ preTargetCon[i].style.fill = preTargetStyle[i].fill
159
208
  }
160
209
  preTargetStyle.splice(0);
161
210
  preTargetCon.splice(0);
162
211
  },
212
+ // 修改颜色要区分text text填充为fill
213
+ // 设置对象的属性 obj是需要被修改的对象, properties是需要修改的属性{}
214
+ setPorperty(obj, properties) {
215
+ if (obj.children.length > 0) {
216
+ let arr = obj.children
217
+ for (let index = 0; index < arr.length; index++) {
218
+ const element = arr[index]
219
+ if (element.nodeName !== 'g') {
220
+ for (const key in properties) {
221
+ // 记录一下原始状态
222
+ element.setAttribute(`data-${key}`, element['style'][key])
223
+ if (key === 'fill') {
224
+ if (element.nodeName === 'text' || (element.style.fill && element.style.fill !== 'none')) {
225
+ element['style']['fill'] = properties[key]
226
+ }
227
+ } else {
228
+ element['style'][key] = properties[key]
229
+ }
230
+ }
231
+ }
232
+ if (element.children && element.children.length > 0) {
233
+ this.setPorperty(element, properties)
234
+ }
235
+ }
236
+ } else {
237
+ if (obj.nodeName !== 'g') {
238
+ for (const key in properties) {
239
+ if (key === 'fill') {
240
+ if (element.nodeName === 'text' || (element.style.fill && element.style.fill !== 'none')) {
241
+ element['style']['fill'] = properties[fill]
242
+ }
243
+ } else {
244
+ element['style'][key] = properties[key]
245
+ }
246
+ }
247
+ }
248
+
249
+ }
250
+ },
251
+ resetPorperty(obj, properties) {
252
+ if (obj.children.length > 0) {
253
+ let arr = obj.children
254
+ for (let index = 0; index < arr.length; index++) {
255
+ const element = arr[index]
256
+ if (element.nodeName !== 'g') {
257
+ properties.forEach(item => {
258
+ element['style'][item] = element.getAttribute(`data-${item}`)
259
+ })
260
+ }
261
+ if (element.children && element.children.length > 0) {
262
+ this.resetPorperty(element, properties)
263
+ }
264
+ }
265
+ } else {
266
+ if (obj.nodeName !== 'g') {
267
+ properties.forEach(item => {
268
+ element['style'][item] = element.getAttribute(`data-${item}`)
269
+ })
270
+ }
271
+ }
272
+ },
273
+ getAllNode() {
274
+ let node = svgCon.querySelector('.svg-pan-zoom_viewport')
275
+ if (node) {
276
+ return node.children
277
+ } else {
278
+ return []
279
+ }
280
+ },
281
+ getNodeById(id) {
282
+ return svgCon.getElementById(id)
283
+ },
163
284
  // 更新图纸/切换图纸
164
285
  /*
165
286
  参数:url: 图纸的路径
@@ -171,13 +292,58 @@
171
292
  inspectionRect = null;
172
293
  pointerRect = null;
173
294
  },
295
+ // 图纸恢复到主视图
296
+ resetView() {
297
+ svgTigger.reset()
298
+ },
299
+ saveCanvas(canvasStyle) {
300
+ this.$refs.AnnToolbar.toolbarShow = false // 关闭画板工具条
301
+ svgTigger.reset()
302
+ svgTigger.zoom(canvasStyle.width / this.svgInitStyle.width)
303
+ svgTigger.pan({ x: -(canvasStyle.scrollLeft), y: -(canvasStyle.scrollTop) })
304
+ this.$nextTick(() => {
305
+ this.screenShot() // 保存批注
306
+ })
307
+ },
308
+ // 截图导出
309
+ screenShot() {
310
+ let shotObj = this.$refs.svgComponent
311
+ html2canvas(shotObj, { allowTaint: true, useCORS: true}).then(canvas => {
312
+ const link = document.createElement('a');
313
+ link.href = canvas.toDataURL('image/png');
314
+ link.download = 'screenshot.png';
315
+ link.click();
316
+ })
317
+ },
318
+ // 打开批注模式
319
+ openAnnToolbar() {
320
+ console.log('openAnnToolbar')
321
+ this.canvasShow = false
322
+ this.toolbarShow = true
323
+ },
324
+ closeAnnMode() {
325
+ this.canvasShow = true
326
+ this.toolbarShow = false
327
+ this.$emit('closeAnn')
328
+ }
174
329
  },
175
330
  };
176
331
  </script>
177
332
  <style lang="scss" scoped>
178
- #svg-tigger {
179
- cursor: pointer;
180
- height: 100%;
181
- width: 100%;
182
- }
333
+ #svg-component, #svg-tigger {
334
+ cursor: pointer;
335
+ height: 100%;
336
+ width: 100%;
337
+ position: relative;
338
+ }
339
+ #toolbar-show{
340
+ z-index: 20;
341
+ position: absolute;
342
+ width: 100%;
343
+ height: 100%;
344
+ top: 0;
345
+ left: 0;
346
+ overflow: hidden;
347
+ background: #fff;
348
+ }
183
349
  </style>
@@ -0,0 +1,127 @@
1
+ <!DOCTYPE html>
2
+ <html lang="zh">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>Three.js OutlinePass 点击高亮 Demo</title>
6
+ <style>
7
+ body { margin: 0; overflow: hidden; background-color: #000; }
8
+ canvas { display: block; }
9
+ .info { position: absolute; top: 10px; left: 10px; color: white; font-family: sans-serif; pointer-events: none; }
10
+ </style>
11
+ </head>
12
+ <body>
13
+ <div class="info">点击立方体或球体触发 OutlinePass 高亮</div>
14
+
15
+ <script type="importmap">
16
+ {
17
+ "imports": {
18
+ "three": "https://unpkg.com/three@0.160.0/build/three.module.js",
19
+ "three/addons/": "https://unpkg.com/three@0.160.0/examples/jsm/"
20
+ }
21
+ }
22
+ </script>
23
+
24
+ <script type="module">
25
+ import * as THREE from 'three';
26
+ import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
27
+ import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
28
+ import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
29
+ import { OutlinePass } from 'three/addons/postprocessing/OutlinePass.js';
30
+
31
+ // --- 1. 基础场景初始化 ---
32
+ const scene = new THREE.Scene();
33
+ const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
34
+ camera.position.set(0, 5, 10);
35
+
36
+ const renderer = new THREE.WebGLRenderer({ antialias: true });
37
+ renderer.setSize(window.innerWidth, window.innerHeight);
38
+ renderer.setPixelRatio(window.devicePixelRatio);
39
+ document.body.appendChild(renderer.domElement);
40
+
41
+ const controls = new OrbitControls(camera, renderer.domElement);
42
+
43
+ // 灯光
44
+ scene.add(new THREE.AmbientLight(0x404040, 2));
45
+ const directionalLight = new THREE.DirectionalLight(0xffffff, 2);
46
+ directionalLight.position.set(5, 5, 5);
47
+ scene.add(directionalLight);
48
+
49
+ // --- 2. 添加测试模型 ---
50
+ const geometry1 = new THREE.BoxGeometry(2, 2, 2);
51
+ const material1 = new THREE.MeshStandardMaterial({ color: 0x44aa88 });
52
+ const cube = new THREE.Mesh(geometry1, material1);
53
+ cube.position.x = -3;
54
+ scene.add(cube);
55
+
56
+ const geometry2 = new THREE.SphereGeometry(1.5, 32, 32);
57
+ const material2 = new THREE.MeshStandardMaterial({ color: 0xaa4488 });
58
+ const sphere = new THREE.Mesh(geometry2, material2);
59
+ sphere.position.x = 3;
60
+ scene.add(sphere);
61
+
62
+ // --- 3. 配置后期处理 (OutlinePass) ---
63
+ const composer = new EffectComposer(renderer);
64
+
65
+ // 常规渲染通道
66
+ const renderPass = new RenderPass(scene, camera);
67
+ composer.addPass(renderPass);
68
+
69
+ // 轮廓线通道
70
+ const outlinePass = new OutlinePass(
71
+ new THREE.Vector2(window.innerWidth, window.innerHeight),
72
+ scene,
73
+ camera
74
+ );
75
+ // 配置轮廓线样式
76
+ outlinePass.edgeStrength = 8.0; // 边框强度
77
+ outlinePass.edgeGlow = 1.0; // 发光度
78
+ outlinePass.edgeThickness = 2.0; // 边框粗细
79
+ outlinePass.pulsePeriod = 2; // 呼吸闪烁周期 (0为不闪烁)
80
+ outlinePass.visibleEdgeColor.set('#ffffff'); // 可见边缘颜色
81
+ outlinePass.hiddenEdgeColor.set('#190a05'); // 被遮挡边缘颜色
82
+
83
+ composer.addPass(outlinePass);
84
+
85
+ // --- 4. 射线检测 (Raycaster) 与点击事件 ---
86
+ const raycaster = new THREE.Raycaster();
87
+ const mouse = new THREE.Vector2();
88
+
89
+ window.addEventListener('click', (event) => {
90
+ // 将鼠标位置归一化
91
+ mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
92
+ mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
93
+
94
+ raycaster.setFromCamera(mouse, camera);
95
+
96
+ // 检测交点
97
+ const intersects = raycaster.intersectObjects(scene.children);
98
+
99
+ if (intersects.length > 0) {
100
+ const selectedObject = intersects[0].object;
101
+ // 将选中的对象放入 OutlinePass 的选中列表
102
+ outlinePass.selectedObjects = [selectedObject];
103
+ } else {
104
+ // 点击空白处取消高亮
105
+ outlinePass.selectedObjects = [];
106
+ }
107
+ });
108
+
109
+ // --- 5. 渲染循环 ---
110
+ function animate() {
111
+ requestAnimationFrame(animate);
112
+ controls.update();
113
+ // 注意:使用后期处理后,需调用 composer.render() 而非 renderer.render()
114
+ composer.render();
115
+ }
116
+
117
+ window.addEventListener('resize', () => {
118
+ camera.aspect = window.innerWidth / window.innerHeight;
119
+ camera.updateProjectionMatrix();
120
+ renderer.setSize(window.innerWidth, window.innerHeight);
121
+ composer.setSize(window.innerWidth, window.innerHeight);
122
+ });
123
+
124
+ animate();
125
+ </script>
126
+ </body>
127
+ </html>
@@ -0,0 +1,187 @@
1
+ <template>
2
+ <div id="three-box"></div>
3
+ </template>
4
+ <script>
5
+ import CameraControls from 'camera-controls';
6
+ import { TilesRenderer } from '3d-tiles-renderer';
7
+ import { RoomEnvironment } from 'three/examples/jsm/environments/RoomEnvironment.js';
8
+ var instructions, renderer, cameraControls, scene, camera, renderTarget, tilesRenderer
9
+ var timeStamp = 0, animateId, singleFrameTime = 1 / 30, frameCount = 0
10
+ var needsRerender = false
11
+ var [fpsClock, timeStamp] = (function* (v) {
12
+ while (true) yield v;
13
+ })(0);
14
+ export default {
15
+ name: 'FlTiles',
16
+ data() {
17
+ return {
18
+ }
19
+ },
20
+ created() {
21
+ CameraControls.install({ THREE: this.THREE });
22
+ fpsClock = new this.THREE.Clock();
23
+ renderTarget = new this.THREE.WebGLRenderTarget(window.innerWidth, window.innerHeight, {
24
+ minFilter: this.THREE.LinearFilter,
25
+ magFilter: this.THREE.LinearFilter,
26
+ format: this.THREE.RGBAFormat,
27
+ stencilBuffer: true,
28
+ });
29
+ },
30
+ mounted() {
31
+ instructions = document.getElementById('three-box');
32
+ this.initRender();
33
+ this.initScene();
34
+ this.initCamera();
35
+ this.initControl();
36
+ this.initLight();
37
+ this.init3tiles();
38
+ cameraControls.addEventListener('wake', () => {
39
+ console.log(cameraControls._camera.position)
40
+ })
41
+ this.animate();
42
+ },
43
+ methods: {
44
+ initRender() {
45
+ renderer = new this.THREE.WebGLRenderer({
46
+ antialias: true,
47
+ logarithmicDepthBuffer: true,
48
+ });
49
+ renderer.setPixelRatio(window.devicePixelRatio * 2);
50
+ renderer.setSize(window.innerWidth, window.innerHeight);
51
+ renderer.domElement.id = 'three-model';
52
+ renderer.shadowMap.enabled = true;
53
+ instructions.appendChild(renderer.domElement);
54
+ },
55
+ initScene() {
56
+ scene = new this.THREE.Scene();
57
+ scene.background = new this.THREE.Color(0xffffff)
58
+ },
59
+ initCamera() {
60
+ camera = new this.THREE.PerspectiveCamera(
61
+ 45,
62
+ window.innerWidth / window.innerHeight,
63
+ 0.1,
64
+ 10000000
65
+ );
66
+ camera.position.set(
67
+ 0,
68
+ 10,
69
+ 105.524230957031
70
+ );
71
+ },
72
+ initControl() {
73
+ // 初始化控件
74
+ cameraControls = new CameraControls(camera, renderer.domElement);
75
+ cameraControls.dollyToCursor = true;
76
+ cameraControls.smoothTime = 0.1;
77
+ cameraControls.draggingSmoothTime = 0.05;
78
+ cameraControls.truckSpeed = 2.0;
79
+ cameraControls.infinityDolly = true;
80
+ cameraControls.minDistance = 4;
81
+ },
82
+ // 初始化光源
83
+ initLight() {
84
+ const pmremGenerator = new this.THREE.PMREMGenerator(renderer);
85
+ scene.environment = pmremGenerator.fromScene(new RoomEnvironment(), 0.04).texture;
86
+ },
87
+ init3tiles() {
88
+ // 这个文件需要放到public文件夹下 Tile_+003_+003/Tile_+003_+003
89
+ tilesRenderer = new TilesRenderer('/tiles/tileset.json')
90
+ tilesRenderer.setCamera(camera);
91
+ tilesRenderer.setResolutionFromRenderer(camera, renderer)
92
+ scene.add(tilesRenderer.group);
93
+
94
+ setTimeout(() => (
95
+ needsRerender = true
96
+ ), 1000)
97
+ },
98
+ // 添加一个标志,等待一帧让TilesRenderer初步解析JSON
99
+ debugTileset() {
100
+ if (tilesRenderer.root && frameCount++ < 10) { // 检查前几帧
101
+ console.log('[调试] TilesRenderer根节点:', tilesRenderer.root);
102
+
103
+ // 1. 尝试获取根节点的包围盒 (通常很大,是整个世界范围)
104
+ if (tilesRenderer.root.boundingVolume) {
105
+ console.log('[调试] 根节点包围盒:', tilesRenderer.root.boundingVolume);
106
+ // 如果是BOX类型,可以获取其中心
107
+ if (tilesRenderer.root.boundingVolume.box) {
108
+ const box = tilesRenderer.root.boundingVolume.box;
109
+ // box: [中心x, 中心y, 中心z, x轴半长, y轴半长, z轴半长]
110
+ console.log(`[调试] 根节点包围盒中心: [${box[0]}, ${box[1]}, ${box[2]}]`);
111
+ }
112
+ }
113
+
114
+ // 2. 检查第一个子节点(中间节点)
115
+ if (tilesRenderer.root.children && tilesRenderer.root.children[0]) {
116
+ const firstChild = tilesRenderer.root.children[0];
117
+ console.log('[调试] 第一个子节点:', firstChild);
118
+ if (firstChild.boundingVolume) {
119
+ console.log('[调试] 第一个子节点包围盒:', firstChild.boundingVolume);
120
+ }
121
+ }
122
+
123
+ // 3. 如果中间节点还有children,继续往下找
124
+ if (tilesRenderer.root.children &&
125
+ tilesRenderer.root.children[0] &&
126
+ tilesRenderer.root.children[0].children &&
127
+ tilesRenderer.root.children[0].children[0]) {
128
+ const deepestChild = tilesRenderer.root.children[0].children[0];
129
+ console.log('[调试] 更深层的子节点:', deepestChild);
130
+ if (deepestChild.content) {
131
+ console.log('[调试] 首个Content URI:', deepestChild.content.uri);
132
+ }
133
+ }
134
+ }
135
+ },
136
+ animate() {
137
+ // const delta = fpsClock.getDelta();
138
+ // timeStamp += delta;
139
+ // requestAnimationFrame(this.animate)
140
+ // if (needsRerender) {
141
+
142
+ // needsRerender = false;
143
+ // let box = new this.THREE.Box3()
144
+
145
+ // if (tilesRenderer.getBoundingBox(box)) {
146
+
147
+ // box.getCenter(tilesRenderer.group.position);
148
+
149
+ // tilesRenderer.group.position.multiplyScalar(-1);
150
+
151
+ // }
152
+
153
+ // tilesRenderer.update();
154
+
155
+ // }
156
+ // cameraControls.update(timeStamp)
157
+ // tilesRenderer.update();
158
+ // renderer.render(scene, camera);
159
+ const delta = fpsClock.getDelta();
160
+ timeStamp += delta;
161
+ animateId = requestAnimationFrame(this.animate);
162
+ if (timeStamp > singleFrameTime) {
163
+ if (needsRerender) {
164
+ needsRerender = false
165
+ let box = new this.THREE.Box3()
166
+ if (tilesRenderer.getBoundingBox(box)) {
167
+ box.getCenter(tilesRenderer.group.position)
168
+ tilesRenderer.group.position.multiplyScalar(-1)
169
+ }
170
+ }
171
+ cameraControls.update(timeStamp);
172
+ // this.debugTileset();
173
+ tilesRenderer.update();
174
+ renderer.render(scene, camera);
175
+ timeStamp = timeStamp % singleFrameTime;
176
+ }
177
+ },
178
+ }
179
+ }
180
+ </script>
181
+ <style lang="scss" scoped>
182
+ #three-box{
183
+ width: 100%;
184
+ height: 100%;
185
+ overflow: hidden;
186
+ }
187
+ </style>