fanLocalDual.threejs.base.ts 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619
  1. import * as THREE from 'three';
  2. // import { setModalCenter } from '/@/utils/threejs/util';
  3. import Smoke from '../../comment/threejs/Smoke';
  4. import { CSS3DObject } from 'three/examples/jsm/renderers/CSS3DRenderer';
  5. import { get } from 'lodash-es';
  6. import { getTextCanvas } from '/@/utils/threejs/util';
  7. // import * as dat from 'dat.gui';
  8. // const gui = new dat.GUI();
  9. // gui.domElement.style = 'position:absolute;top:100px;left:10px;z-index:99999999999999';
  10. class ModelContext {
  11. model;
  12. // modelName = 'jbfj-hd';
  13. modelName = 'jbfj-dual';
  14. /** 本模型的根3D对象 */
  15. group?: THREE.Object3D;
  16. /** 本模型所包含的所有元素合集 */
  17. private elements: unknown[] = [];
  18. /** 本模型支持的 Object3DGroup 模块 */
  19. private modules: {
  20. /** 模块名称 */
  21. name: string;
  22. /** 控制该模块所用的上下文 */
  23. context: THREE.Object3D;
  24. /** 控制时行为声明 */
  25. behavior: (context: THREE.Object3D) => void;
  26. }[] = [];
  27. constructor(model) {
  28. this.model = model;
  29. }
  30. addLight() {
  31. // optional implementation
  32. }
  33. /**
  34. * 设置模型类型并切换,不同的类型通常对应不同的具体模型,在模型总控制器下的具体模型会根据传入的参数彼此交互、切换
  35. *
  36. * 本模型分为外侧(右侧)风机、内侧(左侧)风机,用户选择一个风机后,详情参数的框需要高亮,风机之间需要联动形成风流
  37. *
  38. * @param data 风机数据,第一项应为外侧(右侧)风机,第二项为内侧(左侧)
  39. */
  40. setModelType(modelType: 'inner' | 'outer' | string, data: any[]) {
  41. const fanOuter1Run = get<string>(data[0], 'Fan1StartStatus', '0') == '1';
  42. const fanOuter2Run = get<string>(data[0], 'Fan2StartStatus', '0') == '1';
  43. const fanInner1Run = get<string>(data[1], 'Fan1StartStatus', '0') == '1';
  44. const fanInner2Run = get<string>(data[1], 'Fan2StartStatus', '0') == '1';
  45. if (modelType === 'inner') {
  46. this.execute('fanLeftStrong');
  47. }
  48. if (modelType === 'outer') {
  49. this.execute('fanRightStrong');
  50. }
  51. if (fanOuter1Run && fanInner1Run) {
  52. this.execute('fan1RightOpen&fan1LeftOpen');
  53. } else if (fanOuter1Run && fanInner2Run) {
  54. this.execute('fan1RightOpen&fan2LeftOpen');
  55. } else if (fanOuter2Run && fanInner1Run) {
  56. this.execute('fan2RightOpen&fan1LeftOpen');
  57. } else if (fanOuter2Run && fanInner2Run) {
  58. this.execute('fan2RightOpen&fan2LeftOpen');
  59. } else if (fanOuter1Run) {
  60. this.execute('fan1RightOpen');
  61. } else if (fanOuter2Run) {
  62. this.execute('fan2RightOpen');
  63. } else if (fanInner1Run) {
  64. this.execute('fan1LeftOpen');
  65. } else if (fanInner2Run) {
  66. this.execute('fan2LeftOpen');
  67. } else {
  68. this.stopAnimation(this.elements);
  69. }
  70. }
  71. private execute(cmdname: string) {
  72. this.modules.forEach(({ name, context, behavior }) => {
  73. if (name === cmdname) {
  74. behavior(context);
  75. }
  76. });
  77. }
  78. mountedThree() {
  79. return new Promise((resolve) => {
  80. this.model.setGLTFModel([this.modelName]).then(async (gltf) => {
  81. this.group = gltf[0];
  82. if (this.group) {
  83. // 将管道由黑色不透光的材质修改为半透明材质
  84. const material = new THREE.MeshBasicMaterial({
  85. color: '#000',
  86. transparent: true,
  87. opacity: 0.3,
  88. side: THREE.DoubleSide, // 这里是双面渲染的意思
  89. });
  90. [
  91. this.group.getObjectByName('Cylinder1054'),
  92. this.group.getObjectByName('BuErTaiJuBuFengJi_shupailie_baisezitiCylinder1054'),
  93. this.group.getObjectByName('pCylinder1'),
  94. ].forEach((e: THREE.Mesh) => {
  95. e.material = material;
  96. // e.renderOrder = 300;
  97. });
  98. // this.group.scale.set(2, 2, 2);
  99. // setModalCenter(this.group);
  100. this.addLight();
  101. this.setModelPosition();
  102. this.initModules().then(resolve);
  103. }
  104. });
  105. });
  106. }
  107. destroy() {
  108. if (!this.model) return;
  109. this.elements.forEach((element) => {
  110. this.model.clearGroup(element);
  111. });
  112. }
  113. // 设置模型位置
  114. setModelPosition() {
  115. if (!this.group) return;
  116. this.group.scale.set(0.6, 0.6, 0.6);
  117. // const ff = gui.addFolder(`位置调整`);
  118. // ff.add(this.group.position, 'x', -100, 100);
  119. // ff.add(this.group.position, 'y', -100, 100);
  120. // ff.add(this.group.position, 'z', -100, 100);
  121. this.group.position.set(0, 0, -60);
  122. this.group.rotation.y = Math.PI / 2;
  123. }
  124. // hideElements(eles: THREE.Object3D[]) {
  125. // eles.forEach((g) => {
  126. // g.visible = false;
  127. // });
  128. // }
  129. // showElements(eles: THREE.Object3D[]) {
  130. // eles.forEach((g) => {
  131. // g.visible = true;
  132. // });
  133. // }
  134. weakElements(eles: unknown[]) {
  135. eles.forEach((g) => {
  136. if (g instanceof Smoke) {
  137. g.oldOpacityFactor = 0.4;
  138. }
  139. if (g instanceof CSS3DObject) {
  140. g.element.style.setProperty('opacity', '0.3');
  141. }
  142. });
  143. }
  144. strongElements(eles: unknown[]) {
  145. eles.forEach((g) => {
  146. if (g instanceof Smoke) {
  147. g.oldOpacityFactor = 0.75;
  148. }
  149. if (g instanceof CSS3DObject) {
  150. g.element.style.setProperty('opacity', '1');
  151. }
  152. });
  153. }
  154. startAnimation(eles: unknown[]) {
  155. eles.forEach((g) => {
  156. if (g instanceof Smoke) {
  157. g.startSmoke();
  158. }
  159. });
  160. }
  161. stopAnimation(eles: unknown[]) {
  162. const smokes = eles.filter((g) => {
  163. return g instanceof Smoke;
  164. });
  165. return Promise.all(smokes.map((e) => e.stopSmoke()));
  166. }
  167. /** 核心方法,初始化本模型的各个模块,这些模块可以实现特定场景的展示、控制等功能 */
  168. async initModules() {
  169. if (this.elements.length > 0) return;
  170. // 右侧风机-主风机进风
  171. const curveFan1Right = this.generateSmokePath(
  172. [
  173. new THREE.Vector3(-85.69, 2.18, 51.89),
  174. new THREE.Vector3(-85.69, 2.18, 41.89),
  175. new THREE.Vector3(-85.69, 2.18, 35.32),
  176. new THREE.Vector3(-85.69, 0.78, 33.08),
  177. new THREE.Vector3(-85.69, 0.78, 27.84),
  178. new THREE.Vector3(-85.69, 4.72, 21.56),
  179. new THREE.Vector3(-85.69, 4.72, -13),
  180. new THREE.Vector3(-26.2, 4.72, -13.24),
  181. new THREE.Vector3(-25.61, 4.72, -47.03),
  182. new THREE.Vector3(80.03, 4.72, -47.03),
  183. ],
  184. true
  185. );
  186. // 右侧风机-备风机进风
  187. const curveFan2Right = this.generateSmokePath(
  188. [
  189. new THREE.Vector3(-85.69, -0.53, 51.89),
  190. new THREE.Vector3(-85.69, -0.53, 41.89),
  191. new THREE.Vector3(-85.69, -0.51, 35.32),
  192. new THREE.Vector3(-85.69, 0.78, 33.08),
  193. new THREE.Vector3(-85.69, 0.78, 27.84),
  194. new THREE.Vector3(-85.69, 4.72, 21.56),
  195. new THREE.Vector3(-85.69, 4.72, -13),
  196. new THREE.Vector3(-26.2, 4.72, -13.24),
  197. new THREE.Vector3(-25.61, 4.72, -47.03),
  198. new THREE.Vector3(80.03, 4.72, -47.03),
  199. ],
  200. true
  201. );
  202. // 左侧风机-主风机进风
  203. const curveFan1Left = this.generateSmokePath(
  204. [
  205. new THREE.Vector3(-85.69, 2.18, 12.72),
  206. new THREE.Vector3(-85.69, 2.18, 2.72),
  207. new THREE.Vector3(-85.69, 2.18, -3.85),
  208. new THREE.Vector3(-85.69, 0.78, -6.09),
  209. new THREE.Vector3(-85.69, 0.78, -12.92),
  210. new THREE.Vector3(80.25, 0.78, -12.92),
  211. ],
  212. true
  213. );
  214. // 左侧风机-备风机进风
  215. const curveFan2Left = this.generateSmokePath(
  216. [
  217. new THREE.Vector3(-85.69, -0.53, 12.72),
  218. new THREE.Vector3(-85.69, -0.53, 2.72),
  219. new THREE.Vector3(-85.69, -0.51, -3.85),
  220. new THREE.Vector3(-85.69, 0.78, -6.09),
  221. new THREE.Vector3(-85.69, 0.78, -12.92),
  222. new THREE.Vector3(80.25, 0.78, -12.92),
  223. ],
  224. true
  225. );
  226. // 右侧巷道-回风前段
  227. const curveTunnelRight = this.generateSmokePath([
  228. new THREE.Vector3(86.67, 0.78, -16.57),
  229. new THREE.Vector3(30.11, 0.78, -16.57),
  230. new THREE.Vector3(30.11, 0.78, -50.39),
  231. ]);
  232. // 左侧巷道-回风前段
  233. const curveTunnelLeft = this.generateSmokePath([new THREE.Vector3(30.11, 0.78, -50.39), new THREE.Vector3(-72.58, 0.78, -50.17)]);
  234. // 左侧巷道-回风全长
  235. const curveTunnelMajor = this.generateSmokePath([new THREE.Vector3(86.55, 0.78, -50.39), new THREE.Vector3(-72.58, 0.78, -50.17)]);
  236. const group1 = new THREE.Group();
  237. const smokeFan1Right = new Smoke('/model/img/texture-smoke.png', '#ffffff', 1, 0.75, 0.5, 600);
  238. smokeFan1Right.setPath(curveFan1Right);
  239. this.elements.push(smokeFan1Right);
  240. const smokeFan2Right = new Smoke('/model/img/texture-smoke.png', '#ffffff', 10, 0.75, 0.5, 400);
  241. smokeFan2Right.setPath(curveFan2Right);
  242. this.elements.push(smokeFan2Right);
  243. const smokeFan1Left = new Smoke('/model/img/texture-smoke.png', '#ffffff', 10, 0.75, 0.5, 600);
  244. smokeFan1Left.setPath(curveFan1Left);
  245. this.elements.push(smokeFan1Left);
  246. const smokeFan2Left = new Smoke('/model/img/texture-smoke.png', '#ffffff', 10, 0.75, 0.5, 400);
  247. smokeFan2Left.setPath(curveFan2Left);
  248. this.elements.push(smokeFan2Left);
  249. const smokeTunnelRight = new Smoke('/model/img/texture-smoke.png', '#ffffff', 0, 0.35, 1.5, 150);
  250. smokeTunnelRight.setPath(curveTunnelRight);
  251. this.elements.push(smokeTunnelRight);
  252. const smokeTunnelLeft = new Smoke('/model/img/texture-smoke.png', '#ffffff', 0, 0.35, 1.5, 150);
  253. smokeTunnelLeft.setPath(curveTunnelLeft);
  254. this.elements.push(smokeTunnelLeft);
  255. const smokeTunnelMajor = new Smoke('/model/img/texture-smoke.png', '#ffffff', 0, 0.35, 1.5, 150);
  256. smokeTunnelMajor.setPath(curveTunnelMajor);
  257. this.elements.push(smokeTunnelMajor);
  258. await smokeFan1Right.setPoints();
  259. this.group?.add(smokeFan1Right.points);
  260. await smokeFan2Right.setPoints();
  261. this.group?.add(smokeFan2Right.points);
  262. await smokeFan1Left.setPoints();
  263. this.group?.add(smokeFan1Left.points);
  264. await smokeFan2Left.setPoints();
  265. this.group?.add(smokeFan2Left.points);
  266. await smokeTunnelRight.setPoints();
  267. this.group?.add(smokeTunnelRight.points);
  268. await smokeTunnelLeft.setPoints();
  269. this.group?.add(smokeTunnelLeft.points);
  270. await smokeTunnelMajor.setPoints();
  271. this.group?.add(smokeTunnelMajor.points);
  272. const fanLeftSelectors = [
  273. {
  274. query: '#inputBox2',
  275. position: [-85, 8, -16],
  276. scale: 0.1,
  277. },
  278. {
  279. query: '#T1_1',
  280. position: [93, 18, -65],
  281. scale: 0.2,
  282. },
  283. {
  284. query: '#T1_2',
  285. position: [35, 16, -59],
  286. scale: 0.175,
  287. },
  288. ];
  289. const fanRightSelectors = [
  290. {
  291. query: '#inputBox3',
  292. position: [-85, 8, 24],
  293. scale: 0.1,
  294. },
  295. {
  296. query: '#T2_1',
  297. position: [93, 18, -98],
  298. scale: 0.2,
  299. },
  300. {
  301. query: '#T2_2',
  302. position: [35, 16, -92],
  303. scale: 0.175,
  304. },
  305. ];
  306. const commonSelectors = [
  307. {
  308. query: '#T3',
  309. position: [-26, 14, -86],
  310. scale: 0.15,
  311. },
  312. ];
  313. const fanLeftSprites = this.initCssElement(fanLeftSelectors);
  314. const fanRightSprites = this.initCssElement(fanRightSelectors);
  315. const commonSprites = this.initCssElement(commonSelectors);
  316. // 双巷道的通风机都开启有4种情况
  317. this.modules.push({
  318. name: 'fan1RightOpen&fan1LeftOpen',
  319. context: group1,
  320. behavior: () => {
  321. // this.weakElements(this.elements);
  322. this.stopAnimation(this.elements).then(() => {
  323. // this.strongElements();
  324. this.startAnimation([smokeFan1Right, smokeFan1Left, smokeTunnelRight, smokeTunnelMajor]);
  325. });
  326. },
  327. });
  328. this.modules.push({
  329. name: 'fan2RightOpen&fan1LeftOpen',
  330. context: group1,
  331. behavior: () => {
  332. this.stopAnimation(this.elements).then(() => {
  333. this.startAnimation([smokeFan2Right, smokeFan1Left, smokeTunnelRight, smokeTunnelMajor]);
  334. });
  335. },
  336. });
  337. this.modules.push({
  338. name: 'fan1RightOpen&fan2LeftOpen',
  339. context: group1,
  340. behavior: () => {
  341. this.stopAnimation(this.elements).then(() => {
  342. this.startAnimation([smokeFan1Right, smokeFan2Left, smokeTunnelRight, smokeTunnelMajor]);
  343. });
  344. },
  345. });
  346. this.modules.push({
  347. name: 'fan2RightOpen&fan2LeftOpen',
  348. context: group1,
  349. behavior: () => {
  350. this.stopAnimation(this.elements).then(() => {
  351. this.startAnimation([smokeFan2Right, smokeFan2Left, smokeTunnelRight, smokeTunnelMajor]);
  352. });
  353. },
  354. });
  355. // 只有一个风机启动有4种情况
  356. this.modules.push({
  357. name: 'fan1RightOpen',
  358. context: group1,
  359. behavior: () => {
  360. this.stopAnimation(this.elements).then(() => {
  361. this.startAnimation([smokeFan1Right, smokeTunnelMajor]);
  362. });
  363. },
  364. });
  365. this.modules.push({
  366. name: 'fan2RightOpen',
  367. context: group1,
  368. behavior: () => {
  369. this.stopAnimation(this.elements).then(() => {
  370. this.startAnimation([smokeFan2Right, smokeTunnelMajor]);
  371. });
  372. },
  373. });
  374. this.modules.push({
  375. name: 'fan1LeftOpen',
  376. context: group1,
  377. behavior: () => {
  378. this.stopAnimation(this.elements).then(() => {
  379. this.startAnimation([smokeFan1Left, smokeTunnelRight, smokeTunnelLeft]);
  380. });
  381. },
  382. });
  383. this.modules.push({
  384. name: 'fan2LeftOpen',
  385. context: group1,
  386. behavior: () => {
  387. this.stopAnimation(this.elements).then(() => {
  388. this.startAnimation([smokeFan2Left, smokeTunnelRight, smokeTunnelLeft]);
  389. });
  390. },
  391. });
  392. // 只有一个风机启动有2种告示牌情况
  393. this.modules.push({
  394. name: 'fanLeftStrong',
  395. context: group1,
  396. behavior: () => {
  397. this.weakElements(this.elements);
  398. this.strongElements([...fanLeftSprites, ...commonSprites]);
  399. },
  400. });
  401. this.modules.push({
  402. name: 'fanRightStrong',
  403. context: group1,
  404. behavior: () => {
  405. this.weakElements(this.elements);
  406. this.strongElements([...fanRightSprites, ...commonSprites]);
  407. },
  408. });
  409. }
  410. /** 初始化css元素,将css元素选择器传入,该方法会将这些元素按顺序放入传入的锚点中 */
  411. initCssElement(selectors: { query: string; position: number[]; scale: number }[]) {
  412. const arr: CSS3DObject[] = [];
  413. selectors.forEach(({ query, position, scale }) => {
  414. const element = document.querySelector(query) as HTMLElement;
  415. if (element) {
  416. const css3D = new CSS3DObject(element);
  417. this.elements.push(css3D);
  418. arr.push(css3D);
  419. css3D.name = query;
  420. css3D.scale.set(scale, scale, scale);
  421. css3D.rotation.y = -Math.PI / 2;
  422. css3D.position.set(position[0], position[1], position[2]);
  423. this.group?.add(css3D);
  424. }
  425. });
  426. return arr;
  427. }
  428. /** 生成适用于 Smoke 的曲线数据,输入途径点,输出路径,如果是进风类型,首个线段将有扩散效果,出风则是末尾线段有扩散效果 */
  429. generateSmokePath(points: THREE.Vector3[], airIn?: boolean, airOut?: boolean) {
  430. if (!this.group) return;
  431. const result: any[] = [];
  432. for (let index = 1; index < points.length; index++) {
  433. const path0 = points[index - 1];
  434. const path1 = points[index];
  435. const path = {
  436. path0,
  437. path1,
  438. isSpread: false,
  439. spreadDirection: 0,
  440. // spreadRang: -10,
  441. };
  442. if (airIn && index === 1) {
  443. // 首个线段需要扩散,由大变小
  444. path.isSpread = true;
  445. path.spreadDirection = -1;
  446. }
  447. if (airOut && index === points.length - 1) {
  448. // 末个线段需要扩散,由小变大
  449. path.isSpread = false;
  450. path.spreadDirection = 1;
  451. }
  452. if (!airIn && !airOut) {
  453. path.isSpread = false;
  454. path.spreadDirection = 2;
  455. }
  456. result.push(path);
  457. }
  458. return result;
  459. }
  460. /** 添加风机描述,右侧风机是arr的第一项,左侧风机是第二项 */
  461. addText(arr) {
  462. const positions = [
  463. [-84.79, 0.82, 20.3],
  464. [-84.79, 0.82, 7.6],
  465. ];
  466. arr.forEach((e, i) => {
  467. this.addTextByData(e, positions[i], `text${i}`);
  468. });
  469. }
  470. // 从 .fanLocal.threejs.base 复制
  471. addTextByData(selectData, position, name) {
  472. if (!this.group) {
  473. return;
  474. }
  475. // @ts-ignore
  476. const screenDownText = get(VENT_PARAM, 'modalText', History_Type['type'] == 'remote' ? '国能神东煤炭集团监制' : '煤炭科学技术研究院有限公司研制');
  477. const screenDownTextX = 80 - (screenDownText.length - 10) * 6;
  478. const textArr = [
  479. {
  480. text: `智能局部通风机监测与控制系统`,
  481. font: 'normal 30px Arial',
  482. color: '#009900',
  483. strokeStyle: '#002200',
  484. x: 20,
  485. y: 108,
  486. },
  487. {
  488. text: `供风距离(m):`,
  489. font: 'normal 30px Arial',
  490. color: '#009900',
  491. strokeStyle: '#002200',
  492. x: 0,
  493. y: 152,
  494. },
  495. {
  496. text: `${
  497. selectData.airSupplyDistence_merge
  498. ? selectData.airSupplyDistence_merge
  499. : selectData.fchimenylength
  500. ? selectData.fchimenylength
  501. : selectData.airSupplyDistence_merge
  502. ? selectData.airSupplyDistence_merge
  503. : '-'
  504. }`,
  505. font: 'normal 30px Arial',
  506. color: '#009900',
  507. strokeStyle: '#002200',
  508. x: 228,
  509. y: 152,
  510. },
  511. {
  512. text: `风筒直径(mm): `,
  513. font: 'normal 30px Arial',
  514. color: '#009900',
  515. strokeStyle: '#002200',
  516. x: 0,
  517. y: 200,
  518. },
  519. {
  520. text: ` ${selectData.fchimenydiamlimit ? selectData.fchimenydiamlimit : selectData.ductDiameter_merge ? selectData.ductDiameter_merge : '-'}`,
  521. font: 'normal 30px Arial',
  522. color: '#009900',
  523. strokeStyle: '#002200',
  524. x: 220,
  525. y: 200,
  526. },
  527. {
  528. text: `故障诊断:`,
  529. font: 'normal 30px Arial',
  530. color: '#009900',
  531. strokeStyle: '#002200',
  532. x: 0,
  533. y: 245,
  534. },
  535. {
  536. text: `${selectData.warnLevel_str ? selectData.warnLevel_str : '-'}`,
  537. font: 'normal 30px Arial',
  538. color: '#009900',
  539. strokeStyle: '#002200',
  540. x: 220,
  541. y: 245,
  542. },
  543. {
  544. text: `型号功率:`,
  545. font: 'normal 30px Arial',
  546. color: '#009900',
  547. strokeStyle: '#002200',
  548. x: 0,
  549. y: 285,
  550. },
  551. {
  552. text: `${selectData.model_Power_merge ? selectData.model_Power_merge : '-'}`,
  553. font: 'normal 26px Arial',
  554. color: '#009900',
  555. strokeStyle: '#002200',
  556. x: 220,
  557. y: 285,
  558. },
  559. {
  560. text: screenDownText,
  561. font: 'normal 28px Arial',
  562. color: '#009900',
  563. strokeStyle: '#002200',
  564. x: screenDownTextX,
  565. y: 325,
  566. },
  567. ];
  568. getTextCanvas(526, 346, textArr, '').then((canvas: HTMLCanvasElement) => {
  569. const textMap = new THREE.CanvasTexture(canvas); // 关键一步
  570. const textMaterial = new THREE.MeshBasicMaterial({
  571. // 关于材质并未讲解 实操即可熟悉 这里是漫反射类似纸张的材质,对应的就有高光类似金属的材质.
  572. map: textMap, // 设置纹理贴图
  573. transparent: true,
  574. side: THREE.FrontSide, // 这里是双面渲染的意思
  575. });
  576. textMaterial.blending = THREE.CustomBlending;
  577. const monitorPlane = this.group?.getObjectByName(name);
  578. if (monitorPlane) {
  579. // @ts-ignore-next-line
  580. monitorPlane.material = textMaterial;
  581. } else {
  582. const planeGeometry = new THREE.PlaneGeometry(526, 346); // 平面3维几何体PlaneGeometry
  583. const planeMesh = new THREE.Mesh(planeGeometry, textMaterial);
  584. planeMesh.name = name;
  585. planeMesh.scale.set(0.0135, 0.0135, 0.0135);
  586. planeMesh.rotation.y = -Math.PI / 2;
  587. planeMesh.position.set(position[0], position[1], position[2]);
  588. this.group?.add(planeMesh);
  589. }
  590. });
  591. }
  592. }
  593. export default ModelContext;