renderModel.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638
  1. import * as THREE from 'three'; //导入整个 three.js核心库
  2. import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'; //导入控制器模块,轨道控制器
  3. import { CSS3DRenderer, CSS3DSprite } from 'three/examples/jsm/renderers/CSS3DRenderer.js';
  4. import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
  5. import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
  6. // 用于模型边缘高亮
  7. import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';
  8. import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js';
  9. import { OutlinePass } from 'three/examples/jsm/postprocessing/OutlinePass.js';
  10. import { UnrealBloomPass } from 'three/examples/jsm/postprocessing/UnrealBloomPass.js';
  11. import * as TWEEN from '@tweenjs/tween.js';
  12. import { getBottomMaterial } from './material.js';
  13. import GltfModelManager from '@/views/largeScreen/three/GltfModelManager';
  14. import ComponentHandle from '@/utils/ComponentHandle';
  15. import usageReal from '@/views/largeScreen/dialog/usage-real.vue';
  16. const dracoLoader = new DRACOLoader();
  17. // 设置draco路径
  18. dracoLoader.setDecoderPath('/draco/');
  19. dracoLoader.setDecoderConfig({ type: 'js' });
  20. // 定义一个 class类
  21. class renderModel {
  22. constructor(selector) {
  23. this.container = document.querySelector(selector);
  24. // 相机
  25. this.camera;
  26. // 场景
  27. this.scene;
  28. //渲染器
  29. this.renderer;
  30. // 控制器
  31. this.controls;
  32. // 3d文字渲染器
  33. this.css3DRenderer = null;
  34. // 3d文字控制器
  35. this.css3dControls = null;
  36. // 模型
  37. this.model;
  38. // 室内模型1
  39. this.model1;
  40. // 室内模型2
  41. this.model2;
  42. // 环境光
  43. this.ambientLight;
  44. //模型平面
  45. this.planeGeometry;
  46. this.outlineObjs = [];
  47. this.sharedComposer = null;
  48. this.sharedOutlinePass = null;
  49. this.sharedBloomPass = null;
  50. this.isComposerInitialized = false;
  51. this.raycaster = new THREE.Raycaster();
  52. this.mouse = new THREE.Vector2();
  53. this.autoRotate = true;
  54. this.labels = [];
  55. this.roomLabelGroup = null;
  56. }
  57. controlRotate(bool) {
  58. this.autoRotate = bool;
  59. if (!bool) {
  60. this.model.rotation.z = 0;
  61. }
  62. }
  63. // 初始化共享后处理系统
  64. initSharedPostProcessing() {
  65. if (this.isComposerInitialized) return;
  66. // 创建共享的EffectComposer
  67. this.sharedComposer = new EffectComposer(this.renderer);
  68. this.sharedComposer.renderTarget1.texture.outputColorSpace = THREE.sRGBEncoding;
  69. this.sharedComposer.renderTarget2.texture.outputColorSpace = THREE.sRGBEncoding;
  70. this.sharedComposer.renderTarget1.texture.encoding = THREE.sRGBEncoding;
  71. this.sharedComposer.renderTarget2.texture.encoding = THREE.sRGBEncoding;
  72. // 添加基础渲染通道
  73. const renderPass = new RenderPass(this.scene, this.camera);
  74. this.sharedComposer.addPass(renderPass);
  75. // 创建共享的OutlinePass
  76. this.sharedOutlinePass = new OutlinePass(new THREE.Vector2(window.innerWidth, window.innerHeight), this.scene, this.camera);
  77. // 设置轮廓效果参数
  78. this.sharedOutlinePass.edgeStrength = 5.0; // 边框的亮度
  79. this.sharedOutlinePass.edgeGlow = 0.3; // 光晕[0,1]
  80. this.sharedOutlinePass.edgeThickness = 1.0; // 边框宽度
  81. // this.sharedOutlinePass.pulsePeriod = 3; // 呼吸闪烁的速度
  82. // this.sharedOutlinePass.visibleEdgeColor.set(0x00ff00); // 呼吸显示的颜色
  83. // this.sharedOutlinePass.hiddenEdgeColor.set(0x000000); // 呼吸消失的颜色
  84. // this.sharedOutlinePass.clear = true;
  85. this.sharedComposer.addPass(this.sharedOutlinePass);
  86. // 创建共享的UnrealBloomPass
  87. this.sharedBloomPass = new UnrealBloomPass();
  88. this.sharedBloomPass.strength = 0.1;
  89. this.sharedBloomPass.radius = 0;
  90. this.sharedBloomPass.threshold = 1;
  91. this.sharedComposer.addPass(this.sharedBloomPass);
  92. this.isComposerInitialized = true;
  93. // 监听窗口大小变化,更新后处理分辨率
  94. window.addEventListener('resize', () => {
  95. if (this.sharedOutlinePass) {
  96. this.sharedOutlinePass.resolution.set(window.innerWidth, window.innerHeight);
  97. }
  98. });
  99. }
  100. // 优化后的模型高亮方法
  101. outlineObj(selectedObjects) {
  102. // 初始化共享后处理系统
  103. this.initSharedPostProcessing();
  104. // 更新选中对象
  105. if (this.sharedOutlinePass) {
  106. this.sharedOutlinePass.selectedObjects = selectedObjects;
  107. // 如果之前有高亮对象,先清除
  108. if (this.outlineObjs.length > 0) {
  109. this.outlineObjs = [];
  110. }
  111. // 添加当前共享的composer到数组
  112. this.outlineObjs.push(this.sharedComposer);
  113. }
  114. }
  115. // 清理高亮效果
  116. clearHighlight() {
  117. if (this.sharedOutlinePass) {
  118. this.sharedOutlinePass.selectedObjects = [];
  119. this.outlineObjs = [];
  120. }
  121. }
  122. // 初始化加载模型方法
  123. init(loaded) {
  124. //初始化场景
  125. this.initScene();
  126. //初始化相机
  127. this.initCamera();
  128. //初始化渲染器
  129. this.initRender();
  130. // 创建灯光
  131. this.createLight();
  132. //初始化控制器,控制摄像头,控制器一定要在渲染器后
  133. this.initControls();
  134. this.setFBXModel(loaded);
  135. this.setRoomModel();
  136. //监听场景大小改变,跳转渲染尺寸
  137. window.addEventListener('resize', this.onWindowResizes.bind(this));
  138. //场景渲染
  139. this.sceneAnimation();
  140. // 添加鼠标点击事件
  141. window.addEventListener('click', this.onMouseClick.bind(this));
  142. }
  143. initGltfModel() {
  144. //初始化场景
  145. this.initScene();
  146. //初始化相机
  147. this.initCamera();
  148. //初始化渲染器
  149. this.initRender();
  150. // 创建灯光
  151. this.createLight();
  152. //初始化控制器,控制摄像头,控制器一定要在渲染器后
  153. this.initControls();
  154. this.setGlbModel();
  155. //监听场景大小改变,跳转渲染尺寸
  156. window.addEventListener('resize', this.onWindowResizes.bind(this));
  157. //场景渲染
  158. this.sceneAnimation();
  159. // 添加鼠标点击事件
  160. window.addEventListener('click', this.onMouseClick.bind(this));
  161. }
  162. onMouseClick(event) {
  163. // 计算鼠标在标准化设备坐标中的位置 (-1 to +1)
  164. this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
  165. this.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
  166. // 更新射线
  167. this.raycaster.setFromCamera(this.mouse, this.camera);
  168. // 计算射线与场景中物体的交点
  169. const intersects = this.raycaster.intersectObjects(this.scene.children, true);
  170. if (intersects.length > 0) {
  171. const point = intersects[0].point;
  172. const object = intersects[0].object;
  173. console.log(`Clicked on object: ${object.name} at (${point.x}, ${point.y}, ${point.z})`);
  174. // 示例:点击时高亮对象
  175. // this.outlineObj([object]);
  176. }
  177. }
  178. //创建场景
  179. initScene() {
  180. this.scene = new THREE.Scene();
  181. this.scene.background = new THREE.Color(0x000000);
  182. //底部平面
  183. const geometry = new THREE.PlaneGeometry(1, 1);
  184. // 创建材质,可以设置贴图
  185. const material = getBottomMaterial(
  186. '#ffffff', // 设置模型的颜色
  187. require('@/assets/images/models/bg.png'),
  188. 1,
  189. 5
  190. );
  191. // 创建平面网格
  192. const plane = new THREE.Mesh(geometry, material);
  193. plane.scale.set(1000, 1000, 1000);
  194. plane.position.y = 0;
  195. const plane2 = plane.clone();
  196. plane2.material = getBottomMaterial(
  197. '#000000', // 设置模型的颜色
  198. require('@/assets/images/models/bg.png'),
  199. 2,
  200. 5
  201. );
  202. this.scene.add(plane2);
  203. this.scene.add(plane);
  204. }
  205. // 创建相机
  206. initCamera() {
  207. const { clientHeight, clientWidth } = this.container;
  208. this.camera = new THREE.PerspectiveCamera(18, clientWidth / clientHeight, 0.1, 10000);
  209. }
  210. // 创建渲染器
  211. initRender() {
  212. this.renderer = new THREE.WebGLRenderer({
  213. logarithmicDepthBuffer: true,
  214. antialias: true, // true/false表示是否开启反锯齿
  215. alpha: true, // true/false 表示是否可以设置背景色透明
  216. // precision: 'mediump', // highp/mediump/lowp 表示着色精度选择
  217. // premultipliedAlpha: true // true/false 表示是否可以设置像素深度(用来度量图像的分辨率)
  218. // preserveDrawingBuffer: false, // true/false 表示是否保存绘图缓冲
  219. // physicallyCorrectLights: true, // true/false 表示是否开启物理光照
  220. });
  221. //设置屏幕像素比
  222. this.renderer.setPixelRatio(window.devicePixelRatio);
  223. //渲染的尺寸大小
  224. const { clientHeight, clientWidth } = this.container;
  225. this.renderer.setSize(clientWidth, clientHeight);
  226. this.renderer.toneMapping = Number(THREE.LinearToneMapping);
  227. this.renderer.toneMappingExposure = Math.pow(2, 0.0);
  228. this.container.appendChild(this.renderer.domElement);
  229. // 创建一个CSS3渲染器CSS3DRenderer
  230. this.css3DRenderer = new CSS3DRenderer();
  231. this.css3DRenderer.setSize(clientWidth, clientHeight);
  232. // HTML标签<div id="tag"></div>外面父元素叠加到canvas画布上且重合
  233. this.css3DRenderer.domElement.style.position = 'absolute';
  234. this.css3DRenderer.domElement.style.top = '0px';
  235. this.css3DRenderer.domElement.style.zIndex = '9999';
  236. //设置.pointerEvents=none,解决HTML元素标签对threejs canvas画布鼠标事件的遮挡
  237. this.css3DRenderer.domElement.style.pointerEvents = 'none';
  238. this.css3DRenderer.domElement.style.className = 'css3DRenderer';
  239. this.container.appendChild(this.css3DRenderer.domElement);
  240. }
  241. // 创建光源
  242. createLight() {
  243. const light1 = new THREE.AmbientLight('#FFFFFF', 1);
  244. this.scene.add(light1);
  245. const light2 = new THREE.DirectionalLight('#FFFFFF', 0.8 * Math.PI);
  246. light2.position.set(0.5, 0, 0.866); // ~60º
  247. this.scene.add(light2);
  248. }
  249. initControls() {
  250. this.controls = new OrbitControls(this.camera, this.renderer.domElement);
  251. this.controls.maxPolarAngle = Math.PI / 1.1;
  252. this.controls.minPolarAngle = Math.PI / 2;
  253. this.controls.minAzimuthAngle = 0;
  254. this.controls.maxAzimuthAngle = 0;
  255. this.controls.maxDistance = 2000;
  256. //标签控制器
  257. this.css3dControls = new OrbitControls(this.camera, this.css3DRenderer.domElement);
  258. this.css3dControls.enablePan = false;
  259. this.css3dControls.enableDamping = true;
  260. this.controls.mouseButtons = {
  261. LEFT: THREE.MOUSE.PAN,
  262. MIDDLE: THREE.MOUSE.DOLLY,
  263. RIGHT: THREE.MOUSE.ROTATE
  264. };
  265. }
  266. animate() {
  267. this.renderer.render(this.scene, this.camera);
  268. this.controls.update();
  269. this.css3DRenderer.render(this.scene, this.camera);
  270. TWEEN.update();
  271. if (this.model && this.autoRotate) {
  272. this.model.rotation.z += 0.001; // 每帧绕y轴旋转0.01弧度
  273. }
  274. }
  275. // 使用动画器不断更新场景
  276. sceneAnimation() {
  277. this.renderer.setAnimationLoop(this.animate.bind(this));
  278. }
  279. flyTo(target, scale = 1, duration = 1000) {
  280. const targetPosition = new THREE.Vector3().copy(target.position);
  281. const targetFocus = new THREE.Vector3().copy(target.targetContent);
  282. // 移动相机位置
  283. new TWEEN.Tween(this.camera.position)
  284. .to(targetPosition, target.time || 500)
  285. .easing(TWEEN.Easing.Quadratic.InOut)
  286. .onUpdate(() => {
  287. this.controls.update(); // 更新控制器
  288. })
  289. .start();
  290. // 移动控制器目标
  291. new TWEEN.Tween(this.controls.target)
  292. .to(targetFocus, target.time || 500)
  293. .easing(TWEEN.Easing.Quadratic.InOut)
  294. .onUpdate(() => {
  295. this.controls.update(); // 更新控制器
  296. })
  297. .start();
  298. new TWEEN.Tween(this.model.scale)
  299. .to(
  300. {
  301. x: scale,
  302. y: scale,
  303. z: scale
  304. },
  305. duration
  306. )
  307. .easing(TWEEN.Easing.Quadratic.InOut)
  308. .start();
  309. }
  310. tag3D(name) {
  311. // 创建div元素(作为标签)
  312. let div = document.createElement('div');
  313. div.innerHTML = name;
  314. div.classList.add('tag3d');
  315. //div元素包装为CSS3模型对象CSS3DObject
  316. let label = new CSS3DSprite(div);
  317. div.style.pointerEvents = 'none'; //避免HTML标签遮挡三维场景的鼠标事件
  318. //缩放CSS3DObject模型对象
  319. label.scale.set(0.15, 0.15, 0.15); //根据相机渲染范围控制HTML 3D标签尺寸
  320. label.rotateY(Math.PI / 2); //控制HTML标签CSS3对象姿态角度
  321. return label; //返回CSS3模型标签
  322. }
  323. createDialog(html) {
  324. //div元素包装为CSS3模型对象CSS3DSprite
  325. const element = document.createElement('div');
  326. element.className = 'customDialog';
  327. element.appendChild(html);
  328. const dialog = new CSS3DSprite(element);
  329. element.style.pointerEvents = 'none'; //避免HTML标签遮挡三维场景的鼠标事件
  330. // 设置HTML元素标签在three.js世界坐标中位置
  331. // label.position.set(x, y, z);
  332. //缩放CSS3DSprite模型对象
  333. dialog.scale.set(0.15, 0.15, 0.15); //根据相机渲染范围控制HTML 3D标签尺寸
  334. dialog.rotateY(Math.PI / 2); //控制HTML标签CSS3对象姿态角度
  335. return dialog; //返回CSS3模型标签
  336. }
  337. /**
  338. * 设置加载模型居中
  339. * {Object} object 模型对象
  340. */
  341. setModelPosition(object) {
  342. object.updateMatrixWorld();
  343. // 获得包围盒得min和max
  344. const box = new THREE.Box3().setFromObject(object);
  345. // 返回包围盒的中心点
  346. const center = box.getCenter(new THREE.Vector3());
  347. object.position.x += object.position.x - center.x;
  348. object.position.y += object.position.y - center.y;
  349. object.position.z = 0;
  350. }
  351. setGlbModel() {
  352. new GltfModelManager().loadModel('model-road-pv', '/models/model-road-pv.glb').then(object => {
  353. this.calcMeshCenter(object.scene);
  354. this.model = object.scene;
  355. const selectedObjects = [];
  356. object.scene.traverse(function (child) {
  357. if (child.isMesh) {
  358. child.material.emissive = child.material.color;
  359. child.material.emissiveMap = child.material.map;
  360. }
  361. // console.log(child.name)
  362. // if (["solar_cell097001", "solar_cell097002", "solar_cell097003", "solar_cell097004"].includes(child.name)) {
  363. // selectedObjects.push(child);
  364. // }
  365. });
  366. // 设置相机位置
  367. this.camera.position.set(-20, -652, 500);
  368. this.controls.target.set(-20, -20, 0);
  369. this.model.scale.set(2, 2, 2);
  370. // 设置相机坐标系
  371. this.camera.lookAt(0, 0, 0);
  372. // 将模型添加到场景中去
  373. this.scene.add(this.model);
  374. // 高亮收集的对象
  375. // if (selectedObjects.length > 0) {
  376. // this.outlineObj(selectedObjects);
  377. // }
  378. });
  379. }
  380. setFBXModel(loaded) {
  381. var fbxLoader = new GLTFLoader();
  382. fbxLoader.load('/models/main.glb', object => {
  383. this.calcMeshCenter(object.scene);
  384. this.model = object.scene;
  385. object.scene.traverse(function (child) {
  386. if (child.isMesh) {
  387. child.material.emissive = child.material.color;
  388. child.material.emissiveMap = child.material.map;
  389. }
  390. });
  391. // 设置相机位置
  392. this.camera.position.set(-17.701772776272723, -728.8405392761424, 374.20604159261217);
  393. this.controls.target.set(-17.701772776272723, -307.6331500346563, 148.44436360369687);
  394. this.model.scale.set(0.8, 0.8, 0.8);
  395. // 设置相机坐标系
  396. this.camera.lookAt(0, 0, 0);
  397. // 将模型添加到场景中去
  398. this.scene.add(this.model);
  399. loaded&&loaded()
  400. });
  401. }
  402. setModelDisplay(type, areaInfo) {
  403. if (type == 'model1' || type == 'model2') {
  404. this.camera.position.set(2.9287885309866817, -403.9781890137868, 376.33849737114133);
  405. this.controls.target.set(-22.9298060627659623, 15.311141772539067, 64.637911374941055);
  406. const targetFocus = new THREE.Vector3(-22.9298060627659623, 15.311141772539067, 24.637911374941055);
  407. new TWEEN.Tween(this.controls.target)
  408. .to(targetFocus, 500)
  409. .easing(TWEEN.Easing.Quadratic.InOut)
  410. .onUpdate(() => {
  411. this.controls.update(); // 更新控制器
  412. })
  413. .start();
  414. this.addRoomDialog(
  415. type,
  416. ComponentHandle.createComponent({
  417. component: usageReal,
  418. props: {
  419. areaCode: areaInfo.value
  420. }
  421. }),
  422. `${areaInfo.name}${type == 'model1' ? '负一楼水泵' : '室内主机'}`,
  423. type == 'model1'
  424. ? {
  425. x: -3,
  426. y: 8,
  427. z: 0
  428. }
  429. : {
  430. x: -3,
  431. y: 3,
  432. z: 0
  433. }
  434. );
  435. }
  436. if (type == 'model1') {
  437. new TWEEN.Tween(this.model1.scale)
  438. .to(
  439. {
  440. x: 10,
  441. y: 10,
  442. z: 10
  443. },
  444. 500
  445. )
  446. .easing(TWEEN.Easing.Quadratic.InOut)
  447. .start();
  448. }
  449. if (type == 'model2') {
  450. new TWEEN.Tween(this.model2.scale)
  451. .to(
  452. {
  453. x: 15,
  454. y: 15,
  455. z: 15
  456. },
  457. 500
  458. )
  459. .easing(TWEEN.Easing.Quadratic.InOut)
  460. .start();
  461. }
  462. }
  463. modelChange(type, areaInfo) {
  464. if (this.roomLabelGroup) {
  465. this.model1.remove(this.roomLabelGroup);
  466. this.model2.remove(this.roomLabelGroup);
  467. this.roomLabelGroup.clear();
  468. }
  469. this.setModelDisplay(type, areaInfo);
  470. if (type == 'model') {
  471. this.model1.visible = false;
  472. this.model2.visible = false;
  473. this.model.visible = true;
  474. this.labels.forEach(item => {
  475. item.visible = true;
  476. });
  477. } else if (type == 'model1') {
  478. this.model.visible = false;
  479. this.labels.forEach(item => {
  480. item.visible = false;
  481. });
  482. this.model2.visible = false;
  483. this.model1.visible = true;
  484. } else if (type == 'model2') {
  485. this.model.visible = false;
  486. this.labels.forEach(item => {
  487. item.visible = false;
  488. });
  489. this.model1.visible = false;
  490. this.model2.visible = true;
  491. }
  492. }
  493. setRoomModel() {
  494. const loader = new GLTFLoader();
  495. loader.setDRACOLoader(dracoLoader);
  496. loader.load('/models/model1.glb', object => {
  497. this.calcMeshCenter(object.scene);
  498. const perfectMaterial = new THREE.MeshStandardMaterial({
  499. color: 0xcccccc, //
  500. side: THREE.DoubleSide // 防止面片反向不可见
  501. });
  502. this.model1 = object.scene;
  503. this.model1.scale.set(10, 10, 10);
  504. this.model1.rotation.x = Math.PI / 2;
  505. this.model1.rotation.y = Math.PI / 6;
  506. // 将模型添加到场景中去
  507. this.model1.visible = false;
  508. this.scene.add(this.model1);
  509. });
  510. loader.load('/models/model2.glb', object => {
  511. this.calcMeshCenter(object.scene);
  512. object.scene.traverse(child => {
  513. if (child.isMesh) {
  514. if (child.name == '平面' || child.name == '平面001') {
  515. child.material = new THREE.MeshStandardMaterial({
  516. color: '#B0C4E9',
  517. side: THREE.DoubleSide
  518. });
  519. }
  520. child.material.needsUpdate = true;
  521. }
  522. });
  523. this.model2 = object.scene;
  524. this.model2.scale.set(15, 15, 15);
  525. this.model2.rotation.x = Math.PI / 2;
  526. this.model2.rotation.y = -Math.PI / 3;
  527. // 将模型添加到场景中去
  528. this.model2.visible = false;
  529. this.scene.add(this.model2);
  530. });
  531. }
  532. onWindowResizes() {
  533. if (!this.container) return false;
  534. const { clientHeight, clientWidth } = this.container;
  535. //调整屏幕大小
  536. this.camera.aspect = clientWidth / clientHeight; // 摄像机宽高比例
  537. this.camera.updateProjectionMatrix(); //相机更新矩阵,将3d内容投射到2d面上转换
  538. this.renderer.setSize(clientWidth, clientHeight);
  539. this.css3DRenderer.setSize(clientWidth, clientHeight);
  540. }
  541. calcMeshCenter(group) {
  542. /**
  543. * 包围盒全自动计算:模型整体居中
  544. */
  545. var box3 = new THREE.Box3();
  546. // 计算层级模型group的包围盒
  547. // 模型group是加载一个三维模型返回的对象,包含多个网格模型
  548. box3.expandByObject(group);
  549. // 计算一个层级模型对应包围盒的几何体中心在世界坐标中的位置
  550. var center = new THREE.Vector3();
  551. box3.getCenter(center);
  552. // 重新设置模型的位置,使之居中。
  553. group.position.x = 0;
  554. group.position.y = 0;
  555. group.position.z = 17;
  556. }
  557. addRoomDialog(modelType, html, labelName, position) {
  558. const { x, y, z } = position;
  559. if (this.roomLabelGroup && this[modelType]) {
  560. this[modelType].remove(this.roomLabelGroup);
  561. this.roomLabelGroup.clear();
  562. }
  563. const label3D = this.tag3D(labelName);
  564. const dialog3D = this.createDialog(html);
  565. if (modelType == 'model1') {
  566. label3D.scale.set(0.015, 0.015, 0.015);
  567. dialog3D.scale.set(0.015, 0.015, 0.15);
  568. label3D.position.set(x - 0.2, y - 1.8, z);
  569. } else {
  570. label3D.position.set(x - 0.2, y - 1.5, z);
  571. label3D.scale.set(0.011, 0.011, 0.011);
  572. dialog3D.scale.set(0.011, 0.011, 0.11);
  573. }
  574. // 3. 创建一个组来容纳它们
  575. const group = new THREE.Group();
  576. dialog3D.position.set(x, y, z);
  577. group.add(label3D);
  578. group.add(dialog3D);
  579. this[modelType].add(group);
  580. this.roomLabelGroup = group; // 保存引用
  581. }
  582. addDialog(html, labelName, position) {
  583. const { x, y, z } = position;
  584. const label3D = this.tag3D(labelName);
  585. const dialog3D = this.createDialog(html); //设置标签名称
  586. label3D.position.y += y;
  587. label3D.position.x += x;
  588. label3D.position.z += z;
  589. dialog3D.position.copy(label3D.position);
  590. dialog3D.position.z = z + 20;
  591. this.model && this.model.add(label3D);
  592. this.model && this.model.add(dialog3D);
  593. this.labels.push(label3D);
  594. this.labels.push(dialog3D);
  595. }
  596. }
  597. export default renderModel;