Трехмерные логические операции с тремя CSG

По примеру здесь:

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

И используя Three.js с https://github.com/chandlerprall/ThreeCSG, я пытаюсь для выполнения трехмерных логических операций над узлами модели. Например, если у меня есть стена с окном, я хочу сделать 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": []
    }
}"

Я думаю, это потому, что значение geometry.faces.length равно 0.

Как я могу сделать массив вершин правильным Three.Geometry, чтобы грани не были пустыми? Geometry.elementsNeedsUpdate не работает...

Есть ли пример, в котором многоугольники формы используются как массив Vector3 и преобразуются в csg?


person shinzou    schedule 07.06.2017    source источник


Ответы (1)


Я только что работал над демонстрацией, используя THREE csg: сетки Viewer имеют индексированный массив вершин, поэтому вы не можете создать BSP непосредственно из него. Кроме того, мой код использует веб-воркер для обработки сеток, чтобы пользовательский интерфейс реагировал на большие модели, поэтому мне нужно сначала отправить данные сетки в воркер и реконструировать простую 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 и демо-версия здесь.

person Philippe    schedule 07.06.2017
comment
Работает ли этот пример на всех узлах модели? Поэтому вам понадобился веб-воркер? - person shinzou; 07.06.2017
comment
Не все узлы, только стены и полы, но это требует много ресурсов процессора, если вы имеете дело с большой моделью. Использование веб-воркеров предотвращает сбои браузера и отзывчивость пользовательского интерфейса. Часть, которая преобразует один узел в ThreeBSP, — это весь код, который я только что опубликовал. Внимательно посмотрите, что он делает. Вы можете видеть, что он создает новый ThreeBSP в конце метода buildComponentMesh. Должно быть довольно просто адаптировать логику, если вы не хотите использовать работника. - person Philippe; 07.06.2017
comment
Я вижу, большое спасибо. Вы пытались использовать метод вычитания csg? Я получаю сообщение об ошибке здесь: github.com/chandlerprall/ThreeCSG/ blob/master/threeCSG.es6#L528, что разделитель не определен, когда я пытаюсь инвертировать геометрию следующим образом: let a = new ThreeBSP(new THREE.Geometry()); a.subtract(geometryCsg) - person shinzou; 07.06.2017
comment
Вы добавляете какие-то вершины/грани в эту новую геометрию или это весь код? Вы вычитаете что-то из пустой геометрии, я не уверен, что это должно работать. 3 примера на github.com/chandlerprall/ThreeCSG/blob/master/examples. html работают нормально, поэтому проблема должна заключаться в ваших входных данных. - person Philippe; 07.06.2017
comment
Я хочу инвертировать форму, у них есть обратный метод в оригинальной CSG: evanw.github. io/csg.js/docs, но похоже, что ThreeCSG не имеет этого метода. - person shinzou; 08.06.2017
comment
Это звучит как вопрос к автору библиотеки, извините, мои познания в CSG ограничены предоставленными простыми примерами, поэтому ваш вопрос немного не по теме в отношении Forge и Viewer. - person Philippe; 08.06.2017
comment
Да я понял это. Хотя библиотека выглядит заброшенной. Спасибо, в любом случае. - person shinzou; 08.06.2017
comment
Я вижу, что у него есть обратный метод: github.com/chandlerprall/ThreeCSG /blob/мастер/ThreeCSG.js#L508 - person Philippe; 08.06.2017
comment
Я думаю, что это личное, потому что это не входит в область действия при выполнении const geometryCsg = new ThreeBSP(geometryThree); geometryCsg.invert(); - person shinzou; 08.06.2017
comment
Кажется, он находится в объекте Node, его использование должно быть довольно простым, хотя я не пробовал: var inv = bsp.tree.invert(); новый ThreeBSP (inv): github.com/chandlerprall/ThreeCSG/blob/ мастер/ThreeCSG.js#L91 - person Philippe; 08.06.2017