1 Replies - 3781 Views - Last Post: 06 February 2013 - 07:24 AM

#1 net8floz  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 2
  • Joined: 30-January 13

XNA Lots of billboard sprites, how to draw?

Posted 30 January 2013 - 04:41 PM

Howdy first post!

So I have all these "gameSprites" that contain "BillboardSprites".
They are pngs that are chopped up
and billboarded to be in 3D space.
I'm using them as characters and other things and I'd like to be able to draw a ton of them.

They are drawn in there own buffers whith their own indices and vertices arrays.


This is very slow and reading suggests that the graphics card would prefer if I told it about all
the gamesprites in one buffer instead.

I can add all their vertices and indices together, and draw them all I'd imagine.
But they are also drawn with an effect file, and that is passed its world matrix(Scale and position etc),
plus the billboarding effect and textures.

So how would I keep my ability to individually change scaling,positioning,texture and other things
in each gamesprite while still being able to draw all of them in one batch?

I'm new so I don't know if my understanding of xna is that great but this is where I'm at!
If someone could just give me some ideas or theories or examples that would be great.

Here's my draw for the GameSprite code, excuse the mess, still new:

public void Draw(GraphicsDevice graphics, Camera camera)
        {
            setVertexBuffer(graphics);
            
            //Gets texture from sprite sheet for animation.
            StopAtCurrentFrame(graphics);
            //set previous states
            RasterizerState prevRasterizerState = graphics.RasterizerState;
            BlendState prevBlendState = graphics.BlendState;

            Matrix billboardMatrix = Matrix.CreateConstrainedBillboard(WorldMatrix.Translation, camera.Position, Vector3.Up, camera.Forward, Vector3.UnitZ);
            Matrix ScalePositionFix = Matrix.CreateTranslation(Position * -scale);
            Matrix YFix = Matrix.CreateTranslation(new Vector3(0, (HEIGHT / WIDTH) * 2, 0));
            Matrix AppliedWorldMatrix = WorldMatrix * ScalePositionFix * YFix;
            //First Pass:
            //Render the non-transparent pixels of the billboards and store their depths
            billBoardEffect.Parameters["world"].SetValue(billboardMatrix * AppliedWorldMatrix);
            billBoardEffect.Parameters["view"].SetValue(camera.View);
            billBoardEffect.Parameters["projection"].SetValue(camera.Projection);
            billBoardEffect.Parameters["billboardSize"].SetValue(0);
            billBoardEffect.Parameters["alphaTestDirection"].SetValue(1);
            billBoardEffect.Parameters["colorMap"].SetValue(texture);

            graphics.BlendState = BlendState.Opaque;
            graphics.DepthStencilState = DepthStencilState.Default;
            graphics.RasterizerState = RasterizerState.CullNone;
            foreach (EffectPass pass in billBoardEffect.CurrentTechnique.Passes)
            {
                pass.Apply();
                graphics.DrawIndexedPrimitives(PrimitiveType.TriangleList,
                                                     0,
                                                     0,
                                                     vertexBuffer.VertexCount,
                                                     0,
                                                     (int)(vertexBuffer.VertexCount * 0.5f));
            }
            graphics.Indices = null;
            graphics.SetVertexBuffer(null);

            //Second pass
            billBoardEffect.Parameters["alphaTestDirection"].SetValue(-1.0f);
            graphics.BlendState = BlendState.NonPremultiplied;
            graphics.DepthStencilState = DepthStencilState.DepthRead;
            //graphics.SamplerStates[0] = SamplerState.PointWrap;
            setVertexBuffer(graphics);

            foreach (EffectPass pass in billBoardEffect.CurrentTechnique.Passes)
            {
                pass.Apply();
                graphics.DrawIndexedPrimitives(PrimitiveType.TriangleList,
                                                     0,
                                                     0,
                                                     vertexBuffer.VertexCount,
                                                     0,
                                                     (int)(vertexBuffer.VertexCount * 0.5f));
            }


            graphics.SetVertexBuffer(null);
            graphics.Indices = null;
            //set previous states


            graphics.BlendState = prevBlendState;
            graphics.RasterizerState = prevRasterizerState;
            setBounds();


Is This A Good Question/Topic? 0
  • +

Replies To: XNA Lots of billboard sprites, how to draw?

#2 BBeck  Icon User is offline

  • Here to help.
  • member icon


Reputation: 586
  • View blog
  • Posts: 1,306
  • Joined: 24-April 12

Re: XNA Lots of billboard sprites, how to draw?

Posted 06 February 2013 - 07:24 AM

Combining billboard images into one image/file may make things more efficient. The billboards themselves are just two triangles forming a quad.I have not run into a performance problem doing them programatically.

Here is a program that I wrote and had hoped to do a tutorial on. It's a 3D "world" with terrain, skybox, and a forest of 1000s of billboarded trees. It also included sound for ambient noise and footsteps when moving. Tree locations were randomly generated and I was thinking about trying to change it so they could be "painted" into the scene using a bitmap file or something. Never got around to it though.

Each tree is two quads crossed with an alpha transperancy tree image file projected on each quad. Getting the transparency to work correctly was the most difficult part.

I might also mention that this does terrain height following to make the terrain "solid" when walking around. You don't see that in a lot of the terrain tutorials out there, which greatly takes away from those tutorials.

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace TreeModels
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        GraphicsDevice GraphicsCard;
        BasicEffect Shader;
        AlphaTestEffect ATE;
        VertexPositionNormalTexture[] TerrainVertices;
        //VertexBuffer TerrainVertexBuffer;
        //VertexDeclaration VerticesDeclaration;
        //IndexBuffer TerrainIndexBuffer;
        int[] TerrainIndices;
        private int TerrainXSize = 512;   //Size must be an odd number to divide evenly and balance
        private int TerrainZSize = 512;   //Size must be an odd number to divide evenly and balance
        private float[,] TerrainHeightData; //Two dimensional array of x,z where the float value is height(y)
        private float[,] ForestTreeData;
        private int ForestMapResolution = 1024;
        //VertexPositionTexture[] TreeVertices;
        //int[] TreeIndices;
        //Texture2D BillBoardTexture;
        ////VertexBuffer BillBoardVertexBuffer;
        ////IndexBuffer BillBoardIndexBuffer;
        //List<Vector3> TreeList;
        //float TreeScale = 22f;
        //Matrix BillBoardWorldMatrix;
        //Matrix BBWorldMatrix;
        //TreeModel Tree;
        //List<Vector3> TreeList;
        List<TreeModel> TreeList;

        VertexPositionTexture[] SkyVertices;
        int[] SkyIndices;
        Model SkyBox;
        Matrix SkyBoxWorldMatrix;
        Matrix WorldMatrix = Matrix.Identity;
        BasicCamera Camera;
        Texture2D TerrainHeightMap;
        Texture2D ForestMap;
        int GridScale = 6;
        float HeightScale = 300.0f;     //Scale the height of the terrain. This number should be the max height.
        Random RandomNumberGenerator;
        Texture2D GridTexture;
        Texture2D SkyTexture;
        SoundEffect FootStepEffect;
        SoundEffectInstance FootStepsInstance;
        SoundEffect Ambience;
        SoundEffectInstance AmbienceInstance;
        SoundEffect Ambience2;
        SoundEffectInstance AmbienceInstance2;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            graphics.PreferredBackBufferWidth = 1280;   //Screen width horizontal
            graphics.PreferredBackBufferHeight = 720;   //Screen width vertical
            graphics.PreferredBackBufferFormat = SurfaceFormat.Color;
            graphics.PreferredDepthStencilFormat = DepthFormat.Depth24Stencil8;

            graphics.PreferMultiSampling = true;
            graphics.IsFullScreen = false;

            Content.RootDirectory = "Content";

        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here

            //graphics.ApplyChanges();

            window.Title = "BillBoarding Test";
            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            // TODO: use this.Content to load your game content here
            GraphicsCard = graphics.GraphicsDevice;
            Shader = new BasicEffect(GraphicsCard);
            ATE = new AlphaTestEffect(GraphicsCard);
            ATE.AlphaFunction = CompareFunction.Greater;
            ATE.ReferenceAlpha = 90;

            RandomNumberGenerator = new Random();
            GridTexture = Content.Load<Texture2D>("grass");
            //SkyBox Texture from http://www.ataricommunity.com/forums/showthread.php?t=506748
            //The www.zFight.com website seems to be abandoned
            //Need to contact Hipshot@www.zFight.com to verify.
            //SkyTexture = Content.Load<Texture2D>("miramar_large");
            SkyBox = Content.Load<Model>("Skybox");
            SkyBoxWorldMatrix = Matrix.CreateScale(4000f, 4000f, 4000f);

            //Set the sound effects to use
            FootStepEffect = Content.Load<SoundEffect>("GrassStep");
            FootStepsInstance = FootStepEffect.CreateInstance();
            Ambience2 = Content.Load<SoundEffect>("windtrees");
            AmbienceInstance2 = Ambience2.CreateInstance();
            AmbienceInstance2.IsLooped = true;
            AmbienceInstance2.Volume = 0.70f;
            AmbienceInstance2.Play();

            Ambience = Content.Load<SoundEffect>("BirdsAndStuff");
            AmbienceInstance = Ambience.CreateInstance();
            AmbienceInstance.IsLooped = true;
            AmbienceInstance.Volume = 0.26f;
            AmbienceInstance.Play();


            LoadTerrainHeightData();
            TerrainVertices = SetTerrainVertices();
            SetTerrainIndices();
            TerrainVertices = CalculateNormals(TerrainVertices, TerrainIndices);

            //Trees are planted into the terrain. Therefore they must be defined after the terrain.
            LoadForestMapData();

            TreeList = new List<TreeModel>();
            for (int i =1; i <= 10000; i++)
            {
                float XPos = 0f;
                float ZPos = 0f;
                int RandX = RandomNumberGenerator.Next((TerrainXSize * GridScale)-100);
                int RandZ = RandomNumberGenerator.Next((TerrainZSize * GridScale)-100);
                XPos = (float)RandX-(((TerrainXSize*GridScale)-100)/2);
                ZPos = (float)RandZ - (((TerrainZSize*GridScale)-100) / 2);

                TreeModel NewTree = new TreeModel(Content.Load<Texture2D>("BillBoardTree1"), new Vector3(XPos, TerrainHeightBelow(XPos, ZPos), ZPos), (float) RandomNumberGenerator.Next(628)/100, 22f);
                TreeList.Add(NewTree);
            }

            
            Camera = new BasicCamera(new Vector3(0.0f, TerrainHeightBelow(0, 0), 0.0f), GraphicsDevice.Viewport.AspectRatio);
            Camera.Ground(TerrainHeightBelow(0, 0));

        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }


        private void LoadForestMapData()
        {
            float CellValue = 0f;
            Color[] TreeData;

            ForestMap = Content.Load<Texture2D>("ForestMap");
            ForestTreeData = new float[ForestMapResolution, ForestMapResolution];
            TreeData = new Color[ForestMapResolution * ForestMapResolution];
            ForestMap.GetData<Color>(TreeData);

            //Need to add code here
            for (int z = 0; z < ForestMapResolution; z++)
            {
                for (int x = 0; x < ForestMapResolution; x++)
                {
                    CellValue = TreeData[z * ForestMapResolution+ x].R;   //Get gray scale value from Height Map as a value 0-255
                    CellValue = CellValue / 255.0f;                 //Normalize Height to value between zero and one
                    ForestTreeData[x, z] = CellValue;
                }
            }
        }


        private void LoadTerrainHeightData()
        {
            float HeightValue = 0.0f;
            Color[] HeightData;

            TerrainHeightMap = Content.Load<Texture2D>("TemperateHeight");
            TerrainHeightData = new float[TerrainXSize, TerrainZSize];
            HeightData = new Color[TerrainXSize * TerrainZSize];
            TerrainHeightMap.GetData<Color>(HeightData);


            for (int z = 0; z < TerrainZSize; z++)
            {
                for (int x = 0; x < TerrainXSize; x++)
                {
                    HeightValue = HeightData[z * TerrainXSize + x].R;   //Get gray scale value from Height Map as a value 0-255
                    HeightValue = HeightValue / 255.0f;                 //Normalize Height to value between zero and one
                    TerrainHeightData[x, z] = HeightValue * HeightScale;
                }
            }
        }


        private VertexPositionNormalTexture[] SetTerrainVertices()
        {
            VertexPositionNormalTexture[] TerrainVertices = new VertexPositionNormalTexture[TerrainXSize * TerrainZSize];   //The vertex array index is ALWAYS positive.
            float XMidPoint = TerrainXSize / 2;     //Find the vertex in the middle
            float ZMidPoint = TerrainZSize / 2;     //Find the vertex in the middle


            for (int x = 0; x < TerrainXSize; x++)
            {
                for (int z = 0; z < TerrainZSize; z++)
                {
                    TerrainVertices[x + z * TerrainXSize].Position = new Vector3((x - XMidPoint) * GridScale, TerrainHeightData[x, z], (z - ZMidPoint) * GridScale);
                    TerrainVertices[x + z * TerrainXSize].TextureCoordinate.X = (float)x / 33.2f * GridScale;
                    TerrainVertices[x + z * TerrainXSize].TextureCoordinate.Y = (float)z / 25.4f * GridScale;
                }
            }
            return TerrainVertices;
        }


        private void SetTerrainIndices()
        {
            //Indices need to be wound in clockwise order in XNA.
            TerrainIndices = new int[(TerrainXSize - 1) * (TerrainZSize - 1) * 6];  //-1 because arrays number from 0,0
            int counter = 0;
            //Look at every vertex left to right and then every row back to front and set indices
            for (int z = 0; z < TerrainZSize - 1; z++)      //Go through the vertices from back to front
            {
                for (int x = 0; x < TerrainXSize - 1; x++)  //Go through the vertices from left to right
                {
                    int UpperLeft = x + z * TerrainXSize;
                    int UpperRight = (x + 1) + z * TerrainXSize;
                    int LowerLeft = x + (z + 1) * TerrainXSize;
                    int LowerRight = (x + 1) + (z + 1) * TerrainXSize;

                    //Top Triangle
                    TerrainIndices[counter++] = LowerLeft;
                    TerrainIndices[counter++] = UpperLeft;
                    TerrainIndices[counter++] = UpperRight;
                    //Bottom Triangle
                    TerrainIndices[counter++] = UpperRight;
                    TerrainIndices[counter++] = LowerRight;
                    TerrainIndices[counter++] = LowerLeft;

                    //The shared edge between the two triangles is the one "implied".
                }
            }
        }

        private VertexPositionNormalTexture[] CalculateNormals(VertexPositionNormalTexture[] Vertices, int[] Indices)
        {
            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++)
            {
                int Index1 = Indices[i * 3];
                int Index2 = Indices[i * 3 + 1];
                int Index3 = Indices[i * 3 + 2];

                Vector3 Side1 = Vertices[Index1].Position - Vertices[Index3].Position;
                Vector3 Side2 = Vertices[Index1].Position - Vertices[Index2].Position;
                Vector3 Normal = Vector3.Cross(Side1, Side2);

                Vertices[Index1].Normal += Normal;
                Vertices[Index2].Normal += Normal;
                Vertices[Index3].Normal += Normal;
            }

            for (int i = 0; i < Vertices.Length; i++)
                Vertices[i].Normal.Normalize();

            return Vertices;
        }


        private VertexPositionTexture[] InitializeSkyBoxVertices()
        {
            VertexPositionTexture[] SkyVertices = new VertexPositionTexture[8];
            float XMidPoint = TerrainXSize / 2;     //Find the vertex in the middle
            float ZMidPoint = TerrainZSize / 2;     //Find the vertex in the middle

            //Upper Back Left
            SkyVertices[0].Position = new Vector3(-TerrainXSize, TerrainXSize * 1.5f, -TerrainZSize);
            SkyVertices[0].TextureCoordinate.X = (float)1.0f / 4;
            SkyVertices[0].TextureCoordinate.Y = (float)1.0f / 3;
            //Upper Back Right
            SkyVertices[1].Position = new Vector3(TerrainXSize, TerrainXSize * 1.5f, -TerrainZSize);
            SkyVertices[1].TextureCoordinate.X = (float)2.0f / 4;
            SkyVertices[1].TextureCoordinate.Y = (float)1.0f / 3;
            //Lower Back Left
            SkyVertices[2].Position = new Vector3(-TerrainXSize, -TerrainXSize * 0.5f, -TerrainZSize);
            SkyVertices[2].TextureCoordinate.X = (float)1.0f / 4;
            SkyVertices[2].TextureCoordinate.Y = (float)2.0f / 3;
            //Lower Back Right
            SkyVertices[3].Position = new Vector3(TerrainXSize, -TerrainXSize * 0.5f, -TerrainZSize);
            SkyVertices[3].TextureCoordinate.X = (float)2.0f / 4;
            SkyVertices[3].TextureCoordinate.Y = (float)2.0f / 3;
            //Upper Front Left
            SkyVertices[4].Position = new Vector3(-TerrainXSize, TerrainXSize * 1.5f, TerrainZSize);
            SkyVertices[4].TextureCoordinate.X = (float)0.0f / 4;
            SkyVertices[4].TextureCoordinate.Y = (float)1.0f / 3;
            //Upper Front Right
            SkyVertices[5].Position = new Vector3(TerrainXSize, TerrainXSize * 1.5f, TerrainZSize);
            SkyVertices[5].TextureCoordinate.X = (float)5.0f / 4;
            SkyVertices[5].TextureCoordinate.Y = (float)1.0f / 3;
            //Lower Front Left
            SkyVertices[6].Position = new Vector3(-TerrainXSize, -TerrainXSize * 0.5f, TerrainZSize);
            SkyVertices[6].TextureCoordinate.X = (float)0.0f / 4;
            SkyVertices[6].TextureCoordinate.Y = (float)2.0f / 3;
            //Lower Front Right
            SkyVertices[7].Position = new Vector3(TerrainXSize, -TerrainXSize * 0.5f, TerrainZSize);
            SkyVertices[7].TextureCoordinate.X = (float)5.0f / 4;
            SkyVertices[7].TextureCoordinate.Y = (float)2.0f / 3;


            return SkyVertices;
        }

        private void InitializeSkyIndices()
        {
            SkyIndices = new int[36];

            const int BackUpperLeft = 0;
            const int BackUpperRight = 1;
            const int BackLowerLeft = 2;
            const int BackLowerRight = 3;
            const int FrontUpperLeft = 4;
            const int FrontUpperRight = 5;
            const int FrontLowerLeft = 6;
            const int FrontLowerRight = 7;

            //Need to wind Indices clockwise to be seen
            //Order of defining the faces seems irrelevant
            //UpFace
            SkyIndices[0] = BackUpperRight;
            SkyIndices[1] = BackUpperLeft;
            SkyIndices[2] = FrontUpperLeft;

            SkyIndices[3] = FrontUpperLeft;
            SkyIndices[4] = FrontUpperRight;
            SkyIndices[5] = BackUpperRight;

            //NorthFace
            SkyIndices[6] = BackLowerLeft;
            SkyIndices[7] = BackUpperLeft;
            SkyIndices[8] = BackUpperRight;

            SkyIndices[9] = BackUpperRight;
            SkyIndices[10] = BackLowerRight;
            SkyIndices[11] = BackLowerLeft;
            //EastFace
            SkyIndices[12] = BackLowerRight;
            SkyIndices[13] = BackUpperRight;
            SkyIndices[14] = FrontUpperRight;

            SkyIndices[15] = FrontUpperRight;
            SkyIndices[16] = FrontLowerRight;
            SkyIndices[17] = BackLowerRight;

            //WestFace
            SkyIndices[18] = FrontLowerLeft;
            SkyIndices[19] = FrontUpperLeft;
            SkyIndices[20] = BackUpperLeft;

            SkyIndices[21] = BackUpperLeft;
            SkyIndices[22] = BackLowerLeft;
            SkyIndices[23] = FrontLowerLeft;
            //DownFace
            SkyIndices[24] = FrontLowerLeft;
            SkyIndices[25] = BackLowerLeft;
            SkyIndices[26] = BackLowerRight;

            SkyIndices[27] = BackLowerRight;
            SkyIndices[28] = FrontLowerRight;
            SkyIndices[29] = FrontLowerLeft;
            //SouthFace
            SkyIndices[30] = FrontUpperRight;
            SkyIndices[31] = FrontUpperLeft;
            SkyIndices[32] = FrontLowerLeft;

            SkyIndices[33] = FrontLowerLeft;
            SkyIndices[34] = FrontLowerRight;
            SkyIndices[35] = FrontUpperRight;

        }

        private void UpdateSkyBoxVertices(float x, float y, float z)
        {

            //Upper Back Left
            SkyVertices[0].Position = new Vector3(-TerrainXSize + x, TerrainXSize * 1.5f + y, -TerrainZSize + z);
            //Upper Back Right
            SkyVertices[1].Position = new Vector3(TerrainXSize + x, TerrainXSize * 1.5f + y, -TerrainZSize + z);
            //Lower Back Left
            SkyVertices[2].Position = new Vector3(-TerrainXSize + x, -TerrainXSize * 0.5f + y, -TerrainZSize + z);
            //Lower Back Right
            SkyVertices[3].Position = new Vector3(TerrainXSize + x, -TerrainXSize * 0.5f + y, -TerrainZSize + z);
            //Upper Front Left
            SkyVertices[4].Position = new Vector3(-TerrainXSize + x, TerrainXSize * 1.5f + y, TerrainZSize + z);
            //Upper Front Right
            SkyVertices[5].Position = new Vector3(TerrainXSize + x, TerrainXSize * 1.5f + y, TerrainZSize + z);
            //Lower Front Left
            SkyVertices[6].Position = new Vector3(-TerrainXSize + x, -TerrainXSize * 0.5f + y, TerrainZSize + z);
            //Lower Front Right
            SkyVertices[7].Position = new Vector3(TerrainXSize + x, -TerrainXSize * 0.5f + y, TerrainZSize + z);


        }


        private float TerrainHeightBelow(float x, float z)
        {
            float HeightOfTerrain = 0.0f;
            //Quad has edge between LowerLeft and UpperRight... I think...
            float QuadUpperLeft;
            float QuadLowerLeft;
            float QuadUpperRight;
            float QuadLowerRight;
            float XInQuad = 0.0f;
            float ZInQuad = 0.0f;
            int X = 0;
            int Z = 0;
            float HeightBetweenULandLL = 0.0f;
            float HeightBetweenURandLR = 0.0f;


            //We set the starting point as "center of the grid" and have to offset for that
            x = (x / GridScale) + TerrainXSize / 2;
            z = (z / GridScale) + TerrainZSize / 2;


            X = (int)Math.Floor(x);
            Z = (int)Math.Floor(z);

            XInQuad = x - X;
            ZInQuad = z - Z;

            //Get the height at every corner of the quad (square)
            QuadUpperLeft = TerrainHeightData[X, Z];
            QuadLowerLeft = TerrainHeightData[X, Z + 1];
            QuadUpperRight = TerrainHeightData[X + 1, Z];
            QuadLowerRight = TerrainHeightData[X + 1, Z + 1];

            //If the coordinate is out of bounds (off the grid) just set height to max
            if ((x < 1) || (z < 1) || (x > TerrainXSize - 2) || (z > TerrainZSize - 2))
            {
                HeightOfTerrain = HeightScale;
            }
            else
            {
                //This next formula is the key even though it may be confusing at first glance.
                //Get the height between the two left corners. Then get the height between the right two corners.
                //Then get the height between those two heights. XInQuad is the percentage of the way between
                //the left and right side of the quad. ZInQuad is the percentage of the way between the top
                //of the quad and the bottom. Weighting the average of the four points based on how close the 
                //point being analyzed is to each of the four corners and the height at that corner.
                HeightBetweenULandLL = QuadUpperLeft * (1 - ZInQuad) + QuadLowerLeft * ZInQuad;
                HeightBetweenURandLR = QuadUpperRight * (1 - ZInQuad) + QuadLowerRight * ZInQuad;
                HeightOfTerrain = HeightBetweenULandLL * (1 - XInQuad) + HeightBetweenURandLR * XInQuad;

            }

            return HeightOfTerrain;
        }

        private bool CausesCollision(Vector3 CameraMovement)
        {
            bool CollisionCaused = false;
            int BarrierFromEdge = 120 * GridScale;

            if (CameraMovement.X <= -(((TerrainXSize * GridScale) - BarrierFromEdge) / 2)
                || CameraMovement.X >= (((TerrainXSize * GridScale) - BarrierFromEdge) / 2)
                || CameraMovement.Z <= -(((TerrainZSize * GridScale) - BarrierFromEdge) / 2)
                || CameraMovement.Z >= (((TerrainZSize * GridScale) - BarrierFromEdge) / 2))
            {
                CollisionCaused = true;
            }

            return CollisionCaused;
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            KeyboardState KBState;

            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            // TODO: Add your update logic here
            KBState = Keyboard.GetState();
            if (KBState.IsKeyDown(Keys.Escape)) this.Exit();


            if (KBState.IsKeyDown(Keys.Up) || KBState.IsKeyDown(Keys.W))
            {

                if (!CausesCollision(Camera.TestForward2D()))
                {
                    Camera.Forward2D();
                    Camera.Ground(TerrainHeightBelow(Camera.XPosition(), Camera.ZPosition()));
                }

                if (FootStepsInstance.State == SoundState.Stopped)
                {
                    FootStepsInstance.Volume = 0.50f;
                    FootStepsInstance.IsLooped = true;
                    FootStepsInstance.Play();
                }
                else
                {
                    FootStepsInstance.Resume();
                }
            }
            else
            {
                if (FootStepsInstance.State == SoundState.Playing)
                {
                    FootStepsInstance.Pause();
                }
            };

            if (KBState.IsKeyDown(Keys.Down) || KBState.IsKeyDown(Keys.S))
            {
                Camera.Back2D();
                Camera.Ground(TerrainHeightBelow(Camera.XPosition(), Camera.ZPosition()));

                if (FootStepsInstance.State == SoundState.Stopped)
                {
                    FootStepsInstance.Volume = 0.50f;
                    FootStepsInstance.IsLooped = true;
                    FootStepsInstance.Play();
                }
                else
                {
                    FootStepsInstance.Resume();
                }
            };

            if (KBState.IsKeyDown(Keys.Left) || KBState.IsKeyDown(Keys.A))
            {
                Camera.Rotate2D(0.035f);
            };

            if (KBState.IsKeyDown(Keys.Right) || KBState.IsKeyDown(Keys.D))
            {
                Camera.Rotate2D(-0.035f);
            };

            if (KBState.IsKeyDown(Keys.Q) || KBState.IsKeyDown(Keys.PageDown))
            {
                Camera.PitchView(-0.01f);
            }

            if (KBState.IsKeyDown(Keys.E) || KBState.IsKeyDown(Keys.PageUp))
            {
                Camera.PitchView(0.01f);
            }

            base.Update(gameTime);
        }


        //private Texture2D CreateStaticMap(int resolution)
        //{
        //    Random rand = new Random();
        //    Color[] noisyColors = new Color[resolution * resolution];
        //    for (int x = 0; x < resolution; x++)
        //        for (int y = 0; y < resolution; y++)
        //            noisyColors[x + y * resolution] = new Color(new Vector3((float)rand.Next(1000) / 1000.0f, 0, 0));

        //    Texture2D noiseImage = new Texture2D(device, resolution, resolution, 1, TextureUsage.None, SurfaceFormat.Color);
        //    noiseImage.SetData(noisyColors);
        //    return noiseImage;
        //}


        //private List<Vector3> GenerateTreePositions()
        //{
        //    TreeList = new List<Vector3>();
        //    TreeList.Add(new Vector3(12.5f, TerrainHeightBelow(12.5f, 0.5f), 0.5f));
        //    TreeList.Add(new Vector3(3f, TerrainHeightBelow(3f, 3f), 3f));
        //    TreeList.Add(new Vector3(24f, TerrainHeightBelow(24f, 24f), 24f));
        //    TreeList.Add(new Vector3(0f, TerrainHeightBelow(0f, 0f), 0f));

        //    return TreeList;
        //}


        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            // TODO: Add your drawing code here
            RasterizerState RS = new RasterizerState();
            //DepthStencilState depthBufferState;
            GraphicsCard.Clear(Color.CornflowerBlue);
            RS.CullMode = CullMode.CullClockwiseFace; //Model has it's indices wound backwards.
            RS.FillMode = FillMode.Solid;
            GraphicsCard.RasterizerState = RS;


            foreach (ModelMesh Mesh in SkyBox.Meshes)
            {
                foreach (BasicEffect e in Mesh.Effects)
                {
                    e.EnableDefaultLighting();
                    e.TextureEnabled = true;
                    //e.Texture = SkyTexture;
                    e.LightingEnabled = false;
                    e.World = SkyBoxWorldMatrix * Matrix.CreateWorld(new Vector3(Camera.XPosition(), Camera.YPosition(), Camera.ZPosition()), Vector3.Forward, Vector3.Up);
                    e.View = Camera.View();
                    e.Projection = Camera.Projection();
                }

                Mesh.Draw();
            }

            //Render Terrain
            RS = new RasterizerState();
            RS.CullMode = CullMode.CullCounterClockwiseFace; //Do not show the back side of the mesh
            //RS.FillMode = FillMode.Solid;
            GraphicsCard.RasterizerState = RS;

            Shader.View = Camera.View();
            Shader.Projection = Camera.Projection();
            Shader.World = WorldMatrix;

            Shader.LightingEnabled = true;
            Shader.TextureEnabled = true;
            Shader.AmbientLightColor = Color.DarkSlateBlue.ToVector3();     //Dark Cloud color
            Shader.DiffuseColor = Color.White.ToVector3();
            Shader.SpecularColor = Color.Black.ToVector3();
            Shader.FogEnabled = true;
            Shader.Alpha = 1f;
            Shader.FogColor = Color.SlateGray.ToVector3();
            //Very good fog setting but you can see the edge of the terrain
            //Shader.FogStart = 45.0f;    //Fog start distance
            //Shader.FogEnd = 450.0f;     //Fog begins max, distance

            Shader.FogStart = 45.0f;    //Fog start distance
            Shader.FogEnd = 250.0f;     //Fog begins max, distance
            Shader.PreferPerPixelLighting = true;
            Shader.DirectionalLight0.Enabled = true;
            Shader.DirectionalLight0.Direction = Vector3.Normalize(new Vector3(-700f, -4000f, -3700f));
            Shader.DirectionalLight0.DiffuseColor = Color.Goldenrod.ToVector3();
            Shader.DirectionalLight0.SpecularColor = Color.Black.ToVector3();
            Shader.SpecularPower = 0.6f;
            Shader.Texture = GridTexture;

            foreach (EffectPass pass in Shader.CurrentTechnique.Passes)
            {
                pass.Apply();
                GraphicsCard.DepthStencilState = DepthStencilState.Default;
                GraphicsCard.DrawUserIndexedPrimitives(PrimitiveType.TriangleList, TerrainVertices, 0, TerrainVertices.Length,
                    TerrainIndices, 0, TerrainIndices.Length / 3);
            }

            foreach (TreeModel Tree in TreeList)
            {
                Tree.Draw(GraphicsCard, Shader, ATE);
            }

            //Render Terrain
            RS = new RasterizerState();
            RS.CullMode = CullMode.CullCounterClockwiseFace; //Do not show the back side of the mesh
            //RS.FillMode = FillMode.Solid;
            GraphicsCard.RasterizerState = RS;

            Shader.View = Camera.View();
            Shader.Projection = Camera.Projection();
            Shader.World = WorldMatrix;

            Shader.LightingEnabled = true;
            Shader.TextureEnabled = true;
            Shader.AmbientLightColor = Color.DarkSlateBlue.ToVector3();     //Dark Cloud color
            Shader.DiffuseColor = Color.White.ToVector3();
            Shader.SpecularColor = Color.Black.ToVector3();
            Shader.FogEnabled = true;
            Shader.Alpha = 1f;
            Shader.FogColor = Color.SlateGray.ToVector3();
            //Very good fog setting but you can see the edge of the terrain
            //Shader.FogStart = 45.0f;    //Fog start distance
            //Shader.FogEnd = 450.0f;     //Fog begins max, distance

            Shader.FogStart = 45.0f;    //Fog start distance
            Shader.FogEnd = 250.0f;     //Fog begins max, distance
            Shader.PreferPerPixelLighting = true;
            Shader.DirectionalLight0.Enabled = true;
            Shader.DirectionalLight0.Direction = Vector3.Normalize(new Vector3(-700f, -4000f, -3700f));
            Shader.DirectionalLight0.DiffuseColor = Color.Goldenrod.ToVector3();
            Shader.DirectionalLight0.SpecularColor = Color.Black.ToVector3();
            Shader.SpecularPower = 0.6f;
            Shader.Texture = GridTexture;

            foreach (EffectPass pass in Shader.CurrentTechnique.Passes)
            {
                pass.Apply();
                GraphicsCard.DepthStencilState = DepthStencilState.Default;
                GraphicsCard.DrawUserIndexedPrimitives(PrimitiveType.TriangleList, TerrainVertices, 0, TerrainVertices.Length,
                    TerrainIndices, 0, TerrainIndices.Length / 3);
            }

            base.Draw(gameTime);
        }
    }
}



TreeModel.cs
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;


namespace TreeModels
{
    class TreeModel
    {
        VertexPositionTexture[] TreeVertices;
        int[] TreeIndices;
        //Vector3 TreePosition;
        Texture2D TreeTexture;
        //float TreeScale;
        //float TreeRotation; //In Radians
        Matrix TreeWorldMatrix;


        public TreeModel(Texture2D Texture, Vector3 Position, float Rotation, float Scale)
        {
            //TreeScale = Scale;
            TreeTexture = Texture; 
            //TreeWorldMatrix = Matrix.Identity;
            //TreeRotation = Rotation;
            DefineTree();
            TreeWorldMatrix = Matrix.CreateRotationY(Rotation) * Matrix.CreateScale(Scale) * Matrix.CreateTranslation(Position);
            TreeWorldMatrix *= Matrix.CreateTranslation(new Vector3(0f, -0.1f, 0f)); //Plant it in the ground
        }



        //public void DefineTrees(List<Vector3> TreeList)
        //{
        //    int Index = -1;     //Must be -1 to start the loop at zero and to be correct on each iteration.
        //    int Index2 = -1;    //Must be -1 to start the loop at zero and to be correct on each iteration.
        //    int VerticesPerTree = 8;
        //    int TreesDefined = 0;
        //    Matrix TreeRotation;

        //    VertexDeclaration VerticesDeclaration = new VertexDeclaration(
        //        new VertexElement(0, VertexElementFormat.Vector3, VertexElementUsage.Position, 0),
        //        new VertexElement(12, VertexElementFormat.Vector2, VertexElementUsage.TextureCoordinate, 0)
        //        );

        //    TreeVertices = new VertexPositionTexture[TreeList.Count * VerticesPerTree];
        //    TreeIndices = new int[TreeList.Count * 12];


        //    foreach (Vector3 TreePosition in TreeList)
        //    {
        //        TreeRotation = Matrix.CreateRotationY(MathHelper.ToRadians(45f));
        //        //Bottom Left corner of the quad
        //        TreeVertices[++Index].Position = TreePosition + Vector3.Transform(new Vector3(-0.5f * TreeScale, 0f, 0f), TreeRotation);

        //        TreeVertices[Index].TextureCoordinate = new Vector2(0.0f, 1.0f);
        //        //Top Left corner of the quad
        //        TreeVertices[++Index].Position = TreePosition + Vector3.Transform(new Vector3(-0.5f * TreeScale, 1f * TreeScale, 0f), TreeRotation);
        //        TreeVertices[Index].TextureCoordinate.X = 0.0f;
        //        TreeVertices[Index].TextureCoordinate.Y = 0.0f;
        //        //Top Right
        //        TreeVertices[++Index].Position = TreePosition + Vector3.Transform(new Vector3(0.5f * TreeScale, 1f * TreeScale, 0f), TreeRotation);
        //        TreeVertices[Index].TextureCoordinate.X = 1.0f;
        //        TreeVertices[Index].TextureCoordinate.Y = 0.0f;
        //        //Bottom Right
        //        TreeVertices[++Index].Position = TreePosition + Vector3.Transform(new Vector3(0.5f * TreeScale, 0f, 0f), TreeRotation);
        //        TreeVertices[Index].TextureCoordinate.X = 1.0f;
        //        TreeVertices[Index].TextureCoordinate.Y = 1.0f;

        //        //Bottom Left corner of the quad
        //        TreeVertices[++Index].Position = TreePosition + Vector3.Transform(new Vector3(0f, 0f, -0.5f * TreeScale), TreeRotation);
        //        TreeVertices[Index].TextureCoordinate.X = 0.0f;
        //        TreeVertices[Index].TextureCoordinate.Y = 1.0f;
        //        //Top Left corner of the quad
        //        TreeVertices[++Index].Position = TreePosition + Vector3.Transform(new Vector3(0f, 1f * TreeScale, -0.5f * TreeScale), TreeRotation);
        //        TreeVertices[Index].TextureCoordinate.X = 0.0f;
        //        TreeVertices[Index].TextureCoordinate.Y = 0.0f;
        //        //Top Right
        //        TreeVertices[++Index].Position = TreePosition + Vector3.Transform(new Vector3(0f, 1f * TreeScale, 0.5f * TreeScale), TreeRotation);
        //        TreeVertices[Index].TextureCoordinate.X = 1.0f;
        //        TreeVertices[Index].TextureCoordinate.Y = 0.0f;
        //        //Bottom Right
        //        TreeVertices[++Index].Position = TreePosition + Vector3.Transform(new Vector3(0f, 0f, 0.5f * TreeScale), TreeRotation);
        //        TreeVertices[Index].TextureCoordinate.X = 1.0f;
        //        TreeVertices[Index].TextureCoordinate.Y = 1.0f;

        //        //Clockwise Winding
        //        TreeIndices[++Index2] = VerticesPerTree * TreesDefined + 2;
        //        TreeIndices[++Index2] = VerticesPerTree * TreesDefined + 1;
        //        TreeIndices[++Index2] = VerticesPerTree * TreesDefined + 0;

        //        TreeIndices[++Index2] = VerticesPerTree * TreesDefined + 0;
        //        TreeIndices[++Index2] = VerticesPerTree * TreesDefined + 3;
        //        TreeIndices[++Index2] = VerticesPerTree * TreesDefined + 2;

        //        TreeIndices[++Index2] = VerticesPerTree * TreesDefined + 6;
        //        TreeIndices[++Index2] = VerticesPerTree * TreesDefined + 5;
        //        TreeIndices[++Index2] = VerticesPerTree * TreesDefined + 4;

        //        TreeIndices[++Index2] = VerticesPerTree * TreesDefined + 4;
        //        TreeIndices[++Index2] = VerticesPerTree * TreesDefined + 7;
        //        TreeIndices[++Index2] = VerticesPerTree * TreesDefined + 6;
        //        TreesDefined++;
        //    }

        //}


        public void DefineTree()
        {
            int VerticesPerTree = 8;
            //Matrix RotationMatrix;

            VertexDeclaration VerticesDeclaration = new VertexDeclaration(
                new VertexElement(0, VertexElementFormat.Vector3, VertexElementUsage.Position, 0),
                new VertexElement(12, VertexElementFormat.Vector2, VertexElementUsage.TextureCoordinate, 0)
                );

            TreeVertices = new VertexPositionTexture[VerticesPerTree];
            TreeIndices = new int[12];


            //RotationMatrix = Matrix.CreateRotationY(MathHelper.ToRadians(TreeRotation));
            //Bottom Left corner of the quad
            TreeVertices[0].Position = new Vector3(-0.5f, 0f, 0f);
            TreeVertices[0].TextureCoordinate = new Vector2(0.0f, 1.0f);
            //Top Left corner of the quad
            TreeVertices[1].Position = new Vector3(-0.5f, 1f, 0f);
            TreeVertices[1].TextureCoordinate = new Vector2(0.0f, 0.0f);
            //Top Right
            TreeVertices[2].Position = new Vector3(0.5f, 1f, 0f);
            TreeVertices[2].TextureCoordinate = new Vector2(1.0f, 0.0f);
            //Bottom Right
            TreeVertices[3].Position = new Vector3(0.5f, 0f, 0f);
            TreeVertices[3].TextureCoordinate.X = 1.0f;
            TreeVertices[3].TextureCoordinate.Y = 1.0f;

            //Bottom Left corner of the quad
            TreeVertices[4].Position = new Vector3(0f, 0f, -0.5f);
            TreeVertices[4].TextureCoordinate.X = 0.0f;
            TreeVertices[4].TextureCoordinate.Y = 1.0f;
            //Top Left corner of the quad
            TreeVertices[5].Position = new Vector3(0f, 1f, -0.5f);
            TreeVertices[5].TextureCoordinate.X = 0.0f;
            TreeVertices[5].TextureCoordinate.Y = 0.0f;
            //Top Right
            TreeVertices[6].Position = new Vector3(0f, 1f, 0.5f);
            TreeVertices[6].TextureCoordinate.X = 1.0f;
            TreeVertices[6].TextureCoordinate.Y = 0.0f;
            //Bottom Right
            TreeVertices[7].Position = new Vector3(0f, 0f, 0.5f);
            TreeVertices[7].TextureCoordinate.X = 1.0f;
            TreeVertices[7].TextureCoordinate.Y = 1.0f;

            //Clockwise Winding
            TreeIndices[0] = 2;
            TreeIndices[1] = 1;
            TreeIndices[2] = 0;

            TreeIndices[3] = 0;
            TreeIndices[4] = 3;
            TreeIndices[5] = 2;

            TreeIndices[6] = 6;
            TreeIndices[7] = 5;
            TreeIndices[8] = 4;

            TreeIndices[9] = 4;
            TreeIndices[10] = 7;
            TreeIndices[11] = 6;
        }


        public void Draw(GraphicsDevice GDev, BasicEffect Shader, AlphaTestEffect ATE)
        {
            RasterizerState RS = new RasterizerState();

            RS.CullMode = CullMode.None; //Show both sides
            RS.FillMode = FillMode.Solid;
            GDev.RasterizerState = RS;


            //TreeWorldMatrix = Matrix.CreateWorld(new Vector3(0f, -0.1f, 0f), Vector3.Forward, Vector3.Up); //BillBoardWorldMatrix *Matrix.CreateConstrainedBillboard(new Vector3(0f, TerrainHeightBelow(0, 0) - 0.1f, 0f), new Vector3(Camera.XPosition(), TerrainHeightBelow(Camera.XPosition(), Camera.ZPosition()) - 0.1f, Camera.ZPosition()), Vector3.Up, Camera.Facing(), Vector3.Forward);  //-0.1f to plant the tree in the ground
            ATE.World = TreeWorldMatrix;
            ATE.View = Shader.View;
            ATE.Projection = Shader.Projection;
            ATE.FogEnabled = true;
            ATE.FogColor = Shader.FogColor;
            ATE.FogStart = Shader.FogStart;
            ATE.FogEnd = Shader.FogEnd;
            //Shader.World = TreeWorldMatrix;
            //Shader.LightingEnabled = false;
            //Shader.Texture = TreeTexture;
            ATE.Texture = TreeTexture;

            //foreach (EffectPass pass in Shader.CurrentTechnique.Passes)
            foreach (EffectPass pass in ATE.CurrentTechnique.Passes)
            {
                pass.Apply();
                //GDev.BlendState = BlendState.Opaque;
                GDev.BlendState = BlendState.AlphaBlend;
                //You have to draw this once with the depth buffer on and once with it off in order to draw correctly.
                GDev.DepthStencilState = DepthStencilState.Default;

                //GDev.SamplerStates[0] = SamplerState.LinearClamp;
                GDev.DrawUserIndexedPrimitives(PrimitiveType.TriangleList, TreeVertices, 0, TreeVertices.Length, TreeIndices, 0, TreeIndices.Length / 3);
                
                //GDev.BlendState = BlendState.AlphaBlend;
                //GDev.DepthStencilState = DepthStencilState.DepthRead;
                //GDev.DrawUserIndexedPrimitives(PrimitiveType.TriangleList, TreeVertices, 0, TreeVertices.Length, TreeIndices, 0, TreeIndices.Length / 3);
                //GDev.DepthStencilState = DepthStencilState.Default;
                GDev.BlendState = BlendState.Opaque;
            }
        }

    }
}



BasicCamera.cs
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;


namespace TreeModels
{
    class BasicCamera
    {
        Matrix ViewMatrix;
        Matrix ProjectionMatrix;
        Vector3 CameraFacing;           //3D direction the camera is facing
        Vector3 CameraPosition;         //3D position of camera in world
        float FieldOfView;              //Camera Field of View (view width) in Radians
        //float AspectRatio;              // 4:3 or 16:9 are typical aspect ratios for a screen
        float NearPlaneDistance;        //Clip view image starting here
        float FarPlaneDistance;         //Clip image further than this distance
        float CameraHeight;
        float PitchPercentage;          //Percentage of the max angle that the camera is currently pitched.
        float MaxPitchRadians;          //Maximum angle that the camera can pitch up or down.
        float CameraImpulse;            //Distance camera will move forward when told to

        public BasicCamera(Vector3 CameraStartPosition, float AspectRatio)
        //===================================================================================
        // BasicCamera()
        //
        // Purpose: Constructor for the class initializes the class on creation.
        // Parameters:  CameraStartPosition = The x,y,z coordinates in world space to place the camera at.
        //              AspectRation = The ratio between screen width & height.
        // Returns: void
        //===================================================================================
        {
            FieldOfView = MathHelper.ToRadians(45);     //Width the camera can see. Camparable to Focal Length of the Lens.
            NearPlaneDistance = 0.1f;                   //Don't draw closer than this distance.
            FarPlaneDistance = 10000.0f;                 //Don't draw further than this distance.
            CameraPosition = CameraStartPosition;
            CameraImpulse = 3.8f/60;        //Camera is moved every frame at 60 frames a second.
            CameraHeight = 1.8f;            //Eye height of the average man.
            MaxPitchRadians = MathHelper.ToRadians(45.0f);  //Angle from level to max angle that camera can look upwards.
            PitchPercentage = 0.0f;         //Start out looking straight and level.
            ProjectionMatrix = Matrix.CreatePerspectiveFieldOfView(FieldOfView, 
                AspectRatio, NearPlaneDistance, FarPlaneDistance);
            CameraStartPosition += new Vector3(0, CameraHeight, 0);     //Camera position at initilization
            CameraFacing = new Vector3(0.0f, 0.0f, -1.0f); //Start by looking North (towards -Z).
            ViewMatrix = Matrix.CreateLookAt(CameraPosition, CameraPosition + CameraFacing, Vector3.Up);
        }
        //===================================================================================


        public void Forward2D()
        //===================================================================================
        // Forward2D()
        //
        // Purpose: Move the camera towards the direction it is facing but keep it at terrain level.
        // Parameters: 
        // Returns: void
        //===================================================================================
        {
            //Matrix Translation;
            Vector2 CameraDirection;    //Direction camera is facing ignoring any upward or downward facing.
            Vector3 ImpulseVector;      //Speed & Direction to move the camera every frame (60th of a second).

            CameraDirection = new Vector2(CameraFacing.X, CameraFacing.Z);  //Remove any up/down info.
            CameraDirection.Normalize();            //Vector length changed so we want to reset the length to 1.
            CameraDirection *= CameraImpulse;       //Scale the facing vector to get an impulse vector (multiplying by Impulse with increase the length of the vector that amount.)
            ImpulseVector = new Vector3(CameraDirection.X, 0.0f, CameraDirection.Y);    //Now that we have the proper force and direction to apply to the camera turn it back into 3D for permenant storage.
            CameraPosition += ImpulseVector;        //Apply movement force.
        }
        //===================================================================================
        
        
        public Vector3 TestForward2D()
        //===================================================================================
        // TestForward2D()
        //
        // Purpose: Get a vector that is the path of the camera in the next movement to test for collisions.
        // Parameters: 
        // Returns: Vector3 with a vector of where the camera is going if told to go forward.
        //===================================================================================
        {
            //Matrix Translation;
            Vector2 CameraDirection;    //Direction camera is facing ignoring any upward or downward facing.
            Vector3 ImpulseVector;      //Speed & Direction to move the camera every frame (60th of a second).

            CameraDirection = new Vector2(CameraFacing.X, CameraFacing.Z);  //Remove any up/down info.
            CameraDirection.Normalize();            //Vector length changed so we want to reset the length to 1.
            CameraDirection *= CameraImpulse;       //Scale the facing vector to get an impulse vector (multiplying by Impulse with increase the length of the vector that amount.)
            ImpulseVector = new Vector3(CameraDirection.X, 0.0f, CameraDirection.Y);    //Now that we have the proper force and direction to apply to the camera turn it back into 3D for permenant storage.

            return CameraPosition + ImpulseVector;
        }
        //===================================================================================


        public void Back2D()
        //===================================================================================
        // Back2D()
        //
        // Purpose: Move the camera away from the direction it is facing but keep it at terrain level.
        // Parameters: 
        // Returns: void
        //===================================================================================
        {
            //Matrix Translation;
            Vector2 CameraDirection;
            Vector3 ImpulseVector;

            CameraDirection = new Vector2(CameraFacing.X, CameraFacing.Z);
            CameraDirection.Normalize();
            CameraDirection *= -CameraImpulse;            //Scale the facing vector to get an impulse vector
            ImpulseVector = new Vector3(CameraDirection.X, 0.0f, CameraDirection.Y);
            CameraPosition += ImpulseVector;
        }
        //===================================================================================


        public void Rotate2D(float RotationAngleRadians)
        //===================================================================================
        // Rotate2D()
        //
        // Purpose: Change the direction the camera is facing by a specific degree, but keep it at terrain level.
        // Parameters: 
        // Returns: void
        //===================================================================================
        {
            Matrix RotationMatrix;
            Vector3 TransformedFacing;

            RotationMatrix = Matrix.CreateRotationY(RotationAngleRadians);  //Rotate horizontally.
            TransformedFacing = Vector3.Transform(CameraFacing, RotationMatrix);    //Rotate.
            CameraFacing = new Vector3(TransformedFacing.X, 0.0f, TransformedFacing.Z); //Ignore any pitch change.
            CameraFacing.Normalize();          //CameraFacing is a Unit vector that should always have a length of 1.
        }
        //===================================================================================


        public void Ground(float TerrainHeight)
        //===================================================================================
        // Ground()
        //
        // Purpose: Reset the elevation of the camera to be standing on the terrain whether it's above or below the terrain.
        // Parameters: TerrainHeight = Elevation of the terrain where the camera is currently positioned. 
        // Notes: TerrainHeight has to be determined using the current camera position. So, this class has
        //          to somehow tell the program what it's position is before the TerrainHeight can be determined, asuming the terrain is not 100% level.
        // Returns: void
        //===================================================================================
        {
            CameraPosition = new Vector3(CameraPosition.X, TerrainHeight + CameraHeight, CameraPosition.Z);
        }
        //===================================================================================


        public void SetCameraVelocity(float CameraVelocity)
        //===================================================================================
        // SetCameraVelocity()
        //
        // Purpose: Allows changing of how fast the camera moves forward/backward.
        // Parameters: 
        // Returns: void
        //===================================================================================
        {
            CameraImpulse = CameraVelocity;
        }
        //===================================================================================


        public void PitchView(float IncreaseInPitchPercentage)
        //===================================================================================
        // PitchView()
        //
        // Purpose: Causes the camera to look more upwards or downwards
        // Parameters: IncreaseInPitchPercentage
        // Returns: void
        //===================================================================================
        {
            PitchPercentage += IncreaseInPitchPercentage;
            if (PitchPercentage > 1.0f) PitchPercentage = 1.0f;
            if (PitchPercentage < -1.0f) PitchPercentage = -1.0f;
        }
        //===================================================================================


        public Matrix View()
        //===================================================================================
        // View()
        //
        // Purpose: Converts CameraFacing and CameraPostion to a ViewMatrix that can be sent to the shader.
        // Parameters: none
        // Notes: This was created to make it easy to pass a ViewMatrix to the shader. I intentionally
        //          store the camera position and facing seperately and consider them the "true" state.
        //          This allows the data to be converted to a ViewMatrix because that's what the shader
        //          is going to want and it will need to be done often.
        // Returns: ViewMatrix 
        //===================================================================================
        {
            Vector3 CameraLookTowards;

            CameraLookTowards = new Vector3(CameraFacing.X, PitchPercentage, CameraFacing.Z);
            CameraLookTowards += CameraPosition;    //CameraLookTowards is on the ground and must be raised to camera height or the camera will just look at it's "toes".
            ViewMatrix = Matrix.CreateLookAt(CameraPosition, CameraLookTowards, Vector3.Up);    

            return ViewMatrix;
        }
        //===================================================================================


        public Matrix Projection()
        //===================================================================================
        // Projection()
        //
        // Purpose: Makes the primary storage for the projection matrix here in the class.
        // Parameters: none
        // Notes: This should just be passed straight to the shader as the ProjectionMatrix.
        // Returns: ProjectionMatrix
        //===================================================================================
        {
            return ProjectionMatrix;
        }
        //===================================================================================

        public float XPosition()
        {
            return CameraPosition.X;
        }

        public float YPosition()
        {
            return CameraPosition.Y;
        }

        public float ZPosition()
        {
            return CameraPosition.Z;
        }

        public Vector3 Facing()
        {
            return CameraFacing;
        }
    }
}


This post has been edited by BBeck: 06 February 2013 - 07:29 AM

Was This Post Helpful? 0
  • +
  • -

Page 1 of 1