1

我正在学习使用 LIBGDX 创建一个类似我的世界的世界,由纹理块(立方体)创建。我已经渲染了 3D 世界,并且玩家的运动完美无缺(通过 3D 世界的结果和运动体验感觉符合预期)。

我现在的目标是从第一人称视角检测相机指向块的哪一侧/哪一面。使用Intersector.intersectRayBoundsI 设法确定指向哪个块。我在找到的块周围画了一个边界框以获得视觉效果。

下一步是在视觉上标记块(立方体)的确切侧面/面。我试图通过使用Intersector.intersectRayTrianglesafter 遍历找到的块的网格部分列表并检索网格的索引/顶点来实现这一点。

目前的结果是我能够标记指向立方体的侧面/面,但是,它是不准确的。只有当“指向”特定角落的块时,它才会产生正相交 - 好像我只能测试三角形的一个子集而不是全部。

确定指向哪个块(立方体)的代码示例:

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);
                }
            }
        }
4

0 回答 0