如何在 Three.js 中使用 RayCaster 拾取文本网格?

How can I pick up text mesh with the RayCaster in Three.js?

所以,我 运行 遇到了使用 RayCaster 来获取 TextGeometry 的问题。我正在使用下面的函数 createText() 将此文本呈现到 canvas 上。这工作正常,它显示出来,它甚至将它添加到 RayCaster 的网格数组中,它检查交集,但是,函数 HandleSingleClick() 由于某种原因没有在 TextGeometries 上触发。

我已经使用多维数据集进行了测试,它确实有效。目的是如果您单击 TextGeometry,它会更改颜色以表示您单击了它。但是,由于某种原因,以下脚本在涉及文本时不起作用。我可以确认网格被添加到 RayCaster 签入的阵列中,但由于某种原因它没有拾取它们。

import {
  Font,
  TextGeometry
} from '../js/libs/Three.es.js';
import FontJson from '../fonts/helvetiker_bold.typeface.json';

export default class TextExtension extends Autodesk.Viewing.Extension {

  constructor(viewer, options) {

      super()

      this.viewer = viewer
  }

  load() {
      console.log('Viewing.Extension.MeshSelection loaded')

      this.viewer.addEventListener(
          Autodesk.Viewing.OBJECT_TREE_CREATED_EVENT, () => {

              this.dbIds = this.getAllDbIds()
              console.log(this.dbIds);

          });
      this.viewer.toolController.registerTool(this)

      this.viewer.toolController.activateTool(
          'MeshSelection')

      this.intersectMeshes = [];
      return true
  }


  /////////////////////////////////////////////////////////
  // Tool Interface
  //
  /////////////////////////////////////////////////////////
  getNames() {

      return ['MeshSelection']
  }

  activate() {

  }

  deactivate() {

  }

  /////////////////////////////////////////////////////////
  // Unload callback
  //
  /////////////////////////////////////////////////////////
  unload() {

      console.log('MeshSelection unloaded')

      this.viewer.toolController.deactivateTool(
          'MeshSelection')

      this.viewer.toolController.unregisterTool(this)

      return true
  }

  /////////////////////////////////////////////////////////
  // Adds a box mesh with random size and position
  // to the scene
  //
  /////////////////////////////////////////////////////////
  addMesh() {

      const geometry = new THREE.BoxGeometry(
          Math.random() * 10 + 5.0,
          Math.random() * 10 + 5.0,
          Math.random() * 10 + 5.0)

      const color = Math.floor(Math.random() * 16777215)

      const material = this.createColorMaterial(color)

      const mesh = new THREE.Mesh(geometry, material)

      mesh.position.x = -50 + Math.random() * 25
      mesh.position.y = -50 + Math.random() * 25
      mesh.position.z = 1 + Math.random() * 1

      this.viewer.impl.scene.add(mesh)

      this.viewer.impl.sceneUpdated(true)

      return mesh
  }

  /////////////////////////////////////////////////////////
  // Creates color material from int
  //
  /////////////////////////////////////////////////////////
  createColorMaterial(color) {

      const material = new THREE.MeshPhongMaterial({
          specular: new THREE.Color(color),
          side: THREE.DoubleSide,
          reflectivity: 0.0,
          color
      })

      const materials = this.viewer.impl.getMaterials()

      materials.addMaterial(
          color.toString(16),
          material,
          true)

      return material
  }

  /////////////////////////////////////////////////////////
  // Creates Raycaster object from the pointer
  //
  /////////////////////////////////////////////////////////
  pointerToRaycaster(domElement, camera, pointer) {

      const pointerVector = new THREE.Vector3()
      const pointerDir = new THREE.Vector3()
      const ray = new THREE.Raycaster()

      const rect = domElement.getBoundingClientRect()

      const x = ((pointer.clientX - rect.left) / rect.width) * 2 - 1
      const y = -((pointer.clientY - rect.top) / rect.height) * 2 + 1

      if (camera.isPerspective) {

          pointerVector.set(x, y, 0.5)

          pointerVector.unproject(camera)

          ray.set(camera.position,
              pointerVector.sub(
                  camera.position).normalize())

      } else {

          pointerVector.set(x, y, -1)

          pointerVector.unproject(camera)

          pointerDir.set(0, 0, -1)

          ray.set(pointerVector,
              pointerDir.transformDirection(
                  camera.matrixWorld))
      }

      return ray
  }

  /////////////////////////////////////////////////////////
  // Click handler
  //
  /////////////////////////////////////////////////////////
  handleSingleClick(event) {
      console.log(this.intersectMeshes);

      const pointer = event.pointers ?
          event.pointers[0] :
          event
      console.log(pointer);
      const rayCaster = this.pointerToRaycaster(
          this.viewer.impl.canvas,
          this.viewer.impl.camera,
          pointer)

      const intersectResults = rayCaster.intersectObjects(
          this.intersectMeshes, true)
      console.log(intersectResults);

      const hitTest = this.viewer.model.rayIntersect(
          rayCaster, true, this.dbIds)

      const selections = intersectResults.filter((res) =>

          (!hitTest || (hitTest.distance > res.distance))
      )

      if (selections.length) {

          console.log('Custom meshes selected:')
          console.log(selections)
          selections[0].object.material.color = this.createColorMaterial(Math.floor(Math.random() * 16777215));
          viewer.impl.sceneUpdated(true);
          return true
      }

      return false
  }

  /////////////////////////////////////////////////////////
  // Get list of all dbIds in the model
  //
  /////////////////////////////////////////////////////////
  getAllDbIds() {

      const {
          instanceTree
      } = this.viewer.model.getData()

      const {
          dbIdToIndex
      } = instanceTree.nodeAccess

      return Object.keys(dbIdToIndex).map((dbId) => {
          return parseInt(dbId)
      })
  }

  createColorMaterial(color) {

      const material = new THREE.MeshPhongMaterial({
          specular: new THREE.Color(color),
          side: THREE.DoubleSide,
          reflectivity: 0.0,
          color
      })

      const materials = this.viewer.impl.getMaterials()

      materials.addMaterial(
          color.toString(),
          material,
          true)

      return material
  }


  /////////////////////////////////////////////////////////
  // Wraps TextGeometry object and adds a new mesh to  
  // the scene
  /////////////////////////////////////////////////////////
  createText(params, index) {
      const geometry = new TextGeometry(params.text,
          Object.assign({}, {
              font: new Font(FontJson),
              params
          }))
      geometry.computeBoundingBox();

      const material = this.createColorMaterial(
          params.color)

      const text = new THREE.Mesh(
          geometry, material)

      text.scale.set(params.scale, params.scale, params.scale);

      text.position.set(
          params.position.x,
          params.position.y,
          10)


      this.intersectMeshes[index] = text;
      this.viewer.impl.scene.add(text);

      this.viewer.impl.sceneUpdated(true)
  }
}

Autodesk.Viewing.theExtensionManager.registerExtension(
  'TextSpawner', TextExtension);

您需要调试光线投射逻辑以了解发生了什么。在查看器使用的 Three.js 版本中,有一个检查来查看来自测试网格的几何对象是实例 THREE.BufferGeometry 还是 THREE.Geometry,如果 none 交集不计算逻辑。

为了兼容它同时尽量少加代码,我采用了如下做法:

  /////////////////////////////////////////////////////////
  // Wraps TextGeometry object and adds a new mesh to
  // the scene
  /////////////////////////////////////////////////////////
  createText (params) {

    const textGeometry = new TextGeometry(params.text,
      Object.assign({}, {
        font: new Font(FontJson),
        params
      }))

    // use a geometry recognized by the viewer 
    // THREE.js version
    const geometry = new THREE.BufferGeometry

    geometry.fromGeometry(textGeometry)

    const material = this.createColorMaterial(
      params.color)

    const text = new THREE.Mesh(
      geometry , material)

    text.position.set(
      params.position.x,
      params.position.y,
      params.position.z)

    this.viewer.impl.scene.add(text)

    this.viewer.impl.sceneUpdated(true)

    return text
  }