LIBGDX 3D:无法准确确定相机射线撞击哪个网格三角形

时间:2019-02-09 08:06:10

标签: 3d libgdx ray-picking

我正在学习使用LIBGDX来创建由纹理块(立方体)创建的Minecraft一样的世界。我已经渲染了3D世界,并且玩家动作完美无缺(通过3D世界获得的结果和动作体验都达到了预期)。

我现在的目标是从第一人称视角检测相机指向块的哪一侧/哪一侧。使用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);
                }
            }
        }

0 个答案:

没有答案