Я учусь использовать LIBGDX для создания похожего на майнкрафт мира, созданного из текстурированных блоков (кубов).Я визуализировал трехмерный мир, и движение игрока работает безупречно (результат и опыт движения в трехмерном мире ощущается, как и ожидалось).
Теперь я стремлюсь определить, на какую сторону / границу блока указываюткамерой от первого лица.С помощью 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);
}
}
}