我正在学习使用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;
}
}
}
此代码部分背后的想法是:
结果是,当我左右移动(透视)摄像机时,只有当摄像机指向块体边角区域之一时,我才能得到肯定的结果。我无法想到造成这种情况的原因,如果可以的话,可能会使我朝着解决这个问题的方向出发。
编辑-使用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);
}
}
}