我正在学习使用 LIBGDX 创建一个类似我的世界的世界,由纹理块(立方体)创建。我已经渲染了 3D 世界,并且玩家的运动完美无缺(通过 3D 世界的结果和运动体验感觉符合预期)。
我现在的目标是从第一人称视角检测相机指向块的哪一侧/哪一面。使用Intersector.intersectRayBounds
I 设法确定指向哪个块。我在找到的块周围画了一个边界框以获得视觉效果。
下一步是在视觉上标记块(立方体)的确切侧面/面。我试图通过使用Intersector.intersectRayTriangles
after 遍历找到的块的网格部分列表并检索网格的索引/顶点来实现这一点。
目前的结果是我能够标记指向立方体的侧面/面,但是,它是不准确的。只有当“指向”特定角落的块时,它才会产生正相交 - 好像我只能测试三角形的一个子集而不是全部。
确定指向哪个块(立方体)的代码示例:
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);
}
}
}