sexta-feira, 29 de julho de 2011

XNA 4.0 - Parte 6 Terrenos

Baixe esse arquivo: http://www.megaupload.com/?d=MEROS15K

Ele possui algumas texturas que usaremos para criar nosso terreno e depois com efeitos avançados.

Extraia as imagens, Crie uma Pasta chamada "Textures" no content e adicione essas 4 imagens para dentro da pasta.

Para criarmos uma textura e ilumina-la precusamos calcular a "Normal" de cada vértice para que ele possa ser iluminado. Geralmente um Vector3.Up é o suficiente para criar a normal do vértice mas vamos fazer algo um pouco mais trabalhado. Vamos Criar um novo método antes do método Update(), o método se chamara GenerateTerrainNormals() ele vai receber uma lista de vértices do Tipo VertexPositionColor.

private void GenerateTerrainNormals(VertexPositionColor[] vertices, int[] indices)
{
for (int i = 0; i < indices.Length; i += 3) { Vector3 v1 = vertices[indices[i]].Position; Vector3 v2 = vertices[indices[i + 1]].Position; Vector3 v3 = vertices[indices[i + 2]].Position; Vector3 vu = v3 - v1; Vector3 vt = v2 - v1; Vector3 normal = Vector3.Cross(vu, vt); normal.Normalize(); vertices[indices[i]].Normal += normal; vertices[indices[i + 1]].Normal += normal; vertices[indices[i + 2]].Normal += normal; } }



Ele pega as "normais" dos outros dois vértices e criar algo mais elaborado.

Agora vá no método CreateTerrainVertices e adicione os eguinte código antes de "return vertices"

// Cria a normal do vertice
GenerateTerrainNormals(vertices, terrainIndices);

Fazendo algumas alterações no código, atualmente usamos "VertexPositionColor", foi legal enquanto durou mas queremos usar texturas e luzes, vamos alterar todas as chamadas de VertexPositionColor para VertexPositionNormalTexture, vamos usar o atalho CRTL+H, substitua todas, no total são 7. Vamos Substituir todas as ocorrências de BasicEffect para EnvironmentMapEffect, no total serão 2.

Ainda no método CreateTerrainVertices() altere a referência de cores para coordenada de textura. Então tinhamos:

vertices[vertexCount].Color = Color.Yellow;
if((int)heightMap[vertexCount] < 80) vertices[vertexCount].Color = Color.Green; if ((int)heightMap[vertexCount] > 200)
vertices[vertexCount].Color = Color.Red;

E agora teremos:

vertices[vertexCount].TextureCoordinate = new Vector2(j, i);

Vou colocar o método inteiro para ficar mais claro:

private VertexPositionNormalTexture[] GenerateTerrainVertices(int[] terrainIndices)
{
float halfTerrainWidth = (vertexCountX - 1) * blockScale * 0.5f;
float halfTerrainDepth = (vertexCountZ - 1) * blockScale * 0.5f;

int vertexCount = 0;
VertexPositionNormalTexture[] vertices =
new VertexPositionNormalTexture[vertexCountX * vertexCountZ];

// Define a posicao dos vertices e a coordenada de textura
for (float i = -halfTerrainDepth; i <= halfTerrainDepth; i += blockScale) { for (float j = -halfTerrainWidth; j <= halfTerrainWidth; j += blockScale) { vertices[vertexCount].Position = new Vector3(j, heightMap[vertexCount] * heightScale, i); vertices[vertexCount].TextureCoordinate = new Vector2(j, i); vertexCount++; } } // Gera as normais dos indices GenerateTerrainNormals(vertices, terrainIndices); return vertices; }



No método Load(), na ultima linha substitua o "effect.VertexColorEnabled = true;" para "effect.EnableDefaultLighting();" e logo após adicione a se guinte linha:

effect.Texture = Game.Content.Load("Textures/Terrain2");

Você pode alterar essa linha para:

effect.Texture = Game.Content.Load("Textures/Terrain1");

mudando o ultimo parametro, você pode adicionar as suas próprias texturas, adicionando-as a pasta Textures e depois substituindo o nome dele no ultimo parametro:

effect.Texture = Game.Content.Load("Textures/nomeSuaTexturaSemOTipoDeArquivo");

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Tutorial1.GameBase.Cameras;
using System.IO;

namespace Tutorial1.GameBase.Shapes
{
public class Terrain : DrawableGameComponent
{
#region Variáveis
//Variável que guardará a altura do mapa
byte[] heightMap;

//Variáveis de renderização
VertexBuffer vertexBuffer;
IndexBuffer indexBuffer;

//Guarda o número de Vértices e de triângulos
int numVertices;
int numTriangles;

//Quantidade de vértices nos eixos X e Z
int vertexCountX;
int vertexCountZ;

//Escala do nosso mapa
float blockScale;
float heightScale;

//Efeito que será usado na cena
EnvironmentMapEffect effect;

//Serviços necessários
bool isInitialized;
CameraManager cameraManager;

#endregion

#region Construtor()
public Terrain(Game game)
: base(game)
{
isInitialized = false;
}
#endregion

#region initialize()
//Inicializa todos os nossos serviços e depois de iniciado defini nossa flag como true, permitindo que outros
//métodos possam se executar sem perda de dados
public override void Initialize()
{
cameraManager = Game.Services.GetService(typeof(CameraManager)) as CameraManager;

//Define o nosso RenderState
//Rasterize.FillMode =
//Pode variar entre FillMode.Solid - Para mostrar os objetos da cena de momo completo
//FillMode.WireFrame - Para mostrar os triângulod que formam os objetos na cena
RasterizerState rasterizeState = new RasterizerState();
rasterizeState.FillMode = FillMode.Solid;
Game.GraphicsDevice.RasterizerState = rasterizeState;

isInitialized = true;

base.Initialize();
}
#endregion

#region Load()
public void Load(string heightmapFileName, int vertexCountX, int vertexCountZ, float blockScale, float heightScale)
{
//Cuida para que tudo seja iniciado
if (!isInitialized)
Initialize();


//Passamos alguns parametros para definir o tamanho do Mapa em X e Z e tambem a escala de
//Tamanho e de altura
this.vertexCountX = vertexCountX;
this.vertexCountZ = vertexCountZ;
this.blockScale = blockScale;
this.heightScale = heightScale;

// Carrega o arquivo do mapa de altura
FileStream fileStream = File.OpenRead(Game.Content.RootDirectory + "/Terrains/" + heightmapFileName + ".raw");

int heightmapSize = vertexCountX * vertexCountZ;
heightMap = new byte[heightmapSize];
fileStream.Read(heightMap, 0, heightmapSize);
fileStream.Close();

GenerateTerrainMesh();

effect = new EnvironmentMapEffect(Game.GraphicsDevice);
effect.EnableDefaultLighting();
effect.Texture = Game.Content.Load("Textures/Terrain2");
}
#endregion

#region GenerateTerrainMesh()
private void GenerateTerrainMesh()
{
//Vamos definir o número de vértices e triângulo para renderizar todos eles
numVertices = vertexCountX * vertexCountZ;
numTriangles = (vertexCountX - 1) * (vertexCountZ - 1) * 2;

//Criando nosso índices
int[] indices = GenerateTerrainIndices();

VertexPositionNormalTexture[] vertices = GenerateTerrainVertices(indices);

//Vamos criar a declaração dos vértices
VertexDeclaration decl = new VertexDeclaration(VertexPositionNormalTexture.VertexDeclaration.GetVertexElements());

// Cria o buffer de vertices
vertexBuffer = new VertexBuffer(GraphicsDevice, decl, numVertices, BufferUsage.WriteOnly);

//Adiciona nossos vértices ao Buffer
vertexBuffer.SetData(vertices);

//Cria o Buffer de índices
indexBuffer = new IndexBuffer(Game.GraphicsDevice, typeof(int), indices.Length, BufferUsage.WriteOnly);

//Adiciona nossos índices ao Buffer
indexBuffer.SetData(indices);
}
#endregion

#region GenerateTerrainIndices()
private int[] GenerateTerrainIndices()
{
//Cria todos os índices, como cada triângulo possui 3 vértices
int numIndices = numTriangles * 3;
int[] indices = new int[numIndices];

int indicesCount = 0;

//Cria dois indices no mesmo segmento de indices e outro indice no próximo segmento
//Faz isso 2 vezes, com dois triângulos formando um quadrado
for (int i = 0; i < (vertexCountZ - 1); i++)
{
for (int j = 0; j < (vertexCountX - 1); j++)
{
int index = j + i * vertexCountZ;
indices[indicesCount++] = index;
indices[indicesCount++] = index + 1;
indices[indicesCount++] = index + vertexCountX + 1;

indices[indicesCount++] = index + vertexCountX + 1;
indices[indicesCount++] = index + vertexCountX;
indices[indicesCount++] = index;

}
}

return indices;
}
#endregion

#region GenerateTerrainVertices()
private VertexPositionNormalTexture[] GenerateTerrainVertices(int[] terrainIndices)
{
float halfTerrainWidth = (vertexCountX - 1) * blockScale * 0.5f;
float halfTerrainDepth = (vertexCountZ - 1) * blockScale * 0.5f;

int vertexCount = 0;
VertexPositionNormalTexture[] vertices =
new VertexPositionNormalTexture[vertexCountX * vertexCountZ];

// Define a posicao dos vertices e a coordenada de textura
for (float i = -halfTerrainDepth; i <= halfTerrainDepth; i += blockScale)
{
for (float j = -halfTerrainWidth; j <= halfTerrainWidth; j += blockScale)
{
vertices[vertexCount].Position = new Vector3(j, heightMap[vertexCount] * heightScale, i);
vertices[vertexCount].TextureCoordinate = new Vector2(j, i);

vertexCount++;
}

}
// Gera as normais dos indices
GenerateTerrainNormals(vertices, terrainIndices);

return vertices;
}
#endregion

#region GenerateTerrainNormals()
private void GenerateTerrainNormals(VertexPositionNormalTexture[] vertices, int[] indices)
{
for (int i = 0; i < indices.Length; i += 3)
{
Vector3 v1 = vertices[indices[i]].Position;
Vector3 v2 = vertices[indices[i + 1]].Position;
Vector3 v3 = vertices[indices[i + 2]].Position;

Vector3 vu = v3 - v1;
Vector3 vt = v2 - v1;
Vector3 normal = Vector3.Cross(vu, vt);
normal.Normalize();

vertices[indices[i]].Normal += normal;
vertices[indices[i + 1]].Normal += normal;
vertices[indices[i + 2]].Normal += normal;
}
}
#endregion

#region Update()
public override void Update(GameTime gameTime)
{
effect.View = cameraManager.ActiveCamera.View;
effect.Projection = cameraManager.ActiveCamera.Projection;
base.Update(gameTime);
}
#endregion

#region Draw()
public override void Draw(GameTime gameTime)
{

//Adicionamos o nossos indices e vertices ao nosso dispositivso para desenhar
Game.GraphicsDevice.SetVertexBuffer(vertexBuffer);
Game.GraphicsDevice.Indices = indexBuffer;

foreach (EffectPass pass in effect.CurrentTechnique.Passes)
{
//Adiciona e aplica o efeito
pass.Apply();
//Desenha a lista de linhas
Game.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, numVertices, 0, numTriangles);
}

base.Draw(gameTime);
}
#endregion
}
}

Nenhum comentário:

Postar um comentário