# Multiple layer texture coordinates

## Recommended Posts

football94    211

Hi guys

Ive been trying different things(effects,collision detection,..) with a  multitextured terrain   and thanks to the gamedev  forums and various other sources   I think Im beginning to understand how to approach certain problems, for example with the terrain(2DHeightmap) what I wanted to do is implement a ray collision method that  returned the intersected triangle along with the coordinates for a specific texture but I think that the  technique used in the terrain class below ( someone correct me if Im wrong) is to hook all textures into one layer of texture coordinates based on height and I realized that in order to get the ray to read multiple textures I would have to create multiple coordinate layers so I created a new vertex with multiple texture coordinates and this is where I am now, trying to modify the  code to take in multple texture layers, instead of everything being wrapped up in one layer. my question is where would I begin,would I somehow need to replace the height based clamp info with uv based info, or is there a better technique. any advice would be much appreciated.

Thankyou

below is just a rough draft of how I would setup the method after terrain modifications

 public bool grassuv(Ray ray2)
{

for (int y = 0; y < terrain.Tlength - 1; y++)
{
for (int x = 0; x < terrain.Twidth - 1; x++)
{

pointa = terrain.TerrainVertices[x + (-y * terrain.terrainWidth)].Position; // First Point
pointb = terrain.TerrainVertices[x + ((-y + 1) * terrain.terrainWidth)].Position; // Second
pointc = terrain.TerrainVertices[(x + 1) + (-y * terrain.terrainWidth)].Position; // And third

grassu = terrain.TerrainVertices[(x + 1) + (-y * terrain.terrainWidth)].Texcoord1.Z;
grassv = terrain.TerrainVertices[(x + 1) + (-y * terrain.terrainWidth)].Texcoord1.Y;

bool hit = RayTriangleIntersect(
ray2.Position, ray2.Direction,
pointa,
pointb,
pointc,
out grassu, out grassv)

if (hit)
{

return true;
}

}
}
return false;
}



then call it in the update method

 protected override void Update(GameTime gameTime)
{

if(grassuv( ray))
{
pickedTriangle[0].Position = pointa;
pickedTriangle[0].Color = Color.Blue;
pickedTriangle[1].Position = pointb;
pickedTriangle[1].Color = Color.Blue;
pickedTriangle[2].Position = pointc;
pickedTriangle[2].Color = Color.Blue;

}
else if(sanduv(ray))
{
pickedTriangle[0].Position = pointa;
pickedTriangle[0].Color = Color.Red;
pickedTriangle[1].Position = pointb;
pickedTriangle[1].Color = Color.Red;
pickedTriangle[2].Position = pointc;
pickedTriangle[2].Color = Color.Red;

}

}



and so on....

and in the terrain class Im working on changing this..

                    terrainVertices[x + (y * terrainWidth)].TexWeights.X = MathHelper.Clamp(1.0f - Math.Abs(heightData[x, y] - 0) / 8.0f, 0, 1);
terrainVertices[x + (y * terrainWidth)].TexWeights.Y = MathHelper.Clamp(1.0f - Math.Abs(heightData[x, y] - 12) / 6.0f, 0, 1);
terrainVertices[x + (y * terrainWidth)].TexWeights.Z = MathHelper.Clamp(1.0f - Math.Abs(heightData[x, y] - 20) / 6.0f, 0, 1);
terrainVertices[x + (y * terrainWidth)].TexWeights.W = MathHelper.Clamp(1.0f - Math.Abs(heightData[x, y] - 30) / 6.0f, 0, 1);



to something like this..

  terrainVertices[x + (y * terrainWidth)].TextureCoordinate1.X = (float)x/(grassTexture.Width);
terrainVertices[x + (y * terrainWidth)].TextureCoordinate1.Y = (float)y/(grassTexture.Height);

terrainVertices[x + (y * terrainWidth)].TextureCoordinate2.X = (float)x/(sandTexture.Width);
terrainVertices[x + (y * terrainWidth)].TextureCoordinate2.Y = (float)y/(sandTexture.Height);

terrainVertices[x + (y * terrainWidth)].TextureCoordinate3.X = (float)x/(snowTexture.Width);
terrainVertices[x + (y * terrainWidth)].TextureCoordinate3.Y = (float)y/(snowTexture.Height);

terrainVertices[x + (y * terrainWidth)].TextureCoordinate4.X = (float)x/(rockTexture.Width);
terrainVertices[x + (y * terrainWidth)].TextureCoordinate4.Y = (float)y/(rockTexture.Height);



portion of code trying to modify

terrainVertices[x + (y * terrainWidth)].TexWeights.X = MathHelper.Clamp(1.0f - Math.Abs(heightData[x, y] - 0) / 8.0f, 0, 1);
terrainVertices[x + (y * terrainWidth)].TexWeights.Y = MathHelper.Clamp(1.0f - Math.Abs(heightData[x, y] - 12) / 6.0f, 0, 1);
terrainVertices[x + (y * terrainWidth)].TexWeights.Z = MathHelper.Clamp(1.0f - Math.Abs(heightData[x, y] - 20) / 6.0f, 0, 1);
terrainVertices[x + (y * terrainWidth)].TexWeights.W = MathHelper.Clamp(1.0f - Math.Abs(heightData[x, y] - 30) / 6.0f, 0, 1);

float total = terrainVertices[x + y * terrainWidth].TexWeights.X;
total += terrainVertices[x + y * terrainWidth].TexWeights.Y;
total += terrainVertices[x + y * terrainWidth].TexWeights.Z;
total += terrainVertices[x + y * terrainWidth].TexWeights.W;

terrainVertices[x + y * terrainWidth].TexWeights.X /= total;
terrainVertices[x + y * terrainWidth].TexWeights.Y /= total;
terrainVertices[x + y * terrainWidth].TexWeights.Z /= total;
terrainVertices[x + y * terrainWidth].TexWeights.W /= total;


actual terrain class

namespace TRIANGLE
{
public class Terrain
{
/*
* Struct to hold vertex information with pos,normal,texcoord and texweight
*/
public struct VertexMultitextured
{
public Vector3 Position;
public Vector3 Normal;
public Vector4 TextureCoordinate;
public Vector4 TexWeights;
public Vector2 TextureCoordinate1;
public Vector2 TextureCoordinate2;
public Vector2 TextureCoordinate3;
public Vector2 TextureCoordinate4;

public static int SizeInBytes = (3 + 3 + 4 + 4 + 2 + 2 + 2 + 2) * sizeof(float); // vec3 + vec3 + vec4 +vec4
public static VertexElement[] VertexElements = new VertexElement[]
{
// stream, offset, type, method, usage and usage index
new VertexElement( 0, 0, VertexElementFormat.Vector3, VertexElementMethod.Default, VertexElementUsage.Position, 0 ),
new VertexElement( 0, sizeof(float) * 3, VertexElementFormat.Vector3, VertexElementMethod.Default, VertexElementUsage.Normal, 0 ),
new VertexElement( 0, sizeof(float) * 6, VertexElementFormat.Vector4, VertexElementMethod.Default, VertexElementUsage.TextureCoordinate, 0 ),
new VertexElement( 0, sizeof(float) * 10, VertexElementFormat.Vector4, VertexElementMethod.Default, VertexElementUsage.TextureCoordinate, 1 ),
new VertexElement( 0, sizeof(float) * 14, VertexElementFormat.Vector2, VertexElementMethod.Default, VertexElementUsage.TextureCoordinate, 2 ),
new VertexElement( 0, sizeof(float) * 16, VertexElementFormat.Vector2, VertexElementMethod.Default, VertexElementUsage.TextureCoordinate, 3 ),
new VertexElement( 0, sizeof(float) * 18, VertexElementFormat.Vector2, VertexElementMethod.Default, VertexElementUsage.TextureCoordinate, 4 ),
new VertexElement( 0, sizeof(float) * 20, VertexElementFormat.Vector2, VertexElementMethod.Default, VertexElementUsage.TextureCoordinate, 5 ),
};
}

Game1 GameClass;
GraphicsDevice device;

public int terrainWidth;
public int terrainLength;
public float[,] heightData;
VertexBuffer terrainVertexBuffer;
IndexBuffer terrainIndexBuffer;
VertexDeclaration terrainVertexDeclaration;
VertexMultitextured[] terrainVertices;

public Texture2D grassTexture;
public Texture2D sandTexture;
public Texture2D rockTexture;
public Texture2D snowTexture;

public VertexMultitextured[] TerrainVertices
{
get
{
return terrainVertices;
}

}
public int Twidth
{
get
{
return terrainWidth;
}

}
public int Tlength
{
get
{
return terrainLength;
}

}

public Terrain(Game1 reference,GraphicsDevice device)
{
this.GameClass = reference;
this.device = device;

LoadVertices();
LoadTextures();
}

private void LoadTextures()
{
grassTexture = GameClass.Content.Load<Texture2D>("grass");
sandTexture = GameClass.Content.Load<Texture2D>("sand");
rockTexture = GameClass.Content.Load<Texture2D>("rock");
snowTexture = GameClass.Content.Load<Texture2D>("snow");

}

public void LoadVertices()
{

Texture2D heightMap = GameClass.Content.Load<Texture2D>("heightmap");
LoadHeightData(heightMap);

VertexMultitextured[] terrainVertices = SetUpTerrainVertices();
int[] terrainIndices = SetUpTerrainIndices();
terrainVertices = CalculateNormals(terrainVertices, terrainIndices);
CopyToTerrainBuffers(terrainVertices, terrainIndices);
terrainVertexDeclaration = new VertexDeclaration(device, VertexMultitextured.VertexElements);

}

public int[] TerrainIndices
{
get
{
return terrainIndices;
}

}

public void LoadHeightData(Texture2D heightMap)
{
float minimumHeight = float.MaxValue;
float maximumHeight = float.MinValue;

terrainWidth = heightMap.Width;
terrainLength = heightMap.Height;

// get rgb values for each pixel of hightmap
Color[] heightMapColors = new Color[terrainWidth * terrainLength];
heightMap.GetData(heightMapColors);

heightData = new float[terrainWidth, terrainLength];
for (int x = 0; x < terrainWidth; x++)
for (int y = 0; y < terrainLength; y++)
{
heightData[x, y] = heightMapColors[x + (y * terrainWidth)].R; // read r value
// determine minimum and maximum height value in terrain
if (heightData[x, y] < minimumHeight) minimumHeight = heightData[x, y];
if (heightData[x, y] > maximumHeight) maximumHeight = heightData[x, y];
}

// get height values in a range between 0 and 30
for (int x = 0; x < terrainWidth; x++)
for (int y = 0; y < terrainLength; y++)
heightData[x, y] = (heightData[x, y] - minimumHeight) / (maximumHeight - minimumHeight) * 30.0f;

}

/*
* Define Vertices
*/
private VertexMultitextured[] SetUpTerrainVertices()
{
terrainVertices = new VertexMultitextured[terrainWidth * terrainLength];

for (int x = 0; x < terrainWidth; x++)
{
for (int y = 0; y < terrainLength; y++)
{
// generate a vertex for each pixel of the heightmap
// a terrain is generated in x,-y direction
terrainVertices[x + (y * terrainWidth)].Position = new Vector3(x, heightData[x, y], -y);
terrainVertices[x + (y * terrainWidth)].TextureCoordinate.X = (float)x / 30.0f; // /30 to stretch texture so it looks realistic
terrainVertices[x + (y * terrainWidth)].TextureCoordinate.Y = (float)y / 30.0f;

// X = Sand, Y = Grass, Z = Stone and W = Snow
terrainVertices[x + (y * terrainWidth)].TexWeights.X = MathHelper.Clamp(1.0f - Math.Abs(heightData[x, y] - 0) / 8.0f, 0, 1);
terrainVertices[x + (y * terrainWidth)].TexWeights.Y = MathHelper.Clamp(1.0f - Math.Abs(heightData[x, y] - 12) / 6.0f, 0, 1);
terrainVertices[x + (y * terrainWidth)].TexWeights.Z = MathHelper.Clamp(1.0f - Math.Abs(heightData[x, y] - 20) / 6.0f, 0, 1);
terrainVertices[x + (y * terrainWidth)].TexWeights.W = MathHelper.Clamp(1.0f - Math.Abs(heightData[x, y] - 30) / 6.0f, 0, 1);

float total = terrainVertices[x + y * terrainWidth].TexWeights.X;
total += terrainVertices[x + y * terrainWidth].TexWeights.Y;
total += terrainVertices[x + y * terrainWidth].TexWeights.Z;
total += terrainVertices[x + y * terrainWidth].TexWeights.W;

terrainVertices[x + y * terrainWidth].TexWeights.X /= total;
terrainVertices[x + y * terrainWidth].TexWeights.Y /= total;
terrainVertices[x + y * terrainWidth].TexWeights.Z /= total;
terrainVertices[x + y * terrainWidth].TexWeights.W /= total;
}
}

return terrainVertices;
}

/*
* Set indices clockwise to build faces
*/
private int[] SetUpTerrainIndices()
{
/*
* new int[(terrainWidth - 1) * (terrainLength - 1) * 6]
* Example, plane consisting of 4x3 points: first row is build out of 3 quads, second one also.
*                                        row   col    3 points are needed for each triangle, 6 for one quad
* so for a 4x3 points plane one can say (4-1)*(3-1) * 6
*/
int[] indices = new int[(terrainWidth - 1) * (terrainLength - 1) * 6];
int counter = 0;
for (int y = 0; y < terrainLength - 1; y++)
{
for (int x = 0; x < terrainWidth - 1; x++)
{
int lowerLeft = x + y * terrainWidth;
int lowerRight = (x + 1) + y * terrainWidth;
int topLeft = x + (y + 1) * terrainWidth;
int topRight = (x + 1) + (y + 1) * terrainWidth;

// order clockwise
indices[counter++] = topLeft;
indices[counter++] = lowerRight;
indices[counter++] = lowerLeft;

indices[counter++] = topLeft;
indices[counter++] = topRight;
indices[counter++] = lowerRight;
}
}

return indices;
}

/*
* Calculate Normals on Terrain for realistic shadows
*/
private VertexMultitextured[] CalculateNormals(VertexMultitextured[] vertices, int[] indices)
{
// initialise normals with 0 0 0
for (int i = 0; i < vertices.Length; i++)
vertices[i].Normal = new Vector3(0, 0, 0);

for (int i = 0; i < indices.Length / 3; i++)
{
// Calculate Triangle
int index1 = indices[i * 3];
int index2 = indices[i * 3 + 1];
int index3 = indices[i * 3 + 2];

// Calculate normal on triangle
Vector3 side1 = vertices[index1].Position - vertices[index3].Position;
Vector3 side2 = vertices[index1].Position - vertices[index2].Position;
Vector3 normal = Vector3.Cross(side1, side2);

// apply normal on all three vertices of triangle
vertices[index1].Normal += normal;
vertices[index2].Normal += normal;
vertices[index3].Normal += normal;
}

// normalize all normals
for (int i = 0; i < vertices.Length; i++)
vertices[i].Normal.Normalize();

return vertices;
}

/*
* Copy vertices and indices onto grafikcard buffer to save performance (so this data has to be transferred ONLY ONCE)
*/
private void CopyToTerrainBuffers(VertexMultitextured[] vertices, int[] indices)
{
terrainVertexBuffer = new VertexBuffer(device, vertices.Length * VertexMultitextured.SizeInBytes, BufferUsage.WriteOnly);
terrainVertexBuffer.SetData(vertices);

terrainIndexBuffer = new IndexBuffer(device, typeof(int), indices.Length, BufferUsage.WriteOnly);
terrainIndexBuffer.SetData(indices);
}

/*
* This draws the terrain
*/
public void DrawTerrain(Effect effect)
{
effect.CurrentTechnique = effect.Techniques["MultiTextured"];
effect.Parameters["xTexture0"].SetValue(sandTexture);
effect.Parameters["xTexture1"].SetValue(grassTexture);
effect.Parameters["xTexture2"].SetValue(rockTexture);
effect.Parameters["xTexture3"].SetValue(snowTexture);

Matrix world = Matrix.CreateTranslation(new Vector3(77, 0, 0));
Matrix worldMatrix = Matrix.Identity;
effect.Parameters["xWorld"].SetValue(worldMatrix);
effect.Parameters["xEnableLighting"].SetValue(true);
effect.Parameters["xAmbient"].SetValue(0.4f);
effect.Parameters["xLightDirection"].SetValue(new Vector3(-0.5f, -1, -0.5f));

effect.Begin();
foreach (EffectPass pass in effect.CurrentTechnique.Passes)
{
pass.Begin();

device.Vertices[0].SetSource(terrainVertexBuffer, 0, VertexMultitextured.SizeInBytes); // tell it that this data is in graka buffer
device.Indices = terrainIndexBuffer;
device.VertexDeclaration = terrainVertexDeclaration;

int noVertices = terrainVertexBuffer.SizeInBytes / VertexMultitextured.SizeInBytes;
int noTriangles = terrainIndexBuffer.SizeInBytes / sizeof(int) / 3;
device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, noVertices, 0, noTriangles);

pass.End();
}
effect.End();
}



## Create an account or sign in to comment

You need to be a member in order to leave a comment

## Create an account

Sign up for a new account in our community. It's easy!

Register a new account

## Sign in

Already have an account? Sign in here.

Sign In Now