// three.js
import * as THREE from 'three';

import SpriteText from 'three-spritetext';
// 引入tween
import * as TWEEN from '@tweenjs/tween.js';
import type {
  Group,
  Scene,
  Line,
  Sprite,
  Texture,
  Vector3,
  Vector2,
} from 'three';
import type { GUI } from 'dat.gui';
import type { IPos } from '@/threejs/modules/map/map3d';

import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { ColladaLoader } from 'three/examples/jsm/loaders/ColladaLoader';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
// 导入雷达特效
import Radar from '@/threejs/modules/effect/radar';

import type { IBezierPath } from '../map/type';

interface IPosition {
  x: number;
  y: number;
}

class AGV3D {
  scene: Scene;
  agv: Group;
  gui: GUI;
  // agv小车的高度
  depth: number;
  // 小车的贴图
  textInfo: SpriteText;
  // 小车当前的位置
  curPosition: IPosition;
  startPos: IPos;
  // 雷达特效
  radarMesh: any;

  constructor(scene: Scene, gui: GUI, startPos: IPos) {
    this.scene = scene;
    this.gui = gui;
    this.depth = 0.5;
    this.startPos = startPos;
    this.curPosition = { x: 0, y: 0 };
    this.textInfo = this.createSign();
    this.agv = this.createAGV();
  }

  createAGV() {
    const textureLoader = new THREE.TextureLoader();
    const agv = new THREE.Group();
    //因为需要贴图的原因,所以设置长宽高的时候,先是按照y轴向上的坐标系进行设置,然后进行旋转,转换成z轴向上的坐标系
    const geometry = new THREE.BoxGeometry(1, this.depth, 2);
    geometry.rotateX(Math.PI * 0.5);

    const material = new THREE.MeshStandardMaterial({
      color: 0xffffff,
      map: textureLoader.load('/texture/auo.png'),
    });

    const agvcar = new THREE.Mesh(geometry, material);
    agvcar.material.opacity = 0.8;
    agvcar.material.transparent = true;
    agvcar.name = 'agv';
    this.gui.add(agv.position, 'x').min(-25).max(0).step(0.01);
    agv.position.set(this.startPos.x, this.startPos.y, this.depth * 0.5);
    // agv.position.set(0, 0, this.depth * 0.5);
    // 初始化雷达特效
    const radarMesh = this.initRadar();
    // agv.add(agvcar, this.textInfo, radarMesh);
    agv.add(agvcar);
    this.scene.add(agv);
    return agv;
  }

  initRadar() {
    const radarData = {
      position: {
        x: 0,
        y: 0,
        z: 0,
      },
      radius: 3,
      color: '#ff0062',
      opacity: 0.5,
      speed: 1,
    };
    const radarMesh = Radar(radarData);
    this.radarMesh = radarMesh;
    return radarMesh;
  }

  private createSign() {
    // 创建一个文字精灵图
    const textInfo = new SpriteText('AGV小车', 0.01);
    textInfo.name = 'label';
    textInfo.color = 'yellow';
    textInfo.borderWidth = 0.2;
    textInfo.borderRadius = 2;
    textInfo.backgroundColor = 'rgba(0,122,204,0.5)';
    textInfo.padding = 0.1;

    textInfo.position.z = this.depth * 2.5;
    // textInfo.material.visible = false
    return textInfo;
  }

  public move(target: IPosition) {
    const tween = new TWEEN.Tween(this.curPosition);
    tween.to(target, 10000).onUpdate((object) => {
      this.agv.position.x = object.x;
      this.agv.position.y = object.y;
    });
    tween.start();
    this.curPosition = target;
  }

  public moveByRail() {
    const railPoint = {
      className: 'BezierPath',
      instanceName: 'LM1-LM10',
      startPos: {
        instanceName: 'LM1',
        pos: {
          x: 1.234,
          y: 3.379,
        },
      },
      endPos: {
        instanceName: 'LM10',
        pos: {
          x: -1.876,
          y: 0.246,
        },
      },
      controlPos1: {
        x: 1.322,
        y: 0.242,
      },
      controlPos2: {
        x: -0.815,
        y: 0.246,
      },
    };
    const { rail, points } = this.mapDegenerateBezierPath(railPoint);
    this.scene.add(rail);

    let i = 0;
    let tween;
    const agv = this.agv;

    points.unshift(new THREE.Vector2(agv.position.x, agv.position.y));

    let tempAngle = 0;
    let rotateAngle = 0;
    function move() {
      if (i + 1 >= points.length - 1) return;
      tween = new TWEEN.Tween({
        x: points[i].x,
        y: points[i].y,
        angle: tempAngle,
      });
      tween
        .to(
          {
            x: points[i + 1].x,
            y: points[i + 1].y,
            angle: getAngle(points[i], points[i+1]),
          },
          100
        )
        .onUpdate((object) => {
          agv.position.x = object.x;
          agv.position.y = object.y;
          rotateAngle = object.angle - tempAngle;
          agv.rotateZ(-rotateAngle);
          tempAngle = object.angle;
        })
        .onComplete(() => {
          move();
        })
        .start();
      i++;
    }

    function getAngle(point1: Vector2, point2: Vector2) {
      const point3 = new THREE.Vector2(point1.x, point2.y);
      const v1 = point3.clone().sub(point1);
      const v2 = point2.clone().sub(point1);
      const angle = v1.angle() - v2.angle();
      return angle;
    }
    move();

  }

  private mapDegenerateBezierPath(bezierPath: IBezierPath) {
    const startPos = bezierPath['startPos']['pos'];
    const endPos = bezierPath['endPos']['pos'];
    const controlPos1 = bezierPath['controlPos1'];
    const controlPos2 = bezierPath['controlPos2'];

    const curve = new THREE.CubicBezierCurve(
      new THREE.Vector2(startPos.x, startPos.y),
      new THREE.Vector2(controlPos1.x, controlPos1.y),
      new THREE.Vector2(controlPos2.x, controlPos2.y),
      new THREE.Vector2(endPos.x, endPos.y)
    );

    const points = curve.getPoints(50);
    const geometry = new THREE.BufferGeometry().setFromPoints(points);
    const material = new THREE.LineBasicMaterial({ color: 0xffffff });
    // Create the final object to add to the scene
    const curveObject = new THREE.Line(geometry, material);
    return { rail: curveObject, points };
  }
}

export { AGV3D };

原文地址:http://www.cnblogs.com/zhuoss/p/16899335.html

1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长! 2. 分享目的仅供大家学习和交流,请务用于商业用途! 3. 如果你也有好源码或者教程,可以到用户中心发布,分享有积分奖励和额外收入! 4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解! 5. 如有链接无法下载、失效或广告,请联系管理员处理! 6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需! 7. 如遇到加密压缩包,默认解压密码为"gltf",如遇到无法解压的请联系管理员! 8. 因为资源和程序源码均为可复制品,所以不支持任何理由的退款兑现,请斟酌后支付下载 声明:如果标题没有注明"已测试"或者"测试可用"等字样的资源源码均未经过站长测试.特别注意没有标注的源码不保证任何可用性