threejs 笔记 —— 高频使用工具整理

访问模型信息

const THREE = require("three");
export default class GetBox {
  public box: any
  constructor(box) {
    this.box = box;
  }

  // 返回box3
  getBox() {
    let b = new THREE.Box3();
    b.expandByObject(this.box);
    return b;
  }
  // 获取世界中心坐标
  getWp(v3) {
    this.box.parent.updateMatrixWorld(true);
    this.box.getWorldPosition(v3)
  }

  // 返回中心点坐标
  getCenter(c) {
    this.getBox().getCenter(c);
  }
  // 如果中心点没在模型上,返回距离中心点最近的顶点
  getRayCenter(c) {
    this.getCenter(c)
    // 获取中心点
    let wd = new THREE.Vector3()
    this.getWp(wd) // 获取世界坐标

    const size = new THREE.Vector3()
    this.getSize(size)  // 获取模型尺寸

    const star = new THREE.Vector3(c.x, c.y + size.y, c.z)   // 设置射线起点

    const end = new THREE.Vector3(c.x, c.y - size.y, c.z) // 设置射线终点

    const v3 = new THREE.Vector3()
    var raycaster = new THREE.Raycaster(star, end); // 创建一个正向射线

    var intersects = raycaster.intersectObjects([this.box]);  // 设置相交物体
    if (intersects.length === 0) { // 判断相交物体数量是否为0,如果不为0 则中心点在模型内
      let minLength: number = 0
      let minV3: any = new THREE.Vector3()
      let position = this.box.geometry.getAttribute('position')
      const pArr = position.array // 所有顶点集合
      const pI = position.count // 顶点数量
      const itemsize = position.itemSize  // 
      for (let i = 0; i < pI; i++) {
        const limit = i * itemsize  // 每个顶点都是一个三元组。
        v3.fromArray(pArr, limit)
        v3.add(wd)  // 世界坐标向量添加至顶点,确定顶点具体位置
        let p3 = c.clone().sub(v3); //两点之间的中心点
        let l = p3.length(); // 两点之间的距离
        if (i == 0) {
          minLength = l
          minV3.copy(v3)
        } else {
          if (minLength > l) {
            minLength = l
            minV3.copy(v3)
          }
        }
      }
      c.copy(minV3)
    }
  }

  // 返回尺寸
  getSize(size) {
    this.getBox().getSize(size);
  }
  // 矩形表面积
  areaSize(): number {
    let p = new THREE.Vector3()
    this.getSize(p)
    let a = p.x,
      b = p.z,
      s = a * b;
    return s;
  }
  // 返回vector3是否在当前box3内
  in(p) {
    return this.getBox().containsPoint(p);
  }
}

加载器

/**
 * @desc 加载器
 */
import { GLTFLoader } from "../../node_modules/three/examples/jsm/loaders/GLTFLoader.js";
import { DRACOLoader } from "../../node_modules/three/examples/jsm/loaders/DRACOLoader.js";
import { SVGLoader } from "../../node_modules/three/examples/jsm/loaders/SVGLoader.js";
import { FBXLoader } from "../../node_modules/three/examples/jsm/loaders/FBXLoader.js";
interface Loader {
  url: string
  model: any
  loader: any
  create(url: string): void
}
class LoadGltf implements Loader {
  public loader: any | undefined
  public url: string | undefined
  public model: any | undefined
  constructor() {

  }
  create(url: string) {
    this.url = url
    this.loader = new GLTFLoader();
    return new Promise(
      (resolve) => {
        this.loader.setDRACOLoader(new DRACOLoader());
        this.loader.load(this.url, (gltf: any) => {
          this.model = gltf.scene
          resolve(this.model)
        });
      }
    )
  }
}
class LoadSvg implements Loader {
  public loader: any | undefined
  public url: string | undefined
  public model: any | undefined
  constructor() {
  }
  create(url: string) {
    this.url = url
    this.loader = new SVGLoader();
    return new Promise(
      (resolve) => {
        this.loader.load(this.url, (data: any): void => {
          resolve(data)
        })
      }
    )
  }
}
class LoadFbx implements Loader {
  public loader: any | undefined
  public url: string | undefined
  public model: any | undefined
  constructor() {
  }
  create(url: string) {
    this.url = url
    this.loader = new FBXLoader();
    return new Promise(
      (resolve) => {
        this.loader.load(this.url, (data: any): void => {
          resolve(data)
        })
      }
    )
  }
}
export {
  LoadGltf,
  LoadSvg,
  LoadFbx
}

射线

点射线
import GetBox from "./getBox";

const THREE = require("three");
export default class Raycaster {
  raycaster: any
  intersects: any
  // 检测点是否在模型范围内
  pointRay(v3, child) {
    if (v3.isVector3) {
      let nv3 = v3.clone() // 克隆一个新的位置信息,这样不会影响传入的三维向量的值

      let b = new THREE.Box3();
      b.expandByObject(child); // 设置模型的包围盒

      const star = nv3.clone() // 设置射线的起点
      const end = nv3.clone().normalize() // 将起点转为单位向量
      this.raycaster = new THREE.Raycaster(star, end); // 创建一个正向射线
      if (child.isGroup) this.intersects = this.raycaster.intersectObjects(child.children, true);
      else if (child.isMesh) this.intersects = this.raycaster.intersectObjects([child]);
      return this.intersects
    }
  }
  // 检测两个模型是否相交
meshRay(mesh, child) {
    let boom = 0
    let box3 = new GetBox(mesh)
    let worldPosition = new THREE.Vector3()
    box3.getWp(worldPosition)
    
    if (mesh.geometry.isGeometry) {
      let v3s = mesh.geometry.vertices
      for (let i = 0; i < v3s.length; i++) {
        let v3 = v3s[i].clone()
        let nv3 = v3.add(worldPosition)
        let len = this.pointRay(nv3, child)
        if (len.length !== 0) {
          boom++
        }
      }
    } else if (mesh.geometry.isBufferGeometry) {
      let position = mesh.geometry.getAttribute('position')
      let pcount = position.count // 材质一共有多少个点位信息
      let v3 = new THREE.Vector3()
      for (let i = 0; i < pcount; i++) {
        v3.fromBufferAttribute(position, i)
        let nv3 = v3.add(worldPosition)
        let len = this.pointRay(nv3, child)
        if (len.length !== 0) {
          boom++
        }
      }
    }
    if(boom) return true
        else return false
    }
}

屏幕坐标转世界坐标

vector2to3(point, v3?) {let v2Arr = [
      (point[0] / window.innerWidth) * 2 - 1,
      -(point[1] / window.innerHeight) * 2 + 1
    ];
    // 通过射线 将2D转为3D
    var raycaster = new THREE.Raycaster();
    let v2 = new THREE.Vector2(v2Arr[0], v2Arr[1]);
    raycaster.setFromCamera(v2, this.camera);
    this.camera.updateProjectionMatrix()
    // 获得最终的3D位置
    let eV3 = raycaster.ray.origin;
    if(v3) v3.copy(eV3)
    return eV3
}
应用
this.div = document.createElement('div')
this.div.setAttribute('class', 'box')
document.body.appendChild(this.div)
this.box(new THREE.Vector3())
box(v3) {var geometry = new THREE.BoxGeometry(10, 10, 10);
    var material = new THREE.MeshBasicMaterial({ color: 0xff0000 });
    var cube = new THREE.Mesh(geometry, material);
    cube.position.copy(v3)
    this.cube = cube
    this.scene.add(this.cube )
}
.box{
    width:20px;
    height: 20px;
    background: #ff00ff;
    position: fixed;
    z-index: 999;
    left:600px;
    top: 600px
}
// render渲染函数内
if (this.cube && this.div) {
  let x = this.div.offsetLeft
  let y = this.div.offsetTop
  let point1 = [x, y]
  let v3 = new THREE.Vector3()
  this.vector2to3(point1, v3)
  v3.addScalar(-10)
  this.cube.position.copy(v3)
}
效果展示

世界坐标转屏幕坐标

getViewCp(v3, v2) {var worldVector = v3.clone();
    var standardVector = worldVector.project(this.camera); //世界坐标转标准设备坐标
    var a = window.innerWidth / 2;
    var b = window.innerHeight / 2;
    var vx = Math.round(standardVector.x * a + a); //标准设备坐标转屏幕坐标
    var vy = Math.round(-standardVector.y * b + b); //标准设备坐标转屏幕坐标
    v2.copy(new THREE.Vector2(vx, vy))
}

let btn = document.getElementById('btn')
if (btn) {
  btn.onclick = () => {
    let worldVector = new THREE.Vector3()
    this.box3.getWp(worldVector)
    let v2 = new THREE.Vector2()
    this.getViewCp(worldVector, v2)

    this.div.style.left = v2.width - this.div.offsetWidth / 2 + 'px'
    this.div.style.top = v2.height - this.div.offsetHeight / 2 + 'px'
  }
}

效果展示
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

孙华鹏

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值