LIBGDX 3D: невозможно точно определить, какой треугольник сетки попадает под луч камеры - PullRequest
0 голосов
/ 09 февраля 2019

Я учусь использовать LIBGDX для создания похожего на майнкрафт мира, созданного из текстурированных блоков (кубов).Я визуализировал трехмерный мир, и движение игрока работает безупречно (результат и опыт движения в трехмерном мире ощущается, как и ожидалось).

Теперь я стремлюсь определить, на какую сторону / границу блока указываюткамерой от первого лица.С помощью Intersector.intersectRayBounds мне удалось определить, на какой блок указывает.Я рисую ограничивающую рамку вокруг найденного блока для визуального результата.

Следующим шагом является визуальная маркировка точной стороны / грани блока (куба).Я пытаюсь добиться этого с помощью Intersector.intersectRayTriangles после итерации по списку частей меша найденного блока и получения индексов / вершин меша.

В настоящее время я могу пометитьсторона / сторона куба, на которую указывает, однако, это неточно.Только при «наведении» на блок в определенном углу это приведет к положительному пересечению - как будто я могу проверить только подмножество треугольников, а не все.

Пример кода, чтобы определить, какой блок (куб) указывает на:

Ray ray = camera.getPickRay(1280/2, 720/2+BLOCK_SIZE);//, 0, 0, 1280, 720);
float distance = -1f;
float distance2;
Vector3 position = new Vector3();
Vector3 intersection = new Vector3();
BoundingBox bb = new BoundingBox();
for (BlockBase3D block:mBlocksArray) {
  bb.set(block.getBoundingBox());
  bb.mul(block.getModelInstance().transform);
  position.set(block.getCenterPosition());
  distance2 = ray.origin.dst2(position);
  if (distance >= 0f && distance2 > distance) continue;
  if (Intersector.intersectRayBounds(ray, bb, intersection)) {
    mBlockPointed = block;
    distance = distance2;
  }
}
mBlockPointed.setIsShowingBoundingBox(true);

Эта часть кода приводит к найденному блоку mBlockPointed.

В следующей части кода я пытаюсь определить точную сторону / граньблок, на который указывают:

if (mBlockPointed != null){        
  NodePart np = null;
  MeshPart mp = null;
  float[] meshPartVertices = {};
  short[] meshPartIndices = {};

  Mesh mesh;
  int vertexSize;
  ray = camera.getPickRay(1280/2, 720/2+BLOCK_SIZE);

  for (int j=0; j<mBlockPointed.getModelInstance().nodes.size; j++){
    for (int i = 0; i < mBlockPointed.getModelInstance().nodes.get(j).parts.size; i++) {
      np = mBlockPointed.getModelInstance().nodes.get(j).parts.get(i);
      mp = np.meshPart;
      mesh = mp.mesh.copy(false);
      mesh.transform(mBlockPointed.getModelInstance().transform);
      meshPartIndices = new short[mp.size];
      mesh.getIndices(mp.offset, mp.size, meshPartIndices, 0);
      vertexSize = mesh.getVertexSize() / 4;
      meshPartVertices = new float[mesh.getNumVertices() * vertexSize];
      mesh.getVertices(meshPartVertices);

      if (Intersector.intersectRayTriangles(ray, meshPartVertices, meshPartIndices, vertexSize, null)) {
        np.material.set(mSelectionMaterial);
        //break;
      }                        
    }
  }

Я думал об этой части кода:

  • Итерировать по всем узлам экземпляра модели (в настоящее время задействован только один узел)
  • Итерироватьпо всем частям узла узла (ей) экземпляра модели (6 для каждой стороны / грани блока)
  • Получить сетку узла и преобразовать сетку в соответствии с найденным блоком, на который указывает (чтобы получить мировую позицию,вращение и масштабирование).
  • получить индексы, основанные на смещении в буфере индексов для текущей части сетки
  • получить все вершины сетки, основываясь на количестве вершин и размереиспользуемой вершины
  • выполнить тест на пересечение треугольников для данного луча и индексов / вершин для текущей части сетки

В результате при перемещении (перспективы-)Камера вокруг я получаю положительный результат только тогда, когда камера указывает на одну из угловых областей блока.Я не могу придумать причину этого, которая, если бы я мог, могла заставить меня решить эту проблему.

РЕДАКТИРОВАТЬ - рабочий код после использования ModelInstance.Renderer.worldTransform вместо ModelInstance.transform

Также обновлен мой код с некоторыми встроенными комментариями и оптимизацией.

public void detectObjectPointed(int screenX, int screenY, PerspectiveCamera camera){
        Ray ray = camera.getPickRay(1280/2, 720/2+BLOCK_SIZE);
        float distance = -1f;
        float distance2;

        // if previous block found, restore original materials
        if (mBlockPointed != null){
            for (int i = 0; i < mBlockPointed.getModelInstance().nodes.get(0).parts.size; i++) {
                mBlockPointed.getModelInstance().nodes.get(0).parts.get(i).material.set(mOriginalMaterials.get(i));
            }
            mBlockPointed.setIsShowingBoundingBox(false);
            mBlockPointed = null;
        }

        // attempt to find block pointing at by camera ray
        for (BlockBase3D block:mBlocksArray) {
            mBlockPointedBoundingBox.set(block.getBoundingBox());
            mBlockPointedBoundingBox.mul(block.getModelInstance().transform);
            mBlockPointedPosition.set(block.getCenterPosition().cpy());
            distance2 = ray.origin.dst2(mBlockPointedPosition);
            if (distance >= 0f && distance2 > distance) continue;
            if (Intersector.intersectRayBounds(ray, mBlockPointedBoundingBox, mBlockPointedIntersection)) {
                mBlockPointed = block;
                distance = distance2;
            }
        }

        // if block pointed at is found
        if (mBlockPointed != null){
            // draw the boundingbox (wireframe) to visually mark the block pointed at
            mBlockPointed.setIsShowingBoundingBox(true);

            // get the mesh of the block pointed at and populate the vertices array; assumption made we have 1 mesh only in the model
            float[] meshPartVertices = {};
            short[] meshPartIndices = {};
            int vertexSize;
            // get the worldtransform matrix of the renderable from the ModelInstance
            Matrix4 m4 = mBlockPointed.getModelInstance().getRenderable(new Renderable()).worldTransform;
            mBlockPointedMesh = mBlockPointed.getModel().meshes.get(0).copy(false);
            // transform the vertices of the mesh to match world position/rotation/scaling
            mBlockPointedMesh.transform(m4);
            vertexSize = mBlockPointedMesh.getVertexSize() / 4; // a float is 4 bytes, divide by four to get the number of floats
            meshPartVertices = new float[mBlockPointedMesh.getNumVertices() * vertexSize];
            mBlockPointedMesh.getVertices(meshPartVertices);

            // clear the backup of original materials
            mOriginalMaterials.clear();
            // assume we have one model node only and loop over the nodeparts (expected is 6 nodeparts, 1 for each block/cube side)
            for (int i = 0; i < mBlockPointed.getModelInstance().nodes.get(0).parts.size; i++) {
                // get a nodepart and populate the indices array
                mBlockPointedNodePart = mBlockPointed.getModelInstance().nodes.get(0).parts.get(i);
                meshPartIndices = new short[mBlockPointedNodePart.meshPart.size];
                mBlockPointedMesh.getIndices(mBlockPointedNodePart.meshPart.offset, mBlockPointedNodePart.meshPart.size, meshPartIndices, 0);

                // backup the original material for this nodepart
                mOriginalMaterials.add(i, mBlockPointedNodePart.material.copy());

                // check if the ray intersects with one or more of the triangles for this nodepart
                if (Intersector.intersectRayTriangles(ray, meshPartVertices, meshPartIndices, vertexSize, null)) {
                    // intersection detected, visually mark the nodepart by setting a selection material
                    mBlockPointedNodePart.material.set(mSelectionMaterial);
                }
            }
        }
...