三个 CSG 的 3D 布尔运算

Posted

技术标签:

【中文标题】三个 CSG 的 3D 布尔运算【英文标题】:3D Boolean operations with Three CSG 【发布时间】:2017-11-08 17:01:53 【问题描述】:

按照这里的例子:

http://learningthreejs.com/blog/2011/12/10/constructive-solid-geometry-with-csg-js/

使用 Three.js 和 https://github.com/chandlerprall/ThreeCSG,我正在尝试对模型中的节点进行 3D 布尔运算。例如,如果我有一堵带窗户的墙,我想在上面做invert() 以获得窗户。

我有一个函数可以返回一个节点的多边形的所有顶点,这是一个没有孔的对象的顶点示例https://pastebin.com/8dhYzPwE。

我正在像这样使用 ThreeCSG:

    const geometryThree = new THREE.Geometry();

    geometryThree.vertices.push(
        ...vertices
    );

    const geometryCsg = new ThreeBSP(geometryThree);

但这就是我在geometryCsg 得到的:

"
    "matrix": 
        "elements": 
            "0": 1,
            "1": 0,
            "2": 0,
            "3": 0,
            "4": 0,
            "5": 1,
            "6": 0,
            "7": 0,
            "8": 0,
            "9": 0,
            "10": 1,
            "11": 0,
            "12": 0,
            "13": 0,
            "14": 0,
            "15": 1
        
    ,
    "tree": 
        "polygons": []
    
"

我想是因为the geometry.faces.length is 0。

如何使顶点数组成为正确的 Three.Geometry 以使面不会为空? Geometry.elementsNeedsUpdate 不起作用...

有没有使用一个形状的多边形作为 Vector3s 数组并将其转换为 csg 的示例?

【问题讨论】:

【参考方案1】:

我刚刚使用三个 csg 进行了演示:查看器网格有一个索引顶点数组,因此您不能直接从中创建 BSP。此外,我的代码使用网络工作者来处理网格,以保持 UI 对大型模型的响应,所以我需要首先将网格数据发送给工作者并在工作者端重建一个简单的 THREE.Mesh,代码看起来如下:

// Sends component geometry to the web worker  
postComponent (dbId) 

  const geometry = this.getComponentGeometry(dbId)

  const msg = 
    boundingBox: this.getComponentBoundingBox(dbId),
    matrixWorld: geometry.matrixWorld,
    nbMeshes: geometry.meshes.length,
    msgId: 'MSG_ID_COMPONENT',
    dbId
  

  geometry.meshes.forEach((mesh, idx) => 

    msg['positions' + idx] = mesh.positions
    msg['indices' + idx] = mesh.indices
    msg['stride' + idx] = mesh.stride
  )

  this.worker.postMessage(msg)


// get geometry for all fragments in a component
getComponentGeometry (dbId) 

  const fragIds = Toolkit.getLeafFragIds(
    this.viewer.model, dbId)

  let matrixWorld = null

  const meshes = fragIds.map((fragId) => 

    const renderProxy = this.viewer.impl.getRenderProxy(
      this.viewer.model,
      fragId)

    const geometry = renderProxy.geometry

    const attributes = geometry.attributes

    const positions = geometry.vb
      ? geometry.vb
      : attributes.position.array

    const indices = attributes.index.array || geometry.ib

    const stride = geometry.vb ? geometry.vbstride : 3

    const offsets = geometry.offsets

    matrixWorld = matrixWorld ||
    renderProxy.matrixWorld.elements

    return 
      positions,
      indices,
      offsets,
      stride
    
  )

  return 
    matrixWorld,
    meshes
  



// On the worker side reconstruct THREE.Mesh
// from received data and create ThreeBSP
function buildComponentMesh (data) 

  const vertexArray = []

  for (let idx=0; idx < data.nbMeshes; ++idx) 

    const meshData = 
      positions: data['positions' + idx],
      indices: data['indices' + idx],
      stride: data['stride' + idx]
    

    getMeshGeometry (meshData, vertexArray)
  

  const geometry = new THREE.Geometry()

  for (var i = 0; i < vertexArray.length; i += 3) 

    geometry.vertices.push(vertexArray[i])
    geometry.vertices.push(vertexArray[i + 1])
    geometry.vertices.push(vertexArray[i + 2])

    const face = new THREE.Face3(i, i + 1, i + 2)

    geometry.faces.push(face)
  

  const matrixWorld = new THREE.Matrix4()

  matrixWorld.fromArray(data.matrixWorld)

  const mesh = new THREE.Mesh(geometry)

  mesh.applyMatrix(matrixWorld)

  mesh.boundingBox = data.boundingBox

  mesh.bsp = new ThreeBSP(mesh)

  mesh.dbId = data.dbId

  return mesh


function getMeshGeometry (data, vertexArray) 

  const offsets = [
    count: data.indices.length,
    index: 0,
    start: 0
  ]

  for (var oi = 0, ol = offsets.length; oi < ol; ++oi) 

    var start = offsets[oi].start
    var count = offsets[oi].count
    var index = offsets[oi].index

    for (var i = start, il = start + count; i < il; i += 3) 

      const a = index + data.indices[i]
      const b = index + data.indices[i + 1]
      const c = index + data.indices[i + 2]

      const vA = new THREE.Vector3()
      const vB = new THREE.Vector3()
      const vC = new THREE.Vector3()

      vA.fromArray(data.positions, a * data.stride)
      vB.fromArray(data.positions, b * data.stride)
      vC.fromArray(data.positions, c * data.stride)

      vertexArray.push(vA)
      vertexArray.push(vB)
      vertexArray.push(vC)
    
  

我的示例的完整代码在那里:Wall Analyzer 和现场演示 there。

【讨论】:

此示例是否适用于模型中的所有节点?这就是你需要网络工作者的原因吗? 不是所有节点,只有墙壁和地板,但如果您处理大型模型,则需要大量 CPU。使用 web worker 可以防止浏览器崩溃和 UI 响应。将单个节点转换为 ThreeBSP 的部分是我刚刚发布的整个代码。仔细看看它在做什么。您可以看到它在“buildComponentMesh”方法的末尾创建了一个新的 ThreeBSP。如果您不想使用工作人员,调整逻辑应该相当简单。 我明白了,非常感谢。您是否尝试过使用 csg 减法?我在这里遇到错误:github.com/chandlerprall/ThreeCSG/blob/master/threeCSG.es6#L528 当我尝试像这样反转几何图形时,分隔符未定义:let a = new ThreeBSP(new THREE.Geometry()); a.subtract(geometryCsg) 您是在向这个新几何图形添加一些顶点/面还是所有代码?你正在从一个空的几何图形中减去一些东西,我不确定这是否应该起作用。 github.com/chandlerprall/ThreeCSG/blob/master/examples.html 的 3 个示例运行良好,所以问题一定出在您的输入上。 我想反转形状,他们在原始 CSG 中有一个反转方法:evanw.github.io/csg.js/docs 但看起来 ThreeCSG 没有这个方法。

以上是关于三个 CSG 的 3D 布尔运算的主要内容,如果未能解决你的问题,请参考以下文章

使用OR将3D布尔数组缩减为2D

RvmTranslator-STEP模型简化

ledit布尔运算完怎么查看单个的object

第一章 数据储存 1.1 位与位存储

js布尔值

三元运算符