//--------------------------------------------------------------------------------------------------------------------------------------------------- // // Copyright (C)2007 DarkWynter Studios. All rights reserved. // //--------------------------------------------------------------------------------------------------------------------------------------------------- // {Contact : darkwynter.com for licensing information //--------------------------------------------------------------------------------------------------------------------------------------------------- namespace DarkWynter.Stream.PhysicsGpu { #region Using Statements using System; using System.Collections.Generic; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Audio; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Storage; using System.Xml; using System.ComponentModel; #endregion /// /// GpuVariable is basic data structure used in gpu processing. /// Generally, it will contain a texture in which each pixel represents a single property from an object. /// This system aggregates variables of like type into a single texture... eg- one texture holds all velocity variables across multiple objects. /// This texture is then processed using a simple shader that acts uniformly across all pixels, making for a RISC approach to gpu processing. /// public class GpuVariable : GpuVariableInterface { /// /// Constant width and height of render target in pixels /// public int _RENDER_TARGET_SIZE { get { return RENDER_TARGET_SIZE; } set { RENDER_TARGET_SIZE = value; } } public static int RENDER_TARGET_SIZE = 128; #region Interfacing /// /// Texture used by Gpu to read values in. /// Swapped with write texture each pass. /// public Texture2D _read { get { return read; } set { read = value; } } private Texture2D read; /// /// Texture used by Gpu to write values out. /// Swapped with read texture each pass. /// public Texture2D _write { get { return write; } set { write = value; } } private Texture2D write; /// /// Gpu handle that sets either the read texture each pass after it is swapped with the write texture. /// public EffectParameter _gpuPointer { get { return gpuPointer; } set { gpuPointer = value; } } private EffectParameter gpuPointer; /// /// A render target for the write texture. /// public RenderTarget2D _writeRenderTarget { get { return writeRenderTarget; } set { writeRenderTarget = value; } } private RenderTarget2D writeRenderTarget; /// /// A render target for the read texture. /// public RenderTarget2D _readRenderTarget { get { return readRenderTarget; } set { readRenderTarget = value; } } private RenderTarget2D readRenderTarget; /// /// Needed in case the game window resolution is less than the spatial map resolution. /// If this is the case, the original depth buffer used with the game window also needs to be swapped when we run /// our update pass /// public DepthStencilBuffer _depthBuff { get { return depthBuff; } set { depthBuff = value; } } private DepthStencilBuffer depthBuff; public Texture2D _swapTexture { get { return swapTexture; } set { swapTexture = value; } } private Texture2D swapTexture; public RenderTarget2D _swapTarget { get { return swapTarget; } set { swapTarget = value; } } private RenderTarget2D swapTarget; // Vector4 array holds properties that we pass to gpu texture public Vector4[] _texValues { get { return texValues; } set { texValues = value; } } Vector4[] texValues; public int _texIndex { get { return texIndex; } set { texIndex = value; } } int texIndex; public int _maxCount { get { return maxCount; } set { maxCount = value; } } int maxCount; /// /// Shader technique (aka- program) to use with this variable. /// public string _technique { get { return technique; } set { technique = value; } } public string technique; public string _outTexName { get { return outTexName; } set { outTexName = value; } } private string outTexName; /// /// Shader variable associated with this GpuVariable. /// public string _gpuParamName { get { return gpuParamName; } set { gpuParamName = value; } } public string gpuParamName; /// /// Minimum numerical value of data being added to this texture. /// public float _minimumValue { get { return minimumValue; } set { minimumValue = value; } } public float minimumValue; /// /// Range of numerical data being added to this texture. /// public float _scaleRange { get { return scaleRange; } set { scaleRange = value; } } public float scaleRange; /// /// Data being added to this texture. /// public List _properties { get { return properties; } set { properties = value; } } public List properties = new List(); /// /// Tells GpuProcessor whether it should update this GpuVariable. /// public bool _isDynamic { get { return isDynamic; } set { isDynamic = value; } } public bool isDynamic; #endregion /// /// GpuVariable constructor. /// public GpuVariable() { // Empty Constructor } /// /// Creates a structure for executing a single pass on the shader hardware. /// Each GpuVariable contains a multiple instances of the same data. /// Eg - velocity /// /// Technique to use. /// The Effect Parameter for this variable. /// Process using Spatial or Indexed processing technique. /// Update this Variable each frame. /// Minimum value of data added using AddProperty methods. /// Maximum value of data added using AddProperty methods. public GpuVariable(GraphicsDevice gd, string shadeTechnique, EffectParameter gpuParameterPointer, bool Update, float minValue, float maxValue) { // Store info technique = shadeTechnique; outTexName = shadeTechnique; isDynamic = Update; // Create Render Targets writeRenderTarget = new RenderTarget2D(gd, RENDER_TARGET_SIZE, RENDER_TARGET_SIZE, 1, SurfaceFormat.Vector4); readRenderTarget = new RenderTarget2D(gd, RENDER_TARGET_SIZE, RENDER_TARGET_SIZE, 1, SurfaceFormat.Vector4); depthBuff = new DepthStencilBuffer(gd, RENDER_TARGET_SIZE, RENDER_TARGET_SIZE, DepthFormat.Depth24); texValues = new Vector4[RENDER_TARGET_SIZE * RENDER_TARGET_SIZE]; maxCount = RENDER_TARGET_SIZE * RENDER_TARGET_SIZE; texIndex = 0; DepthStencilBuffer temp = gd.DepthStencilBuffer; // Clear out textures gd.SetRenderTarget(0, readRenderTarget); gd.DepthStencilBuffer = depthBuff; gd.Clear(ClearOptions.Target, Color.DarkGreen, 1.0f, 0); gd.SetRenderTarget(0, null); read = readRenderTarget.GetTexture(); gd.SetRenderTarget(0, writeRenderTarget); gd.Clear(ClearOptions.Target, Color.Cyan, 1.0f, 0); gd.SetRenderTarget(0, null); write = writeRenderTarget.GetTexture(); gd.DepthStencilBuffer = temp; // Turn off RTT // Set pointer to shader variable gpuPointer = gpuParameterPointer; // Save the min value for decompression minimumValue = minValue; // Scaling - Get the data range if (minimumValue < 0.0f) { scaleRange = maxValue; } else { scaleRange = maxValue - minimumValue; } } #region Add Property /// /// Add a single property to the GpuVariable, before setting texture data. /// /// A value to add to the texture. public virtual void AddProperty(float value) { properties.Add(new Vector4(value, scaleRange, scaleRange, scaleRange)); } /// /// Add a single property to the GpuVariable, before setting texture data. /// /// A value to add to the texture. public virtual void AddProperty(Vector2 value) { properties.Add(new Vector4(value.X, value.Y, scaleRange, scaleRange)); } /// /// Add a single property to the GpuVariable, before setting texture data. /// /// A value to add to the texture. public virtual void AddProperty(Vector3 value) { properties.Add(new Vector4(value.X, value.Y, value.Z, scaleRange)); } /// /// Add a single property to the GpuVariable, before setting texture data. /// /// A value to add to the texture. public virtual void AddProperty(Vector4 value) { properties.Add(new Vector4(value.X, value.Y, value.Z, value.W)); } /// /// Add a single property to the GpuVariable, before setting texture data. /// /// A value to add to the texture. public virtual void AddProperty(Quaternion value) { properties.Add(new Vector4(value.X, value.Y, value.Z, value.W)); } #endregion #region Clear Property /// /// Removes a value from the property list at specified index. /// /// Index at which to remove property. public virtual void ClearProperty(int value) { properties[value] = new Vector4(-scaleRange); } #endregion /// /// Set the texture with all added properties. /// Scales values based on minValue and maxValue, to between 0-1. /// public virtual void SetTexData() { // Copy values from properties to texture for (int i = 0; i < properties.Count; i++) { texValues[i].X = (properties[i].X ) / scaleRange; texValues[i].Y = (properties[i].Y ) / scaleRange; texValues[i].Z = (properties[i].Z ) / scaleRange; texValues[i].W = (properties[i].W ) / scaleRange; } texIndex = properties.Count; // Initialize both textures with values read.SetData(texValues); write.SetData(texValues); gpuPointer.SetValue(read); } /// /// Adds a property to texture while it is in use. /// Used by attack methods. /// /// Location in properties list to begin adding data to texture. /// Number of properties to add from list. public virtual void SetSubTexData(int startIndex, int count) { for (int i = startIndex; i < startIndex + count; i++) { if (texIndex >= maxCount) { // Restart from top of texture texIndex = 0; } texValues[texIndex].X = (properties[i].X ) / scaleRange; texValues[texIndex].Y = (properties[i].Y ) / scaleRange; texValues[texIndex].Z = (properties[i].Z ) / scaleRange; texValues[texIndex].W = (properties[i].W ) / scaleRange; Rectangle rect = new Rectangle(texIndex % 256, (int)texIndex / 256, 1, 1); try { read.SetData(0, rect, texValues, texIndex, 1, SetDataOptions.Discard); write.SetData(0, rect, texValues, texIndex, 1, SetDataOptions.Discard); } catch(Exception ex) { Console.WriteLine(ex.Message); // BUG FIX: // XNA seems to have trouble with synch'ing writes ops to textures on the gpu. // This catches the fault and allows the update it to proceed quietly... -j } texIndex++; } gpuPointer.SetValue(read); } /// /// Unscale the data coming out of the texture to it's original scale. /// public virtual void UnScale() { // Vector4 array holds properties that we pass to gpu texture Vector4[] texValues = new Vector4[GpuVariable.RENDER_TARGET_SIZE * GpuVariable.RENDER_TARGET_SIZE]; write.GetData(texValues); // Copy values from properties to texture for (int i = 0; i < properties.Count; i++) { properties[i] = (texValues[i] * scaleRange); } } /// /// Swap the read and write textures. /// This is required b/c the gpu does not guarentee that we can read and write to the same texture in the same pass. /// public void SwapBuffers() { // Swap the textures swapTexture = read; read = write; write = swapTexture; // Swap the render targets swapTarget = readRenderTarget; readRenderTarget = writeRenderTarget; writeRenderTarget = swapTarget; // Reset GPU Variable gpuPointer.SetValue(read); } /// /// Debug function that writes the read and write textures to files named using the outTexName value. /// public void WriteToFile() { write.Save(outTexName + "_Write.dds", ImageFileFormat.Dds); read.Save(outTexName + "_Read.dds", ImageFileFormat.Dds); } } }