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;
}
}
}
このコードパートの背後にある私の考えは次のとおりです。
- モデル インスタンスのすべてのノードを反復処理します (現在、関与するノードは 1 つだけです)。
- モデル インスタンスのノードのすべてのノード パーツを反復処理します (ブロックの側面/面ごとに 6 つ)。
- ノードのメッシュを取得し、検出されたブロックが指し示すようにメッシュを変換します (ワールド位置、回転、スケーリングを取得するため)。
- 現在のメッシュ パーツのインデックス バッファ内のオフセットに基づいてインデックスを取得します
- 頂点の数と使用される頂点のサイズに基づいて、メッシュのすべての頂点を取得します
- 指定された光線と現在のメッシュ パーツのインデックス/頂点に対して交差三角形テストを実行します。
その結果、(パースペクティブ) カメラを動かしたときに、カメラがブロックサイドのコーナー領域の 1 つを指している場合にのみ、肯定的な結果が得られます。私はこの原因を考えることができません。それができれば、これを解決する方向に進むことができます.
編集 -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);
}
}
}