| Topic: The last refactor, for now
|
|
 | The last refactor, for now | |  |
| Poster | : Armigus | | Posts | : 34 | | Country | : USA | | City | : WEston |
| | | | Posted by Armigus on 10/02/2009 at 20:00:45
| | I am going to present my "final" version in stages, starting with Game1.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;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
namespace ShowTerrain
{
public struct VertexPositionNormalColored
{
public Vector3 Position;
public Color Color;
public Vector3 Normal;
public static int SizeInBytes = 7 * 4;
public static VertexElement[] VertexElements = new VertexElement[]
{
new VertexElement( 0, 0, VertexElementFormat.Vector3, VertexElementMethod.Default, VertexElementUsage.Position, 0 ),
new VertexElement( 0, sizeof(float) * 3, VertexElementFormat.Color, VertexElementMethod.Default, VertexElementUsage.Color, 0 ),
new VertexElement( 0, sizeof(float) * 4, VertexElementFormat.Vector3, VertexElementMethod.Default, VertexElementUsage.Normal, 0 ),
};
}
public struct VertexMultitextured
{
public Vector3 Position;
public Vector3 Normal;
public Vector4 TextureCoordinate;
public Vector4 TexWeights;
public static int SizeInBytes = (3 + 3 + 4 + 4) * sizeof(float);
public static VertexElement[] VertexElements = new VertexElement[]
{
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 ),
};
}
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
Terrain land;
Camera camera;
Skydome dome;
bBoard trees;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
protected override void Initialize()
{
graphics.PreferredBackBufferWidth = 1280;
graphics.PreferredBackBufferHeight = 1024;
graphics.ApplyChanges();
Window.Title = "Riemer's XNA Tutorials -- Series 4";
land = new Terrain("heightmap");
camera = new Camera();
dome = new Skydome();
trees = new bBoard();
base.Initialize();
}
protected override void LoadContent()
{
camera.Setup(Content, GraphicsDevice, MathHelper.PiOver4, 2000.0f);
camera.Aim(new Vector3(130, 70, -50), MathHelper.PiOver2, -MathHelper.Pi / 10.0f);
camera.sun = new Vector3(-0.5f, -1, -0.5f);
dome.LoadContent(Content, camera);
land.LoadContent(Content, camera);
trees.LoadContent(Content, land, camera);
}
protected override void UnloadContent()
{
}
protected override void Update(GameTime gameTime)
{
KeyboardState ks = Keyboard.GetState();
if (ks.IsKeyDown(Keys.Escape)) this.Exit();
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
float timeDifference = (float)gameTime.ElapsedGameTime.TotalMilliseconds / 1000.0f;
camera.InputHandler(timeDifference);
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
float time = (float)gameTime.TotalGameTime.TotalMilliseconds / 100.0f;
land.MapWater(camera);
land.MapMirror(camera, dome);
dome.MapClouds(camera, time);
camera.device.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1.0f, 0);
dome.Draw(camera,false);
land.DrawBase(camera,false);
land.DrawWater(camera, time);
trees.Draw(camera);
base.Draw(gameTime);
}
}
}
I kept the code in here to a minimum, but I did put the public structs here. Note that the class methods replace about 90-95% of the original code. There was extra work involved, especially in providing access to otherwise private properties, but more on that within the other classes. | |
|
| | | | | | Poster | : Armigus | | Posts | : 34 | | Country | : USA | | City | : WEston |
| | | | Posted by Armigus on 10/02/2009 at 20:09:16
| | The camera class is the most critical external class as the others depend on it heavily. It contains the graphics device and the primary camera view effect and matrices.
Many of the members are private with read-only properties. I also have generator functions that return objects needed by other classes.
using System;
using System.Collections.Generic;
using System.Linq;
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 ShowTerrain
{
class Camera
{
private Matrix view, projection, mirror;
private Effect fx;
public GraphicsDevice device;
private PresentationParameters pp;
public Vector3 sun;
public Vector3 location;
public float longitude, latitude, sealevel;
static float angVelocity = 0.3f;
static float moveVelocity = 30.0f;
static string source = "Series4Effects";
public bool Reflecting;
MouseState mouse;
public Camera()
{
}
public void Setup(ContentManager cm, GraphicsDevice g, float angle, float distance)
{
device = g;
pp = device.PresentationParameters;
fx = cm.Load<Effect>(source);
projection = Matrix.CreatePerspectiveFieldOfView(angle, device.Viewport.AspectRatio, 0.3f, distance);
Mouse.SetPosition(device.Viewport.Width / 2, device.Viewport.Height / 2);
mouse = Mouse.GetState();
}
public void Aim(Vector3 pos, float lng, float lat)
{
location = pos;
longitude = lng;
latitude = lat;
Update();
}
public void Begin()
{
fx.Begin();
}
public void End()
{
fx.End();
}
public Effect CloneFX()
{
return fx.Clone(device);
}
public EffectTechnique Technique
{
get
{
return fx.CurrentTechnique;
}
}
public void SetTechnique(string name)
{
fx.CurrentTechnique = fx.Techniques[name];
}
public void SetParam(string name, Texture2D param)
{
fx.Parameters[name].SetValue(param);
}
public void SetParam(string name, Matrix mat)
{
fx.Parameters[name].SetValue(mat);
}
public void SetParam(string name, float value)
{
fx.Parameters[name].SetValue(value);
}
public void SetParam(string name, Vector3 value)
{
fx.Parameters[name].SetValue(value);
}
public Matrix View
{
get
{
return view;
}
}
public Matrix Projection
{
get
{
return projection;
}
}
public Matrix Mirror
{
get
{
return mirror;
}
}
public RenderTarget2D MakeTarget()
{
return new RenderTarget2D(device, pp.BackBufferWidth, pp.BackBufferHeight, 1, device.DisplayMode.Format);
}
public void SetTarget(RenderTarget2D trg)
{
device.SetRenderTarget(0, trg);
device.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1.0f, 0);
}
public void RevertTarget()
{
device.SetRenderTarget(0, null);
}
public Matrix InverseView()
{
Matrix worldViewProjection;
if (Reflecting)
worldViewProjection = mirror * projection;
else
worldViewProjection = view * projection;
Matrix inverseWorldViewProjection = Matrix.Invert(worldViewProjection);
inverseWorldViewProjection = Matrix.Transpose(inverseWorldViewProjection);
return inverseWorldViewProjection;
}
public void Snap()
{
if (Reflecting)
fx.Parameters["xView"].SetValue(mirror);
else
fx.Parameters["xView"].SetValue(view);
fx.Parameters["xProjection"].SetValue(projection);
fx.Parameters["xEnableLighting"].SetValue(true);
fx.Parameters["xAmbient"].SetValue(0.4f);
fx.Parameters["xLightDirection"].SetValue(sun);
}
public void InputHandler(float time)
{
bool Dirty = false; // Do we need to update?
MouseState newMouse = Mouse.GetState();
if (newMouse != mouse)
{
Dirty = true;
float dx = newMouse.X - mouse.X;
float dy = newMouse.Y - mouse.Y;
longitude -= angVelocity * dx * time;
latitude -= angVelocity * dy * time;
Mouse.SetPosition(device.Viewport.Width / 2, device.Viewport.Height / 2);
}
Vector3 moveVector = new Vector3(0, 0, 0);
KeyboardState keyState = Keyboard.GetState();
if (keyState.IsKeyDown(Keys.Up) || keyState.IsKeyDown(Keys.W))
moveVector += new Vector3(0, 0, -1);
if (keyState.IsKeyDown(Keys.Down) || keyState.IsKeyDown(Keys.S))
moveVector += new Vector3(0, 0, 1);
if (keyState.IsKeyDown(Keys.Right) || keyState.IsKeyDown(Keys.D))
moveVector += new Vector3(1, 0, 0);
if (keyState.IsKeyDown(Keys.Left) || keyState.IsKeyDown(Keys.A))
moveVector += new Vector3(-1, 0, 0);
if (keyState.IsKeyDown(Keys.Q))
moveVector += new Vector3(0, 1, 0);
if (keyState.IsKeyDown(Keys.Z))
moveVector += new Vector3(0, -1, 0);
if (moveVector.Length() > 0.0f)
{
Dirty = true;
moveVector *= time;
Matrix aimold = Matrix.CreateRotationX(latitude) * Matrix.CreateRotationY(longitude);
Vector3 direction = Vector3.Transform(moveVector, aimold);
location += moveVelocity * direction;
}
if (Dirty) Update();
}
private void Update()
{
Matrix cameraRotation = Matrix.CreateRotationX(latitude) * Matrix.CreateRotationY(longitude);
Vector3 target = Vector3.Transform(Vector3.Forward, cameraRotation)+location;
Vector3 up = Vector3.Transform(Vector3.Up, cameraRotation);
view = Matrix.CreateLookAt(location, target, up);
Vector3 subpos = new Vector3(location.X,-location.Y+sealevel*2,location.Z);
Vector3 subtrg = new Vector3(target.X,-target.Y+sealevel*2,target.Z);
Vector3 right = Vector3.Transform(Vector3.Right, cameraRotation);
Vector3 subup = Vector3.Cross(right, subtrg - subpos);
mirror = Matrix.CreateLookAt(subpos, subtrg, subup);
}
}
}
| |
|
| | | | | | Poster | : Armigus | | Posts | : 34 | | Country | : USA | | City | : WEston |
| | | | Posted by Armigus on 10/02/2009 at 20:18:39
| | The next, and perhaps largest in terms of code, is the terrain class. Here is where the landscape, including water, is made and drawn.
The terrain's overloaded constructor allows you to use alternate images for the heightmap. Note that you can pass the game's content manager into your classes so that you can effectively encapsulate your content loads.
Often the class members remove the need for some parameters in methods. The object itself is passed instead and you use its properties and methods. This is done extensively with the camera class.
using System;
using System.Collections.Generic;
using System.Linq;
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 ShowTerrain
{
class Terrain : IDisposable
{
private string Name;
private int width, length;
private float[,] Altitudes;
private Texture2D Map, waterMap, mirrorMap, rippleMap;
private Texture2D[] Textures = new Texture2D[4];
private VertexBuffer vBuffer, seaBuffer;
private IndexBuffer iBuffer;
private VertexDeclaration vDeclaration, seaDeclaration;
private RenderTarget2D Refractor, Reflector;
public VertexMultitextured[] Vertices;
public Matrix location;
public Vector3 wind;
public static float maxAlt = 30.0f;
public static float waterHeight = 5.0f;
public Terrain()
{
Name = "heightmap";
}
public Terrain(string name)
{
Name = name;
}
public void Dispose() {}
public int Width
{
get
{
return width;
}
}
public int Length
{
get
{
return length;
}
}
public float Height(int x, int y)
{
// Validate boundaries
if (x < 0) return -1f;
if (y < 0) return -1f;
if (x >= width) return -1f;
if (y >= length) return -1f;
return Altitudes[x, y];
}
public void LoadContent(ContentManager cm, Camera cam)
{
location = Matrix.Identity;
wind = Vector3.Right;
Map = cm.Load<Texture2D>(Name);
width = Map.Width;
length = Map.Height;
rippleMap = cm.Load<Texture2D>("waterbump");
Textures[0] = cm.Load<Texture2D>("sand");
Textures[1] = cm.Load<Texture2D>("grass");
Textures[2] = cm.Load<Texture2D>("rock");
Textures[3] = cm.Load<Texture2D>("snow");
Refractor = cam.MakeTarget();
Reflector = cam.MakeTarget();
LoadHeights();
Vertices = SetVertices();
int[] indices = SetIndices();
Vertices = CalculateNormals(Vertices, indices);
SetBuffers(cam.device, Vertices, indices);
vDeclaration = new VertexDeclaration(cam.device, VertexMultitextured.VertexElements);
SetWater(cam.device);
cam.sealevel = waterHeight;
}
private Plane CreatePlane(float height, Vector3 normal, Camera cam, bool clipSide)
{
normal.Normalize();
Vector4 planeCoeffs = new Vector4(normal, height);
if (clipSide)
planeCoeffs *= -1;
planeCoeffs = Vector4.Transform(planeCoeffs, cam.InverseView());
Plane finalPlane = new Plane(planeCoeffs);
return finalPlane;
}
private void SetWater(GraphicsDevice device)
{
VertexPositionTexture[] waterVertices = new VertexPositionTexture[6];
waterVertices[0] = new VertexPositionTexture(new Vector3(0, waterHeight, 0), new Vector2(0, 1));
waterVertices[2] = new VertexPositionTexture(new Vector3(width, waterHeight, -length), new Vector2(1, 0));
waterVertices[1] = new VertexPositionTexture(new Vector3(0, waterHeight, -length), new Vector2(0, 0));
waterVertices[3] = new VertexPositionTexture(new Vector3(0, waterHeight, 0), new Vector2(0, 1));
waterVertices[5] = new VertexPositionTexture(new Vector3(width, waterHeight, 0), new Vector2(1, 1));
waterVertices[4] = new VertexPositionTexture(new Vector3(width, waterHeight, -length), new Vector2(1, 0));
seaBuffer = new VertexBuffer(device, waterVertices.Length * VertexPositionTexture.SizeInBytes, BufferUsage.WriteOnly);
seaBuffer.SetData(waterVertices);
seaDeclaration = new VertexDeclaration(device, VertexPositionTexture.VertexElements);
}
private void LoadHeights()
{
float minimumHeight = float.MaxValue;
float maximumHeight = float.MinValue;
Color[] heightMapColors = new Color[width * length];
Map.GetData(heightMapColors);
Altitudes = new float[width, length];
for (int x = 0; x < width; x++)
for (int y = 0; y < length; y++)
{
Altitudes[x, y] = heightMapColors[x + y * width].PackedValue;
if (Altitudes[x, y] < minimumHeight) minimumHeight = Altitudes[x, y];
if (Altitudes[x, y] > maximumHeight) maximumHeight = Altitudes[x, y];
}
for (int x = 0; x < width; x++)
for (int y = 0; y < length; y++)
Altitudes[x, y] = (Altitudes[x, y] - minimumHeight) / (maximumHeight - minimumHeight) * maxAlt;
}
private VertexMultitextured[] SetVertices()
{
VertexMultitextured[] Vertices = new VertexMultitextured[width * length];
for (int x = 0; x < width; x++)
{
for (int y = 0; y < length; y++)
{
float x0, y0, z0, w0, p0;
Vertices[x + y * width].Position = new Vector3(x, Altitudes[x, y], -y);
Vertices[x + y * width].TextureCoordinate.X = (float)x / maxAlt;
Vertices[x + y * width].TextureCoordinate.Y = (float)y / maxAlt;
p0 = Altitudes[x, y] / maxAlt;
x0 = MathHelper.Clamp(1.0f - Math.Abs(p0 - 0.0f) * 6, 0, 1);
y0 = MathHelper.Clamp(1.0f - Math.Abs(p0 - 0.33f) * 5, 0, 1);
z0 = MathHelper.Clamp(1.0f - Math.Abs(p0 - 0.67f) * 5, 0, 1);
w0 = MathHelper.Clamp(1.0f - Math.Abs(p0 - 1.0f) * 5, 0, 1);
float isum = 1/(x0 + y0 + z0 + w0);
Vertices[x + y * width].TexWeights.X = x0 * isum;
Vertices[x + y * width].TexWeights.Y = y0 * isum;
Vertices[x + y * width].TexWeights.Z = z0 * isum;
Vertices[x + y * width].TexWeights.W = w0 * isum;
}
}
return Vertices;
}
private int[] SetIndices()
{
int[] indices = new int[(width - 1) * (length - 1) * 6];
int counter = 0;
for (int y = 0; y < length - 1; y++)
{
for (int x = 0; x < width - 1; x++)
{
int lowerLeft = x + y * width;
int lowerRight = (x + 1) + y * width;
int topLeft = x + (y + 1) * width;
int topRight = (x + 1) + (y + 1) * width;
indices[counter++] = topLeft;
indices[counter++] = lowerRight;
indices[counter++] = lowerLeft;
indices[counter++] = topLeft;
indices[counter++] = topRight;
indices[counter++] = lowerRight;
}
}
return indices;
}
private VertexMultitextured[] CalculateNormals(VertexMultitextured[] 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 void SetBuffers(GraphicsDevice device, VertexMultitextured[] vertices, int[] indices)
{
vBuffer = new VertexBuffer(device, vertices.Length * VertexMultitextured.SizeInBytes, BufferUsage.WriteOnly);
vBuffer.SetData(vertices);
iBuffer = new IndexBuffer(device, typeof(int), indices.Length, BufferUsage.WriteOnly);
iBuffer.SetData(indices);
}
public void DrawBase(Camera camera, bool mirror)
{
camera.Reflecting = mirror;
camera.Snap();
camera.SetTechnique("MultiTextured");
camera.SetParam("xTexture0", Textures[0]);
camera.SetParam("xTexture1", Textures[1]);
camera.SetParam("xTexture2", Textures[2]);
camera.SetParam("xTexture3", Textures[3]);
camera.SetParam("xWorld",Matrix.Identity);
camera.Begin();
foreach (EffectPass pass in camera.Technique.Passes)
{
pass.Begin();
camera.device.Vertices[0].SetSource(vBuffer, 0, VertexMultitextured.SizeInBytes);
camera.device.Indices = iBuffer;
camera.device.VertexDeclaration = vDeclaration;
int noVertices = vBuffer.SizeInBytes / VertexMultitextured.SizeInBytes;
int noTriangles = iBuffer.SizeInBytes / sizeof(int) / 3;
camera.device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, noVertices, 0, noTriangles);
pass.End();
}
camera.End();
}
public void MapWater(Camera camera)
{
camera.Reflecting = false;
Plane seaPlane = CreatePlane(waterHeight + 1.5f, Vector3.Down, camera, false);
camera.device.ClipPlanes[0].Plane = seaPlane;
camera.device.ClipPlanes[0].IsEnabled = true;
camera.SetTarget(Refractor);
DrawBase(camera,false);
camera.device.ClipPlanes[0].IsEnabled = false;
camera.RevertTarget();
waterMap = Refractor.GetTexture();
//waterMap.Save("watermap.jpg", ImageFileFormat.Jpg);
}
public void MapMirror(Camera camera, Skydome sky)
{
camera.Reflecting = true;
Plane seaPlane = CreatePlane(waterHeight - 0.5f, Vector3.Down, camera, true);
camera.device.ClipPlanes[0].Plane = seaPlane;
camera.device.ClipPlanes[0].IsEnabled = true;
camera.SetTarget(Reflector);
sky.Draw(camera, true);
DrawBase(camera, true);
camera.device.ClipPlanes[0].IsEnabled = false;
camera.RevertTarget();
mirrorMap = Reflector.GetTexture();
//mirrorMap.Save("mirrormap.jpg", ImageFileFormat.Jpg);
}
public void DrawWater(Camera camera, float time)
{
camera.SetTechnique("Water");
camera.SetParam("xWorld", Matrix.Identity);
camera.SetParam("xView", camera.View);
camera.SetParam("xProjection", camera.Projection);
camera.SetParam("xReflectionView", camera.Mirror);
camera.SetParam("xReflectionMap", mirrorMap);
camera.SetParam("xRefractionMap", waterMap);
camera.SetParam("xWaterBumpMap", rippleMap);
camera.SetParam("xWaveLength", 0.1f);
camera.SetParam("xWaveHeight", 0.3f);
camera.SetParam("xCamPos", camera.location);
camera.SetParam("xTime", time);
camera.SetParam("xWindForce", 0.002f);
camera.SetParam("xWindDirection", wind);
camera.Begin();
foreach (EffectPass pass in camera.Technique.Passes)
{
pass.Begin();
camera.device.Vertices[0].SetSource(seaBuffer, 0, VertexPositionTexture.SizeInBytes);
camera.device.Indices = iBuffer;
camera.device.VertexDeclaration = seaDeclaration;
int noVertices = seaBuffer.SizeInBytes / VertexPositionTexture.SizeInBytes;
camera.device.DrawPrimitives(PrimitiveType.TriangleList, 0, noVertices / 3);
pass.End();
}
camera.End();
}
}
}
| |
|
| | | | | | Poster | : Armigus | | Posts | : 34 | | Country | : USA | | City | : WEston |
| | | | Posted by Armigus on 10/02/2009 at 20:29:36
| | The billboard class uses the terrain class to place the trees and the camera class for drawing and buffer creation.
using System;
using System.Collections.Generic;
using System.Linq;
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 ShowTerrain
{
class bBoard
{
public List<Vector3> Locations;
private VertexBuffer vBuffer;
private VertexDeclaration vDeclaration;
private Texture2D treeTexture, treeMap;
private Effect fx;
public void LoadContent(ContentManager cm, Terrain land, Camera cam)
{
fx = cm.Load<Effect>("bbEffect");
treeTexture = cm.Load<Texture2D>("tree");
treeMap = cm.Load<Texture2D>("treeMap");
Locations = new List<Vector3>();
GenerateTreePositions(land);
List2Buffer(cam.device);
}
private void GenerateTreePositions(Terrain land)
{
Color[] treeMapColors = new Color[treeMap.Width * treeMap.Height];
treeMap.GetData(treeMapColors);
int[,] noiseData = new int[treeMap.Width, treeMap.Height];
for (int x = 0; x < treeMap.Width; x++)
for (int y = 0; y < treeMap.Height; y++)
noiseData[x, y] = treeMapColors[y + x * treeMap.Height].R;
Random random = new Random();
float steep = (float)Math.Cos(MathHelper.ToRadians(15));
for (int x = 0; x < land.Width; x++)
{
for (int y = 0; y < land.Length; y++)
{
float alt = land.Height(x, y);
if ((alt < 8) || (alt > 14)) continue;
float level = Vector3.Dot(land.Vertices[x + y * land.Width].Normal, Vector3.Up);
if (level < steep) continue;
float relx = (float)x / (float)land.Width;
float rely = (float)y / (float)land.Length;
float noise = noiseData[(int)(relx * treeMap.Width), (int)(rely * treeMap.Height)];
float treeDensity;
if (noise > 200)
treeDensity = 5;
else if (noise > 150)
treeDensity = 4;
else if (noise > 100)
treeDensity = 3;
else
treeDensity = 0;
for (int currDetail = 0; currDetail < treeDensity; currDetail++)
{
float rand1 = (float)random.Next(1000) / 1000.0f;
float rand2 = (float)random.Next(1000) / 1000.0f;
Vector3 treePos = new Vector3((float)x - rand1, alt, -(float)y - rand2);
Locations.Add(treePos);
}
}
}
}
private void List2Buffer(GraphicsDevice device)
{
VertexPositionTexture[] billboardVertices = new VertexPositionTexture[Locations.Count * 6];
int i = 0;
foreach (Vector3 currentV3 in Locations)
{
billboardVertices[i++] = new VertexPositionTexture(currentV3, new Vector2(0, 0));
billboardVertices[i++] = new VertexPositionTexture(currentV3, new Vector2(1, 0));
billboardVertices[i++] = new VertexPositionTexture(currentV3, new Vector2(1, 1));
billboardVertices[i++] = new VertexPositionTexture(currentV3, new Vector2(0, 0));
billboardVertices[i++] = new VertexPositionTexture(currentV3, new Vector2(1, 1));
billboardVertices[i++] = new VertexPositionTexture(currentV3, new Vector2(0, 1));
}
vBuffer = new VertexBuffer(device, billboardVertices.Length * VertexPositionTexture.SizeInBytes, BufferUsage.WriteOnly);
vBuffer.SetData(billboardVertices);
vDeclaration = new VertexDeclaration(device, VertexPositionTexture.VertexElements);
}
public void Draw(Camera cam)
{
fx.CurrentTechnique = fx.Techniques["CylBillboard"];
fx.Parameters["xWorld"].SetValue(Matrix.Identity);
fx.Parameters["xView"].SetValue(cam.View);
fx.Parameters["xProjection"].SetValue(cam.Projection);
fx.Parameters["xCamPos"].SetValue(cam.location);
fx.Parameters["xAllowedRotDir"].SetValue(new Vector3(0, 1, 0));
fx.Parameters["xBillboardTexture"].SetValue(treeTexture);
fx.Begin();
cam.device.Vertices[0].SetSource(vBuffer, 0, VertexPositionTexture.SizeInBytes);
cam.device.VertexDeclaration = vDeclaration;
int noVertices = vBuffer.SizeInBytes / VertexPositionTexture.SizeInBytes;
int noTriangles = noVertices / 3;
{
cam.device.RenderState.AlphaTestEnable = true;
cam.device.RenderState.AlphaFunction = CompareFunction.GreaterEqual;
cam.device.RenderState.ReferenceAlpha = 200;
fx.CurrentTechnique.Passes[0].Begin();
cam.device.DrawPrimitives(PrimitiveType.TriangleList, 0, noTriangles);
fx.CurrentTechnique.Passes[0].End();
}
{
cam.device.RenderState.DepthBufferWriteEnable = false;
cam.device.RenderState.AlphaBlendEnable = true;
cam.device.RenderState.SourceBlend = Blend.SourceAlpha;
cam.device.RenderState.DestinationBlend = Blend.InverseSourceAlpha;
cam.device.RenderState.AlphaTestEnable = true;
cam.device.RenderState.AlphaFunction = CompareFunction.Less;
cam.device.RenderState.ReferenceAlpha = 200;
fx.CurrentTechnique.Passes[0].Begin();
cam.device.DrawPrimitives(PrimitiveType.TriangleList, 0, noTriangles);
fx.CurrentTechnique.Passes[0].End();
}
cam.device.RenderState.AlphaBlendEnable = false;
cam.device.RenderState.DepthBufferWriteEnable = true;
cam.device.RenderState.AlphaTestEnable = false;
fx.End();
}
}
}
| |
|
| | | | | | Poster | : Armigus | | Posts | : 34 | | Country | : USA | | City | : WEston |
| | | | Posted by Armigus on 10/02/2009 at 20:32:44
| | The noise generator class is really very basic. It stores the noise map and uses some read-only properties. I kept it distinct in case future classes needed it:
using System;
using System.Collections.Generic;
using System.Linq;
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 ShowTerrain
{
class Noise
{
private Texture2D map;
private int mapsize;
private VertexPositionTexture[] vertices;
public Noise() {}
public void Initialize(GraphicsDevice device, int size)
{
mapsize = size;
Random rand = new Random();
Color[] noisyColors = new Color[size * size];
for (int x = 0; x < size; x++)
for (int y = 0; y < size; y++)
noisyColors[x + y * size] = new Color(new Vector3((float)rand.Next(1000) / 1000.0f, 0, 0));
map = new Texture2D(device, size, size, 1, TextureUsage.None, SurfaceFormat.Color);
map.SetData(noisyColors);
vertices = new VertexPositionTexture[4];
vertices[0] = new VertexPositionTexture(new Vector3(-1, 1, 0f), new Vector2(0, 1));
vertices[1] = new VertexPositionTexture(new Vector3(1, 1, 0f), new Vector2(1, 1));
vertices[2] = new VertexPositionTexture(new Vector3(-1, -1, 0f), new Vector2(0, 0));
vertices[3] = new VertexPositionTexture(new Vector3(1, -1, 0f), new Vector2(1, 0));
}
public VertexPositionTexture[] Vertices
{
get { return vertices; }
}
public Texture2D Map
{
get { return map; }
}
}
}
| |
|
| | | | | | Poster | : Armigus | | Posts | : 34 | | Country | : USA | | City | : WEston |
| | | | Posted by Armigus on 10/02/2009 at 20:39:47
| | I will conclude the code with the sky dome, which uses the Noise and Camera classes. The terrain uses it for drawing mirror maps.
It's not a very complex class, though. HLSL does the heavy lifting.
namespace ShowTerrain
{
class Skydome
{
private Texture2D map, clouds;
private Model model;
private RenderTarget2D cloudsRenderTarget;
private VertexDeclaration vDeclaration;
private Noise perlin;
public Skydome() {
perlin = new Noise();
}
public void LoadContent(ContentManager cm, Camera cam)
{
model = cm.Load<Model>("dome");
model.Meshes[0].MeshParts[0].Effect = cam.CloneFX();
map = cm.Load<Texture2D>("cloudMap");
perlin.Initialize(cam.device, 96);
cloudsRenderTarget = cam.MakeTarget();
vDeclaration = new VertexDeclaration(cam.device, VertexPositionTexture.VertexElements);
}
public void MapClouds(Camera cam, float time)
{
cam.SetTarget(cloudsRenderTarget);
cam.SetTechnique("PerlinNoise");
cam.SetParam("xTexture", perlin.Map);
cam.SetParam("xOvercast", 1.1f);
cam.SetParam("xTime", time*0.001f);
cam.Begin();
foreach (EffectPass pass in cam.Technique.Passes)
{
pass.Begin();
cam.device.VertexDeclaration = vDeclaration;
cam.device.DrawUserPrimitives(PrimitiveType.TriangleStrip, perlin.Vertices, 0, 2);
pass.End();
}
cam.End();
cam.RevertTarget();
clouds = cloudsRenderTarget.GetTexture();
}
public void Draw(Camera cam, bool mirror)
{
cam.device.RenderState.DepthBufferWriteEnable = false;
Matrix[] modelTransforms = new Matrix[model.Bones.Count];
model.CopyAbsoluteBoneTransformsTo(modelTransforms);
Matrix wMatrix = Matrix.CreateTranslation(0, -0.4f, 0) * Matrix.CreateScale(500) * Matrix.CreateTranslation(cam.location);
foreach (ModelMesh mesh in model.Meshes)
{
foreach (Effect currentEffect in mesh.Effects)
{
Matrix worldMatrix = modelTransforms[mesh.ParentBone.Index] * wMatrix;
currentEffect.CurrentTechnique = currentEffect.Techniques["SkyDome"];
currentEffect.Parameters["xWorld"].SetValue(worldMatrix);
if (mirror)
currentEffect.Parameters["xView"].SetValue(cam.Mirror);
else
currentEffect.Parameters["xView"].SetValue(cam.View);
currentEffect.Parameters["xProjection"].SetValue(cam.Projection);
currentEffect.Parameters["xTexture"].SetValue(clouds);
currentEffect.Parameters["xAmbient"].SetValue(1.0f);
currentEffect.Parameters["xEnableLighting"].SetValue(false);
}
mesh.Draw();
}
cam.device.RenderState.DepthBufferWriteEnable = true;
}
}
}
| |
|
|
 | | |  |
|
|
|
If you appreciate the amount of time I spend creating and updating these pages, feel free to donate -- any amount is welcome !
|
- Website design & DirectX code : Riemer Grootjans - ©2006 Riemer Grootjans
|
|