fanLocal.three.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. import * as THREE from 'three';
  2. import { animateCamera, getTextCanvas, renderVideo, drawLine } from '/@/utils/threejs/util';
  3. import UseThree from '../../../../hooks/core/threejs/useThree';
  4. import Fly from '/@/utils/threejs/FlyLine1';
  5. import fcFan from './fcfanLocal.three';
  6. import fmFan from './fmfanLocal.three';
  7. import { types } from "util";
  8. import num from "/@/views/demo/charts/Num.vue";
  9. const modelName = 'jbfj_hd';
  10. // 模型对象、 文字对象
  11. let model, group, fcFanObj, fmFanObj, player1, fanType, flyArr = [];
  12. // 打灯光
  13. const addLight = (scene) => {
  14. // const pointLight2 = new THREE.PointLight(0xffeeee, 1.5, 100);
  15. // pointLight2.position.set(-120, 16, -33);
  16. // pointLight2.shadow.bias = 0.05;
  17. // scene.add(pointLight2);
  18. //
  19. // const pointLight3 = new THREE.PointLight(0xffffff, 1, 40);
  20. // pointLight3.position.set(-66, 40, 1);
  21. // pointLight3.shadow.bias = 0.05;
  22. // scene.add(pointLight3);
  23. //
  24. // const pointLight4 = new THREE.PointLight(0xffeeee, 0.6, 230);
  25. // pointLight4.position.set(-18, 30, 12);
  26. // pointLight4.shadow.bias = 0.05;
  27. // scene.add(pointLight4);
  28. //
  29. // const pointLight5 = new THREE.PointLight(0xffffff, 0.8, 90);
  30. // pointLight5.position.set(-57, 7, -30);
  31. // pointLight5.shadow.bias = 0.05;
  32. // scene.add(pointLight5);
  33. //
  34. // const pointLight6 = new THREE.PointLight(0xffffff, 0.8, 270);
  35. // pointLight6.position.set(72, -33, 11.4);
  36. // pointLight6.shadow.bias = 0.05;
  37. // scene.add(pointLight6);
  38. const pointLight7 = new THREE.PointLight(0xffffff, 0.8, 500);
  39. pointLight7.position.set(-20, -43, 12);
  40. pointLight7.shadow.bias = -0.05;
  41. scene.add(pointLight7);
  42. const spotLight = new THREE.SpotLight();
  43. spotLight.angle = Math.PI / 16;
  44. spotLight.penumbra = 0;
  45. spotLight.castShadow = true;
  46. spotLight.position.set(0, 463, 687);
  47. scene.add(spotLight);
  48. spotLight.shadow.camera.near = 0.5; // default
  49. spotLight.shadow.camera.far = 1000; // default
  50. spotLight.shadow.focus = 1;
  51. spotLight.shadow.bias = -0.000002;
  52. // gui.add(pointLight6.position, 'x', -200, 200);
  53. // gui.add(pointLight6.position, 'y', -200, 200);
  54. // gui.add(pointLight6.position, 'z', -200, 200);
  55. // gui.add(pointLight6, 'distance', 0, 500);
  56. };
  57. // 重置摄像头
  58. const resetCamera = () => {
  59. model.camera.position.set(0, 30, 80);
  60. model.orbitControls?.update();
  61. model.camera.updateProjectionMatrix();
  62. };
  63. // 设置模型位置
  64. const setModalPosition = () => {
  65. group.position.set(0, 18, -50);
  66. group.rotation.y = Math.PI / 2;
  67. };
  68. // 切换局部通风机类型
  69. export const setModelType = (type) => {
  70. fanType = type;
  71. model.camera.position.set(500, 500, 500);
  72. return new Promise((resolve) => {
  73. // 显示双道风窗
  74. if (fanType === 'fm') {
  75. if (group.getObjectByName('jbfj_fc')) {
  76. group.remove(fcFanObj.group);
  77. }
  78. setTimeout(() => {
  79. resolve(null);
  80. const position = { x: 0, y: 0, z: 0 };
  81. animateCamera({ x: 500, y: 500, z: 500 }, { x: 0, y: 0, z: 0 }, { x: 0, y: 30, z: 80 }, { x: position.x, y: position.y, z: position.z }, model);
  82. group.add(fmFanObj.group);
  83. }, 300);
  84. } else if (fanType === 'fc') {
  85. // 显示单道风窗
  86. if (group.getObjectByName('jbfj_fm')) {
  87. group.remove(fmFanObj.group);
  88. }
  89. setTimeout(() => {
  90. resolve(null);
  91. const position = { x: 0, y: 0, z: 0 };
  92. animateCamera({ x: 500, y: 500, z: 500 }, { x: 0, y: 0, z: 0 }, { x: 0, y: 30, z: 80 }, { x: position.x, y: position.y, z: position.z }, model);
  93. group.add(fcFanObj.group);
  94. }, 300);
  95. }
  96. });
  97. };
  98. const setControls = () => {
  99. model.orbitControls.panSpeed = 0.5;
  100. model.orbitControls.rotateSpeed = 0.5;
  101. model.orbitControls.maxPolarAngle = Math.PI / 2.4;
  102. model.orbitControls.minPolarAngle = Math.PI / 3;
  103. };
  104. export const addFmText = (selectData) => {
  105. if (!group) {
  106. return;
  107. }
  108. const textArr = [
  109. {
  110. text: `煤矿巷道远程风门系统`,
  111. font: 'normal 2.2rem Arial',
  112. color: '#009900',
  113. strokeStyle: '#002200',
  114. x: 80,
  115. y: 95,
  116. },
  117. {
  118. text: `压力(Pa):`,
  119. font: 'normal 30px Arial',
  120. color: '#009900',
  121. strokeStyle: '#002200',
  122. x: 0,
  123. y: 155,
  124. },
  125. {
  126. text: `${selectData.frontRearDP}`,
  127. font: 'normal 30px Arial',
  128. color: '#009900',
  129. strokeStyle: '#002200',
  130. x: 290,
  131. y: 155,
  132. },
  133. {
  134. text: `动力源压力(MPa): `,
  135. font: 'normal 30px Arial',
  136. color: '#009900',
  137. strokeStyle: '#002200',
  138. x: 0,
  139. y: 215,
  140. },
  141. {
  142. text: ` ${selectData.sourcePressure}`,
  143. font: 'normal 30px Arial',
  144. color: '#009900',
  145. strokeStyle: '#002200',
  146. x: 280,
  147. y: 215,
  148. },
  149. {
  150. text: `故障诊断:`,
  151. font: 'normal 30px Arial',
  152. color: '#009900',
  153. strokeStyle: '#002200',
  154. x: 0,
  155. y: 275,
  156. },
  157. {
  158. text: `${selectData.fault}`,
  159. font: 'normal 30px Arial',
  160. color: '#009900',
  161. strokeStyle: '#002200',
  162. x: 280,
  163. y: 275,
  164. },
  165. {
  166. text: `煤炭科学技术研究院有限公司研制`,
  167. font: 'normal 28px Arial',
  168. color: '#009900',
  169. strokeStyle: '#002200',
  170. x: 20,
  171. y: 325,
  172. },
  173. ];
  174. getTextCanvas(526, 346, textArr, '').then((canvas) => {
  175. const textMap = track(new THREE.CanvasTexture(canvas)); // 关键一步
  176. const textMaterial = track(
  177. new THREE.MeshBasicMaterial({
  178. // 关于材质并未讲解 实操即可熟悉 这里是漫反射类似纸张的材质,对应的就有高光类似金属的材质.
  179. map: textMap, // 设置纹理贴图
  180. transparent: true,
  181. side: THREE.FrontSide, // 这里是双面渲染的意思
  182. })
  183. );
  184. textMaterial.blending = THREE.CustomBlending;
  185. const monitorPlane = group.getObjectByName('monitorText');
  186. if (monitorPlane) {
  187. monitorPlane.material = textMaterial;
  188. } else {
  189. const planeGeometry = track(new THREE.PlaneGeometry(526, 346)); // 平面3维几何体PlaneGeometry
  190. const planeMesh = track(new THREE.Mesh(planeGeometry, textMaterial));
  191. planeMesh.name = 'monitorText';
  192. planeMesh.scale.set(0.002, 0.002, 0.002);
  193. planeMesh.position.set(-1.255, 0.09, -0.41);
  194. group.add(planeMesh);
  195. }
  196. });
  197. };
  198. const setFly = () => {
  199. // 路径
  200. const curve = new THREE.CatmullRomCurve3([
  201. // new THREE.Vector3(-90.860, 2.380, 4.854),
  202. new THREE.Vector3(-88.020, 2.360, 3.70),
  203. new THREE.Vector3(-85.872, 2.352, 3.672),
  204. new THREE.Vector3(-85.652, 2.342, 3.442),
  205. new THREE.Vector3(-85.669, 2.353, -0.002),
  206. new THREE.Vector3(-85.690, 2.367, -3.824),
  207. new THREE.Vector3(-85.650, 2.217, -4.154),
  208. new THREE.Vector3(-85.680, 1.067, -5.674),
  209. new THREE.Vector3(-85.680, 0.947, -6.024),
  210. new THREE.Vector3(-85.680, 0.947, -12.894),
  211. new THREE.Vector3(-85.450, 0.947, -12.894),
  212. new THREE.Vector3(85.000, 0.920, -12.910)
  213. ]);
  214. // 采样
  215. for(let i = 0; i < 80; i++){
  216. setTimeout(() => {
  217. const points: any = [];
  218. const mvPosition = {
  219. 'x' : (Math.random() * 2 - 1) * 0.01,
  220. 'y' : (Math.random() * 2 - 1) * 0.01,
  221. 'z' : (Math.random() * 2 - 1) * 0.01,
  222. }
  223. curve.getSpacedPoints(500).forEach((point) => {
  224. const { x, y, z } = point;
  225. // points.push([x + mvPosition.x, y + mvPosition.y, z + mvPosition.z]);
  226. points.push([x, y, z]);
  227. });
  228. const length = 50;
  229. const circle = i * 2 + 8;
  230. const color = '#ffffff';
  231. const opacity = 0.3;
  232. const size = 3;
  233. const fly = new Fly(points, length, circle, color, opacity, size);
  234. fly.move();
  235. group.add(fly.obj);
  236. flyArr.push(fly)
  237. }, i * 500)
  238. }
  239. const stopFly = () => {
  240. flyArr.forEach((fly) => {
  241. fly.stop()
  242. })
  243. }
  244. // 路径轨迹
  245. const material = new THREE.LineBasicMaterial({
  246. color: '#FFff00',
  247. });
  248. const geometry = new THREE.BufferGeometry().setFromPoints(curve.getSpacedPoints(5000));
  249. const line = new THREE.Line(geometry, material);
  250. model.scene.add(line);
  251. };
  252. export const mountedThree = (playerVal1) => {
  253. player1 = playerVal1;
  254. return new Promise((resolve) => {
  255. model = new UseThree('#fanLocal3D');
  256. model.setEnvMap('test1');
  257. model.renderer.toneMappingExposure = 0.8;
  258. model.setModel(modelName).then(async (gltf) => {
  259. group = gltf.scene;
  260. model.scene?.add(group);
  261. if (gltf.animations && gltf.animations.length > 0) {
  262. model.mixers = [];
  263. model.animations = [];
  264. gltf.animations.forEach((animation) => {
  265. const mixer = new THREE.AnimationMixer(group);
  266. model.mixers.push(mixer);
  267. model.animations.push(animation);
  268. });
  269. }
  270. addLight(model.scene);
  271. resetCamera();
  272. setModalPosition();
  273. setControls();
  274. fcFanObj = new fcFan(model);
  275. await fcFanObj.mountedThree();
  276. fmFanObj = new fmFan(model);
  277. await fmFanObj.mountedThree();
  278. model.animate();
  279. setFly();
  280. const videoPlayer1 = document.getElementById('jb-player1')?.getElementsByClassName('vjs-tech')[0];
  281. if (videoPlayer1) {
  282. const mesh = renderVideo(group, videoPlayer1, 'player1');
  283. mesh.scale.set(0.222, 0.190, 0.200);
  284. mesh.position.set(-84.870, 0.298, 24.760);
  285. mesh.rotation.y = -Math.PI/2
  286. group.add(mesh)
  287. }
  288. // const curve = new THREE.CatmullRomCurve3([
  289. // new THREE.Vector3(-90.860, 2.380, 25.854),
  290. // new THREE.Vector3(-91.020, 2.360, 4.220),
  291. // new THREE.Vector3(-85.872, 2.352, 3.672),
  292. // new THREE.Vector3(-85.652, 2.342, 3.442),
  293. // new THREE.Vector3(-85.669, 2.353, -0.002),
  294. // new THREE.Vector3(-85.690, 2.367, -3.824),
  295. // new THREE.Vector3(-85.650, 2.217, -4.154),
  296. // new THREE.Vector3(-85.680, 1.067, -5.674),
  297. // new THREE.Vector3(-85.680, 0.947, -6.024),
  298. // new THREE.Vector3(-85.680, 0.947, -12.894),
  299. // new THREE.Vector3(-85.450, 0.947, -12.894),
  300. // new THREE.Vector3(85.000, 0.920, -12.910)
  301. // // new THREE.Vector3(76, 28, 27),
  302. // ]);
  303. //
  304. // drawLine(curve.getPoints(10), model, group)
  305. resolve(model);
  306. });
  307. });
  308. };
  309. export const destroy = () => {
  310. if (model) {
  311. model.deleteModal();
  312. model = null;
  313. group = null;
  314. }
  315. };