Browse Source

[Wip 0000] 三维模型添加箭头流相关的工具及方法,瓦斯抽采泵页面添加部分箭头流

houzekong 2 months ago
parent
commit
17350775df

BIN
public/model/img/blueArrow.png


BIN
public/model/img/greenArrow.png


BIN
public/model/img/redArrow.png


+ 79 - 0
src/views/vent/comment/threejs/ArrowFlow.ts

@@ -0,0 +1,79 @@
+import * as THREE from 'three';
+import gasp from 'gsap';
+
+/**
+ * 箭头流工具类,用于创建箭头流动的相关材质、几何、动画并管理其生命周期
+ */
+export default class ArrowFlow extends THREE.MeshBasicMaterial {
+  /** 箭头流材质 */
+  texture: THREE.Texture;
+  /** 流线起点 */
+  origin: THREE.Vector3;
+  /** 流线终点 */
+  destiny: THREE.Vector3;
+  /** 流线 */
+  path: THREE.LineCurve3;
+  /** 重复次数 */
+  repeat: THREE.Vector2;
+  /** 图形偏移量 */
+  offset: THREE.Vector2;
+  /** 动画控制器 */
+  tween: gsap.core.Tween | null = null;
+
+  constructor(
+    texturePath: '/model/img/blueArrow.png' | '/model/img/greenArrow.png' | '/model/img/redArrow.png',
+    origin: THREE.Vector3,
+    destiny: THREE.Vector3,
+    {
+      repeatX = 20,
+      repeatY = 1,
+      /** 0-1 */
+      offsetX = 0,
+      /** 0-1 */
+      offsetY = 0.5,
+    } = {}
+  ) {
+    const t = new THREE.TextureLoader().load(texturePath);
+    t.wrapS = THREE.RepeatWrapping;
+    t.wrapT = THREE.RepeatWrapping;
+    t.repeat = new THREE.Vector2(repeatX, repeatY);
+    t.offset = new THREE.Vector2(offsetX, offsetY);
+    super({ map: t, transparent: true });
+    this.path = new THREE.LineCurve3(origin, destiny);
+    this.texture = t;
+    this.origin = origin;
+    this.destiny = destiny;
+    this.repeat = t.repeat;
+    this.offset = t.offset;
+  }
+
+  /**
+   * 启动动画效果
+   * @param speed number 类型,配合 offset 使用,越小速度越慢,大于 0
+   * @param offsetX
+   * @param offsetY
+   */
+  startAnimation(speed: number = 1, offsetX: number = -0.01, offsetY: number = 0) {
+    if (this.tween) {
+      this.tween.kill();
+    }
+    this.tween = gasp.to(this, {
+      duration: 1,
+      repeat: -1,
+      onUpdate: () => {
+        this.texture.offset.setX(this.texture.offset.x + offsetX * speed);
+        this.texture.offset.setY(this.texture.offset.y + offsetY * speed);
+      },
+    });
+  }
+
+  pauseAnimation() {
+    if (!this.tween) return;
+    this.tween.pause();
+  }
+
+  stopAnimation() {
+    if (!this.tween) return;
+    this.tween.kill();
+  }
+}

+ 0 - 1
src/views/vent/monitorManager/gasPumpMonitor/gasPump.threejs.ts

@@ -22,7 +22,6 @@ const mouseEvent = (event) => {
         // gasPumpBaseObj.mousedownModel.call(gasPumpBaseObj, intersects);
       }
     });
-    console.log('摄像头控制信息', model?.orbitControls, model?.camera);
   }
 };
 

+ 29 - 0
src/views/vent/monitorManager/gasPumpMonitor/gasPump.threejs.under.ts

@@ -1,6 +1,10 @@
 import * as THREE from 'three';
 import { CSS3DObject, CSS3DSprite } from 'three/examples/jsm/renderers/CSS3DRenderer.js';
 import { modelMonitorTags } from './gasPump.data';
+import ArrowFlow from '../../comment/threejs/ArrowFlow';
+// import * as dat from 'dat.gui';
+// const gui = new dat.GUI();
+// gui.domElement.style = 'position:absolute;top:100px;left:10px;z-index:99999999999999';
 
 class gasPumpUnder {
   model;
@@ -67,6 +71,7 @@ class gasPumpUnder {
           // this.group.position.y += 40;
           resolve(null);
           this.addLight();
+          this.addFlows();
         }
       });
     });
@@ -77,5 +82,29 @@ class gasPumpUnder {
     this.model = null;
     this.group = null;
   }
+
+  // 添加箭头流线
+  addFlows() {
+    const arrowflow = new ArrowFlow('/model/img/blueArrow.png', new THREE.Vector3(-12.284, 0.942, -0.359), new THREE.Vector3(-3.909, 0.942, -0.359));
+    // const t = new THREE.TextureLoader().load('/model/img/blueArrow.png');
+    // t.wrapS = THREE.RepeatWrapping;
+    // t.wrapT = THREE.RepeatWrapping;
+    // t.repeat = new THREE.Vector2(10, 1);
+    // t.offset = new THREE.Vector2(0, 0.5);
+    // const b = new THREE.MeshBasicMaterial({ map: t, transparent: true });
+    // const path = new THREE.LineCurve3(new THREE.Vector3(-12.284, 0.942, -0.359), new THREE.Vector3(-3.909, 0.942, -0.359));
+
+    // const a = this.group?.getObjectByName('dian');
+    // const b = this.group?.getObjectByName('dian1');
+    // a?.getWorldPosition(origin);
+    // b?.getWorldPosition(dest);
+
+    const geometry = new THREE.TubeGeometry(arrowflow.path, 1, 0.08, 8, false);
+    const mesh = new THREE.Mesh(geometry, arrowflow);
+
+    arrowflow.startAnimation();
+    mesh.name = 'flow1';
+    this.group?.add(mesh);
+  }
 }
 export default gasPumpUnder;

+ 292 - 0
src/views/vent/monitorManager/workFaceMonitor/workFaceGas.threejs.base.ts

@@ -0,0 +1,292 @@
+import * as THREE from 'three';
+import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';
+import { OutlinePass } from 'three/examples/jsm/postprocessing/OutlinePass.js';
+import { setModalCenter } from '/@/utils/threejs/util';
+
+// import * as dat from 'dat.gui';
+// const gui = new dat.GUI();
+// gui.domElement.style = 'position:absolute;top:100px;left:10px;z-index:99999999999999';
+
+class WorkFaceGas {
+  model;
+  modelName = 'workFaceGas';
+  group: THREE.Object3D = new THREE.Object3D();
+  planeGroup: THREE.Group = new THREE.Group();
+  bloomComposer: EffectComposer | null = null;
+  finalComposer: EffectComposer | null = null;
+  outlinePass: OutlinePass | null = null;
+  positions: THREE.Vector3[][] = [];
+  bloomLayer = new THREE.Layers();
+  darkMaterial = new THREE.MeshBasicMaterial({ color: 'black', transparent: true, side: THREE.DoubleSide });
+  materials = {};
+  glob = {
+    ENTIRE_SCENE: 0,
+    BLOOM_SCENE: 10,
+    N: 100,
+  };
+  locationTexture: THREE.Texture | null = null;
+  warningLocationTexture: THREE.Texture | null = null;
+  errorLocationTexture: THREE.Texture | null = null;
+  playerStartClickTime1 = new Date().getTime();
+  playerStartClickTime2 = new Date().getTime();
+  planeNum = 0;
+
+  constructor(model) {
+    this.model = model;
+    this.group.name = this.modelName;
+  }
+  addLight() {
+    const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
+    directionalLight.position.set(-196, 150, 258);
+    this.group.add(directionalLight);
+    directionalLight.target = this.group;
+  }
+  setControls = () => {
+    if (this.model && this.model.orbitControls) {
+      this.model.orbitControls.panSpeed = 0.5;
+      this.model.orbitControls.rotateSpeed = 0.5;
+      this.model.orbitControls.maxPolarAngle = Math.PI / 3;
+      this.model.orbitControls.minPolarAngle = Math.PI / 4;
+      this.model.orbitControls.minAzimuthAngle = -Math.PI / 3;
+      this.model.orbitControls.maxAzimuthAngle = Math.PI / 4;
+    }
+  };
+
+  render() {
+    this.model.renderer?.render(this.model.scene as THREE.Scene, this.model.camera as THREE.PerspectiveCamera);
+  }
+
+  /* 点击 */
+  mousedownModel(rayCaster: THREE.Raycaster) {
+    const opticalFiber = this.group.getObjectByName('opticalfiber');
+    if (opticalFiber) {
+      const intersects = rayCaster?.intersectObjects([...opticalFiber.children]) as THREE.Intersection[];
+
+      // 判断是否点击到视频
+      intersects.find((intersect) => {
+        const mesh = intersect.object;
+        if (mesh.name.startsWith('optical_fiber_')) {
+          // outlinePass?.selectedObjects.push(mesh);
+          return true;
+        }
+
+        return false;
+      });
+    }
+    this.render();
+  }
+
+  mouseUpModel() {
+    //
+  }
+
+  setThreePlane() {
+    const gltfModal = this.group.getObjectByName('workFace');
+    const PouMian01 = gltfModal?.getObjectByName('WaSiPouMian01');
+    const DiXing = PouMian01?.getObjectByName('DiXing');
+    // 绘制采空区三带
+    // new THREE.Vector3(934.695, -141.85, -365.375),
+    // new THREE.Vector3(934.695, 623.933, -365.375),
+    //-365.355
+    const material1 = new THREE.MeshBasicMaterial({ side: THREE.DoubleSide, vertexColors: true });
+    const offeset = 10;
+    const yellowLen = offeset * 8;
+    // 红=》红
+    const curveRed = new THREE.CatmullRomCurve3([new THREE.Vector3(-477.721, -141.83, -365.375), new THREE.Vector3(-477.721, 623.933, -365.375)]);
+    const curve0 = new THREE.CatmullRomCurve3([
+      new THREE.Vector3(-231.811 - 150, -141.83, -365.375),
+      new THREE.Vector3(-121.899 - 150, 67.201, -365.375),
+      new THREE.Vector3(-242.441 - 150, 408.812, -365.375),
+      new THREE.Vector3(-179.811 - 150, 620.836, -365.375),
+    ]);
+    const pointsRed = curveRed.getPoints(80);
+    const points0 = curve0.getPoints(80);
+    const newPointRed: number[] = [];
+    const normalsRed: number[] = [];
+    const colorsRed: number[] = [];
+    for (let i = 0; i < 80; i++) {
+      newPointRed.push(pointsRed[i].x, pointsRed[i].y, pointsRed[i].z);
+      newPointRed.push(points0[i].x, points0[i].y, points0[i].z);
+      newPointRed.push(points0[i + 1].x, points0[i + 1].y, points0[i + 1].z);
+      newPointRed.push(points0[i + 1].x, points0[i + 1].y, points0[i + 1].z);
+      newPointRed.push(pointsRed[i + 1].x, pointsRed[i + 1].y, pointsRed[i + 1].z);
+      newPointRed.push(pointsRed[i].x, pointsRed[i].y, pointsRed[i].z);
+      normalsRed.push(0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1);
+      colorsRed.push(1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0);
+    }
+    const geometryRed = new THREE.BufferGeometry();
+    geometryRed.setAttribute('position', new THREE.BufferAttribute(new Float32Array(newPointRed), 3));
+    geometryRed.setAttribute('normal', new THREE.BufferAttribute(new Float32Array(normalsRed), 3));
+    geometryRed.setAttribute('color', new THREE.BufferAttribute(new Float32Array(colorsRed), 3));
+    const meshRed = new THREE.Mesh(geometryRed, material1);
+    meshRed.position.setZ(-1);
+    DiXing?.add(meshRed);
+
+    // 红=》黄
+
+    const curve1 = new THREE.CatmullRomCurve3([
+      new THREE.Vector3(-231.811 - yellowLen, -141.83, -365.375),
+      new THREE.Vector3(-121.899 - yellowLen, 67.201, -365.375),
+      new THREE.Vector3(-242.441 - yellowLen, 408.812, -365.375),
+      new THREE.Vector3(-179.811 - yellowLen, 620.836, -365.375),
+    ]);
+
+    const points1 = curve1.getPoints(80);
+    const newPoints0: number[] = [];
+    const normals0: number[] = [];
+    const colors0: number[] = [];
+    for (let i = 0; i < 80; i++) {
+      newPoints0.push(points0[i].x, points0[i].y, points0[i].z);
+      newPoints0.push(points1[i].x, points1[i].y, points1[i].z);
+      newPoints0.push(points1[i + 1].x, points1[i + 1].y, points1[i + 1].z);
+      newPoints0.push(points1[i + 1].x, points1[i + 1].y, points1[i + 1].z);
+      newPoints0.push(points0[i + 1].x, points0[i + 1].y, points0[i + 1].z);
+      newPoints0.push(points0[i].x, points0[i].y, points0[i].z);
+      normals0.push(0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1);
+      colors0.push(1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0);
+    }
+    const geometry0 = new THREE.BufferGeometry();
+    geometry0.setAttribute('position', new THREE.BufferAttribute(new Float32Array(newPoints0), 3));
+    geometry0.setAttribute('normal', new THREE.BufferAttribute(new Float32Array(normals0), 3));
+    geometry0.setAttribute('color', new THREE.BufferAttribute(new Float32Array(colors0), 3));
+    geometry0.computeBoundingBox();
+
+    // const material1 = new THREE.MeshBasicMaterial({ side: THREE.DoubleSide, color: '#fff' });
+    const mesh0 = new THREE.Mesh(geometry0, material1);
+    mesh0.name = 'yellow';
+    mesh0.position.setZ(-1);
+    DiXing?.add(mesh0);
+    // 黄=》黄
+    const curveYellow1 = new THREE.CatmullRomCurve3([
+      new THREE.Vector3(784.664 - yellowLen, 623.933, -365.355),
+      new THREE.Vector3(167.212 - yellowLen, 450.517, -365.355),
+      new THREE.Vector3(7.587 - yellowLen, 262.225, -365.355),
+      new THREE.Vector3(426.499 - yellowLen, 31.416, -365.355),
+      new THREE.Vector3(261.665 - yellowLen, -139.495, -365.355),
+    ]);
+    const curveYellow2 = new THREE.CatmullRomCurve3([
+      new THREE.Vector3(-179.811 - yellowLen, 620.836, -365.375),
+      new THREE.Vector3(-242.441 - yellowLen, 408.812, -365.375),
+      new THREE.Vector3(-121.899 - yellowLen, 67.201, -365.375),
+      new THREE.Vector3(-231.811 - yellowLen, -141.83, -365.375),
+    ]);
+    const pointsYellow1 = curveYellow1.getPoints(80);
+    const pointsYellow2 = curveYellow2.getPoints(80);
+    const newPointYellow: number[] = [];
+    const normalsYellow: number[] = [];
+    const colorsYellow: number[] = [];
+    for (let i = 0; i < 80; i++) {
+      newPointYellow.push(pointsYellow1[i].x, pointsYellow1[i].y, pointsYellow1[i].z);
+      newPointYellow.push(pointsYellow2[i].x, pointsYellow2[i].y, pointsYellow2[i].z);
+      newPointYellow.push(pointsYellow2[i + 1].x, pointsYellow2[i + 1].y, pointsYellow2[i + 1].z);
+      newPointYellow.push(pointsYellow2[i + 1].x, pointsYellow2[i + 1].y, pointsYellow2[i + 1].z);
+      newPointYellow.push(pointsYellow1[i + 1].x, pointsYellow1[i + 1].y, pointsYellow1[i + 1].z);
+      newPointYellow.push(pointsYellow1[i].x, pointsYellow1[i].y, pointsYellow1[i].z);
+      normalsYellow.push(0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1);
+      colorsYellow.push(1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0);
+    }
+    const geometryYellow = new THREE.BufferGeometry();
+    geometryYellow.setAttribute('position', new THREE.BufferAttribute(new Float32Array(newPointYellow), 3));
+    geometryYellow.setAttribute('normal', new THREE.BufferAttribute(new Float32Array(normalsYellow), 3));
+    geometryYellow.setAttribute('color', new THREE.BufferAttribute(new Float32Array(colorsYellow), 3));
+    const meshYellow = new THREE.Mesh(geometryYellow, material1);
+    meshYellow.position.setZ(-1);
+    DiXing?.add(meshYellow);
+
+    // 蓝 =》 蓝
+    const curve3 = new THREE.CatmullRomCurve3([
+      new THREE.Vector3(784.664, 623.933, -365.355),
+      new THREE.Vector3(167.212, 450.517, -365.355),
+      new THREE.Vector3(7.587, 262.225, -365.355),
+      new THREE.Vector3(426.499, 31.416, -365.355),
+      new THREE.Vector3(261.665, -139.495, -365.355),
+    ]);
+
+    const points = curve3.getPoints(80);
+    points.unshift(new THREE.Vector3(934.695, 623.933, -365.375));
+    points.unshift(new THREE.Vector3(934.695, -141.85, -365.375));
+    points.push(new THREE.Vector3(934.695, -141.85, -365.375));
+    const newPoints: THREE.Vector2[] = [];
+    for (let i = 0; i < points.length; i++) {
+      const point = points[i];
+      newPoints.push(new THREE.Vector2(point.x, point.y));
+    }
+    const shape = new THREE.Shape(newPoints);
+    const geometry = new THREE.ShapeGeometry(shape);
+
+    const material = new THREE.MeshBasicMaterial({ side: THREE.BackSide, color: '#00F' });
+    const mesh = new THREE.Mesh(geometry, material);
+    mesh.position.setZ(-366.485);
+    DiXing?.add(mesh);
+
+    // 黄色-》蓝色
+    const curve2 = new THREE.CatmullRomCurve3([
+      new THREE.Vector3(784.664 - yellowLen, 623.933, -365.355),
+      new THREE.Vector3(167.212 - yellowLen, 450.517, -365.355),
+      new THREE.Vector3(7.587 - yellowLen, 262.225, -365.355),
+      new THREE.Vector3(426.499 - yellowLen, 31.416, -365.355),
+      new THREE.Vector3(261.665 - yellowLen, -139.495, -365.355),
+    ]);
+
+    const curve4 = new THREE.CatmullRomCurve3([
+      new THREE.Vector3(784.664, 623.933, -365.355),
+      new THREE.Vector3(167.212, 450.517, -365.355),
+      new THREE.Vector3(7.587, 262.225, -365.355),
+      new THREE.Vector3(426.499, 31.416, -365.355),
+      new THREE.Vector3(261.665, -139.495, -365.355),
+    ]);
+
+    const points2 = curve2.getPoints(80);
+    const points3 = curve4.getPoints(80);
+    const newPoints1: number[] = [];
+    const normals: number[] = [];
+    const colors: number[] = [];
+    for (let i = 0; i < 80; i++) {
+      newPoints1.push(points2[i].x, points2[i].y, points2[i].z);
+      newPoints1.push(points3[i].x, points3[i].y, points3[i].z);
+      newPoints1.push(points3[i + 1].x, points3[i + 1].y, points3[i + 1].z);
+      newPoints1.push(points3[i + 1].x, points3[i + 1].y, points3[i + 1].z);
+      newPoints1.push(points2[i + 1].x, points2[i + 1].y, points2[i + 1].z);
+      newPoints1.push(points2[i].x, points2[i].y, points2[i].z);
+      normals.push(0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1);
+      colors.push(1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0);
+    }
+    const geometry1 = new THREE.BufferGeometry();
+    geometry1.setAttribute('position', new THREE.BufferAttribute(new Float32Array(newPoints1), 3));
+    geometry1.setAttribute('normal', new THREE.BufferAttribute(new Float32Array(normals), 3));
+    geometry1.setAttribute('color', new THREE.BufferAttribute(new Float32Array(colors), 3));
+    geometry1.computeBoundingBox();
+    const mesh1 = new THREE.Mesh(geometry1, material1);
+    mesh1.name = 'blue';
+    mesh1.position.setZ(-1);
+    DiXing?.add(mesh1);
+  }
+
+  mountedThree() {
+    return new Promise(async (resolve) => {
+      this.model.renderer.sortObjects = true;
+      this.model.orbitControls.update();
+      this.model.setGLTFModel('workFace').then(async (gltf) => {
+        const gltfModal = gltf[0];
+        this.group = gltfModal;
+        this.group.name = this.modelName;
+        setModalCenter(this.group);
+        this.group.scale.set(2.5, 2.5, 2.5);
+        this.addLight();
+        this.setThreePlane();
+        this.setControls();
+        resolve(null);
+      });
+    });
+  }
+
+  destroy() {
+    this.model.clearGroup(this.group);
+    this.model = null;
+    this.group = null;
+    this.bloomComposer?.dispose();
+    this.finalComposer?.dispose();
+  }
+}
+
+export default WorkFaceGas;