gasAssessmen.threejs.base.ts 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. import * as THREE from 'three';
  2. import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';
  3. import { OutlinePass } from 'three/examples/jsm/postprocessing/OutlinePass.js';
  4. import { setModalCenter, setTag3D, gradientColors } from '/@/utils/threejs/util';
  5. import { CSS3DObject } from 'three/examples/jsm/renderers/CSS3DRenderer.js';
  6. import { green, yellow } from '@ant-design/colors';
  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 GasAssessmen {
  11. model;
  12. modelName = 'workFace';
  13. group: THREE.Object3D = new THREE.Object3D();
  14. planeGroup: THREE.Group = new THREE.Group();
  15. bloomComposer: EffectComposer | null = null;
  16. finalComposer: EffectComposer | null = null;
  17. outlinePass: OutlinePass | null = null;
  18. positions: THREE.Vector3[][] = [];
  19. bloomLayer = new THREE.Layers();
  20. darkMaterial = new THREE.MeshBasicMaterial({ color: 'black', transparent: true, side: THREE.DoubleSide });
  21. materials = {};
  22. glob = {
  23. ENTIRE_SCENE: 0,
  24. BLOOM_SCENE: 10,
  25. N: 100,
  26. };
  27. locationTexture: THREE.Texture | null = null;
  28. warningLocationTexture: THREE.Texture | null = null;
  29. errorLocationTexture: THREE.Texture | null = null;
  30. playerStartClickTime1 = new Date().getTime();
  31. playerStartClickTime2 = new Date().getTime();
  32. planeNum = 0;
  33. constructor(model) {
  34. this.model = model;
  35. this.group.name = this.modelName;
  36. }
  37. addLight() {
  38. // const _this = this;
  39. const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
  40. directionalLight.position.set(-196, 150, 258);
  41. this.group.add(directionalLight);
  42. directionalLight.target = this.group;
  43. }
  44. render() {
  45. this.model.renderer?.render(this.model.scene as THREE.Scene, this.model.camera as THREE.PerspectiveCamera);
  46. }
  47. // 绘制抽采单元
  48. setPlanes1 = (n, colors = new Array(n).fill(new THREE.Color('rgb(100%, 0%, 0%)'))) => {
  49. const sizeList = [0.2, 0.3, 0.1, 0.4];
  50. // width = 7.713 height =3.717
  51. colors = gradientColors('#00FF2C', '#FF0000', n, 2);
  52. this.planeNum = n;
  53. const lenScale = 0.77 / n;
  54. const planeGeo = new THREE.PlaneGeometry();
  55. planeGeo.applyMatrix4(new THREE.Matrix4().makeTranslation(-1, 0, 0));
  56. for (let i = 0; i < n; i++) {
  57. const material = new THREE.MeshBasicMaterial({ color: colors[i], transparent: true, opacity: 0.6, depthTest: false, depthWrite: false });
  58. const plane = new THREE.Mesh(planeGeo, material);
  59. plane.name = 'unit' + i;
  60. plane.rotation.x = -Math.PI / 2;
  61. plane.scale.set(lenScale - 0.001, 0.375, 1.0);
  62. plane.position.set(0.282 - lenScale * (i - 0.5), 0.015, 0.142);
  63. this.planeGroup.add(plane);
  64. }
  65. this.group.add(this.planeGroup);
  66. };
  67. setPlanes = (n) => {
  68. // const sizeList = [0.2, 0.3, 0.1, 0.2, 0.2];
  69. const colors = {
  70. c1: new THREE.Color(0x00fe00), // >90
  71. c2: new THREE.Color(0xf9b866), // >75 <90 249,184,102
  72. c3: new THREE.Color(0xfefe00), // 50-75 254,254,0
  73. c4: new THREE.Color(0xfe6600), // 25-50 254,102,0
  74. c5: new THREE.Color(0xb00101), // <25 176,1,1
  75. };
  76. const sizeList = [
  77. {
  78. ratio: 0.2,
  79. color: colors.c1,
  80. },
  81. {
  82. ratio: 0.3,
  83. color: colors.c2,
  84. },
  85. {
  86. ratio: 0.1,
  87. color: colors.c4,
  88. },
  89. {
  90. ratio: 0.2,
  91. color: colors.c5,
  92. },
  93. {
  94. ratio: 0.2,
  95. color: colors.c3,
  96. },
  97. ];
  98. // width = 7.713 height =3.717
  99. const geometry = new THREE.PlaneGeometry(7.723, 3.72, 1, 1);
  100. // 初始化累积比例数组和颜色数组
  101. const accumulatedRatios = [];
  102. const colorsArray = new Float32Array(3 * sizeList.length);
  103. // 计算累积比例和颜色数组
  104. function updateShaderData(sizeList) {
  105. let accRatio = 0;
  106. for (let i = 0; i < sizeList.length; i++) {
  107. const item = sizeList[i];
  108. accRatio += item.ratio;
  109. accumulatedRatios.push(accRatio);
  110. colorsArray[i * 3] = item.color.r;
  111. colorsArray[i * 3 + 1] = item.color.g;
  112. colorsArray[i * 3 + 2] = item.color.b;
  113. }
  114. }
  115. updateShaderData(sizeList); // 初始调用
  116. // 定义着色器代码
  117. const vertexShader = `
  118. varying vec2 vUv;
  119. void main() {
  120. vUv = uv;
  121. gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
  122. }
  123. `;
  124. const fragmentShader = `
  125. varying vec2 vUv;
  126. uniform float ratios[${sizeList.length}];
  127. uniform vec3 colors[${sizeList.length}];
  128. void main() {
  129. for(int i = 0; i < ${sizeList.length}; i++) {
  130. if(vUv.x < ratios[i]) {
  131. gl_FragColor = vec4(colors[i], 1.0);
  132. return;
  133. }
  134. }
  135. gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
  136. }
  137. `;
  138. // const fragmentShader = `
  139. // varying vec2 vUv;
  140. // uniform float ratios[${sizeList.length + 1}]; // 加入了起始点 0
  141. // uniform vec3 colors[${sizeList.length}];
  142. // vec3 lerp(vec3 a, vec3 b, float t) {
  143. // return a + t * (b - a);
  144. // }
  145. // void main() {
  146. // int index = 0;
  147. // for(int i = 1; i <= ${sizeList.length}; i++) {
  148. // if(vUv.x >= ratios[i-1] && vUv.x < ratios[i]) {
  149. // index = i - 1;
  150. // break;
  151. // }
  152. // }
  153. // // 如果是最后一个颜色块,直接使用其颜色
  154. // if (index == ${sizeList.length - 1}) {
  155. // gl_FragColor = vec4(colors[index], 1.0);
  156. // } else {
  157. // // 计算该像素在当前颜色块内的相对位置
  158. // float t = (vUv.x - ratios[index]) / (ratios[index + 1] - ratios[index]);
  159. // // 在相邻颜色间进行线性插值
  160. // vec3 color = lerp(colors[index], colors[index + 1], t);
  161. // gl_FragColor = vec4(color, 1.0);
  162. // }
  163. // }
  164. // `;
  165. // 创建着色器材质
  166. const material = new THREE.ShaderMaterial({
  167. uniforms: {
  168. ratios: { value: accumulatedRatios },
  169. colors: { value: colorsArray },
  170. },
  171. vertexShader: vertexShader,
  172. fragmentShader: fragmentShader,
  173. depthTest: false,
  174. depthWrite: false,
  175. });
  176. // // 当 sizeList 数据变化时调用此函数
  177. // function updateSizeList(newSizeList) {
  178. // accumulatedRatios.length = 0; // 清空累积比例数组
  179. // updateShaderData(newSizeList);
  180. // material.uniforms.ratios.value = accumulatedRatios;
  181. // material.uniforms.colors.value = colorsArray;
  182. // material.needsUpdate = true;
  183. // }
  184. // 创建网格并添加到场景中
  185. const plane = new THREE.Mesh(geometry, material);
  186. plane.rotation.x = -Math.PI / 2;
  187. plane.position.set(-0.2, 0.15, -0.03);
  188. this.group.add(plane);
  189. };
  190. // 清除抽采单元绘制面
  191. clearPlanes = () => {
  192. for (let i = 0; i < this.planeNum; i++) {
  193. const plane = this.planeGroup.getObjectByName(`unit${i}`);
  194. const label = this.planeGroup.getObjectByName(`planeText${i}`);
  195. if (plane) this.planeGroup.remove(plane);
  196. if (label) this.planeGroup.remove(label);
  197. }
  198. };
  199. // 抽采单元内容显示
  200. setCss3D = () => {
  201. // width = 7.713 height =3.717
  202. const obj = this.group.getObjectByName(`unitText`);
  203. if (!obj) {
  204. const element = document.getElementById(`gasUnitBox`) as HTMLElement;
  205. if (element) {
  206. const gasUnitCSS3D = new CSS3DObject(element);
  207. gasUnitCSS3D.name = `unitText`;
  208. gasUnitCSS3D.scale.set(0.0009, 0.0009, 0.0009);
  209. gasUnitCSS3D.position.set(-0.1, 0.11, 0.05);
  210. gasUnitCSS3D.lookAt(-0.1, 0.5, 1);
  211. this.planeGroup.add(gasUnitCSS3D);
  212. }
  213. }
  214. for (let i = 0; i < this.planeNum; i++) {
  215. const lenScale = 0.77 / this.planeNum;
  216. const label = setTag3D(`抽采单元${i + 1}`, 'gas_unit_text');
  217. label.scale.set(0.0018, 0.0018, 1); //根据相机渲染范围控制HTML 3D标签尺寸
  218. label.position.set(0.282 - lenScale * (i + 0.5), 0.015, 0.142);
  219. label.name = 'planeText' + i;
  220. this.planeGroup.add(label);
  221. }
  222. };
  223. // 显示或隐藏抽采单元显示内容
  224. changeCss3D = (isHide) => {
  225. for (let i = 0; i < this.planeNum; i++) {
  226. const obj = this.group.getObjectByName(`unitText${i}`);
  227. if (obj) {
  228. obj.visible = isHide;
  229. }
  230. }
  231. };
  232. // 清除抽采单元显示内容
  233. clearCss3D = () => {
  234. const obj = this.group.getObjectByName(`unitText`);
  235. if (obj) this.group.remove(obj);
  236. const element = document.getElementById(`gasUnitBox`) as HTMLElement;
  237. if (element) {
  238. element.remove();
  239. }
  240. for (let i = 0; i < this.planeNum; i++) {
  241. const label = this.planeGroup.getObjectByName(`planeText${i}`);
  242. if (label) this.planeGroup.remove(label);
  243. }
  244. };
  245. /* 点击 */
  246. mousedownModel(rayCaster: THREE.Raycaster) {
  247. this.render();
  248. }
  249. mouseUpModel() {
  250. //
  251. }
  252. mountedThree() {
  253. return new Promise(async (resolve) => {
  254. this.model.renderer.sortObjects = true;
  255. this.model.orbitControls.update();
  256. this.model.setGLTFModel(['workFace1'], this.group).then(async () => {
  257. this.group.children.forEach((object: THREE.Object3D) => {
  258. if (object.name.startsWith('workFace')) {
  259. setModalCenter(object);
  260. }
  261. });
  262. this.group.name = this.modelName;
  263. this.addLight();
  264. resolve(null);
  265. });
  266. });
  267. }
  268. destroy() {
  269. this.model.clearGroup(this.group);
  270. this.model = null;
  271. this.group = null;
  272. this.bloomComposer?.dispose();
  273. this.finalComposer?.dispose();
  274. }
  275. }
  276. export default GasAssessmen;