Título praticamente diz tudo. Estou trabalhando em um projeto simples 'vamos nos acostumar com o lwjgl' envolvendo a manipulação de um cubo de rubik, e não consigo descobrir como saber de que lado / quadrado o usuário está apontando.
Título praticamente diz tudo. Estou trabalhando em um projeto simples 'vamos nos acostumar com o lwjgl' envolvendo a manipulação de um cubo de rubik, e não consigo descobrir como saber de que lado / quadrado o usuário está apontando.
Respostas:
Você vai querer usar a seleção 3D. Aqui está um código que eu uso no meu jogo.
Primeiro, lancei um raio da minha câmera. Estou usando o mouse, mas se você estiver usando apenas para onde o usuário está olhando, basta usar o centro da janela. Este é o código da minha classe de câmera:
public Ray GetPickRay() {
int mouseX = Mouse.getX();
int mouseY = WORLD.Byte56Game.getHeight() - Mouse.getY();
float windowWidth = WORLD.Byte56Game.getWidth();
float windowHeight = WORLD.Byte56Game.getHeight();
//get the mouse position in screenSpace coords
double screenSpaceX = ((float) mouseX / (windowWidth / 2) - 1.0f) * aspectRatio;
double screenSpaceY = (1.0f - (float) mouseY / (windowHeight / 2));
double viewRatio = Math.tan(((float) Math.PI / (180.f/ViewAngle) / 2.00f)) * zoomFactor;
screenSpaceX = screenSpaceX * viewRatio;
screenSpaceY = screenSpaceY * viewRatio;
//Find the far and near camera spaces
Vector4f cameraSpaceNear = new Vector4f((float) (screenSpaceX * NearPlane), (float) (screenSpaceY * NearPlane), (float) (-NearPlane), 1);
Vector4f cameraSpaceFar = new Vector4f((float) (screenSpaceX * FarPlane), (float) (screenSpaceY * FarPlane), (float) (-FarPlane), 1);
//Unproject the 2D window into 3D to see where in 3D we're actually clicking
Matrix4f tmpView = Matrix4f(view);
Matrix4f invView = (Matrix4f) tmpView.invert();
Vector4f worldSpaceNear = new Vector4f();
Matrix4f.transform(invView, cameraSpaceNear, worldSpaceNear);
Vector4f worldSpaceFar = new Vector4f();
Matrix4f.transform(invView, cameraSpaceFar, worldSpaceFar);
//calculate the ray position and direction
Vector3f rayPosition = new Vector3f(worldSpaceNear.x, worldSpaceNear.y, worldSpaceNear.z);
Vector3f rayDirection = new Vector3f(worldSpaceFar.x - worldSpaceNear.x, worldSpaceFar.y - worldSpaceNear.y, worldSpaceFar.z - worldSpaceNear.z);
rayDirection.normalise();
return new Ray(rayPosition, rayDirection);
}
Então eu sigo o raio até ele se cruzar com um objeto, você pode fazer isso com caixas delimitadoras ou algo semelhante, já que isso é específico para o seu jogo, eu vou deixar você lidar com isso. Geralmente, isso é feito seguindo o raio para fora (adicionando a direção do raio ao ponto de partida várias vezes até que você esbarre em algo).
Em seguida, você deseja ver qual face está sendo escolhida, iterando sobre os triângulos em seu cubo para ver se o raio os intercepta. A função a seguir faz isso e retorna a distância para o rosto escolhido, então apenas uso o rosto cruzado mais próximo da câmera (para que você não escolha o rosto de trás).
public static float RayIntersectsTriangle(Ray R, Vector3f vertex1, Vector3f vertex2, Vector3f vertex3) {
// Compute vectors along two edges of the triangle.
Vector3f edge1 = null, edge2 = null;
edge1 = Vector3f.sub(vertex2, vertex1, edge1);
edge2 = Vector3f.sub(vertex3, vertex1, edge2);
// Compute the determinant.
Vector3f directionCrossEdge2 = null;
directionCrossEdge2 = Vector3f.cross(R.Direction, edge2, directionCrossEdge2);
float determinant = Vector3f.dot(directionCrossEdge2, edge1);
// If the ray and triangle are parallel, there is no collision.
if (determinant > -.0000001f && determinant < .0000001f) {
return Float.MAX_VALUE;
}
float inverseDeterminant = 1.0f / determinant;
// Calculate the U parameter of the intersection point.
Vector3f distanceVector = null;
distanceVector = Vector3f.sub(R.Position, vertex1, distanceVector);
float triangleU = Vector3f.dot(directionCrossEdge2, distanceVector);
triangleU *= inverseDeterminant;
// Make sure the U is inside the triangle.
if (triangleU < 0 || triangleU > 1) {
return Float.MAX_VALUE;
}
// Calculate the V parameter of the intersection point.
Vector3f distanceCrossEdge1 = null;
distanceCrossEdge1 = Vector3f.cross(distanceVector, edge1, distanceCrossEdge1);
float triangleV = Vector3f.dot(R.Direction, distanceCrossEdge1);
triangleV *= inverseDeterminant;
// Make sure the V is inside the triangle.
if (triangleV < 0 || triangleU + triangleV > 1) {
return Float.MAX_VALUE;
}
// Get the distance to the face from our ray origin
float rayDistance = Vector3f.dot(distanceCrossEdge1, edge2);
rayDistance *= inverseDeterminant;
// Is the triangle behind us?
if (rayDistance < 0) {
rayDistance *= -1;
return Float.MAX_VALUE;
}
return rayDistance;
}
O triângulo com a menor distância é o triângulo escolhido. Além disso, plug descarado para o meu jogo, você deve conferir, segui-lo e votar nas pesquisas que eu ocasionalmente publico. Obrigado! http://byte56.com
A técnica que você está procurando é chamada de "picking" ou "picking 3D". Há várias maneiras de o fazer; um dos mais comuns é transformar um ponto 2D na tela em espaço ocular usando o inverso da transformação da projeção. Isso permitirá que você gere um raio no espaço de visualização, que você pode usar para testar a colisão com a representação física de seus vários bits da geometria da cena para determinar qual objeto o usuário 'bateu'.
Você também pode usar um "buffer de picking" (ou "buffer de seleção") que o GL tenha suporte. Isso basicamente envolve escrever um identificador de objeto exclusivo em um buffer para cada pixel e simplesmente testar esse buffer.
O FAQ do OpenGL tem breves discussões sobre ambos (ele se concentra mais no buffer de seleção, pois esse é um recurso GL; a seleção de raios é independente da API, exceto talvez para extrair as matrizes ativas do pipeline). Aqui está um exemplo mais específico da técnica de seleção de raios (para iOS, mas deve ser traduzida com bastante facilidade). Este site possui algum código fonte para alguns dos exemplos do OpenGL Red Book portados para o LWJGL, que incluem uma demonstração de picking.
Veja também esta pergunta no SO.