sábado, 30 de julho de 2011

XNA 4.0 - Recuperando Altura de um terreno.

Vamos agora recuperar a altura de nosso terreno, é relativamente fácil.
Fornecemos as posições em forma de um vetor do tipo Vector3, Obtemos a posição na malha, comparamos com a posição do bloco inicial e verificamos se essa posição está mesmo dentro de nossa malha, caso sim calcula o quadrado que estamos, pega os 4 indices que formam esse quadrado, faz alguns calculos vetoriais e mostra exatamente a posição. O código é esse:

public float GetHeight(Vector3 position)
{
return GetHeight(position.X, position.Z);
}

private float GetHeight(float positionX, float positionZ)
{
float height = -999999.0f;
if (heightMap == null) return height;

// Obter a posicao do objeto na grade do terreno
Vector2 positionInGrid = new Vector2(
positionX - (StartPosition.X),
positionZ - (StartPosition.Y));

// Calcula a posicao do bloco inicial
Vector2 blockPosition = new Vector2(
positionInGrid.X / blockScale,
positionInGrid.Y / blockScale);

// Verifica se o objeto esta dentro da grade
if (blockPosition.X >= 0 && blockPosition.X < (vertexCountX - 1) && blockPosition.Y >= 0 && blockPosition.Y < (vertexCountZ - 1)) { Vector2 blockOffset = new Vector2( blockPosition.X - (int)blockPosition.X, blockPosition.Y - (int)blockPosition.Y); // Obteem a altura dos quatro vertices do bloco int vertexIndex = (int)blockPosition.X + (int)blockPosition.Y * vertexCountX; float height1 = heightMap[vertexIndex + 1]; float height2 = heightMap[vertexIndex]; float height3 = heightMap[vertexIndex + vertexCountX + 1]; float height4 = heightMap[vertexIndex + vertexCountX]; // Triangule de baixo float heightIncX, heightIncY; if (blockOffset.X > blockOffset.Y)
{
heightIncX = height1 - height2;
heightIncY = height3 - height1;
}
// Triangulo de cima
else
{
heightIncX = height3 - height4;
heightIncY = height4 - height2;
}

// Interpolacao linear para encontrar a altura do triangulo
float lerpHeight = height2 + heightIncX * blockOffset.X + heightIncY * blockOffset.Y;
height = lerpHeight * heightScale;
}

return height;
}

Criamos dois métodos, um para retornar a altura em uma determinada posição e outro cuida de todos os calculos envovendo isso.

Vamos inserir uma propriedade para que a posição inicial seja calculada tempo como base os vértices que escolhemos para ser o inicial. Adicione entre as variáveis e o mé todo Initialize():

public Vector2 StartPosition
{
get
{
float terrainHalfWidth = (vertexCountX - 1) * blockScale * 0.5f;
float terrainHalfDepth = (vertexCountZ - 1) * blockScale * 0.5f;

return new Vector2(-terrainHalfWidth, -terrainHalfDepth);
}
}

Agora um código para que a nossa câmera siga a altura do mapa, para termos a noção de relevo do mapa, vá ao arquivo "Game1.cs" no método Update() e insira, depois do CameraManager.ActiveCamera.addToPosition()

cameraManager.ActiveCamera.Position = new Vector3(cameraManager.ActiveCamera.Position.X,
terrain.GetHeight(cameraManager.ActiveCamera.Position) + 1.86f,
cameraManager.ActiveCamera.Position.Z
);

Esse código atualiza a posição do eixo Y tendo como relação a posição fornecida e é acrescida de 1.86f enquando as posições X e Z são mantidas.
1.86f é a minha altura, se você quiser uma escala de 1:1 ou seja, 1.0f em nosso mundo equivale a 1 metro de altura então temos que 1.86f representa 1 metro e 86 centimentros e que o nosso mapa tem 256m2, o que é espaço pra caramba para brincar.

Nenhum comentário:

Postar um comentário