Sign in to follow this  
football94

Multiple layer texture coordinates

Recommended Posts

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();
        }




Share this post


Link to post
Share on other sites

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

Sign in to follow this