//---------------------------------------------------------------------------------------------------------------------------------------------------
//
// Copyright (C)2007 DarkWynter Studios. All rights reserved.
//
//---------------------------------------------------------------------------------------------------------------------------------------------------
// {Contact : darkwynter.com for licensing information
//---------------------------------------------------------------------------------------------------------------------------------------------------
namespace DarkWynterEngine.ObjectLib
{
#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.Diagnostics;
#endregion
using Globals;
using GameObjects;
using Physics;
using PhysicsGpu;
///
/// GpuObjectList contains a list of GpuObjects.
/// It takes an object at a time and holds it till ConvertToVariables is called, at which time it transfers converts the data into textures.
/// After this initialization, GpuObjects can still be added by using ConvertSubsetToVariables.
///
public class GpuObjectList : GameObject
{
// Type information
private string type;
///
/// A list of GpuVariables used in our algorithm.
///
public List variables = new List();
///
/// GpuProcessor is used to process each gpu object each pass
///
public GpuProcessor gpu_Processor;
///
/// List of mass objects being converted to GpuVariables.
///
private List masses = new List();
///
/// A forceMap simulating wind currents.
///
private GpuWindMap windMap = new GpuWindMap();
///
/// Spacial Data Structure used in Gpu Collision algorithm.
///
public SpatialMap locationMap;
// Draw Shader =======================================================================================
private EffectParameter scaleRange; // To save the Scale Range to ElementalGPU
private EffectParameter minimumValue; // To save the Minimum Value to ElementalGPU
private EffectParameter cpScaleRangeY;
private EffectParameter cpScaleMinY;
private EffectParameter currentPositionParam; // To save the CurrentPosition texture to ElementalGPU
// Update Shader =====================================================================================
private EffectParameter gpu_scaleMinY;
private EffectParameter gpu_scaleRangeY;
private EffectParameter playerPositionParam; // Current Player Position
private EffectParameter gpu_mass;
// The instance data itself
private List instanceList;
private List removeObjects;
private List addObjects;
private VertexFogBinormalTangent[] instanceData;
// Tells the GPU how to format the streams
private VertexElement[] Elements = 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.Vector2, VertexElementMethod.Default, VertexElementUsage.TextureCoordinate, 0),
new VertexElement(1, 0, VertexElementFormat.Vector4, VertexElementMethod.Default, VertexElementUsage.Fog, 0),
new VertexElement(1, sizeof(float) * 4, VertexElementFormat.Vector4, VertexElementMethod.Default, VertexElementUsage.Binormal, 0),
new VertexElement(1, sizeof(float) * 8, VertexElementFormat.Vector4, VertexElementMethod.Default, VertexElementUsage.Tangent, 0),
};
///
/// GpuObjectList constructor.
/// Creates a list of GpuVariables used in Physics and Collision.
///
public GpuObjectList()
{
vertexDeclaration = new VertexDeclaration(Statics.SystemSettings.graphics.GraphicsDevice, Elements);
gpu_Processor = new GpuProcessor();
// Get Draw Handles
currentPositionParam = ShaderParameters.effect_draw.Parameters["CurrentPosition"];
scaleRange = ShaderParameters.effect_draw.Parameters["scaleRange"];
minimumValue = ShaderParameters.effect_draw.Parameters["minimumValue"];
cpScaleRangeY = ShaderParameters.effect_update.Parameters["cpScaleRangeY"];
cpScaleMinY = ShaderParameters.effect_update.Parameters["cpScaleMinY"];
gpu_scaleMinY = ShaderParameters.effect_update.Parameters["scaleMinY"];
gpu_scaleRangeY = ShaderParameters.effect_update.Parameters["scaleRangeY"];
playerPositionParam = ShaderParameters.effect_update.Parameters["playerPosition"];
gpu_mass = ShaderParameters.effect_update.Parameters["mass"];
// Create Location Map
locationMap = new SpatialMap(ShaderParameters.effect_update, "SpatialMain", "LocationMap",
Enums.GpuProcessorMode.Spatial, true, 0, 100);
// Create Variables and add to the Object
variables.Add(new Vec3(ShaderParameters.effect_update, "TotalForce", "TotalForce", Enums.GpuProcessorMode.Indexed,
true, -Statics.TerrainSettings.collisionMapSize, Statics.TerrainSettings.collisionMapSize));
variables.Add(new Vec3(ShaderParameters.effect_update, "Acceleration", "Acceleration", Enums.GpuProcessorMode.Indexed,
true, -Statics.TerrainSettings.collisionMapSize, Statics.TerrainSettings.collisionMapSize));
variables.Add(new Vec3(ShaderParameters.effect_update, "Velocity", "Velocity", Enums.GpuProcessorMode.Indexed,
true, -Statics.TerrainSettings.collisionMapSize, Statics.TerrainSettings.collisionMapSize));
variables.Add(new Quat(ShaderParameters.effect_update, "Rotation", "Rotation", Enums.GpuProcessorMode.Indexed,
true, -100, 100));
variables.Add(new Vec3(ShaderParameters.effect_update, "CurrentPosition", "CurrentPosition", Enums.GpuProcessorMode.Indexed,
true, -Statics.TerrainSettings.collisionMapSize, Statics.TerrainSettings.collisionMapSize));
variables.Add(new Vec3(ShaderParameters.effect_update, "Dummy", "Dummy", Enums.GpuProcessorMode.Indexed,
true, -Statics.TerrainSettings.collisionMapSize, Statics.TerrainSettings.collisionMapSize));
variables.Add(new Vec1(ShaderParameters.effect_update, "", "Radius", Enums.GpuProcessorMode.Indexed,
false, 0, 100));
variables.Add(new Vec1(ShaderParameters.effect_update, "", "Scale", Enums.GpuProcessorMode.Indexed,
false, 0, 100));
variables.Add(new Vec1(ShaderParameters.effect_update, "", "Mass", Enums.GpuProcessorMode.Indexed,
false, 0, 100));
removeObjects = new List();
addObjects = new List();
}
///
/// Override load function, parses and loads xml data.
///
/// GpuObject xml data.
/// ObjectLibrary that this list belongs to.
/// True if load was successful
public override bool Load(XmlNode objectNode, ObjectLibrary objectLibrary)
{
// Set up Model Manager
type = objectNode.Attributes["type"].Value;
objectLibrary.modelManager.AddNewModel(type,
objectNode.Attributes["model"].Value,
objectNode.Attributes["texture"].Value,
objectNode.Attributes["bumpTexture"].Value);
// Get the Location Map
Texture2D locationMap = Statics.SystemSettings.content.Load(objectNode.Attributes["locationMap"].Value);
int length = locationMap.Width * locationMap.Width;
Color[] locationColorValues = new Color[length];
locationMap.GetData(locationColorValues);
Random rand = new Random();
// Precalculate the vertexmap/locationmap ratio for use in the for loop below
float vertexmapToObjectmapRelationship = Statics.TerrainSettings.vertexMapSize / locationMap.Width;
// Create a ruler 1 terrain mod long (distance between verticies
float terrainModWidth = Statics.TerrainSettings.terrainScaleFactor * vertexmapToObjectmapRelationship;
// Get the color value of the node
int mapColorR = int.Parse(objectNode.Attributes["red"].Value);
int mapColorG = int.Parse(objectNode.Attributes["green"].Value);
int mapColorB = int.Parse(objectNode.Attributes["blue"].Value);
Vector3 scale = new Vector3((int)(float.Parse(objectNode.Attributes["maxScale"].Value)));
float mass = float.Parse(objectNode.Attributes["mass"].Value);
bool isCollectable = bool.Parse(objectNode.Attributes["isCollectable"].Value);
bool isKey = bool.Parse(objectNode.Attributes["isKey"].Value);
// For each pixel of the treeMap
for (int l = 0; l < length; l++)
{
// If the color matches create a prop
if (locationColorValues[l].R == mapColorR && locationColorValues[l].G == mapColorG && locationColorValues[l].B == mapColorB)
{
// Get the 2D location from 1D array
int h = l % locationMap.Width;
int w = (int)l / locationMap.Width;
// Create Object based on type matching in GameObjectTypes
GameObject gameObject = new GameObject();
if (!objectLibrary.CreateGameObjectType(objectNode, ref gameObject))
{
// GameObject could not be created
return false;
}
// Start Type properties
gameObject.mass.gameObjectPointer = gameObject;
gameObject.objectModelName = type;
// Get terrain height at position
float yPosition = objectLibrary.terrain.GetTerrainHeight(w * vertexmapToObjectmapRelationship,
h * vertexmapToObjectmapRelationship);
// Set the object world properties
Vector3 locpos = new Vector3(w * terrainModWidth, yPosition, h * terrainModWidth);
Vector3 refpos = new Vector3(w * terrainModWidth, yPosition, h * terrainModWidth - 1);
gameObject.mass.SetPosition(locpos, refpos);
gameObject.mass.Rotate(0, (float)(2 * Math.PI * rand.NextDouble()));
gameObject.mass.scale = scale;
gameObject.mass.mass = mass;
gameObject.mass.isMoving = false;
gameObject.isCollectable = isCollectable;
gameObject.isKey = isKey;
gameObject.objectValues = new VertexFogBinormalTangent();
gameObject.objectValues.Fog = new Vector4(gameObject.mass.scale, 0.0f); // Store the scale
gameObject.objectValues.Binormal = new Vector4(gameObject.mass.currentRotation.X,
gameObject.mass.currentRotation.Y,
gameObject.mass.currentRotation.Z,
gameObject.mass.currentRotation.W); // Store the quaternion rotation
// Add mass to attach tangent
masses.Add(gameObject.mass);
variables[0].AddProperty(gameObject.mass.totalForce);
variables[1].AddProperty(gameObject.mass.acceleration);
variables[2].AddProperty(gameObject.mass.velocity);
variables[3].AddProperty(gameObject.mass.currentRotation);
variables[4].AddProperty(gameObject.mass.currentPosition);
variables[5].AddProperty(Vector3.Zero); // Dummy Texture
variables[6].AddProperty(gameObject.mass.boundingVolume.radius);
variables[7].AddProperty(gameObject.mass.scale);
variables[8].AddProperty(gameObject.mass.mass);
}
}
if (masses.Count > 0)
{
// Add a list of CPU Objects to the GPU Object
ConvertToVariables();
// HACK
gpu_mass.SetValue(masses[0].mass / 100.0f);
instanceList = new List();
for (int i = 0; i < masses.Count; i++)
{
instanceList.Add(masses[i].gameObjectPointer.objectValues);
}
instanceData = new VertexFogBinormalTangent[instanceList.Count];
instanceList.CopyTo(instanceData);
vertexBuffer = new VertexBuffer(Statics.SystemSettings.graphics.GraphicsDevice,
typeof(VertexFogBinormalTangent),
instanceData.Length,
BufferUsage.WriteOnly);
vertexBuffer.SetData(instanceData);
}
// Add Wind Map
windMap.Load(objectNode.Attributes["windMap"].Value);
return true;
}
///
/// Adds a mass object to be converted to a GpuObject.
/// Used by attack methods.
///
/// Mass being added.
public void AddMass(Mass mass)
{
//RetrieveValues();
masses.Add(mass);
variables[0].AddProperty(mass.totalForce);
variables[1].AddProperty(mass.acceleration);
variables[2].AddProperty(mass.velocity);
variables[3].AddProperty(mass.currentRotation);
variables[4].AddProperty(mass.currentPosition);
variables[5].AddProperty(Vector3.Zero); // Dummy Texture
variables[6].AddProperty(mass.boundingVolume.radius);
variables[7].AddProperty(mass.scale);
variables[8].AddProperty(mass.mass);
// Set Tangents for
// Store the translation
// Set Tangents for
int i = masses.Count - 1;
mass.gameObjectPointer.objectValues.Tangent = new Vector4((float)i % 256 / 256, (float)((int)i / 256) / 256, 0, 0);
addObjects.Add(mass.gameObjectPointer.objectValues);
ConvertSubsetToVariables(i, 1);
}
///
/// Remove a mass object from the GpuObjectList.
///
/// Mass to be removed.
public void RemoveMass(Mass mass)
{
int index;
index = masses.IndexOf(mass);
masses.Remove(mass);
removeObjects.Add(mass.gameObjectPointer.objectValues);
variables[0].ClearProperty(index);
variables[1].ClearProperty(index);
variables[2].ClearProperty(index);
variables[3].ClearProperty(index);
variables[4].ClearProperty(index);
variables[5].ClearProperty(index); // Dummy Texture
variables[6].ClearProperty(index);
variables[7].ClearProperty(index);
variables[8].ClearProperty(index);
ConvertSubsetToVariables(index, 1);
}
///
/// Convert a list of GameObjects into a Gpu Object
///
private void ConvertToVariables()
{
variables[0].SetTexData(); // totalForce
variables[1].SetTexData(); // acceleration
variables[2].SetTexData(); // velocity
variables[3].SetTexData(); // rotation
variables[4].SetTexData(); // currentPosition
variables[5].SetTexData(); // dummy
variables[6].SetTexData(); // radius
variables[7].SetTexData(); // scale
variables[8].SetTexData(); // mass
// Set Tangents for
for (int i = 0; i < masses.Count; i++)
{
// Store the translation
masses[i].gameObjectPointer.objectValues.Tangent = new Vector4((float)i % 256 / 256, (float)((int)i / 256) / 256, 0, 0);
}
}
private void RetrieveValues()
{
// Unscale the Gpu Objectvariables
for (int variableCount = 0; variableCount < variables.Count; variableCount++)
{
variables[variableCount].UnScale();
}
// Get current position for each object
for (int massCount = 0; massCount < masses.Count; massCount++)
{
masses[massCount].totalForce = new Vector3(variables[0].properties[massCount].X,
variables[0].properties[massCount].Y,
variables[0].properties[massCount].Z);
masses[massCount].acceleration = new Vector3(variables[1].properties[massCount].X,
variables[1].properties[massCount].Y,
variables[1].properties[massCount].Z);
masses[massCount].velocity = new Vector3(variables[2].properties[massCount].X,
variables[2].properties[massCount].Y,
variables[2].properties[massCount].Z);
masses[massCount].currentRotation = new Quaternion(variables[3].properties[massCount].X,
variables[3].properties[massCount].Y,
variables[3].properties[massCount].Z,
variables[3].properties[massCount].W);
masses[massCount].currentPosition = new Vector3(variables[4].properties[massCount].X,
variables[4].properties[massCount].Y,
variables[4].properties[massCount].Z);
masses[massCount].scale = new Vector3(variables[7].properties[massCount].X);
masses[massCount].mass = variables[8].properties[massCount].X;
}
}
private void ConvertSubsetToVariables(int index, int count)
{
variables[0].SetSubTexData(index, count); // totalForce
variables[1].SetSubTexData(index, count); // acceleration
variables[2].SetSubTexData(index, count); // velocity
variables[3].SetSubTexData(index, count); // rotation
variables[4].SetSubTexData(index, count); // currentPosition
variables[5].SetSubTexData(index, count); // dummy
variables[6].SetSubTexData(index, count); // radius
variables[7].SetSubTexData(index, count); // scale
variables[8].SetSubTexData(index, count); // mass
}
///
/// Update using GpuProcessor.
///
/// ObjectLibrary that this list belongs to.
public void Update(ObjectLibrary objectLibrary)
{
// Calc the player's current position
Vector2 playerPos2d =
new Vector2( Statics.PlayerSettings.playerPosition.X, Statics.PlayerSettings.playerPosition.Z ) /
new Vector2(Statics.TerrainSettings.collisionMapSize) / new Vector2(2.0f) + new Vector2(0.5f);
playerPositionParam.SetValue(playerPos2d);
// Set Wind Map handle in shader
windMap.SetWindMap(ShaderParameters.effect_update);
// Set Scale and Min Value range
ShaderParameters.scaleRange.SetValue(variables[4].scaleRange);
ShaderParameters.minimumValue.SetValue(variables[4].minimumValue);
cpScaleRangeY.SetValue(variables[4].scaleRange);
cpScaleMinY.SetValue(variables[4].minimumValue);
gpu_scaleMinY.SetValue(objectLibrary.terrain.terrainScaleMinY);
gpu_scaleRangeY.SetValue(objectLibrary.terrain.terrainScaleMaxY - objectLibrary.terrain.terrainScaleMinY);
gpu_Processor.ExecuteRTT(this, objectLibrary);
}
///
/// Override Draw function
///
/// ModelManager containing this objects model info.
/// Shader technique to draw this object with.
public override void Draw(ModelManager modelManager, string technique)
{
#if !XBOX360
ModelInfo modelInfo = modelManager.GetModelInfo(type);
ShaderParameters.modelTexture1.SetValue(modelInfo.currentTexture);
ShaderParameters.bumpTexture1.SetValue(modelInfo.bumpTexture);
// Transfer Current Position Reference from Update Effect to Draw Effect
currentPositionParam.SetValue(variables[4]._read);
if (removeObjects.Count != 0)
{
for (int i = 0; i < removeObjects.Count; i++)
{
instanceList.Remove(removeObjects[i]);
}
instanceList.CopyTo(instanceData);
vertexBuffer = new VertexBuffer(Statics.SystemSettings.graphics.GraphicsDevice,
typeof(VertexFogBinormalTangent),
instanceData.Length,
BufferUsage.WriteOnly);
vertexBuffer.SetData(instanceData);
removeObjects.Clear();
}
if (addObjects.Count != 0)
{
for (int i = 0; i < addObjects.Count; i++)
{
instanceList.Add(addObjects[i]);
}
instanceData = new VertexFogBinormalTangent[instanceList.Count];
instanceList.CopyTo(instanceData);
vertexBuffer = new VertexBuffer(Statics.SystemSettings.graphics.GraphicsDevice,
typeof(VertexFogBinormalTangent),
instanceData.Length,
BufferUsage.WriteOnly);
vertexBuffer.SetData(instanceData);
addObjects.Clear();
}
// Set the vertex declaration
Statics.SystemSettings.graphics.GraphicsDevice.VertexDeclaration = vertexDeclaration;
Statics.SystemSettings.graphics.GraphicsDevice.Vertices[0].SetFrequencyOfIndexData(instanceData.Length);
// Tell GPU how many times to run through the instance data and set the stream.
Statics.SystemSettings.graphics.GraphicsDevice.Vertices[1].SetSource(vertexBuffer, 0, vertexDeclaration.GetVertexStrideSize(1));
Statics.SystemSettings.graphics.GraphicsDevice.Vertices[1].SetFrequencyOfInstanceData(1);
// Draw the model
base.DrawInstancedTriStrips(modelInfo.model, technique, vertexDeclaration.GetVertexStrideSize(0));
#endif
}
}
}