/*
* ColladaVertex.cs
* Authors: August Zinsser
*
* Copyright August Zinsser 2007
* This program is distributed under the terms of the GNU General Public License
*/
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Pina3D
{
///
/// The basic collada vertex type. Dynamically configures the Vertex Buffer to accept data of different formats depending
/// on what data each instance of this class contains.
///
internal class ColladaVertex
{
private Vector3? mPosition; // Position of the vertex
private Vector3? mNormal; // Normal for this vertex
private Vector2? mUV; // Texture coord of this vertex
private Vector3? mTangent; // Tangent for this vertex
private Vector3? mBlendWeights; // Weight of 3 bones for the skin (if applicable)
private Vector3? mBlendIndices; // Index of bones for the skin (if applicable)
///
/// Returns true if two vertices are nearly equal. For example the
/// tangent or normal data does not have to match 100%.
/// Used to optimize vertex buffers and to generate indices.
///
/// A
/// B
/// Bool
public static bool NearlyEquals(ColladaVertex a, ColladaVertex b)
{
// Positions must be identical
if (a.mPosition != b.mPosition)
return false;
// Check UV similarity
if (a.mUV.HasValue && b.mUV.HasValue)
if (Math.Abs(a.mUV.Value.X - b.mUV.Value.X) > 0.001f ||
Math.Abs(a.mUV.Value.Y - b.mUV.Value.Y) > 0.001f)
return false;
// Check Normals
if (a.mNormal.HasValue && b.mNormal.HasValue)
if ((a.mNormal.Value - b.mNormal.Value).Length() > 0.1f)
return false;
// Check Tangents
if (a.mTangent.HasValue && b.mNormal.HasValue)
if ((a.mTangent.Value - b.mTangent.Value).Length() > 0.1f)
return false;
// Everything passed, so they're close enough
return true;
}
///
/// Analyzes the vertices and returns a vertex buffer with the appropriate settings to accept data from this type of vertex.
///
/// A list of collada verticies. Null parameters are allowed.
///
public static VertexBuffer GenerateVertexBuffer(List vertices)
{
VertexBuffer vertBuff =
new VertexBuffer(
Pina.Graphics.GraphicsDevice,
vertices[0].SizeInBytes * vertices.Count,
ResourceUsage.WriteOnly,
ResourceManagementMode.Automatic);
return vertBuff;
}
///
/// Returns the raw vertex data, used to send data to the vertex buffer
///
///
///
public static float[] GetRawArray(List vertices)
{
int stride = vertices[0].SizeInBytes / 4; // 4 bytes per float
float[] rawArray = new float[stride * vertices.Count];
for (int i = 0; i < vertices.Count; i++)
{
ColladaVertex curVert = vertices[i];
int j = 0;
if (curVert.mPosition.HasValue)
{
rawArray[i * stride + j++] = curVert.mPosition.Value.X;
rawArray[i * stride + j++] = curVert.mPosition.Value.Y;
rawArray[i * stride + j++] = curVert.mPosition.Value.Z;
}
if (curVert.mBlendWeights.HasValue)
{
rawArray[i * stride + j++] = curVert.mBlendWeights.Value.X;
rawArray[i * stride + j++] = curVert.mBlendWeights.Value.Y;
rawArray[i * stride + j++] = curVert.mBlendWeights.Value.Z;
}
if (curVert.mBlendIndices.HasValue)
{
rawArray[i * stride + j++] = curVert.mBlendIndices.Value.X;
rawArray[i * stride + j++] = curVert.mBlendIndices.Value.Y;
rawArray[i * stride + j++] = curVert.mBlendIndices.Value.Z;
}
if (curVert.mUV.HasValue)
{
rawArray[i * stride + j++] = curVert.mUV.Value.X;
rawArray[i * stride + j++] = curVert.mUV.Value.Y;
}
if (curVert.mNormal.HasValue)
{
rawArray[i * stride + j++] = curVert.mNormal.Value.X;
rawArray[i * stride + j++] = curVert.mNormal.Value.Y;
rawArray[i * stride + j++] = curVert.mNormal.Value.Z;
}
if (curVert.mTangent.HasValue)
{
rawArray[i * stride + j++] = curVert.mTangent.Value.X;
rawArray[i * stride + j++] = curVert.mTangent.Value.Y;
rawArray[i * stride + j++] = curVert.mTangent.Value.Z;
}
}
return rawArray;
}
///
/// Generates Vertex elements
///
public static VertexElement[] GenerateVertexElements(List vertices)
{
return DynamicallyGenerateVertexElements(vertices);
}
///
/// Generates a Vertex declaration for vertex buffers.
///
public static VertexDeclaration GenerateVertexDeclaration(List vertices)
{
return new VertexDeclaration(Pina.Graphics.GraphicsDevice, DynamicallyGenerateVertexElements(vertices));
}
///
/// Generate vertex declaration based on what data the first element of vertices is holding. Assumes
/// all vertices contain the same attributes as the first vertex.
///
private static VertexElement[] DynamicallyGenerateVertexElements(List vertices)
{
VertexElement[] vertEl = null;
ColladaVertex vert = vertices[0];
// Have to make manual cases here
if (vert.mPosition.HasValue && vert.mNormal.HasValue && vert.mUV.HasValue && vert.mTangent.HasValue && vert.mBlendWeights.HasValue && vert.mBlendIndices.HasValue)
{
vertEl = new VertexElement[]
{
new VertexElement(0, 0, VertexElementFormat.Vector3, VertexElementMethod.Default, VertexElementUsage.Position, 0),
new VertexElement(0, 12, VertexElementFormat.Vector3, VertexElementMethod.Default, VertexElementUsage.BlendWeight, 0),
new VertexElement(0, 24, VertexElementFormat.Vector3, VertexElementMethod.Default, VertexElementUsage.BlendIndices, 0),
new VertexElement(0, 36, VertexElementFormat.Vector2, VertexElementMethod.Default, VertexElementUsage.TextureCoordinate, 0),
new VertexElement(0, 44, VertexElementFormat.Vector3, VertexElementMethod.Default, VertexElementUsage.Normal, 0),
new VertexElement(0, 56, VertexElementFormat.Vector3, VertexElementMethod.Default, VertexElementUsage.Tangent, 0),
};
}
else if (vert.mPosition.HasValue && vert.mNormal.HasValue && vert.mUV.HasValue && vert.mTangent.HasValue && !vert.mBlendWeights.HasValue && !vert.mBlendIndices.HasValue)
{
vertEl = new VertexElement[]
{
new VertexElement(0, 0, VertexElementFormat.Vector3, VertexElementMethod.Default, VertexElementUsage.Position, 0),
new VertexElement(0, 12, VertexElementFormat.Vector2, VertexElementMethod.Default, VertexElementUsage.TextureCoordinate, 0),
new VertexElement(0, 20, VertexElementFormat.Vector3, VertexElementMethod.Default, VertexElementUsage.Normal, 0),
new VertexElement(0, 32, VertexElementFormat.Vector3, VertexElementMethod.Default, VertexElementUsage.Tangent, 0),
};
}
else
{
System.Diagnostics.Debug.Assert(false, "Unsupported Vertex Delcaration Type in DynamicallyGenerateVertexElements!");
return null;
}
return vertEl;
}
///
/// Fill a vertex with given information. Each parameter is nullable.
///
/// Position of this vertex
/// 3 weights of each bone (often just 1, 0, 0 to use only 1 bone)
/// Indices for used bones. Uses float values instead of ints because shader expects floats.
/// Texture coordinates
/// Normal vector for this vertex
/// Tangent vector for this vertex
public ColladaVertex(
Vector3? position,
Vector3? normal,
Vector2? uv,
Vector3? tangent,
Vector3? blendWeights,
Vector3? blendIndices
)
{
mPosition = position;
mNormal = normal;
mUV = uv;
mTangent = tangent;
mBlendWeights = blendWeights;
mBlendIndices = blendIndices;
}
///
/// AKA Stride Size
///
public int SizeInBytes
{
get
{
// 4 bytes per float:
return 4 * (
(mPosition.HasValue ? 3 : 0) +
(mNormal.HasValue ? 3 : 0) +
(mUV.HasValue ? 2 : 0) +
(mTangent.HasValue ? 3 : 0) +
(mBlendWeights.HasValue ? 3 : 0) +
(mBlendIndices.HasValue ? 3 : 0)
);
}
}
}
}