public static BoundingSphere ComputeBoundingSphere(Mesh mesh)
{
List<Vector3> verts = new List<Vector3>(mesh.VertexCount);
DataStream ds = mesh.LockVertexBuffer(LockFlags.ReadOnly);
while (ds.Position < ds.Length)
{
long oldPos = ds.Position;
Vector3 pos = ds.Read<Vector3>();
verts.Add(pos);
ds.Position = oldPos + mesh.BytesPerVertex;
}
mesh.UnlockVertexBuffer();
return BoundingSphere.FromPoints(verts.ToArray());
}
public static BoundingBox ComputeBoundingBox(Mesh mesh)
{
List<Vector3> verts = new List<Vector3>(mesh.VertexCount);
DataStream ds = mesh.LockVertexBuffer(LockFlags.ReadOnly);
while (ds.Position < ds.Length)
{
long oldPos = ds.Position;
Vector3 pos = ds.Read<Vector3>();
verts.Add(pos);
ds.Position = oldPos + mesh.BytesPerVertex;
}
mesh.UnlockVertexBuffer();
return BoundingBox.FromPoints(verts.ToArray());
}
public static int FindElementIndex(VertexElement[] elems, DeclarationUsage usage)
{
for (int i = 0; i < elems.Length; ++i)
{
if (elems.Usage == usage)
return i;
}
return -1;
}
// This code uses the methods described in:
// www.mvps.org/directx/articles/spheremap.htm
public static void ComputeTexCoords(Device device, ref Mesh mesh, bool useNormals)
{
if (useNormals)
{
if ((mesh.VertexFormat & VertexFormat.Normal) == 0)
{
mesh.ComputeNormals();
}
}
// Make room for texture coordinates
// The 'newMesh' variable is not needed anymore. The extra {} ensure that it is not used accidentally
{
Mesh newMesh = mesh.Clone(device, mesh.CreationOptions, mesh.VertexFormat | VertexFormat.Texture1);
mesh.Dispose();
mesh = newMesh;
}
VertexElement[] elems = mesh.GetDeclaration();
int posElem = FindElementIndex(elems, DeclarationUsage.Position);
int normalElem = FindElementIndex(elems, DeclarationUsage.Normal);
int texCoordsElem = FindElementIndex(elems, DeclarationUsage.TextureCoordinate);
// Needed for positional spherical mapping
BoundingBox bbox = ComputeBoundingBox(mesh);
DataStream ds = mesh.LockVertexBuffer(LockFlags.None);
while (ds.Position < ds.Length)
{
long oldPos = ds.Position;
Vector3 vec;
if (useNormals) // Spherical mapping with normals
{
ds.Position += elems[normalElem].Offset;
vec = ds.Read<Vector3>();
ds.Position = oldPos;
}
else // Spherical mapping with positions
{
ds.Position += elems[posElem].Offset;
Vector3 pos = ds.Read<Vector3>();
ds.Position = oldPos;
Vector3 center = (bbox.Minimum + bbox.Maximum) * 0.5f;
vec = Vector3.Normalize(pos - center);
}
float u = (float)Math.Asin(vec.X) / (float)Math.PI + 0.5f;
float v = (float)Math.Asin(vec.Y) / (float)Math.PI + 0.5f;
ds.Position += elems[texCoordsElem].Offset;
ds.Write<float>(u);
ds.Write<float>(v);
ds.Position = oldPos + mesh.BytesPerVertex;
}
mesh.UnlockVertexBuffer();
}
[SlimDX] Mesh utility functions (source included)
I'm playing around with SlimDX to learn more about shaders, and in the process I wrote a few mesh utility functions.
Currently I have functions to:
* Compute a bounding sphere/box
* Compute texture coordinates
Here's the code:
The functions seem to work, but I have not tested them thoroughly, so I can't say that with full certainty. If you see any errors, please let me know.
I'm also not too happy with the implementation, mostly because there's a lot of duplicate code. If you know of a better way to do this sort of thing, I would love to hear about it.
I hope this will be useful to someone.
You can cut down on a lot of that code by using a little LINQ and a few helper methods that exist in SlimDX already. This is how I'd write the function. It should be the same thing, although I wrote it on the fly so it might have a few typos:
public static Mesh ComputeTextureCoords(Device device, Mesh mesh, bool useNormals){ if (useNormals && (mesh.VertexFormat & VertexFormat.Normal) == 0) mesh.ComputeNormals(); mesh = mesh.Clone(device, mesh.CreationOptions, mesh.VertexFormat | VertexFormat.Texture1); var elements = mesh.GetDeclaration(); var dataStream = mesh.LockVertexBuffer(LockFlags.None); var box = BoundingBox.FromPoints(D3DX.GetVectors(dataStream, mesh.VertexCount, mesh.BytesPerVertex)); var center = (box.Minimum + box.Maximum) * 0.5f; dataStream.Position = elements.FirstOrDefault(e => useNormals ? e.Usage == DeclarationUsage.Normal : e.Usage == DeclarationUsage.Position).Offset; var vectors = D3DX.GetVectors(dataStream, mesh.VertexCount, mesh.BytesPerVertex).Select(v => useNormals ? v : Vector3.Normalize(v - center)); dataStream.Position = elements.FirstOrDefault(e => e.Usage == DeclarationUsage.TextureCoordinate).Offset; foreach (var vector in vectors) { dataStream.Write((float)(Math.Asin(vector.X) / Math.PI + 0.5)); dataStream.Write((float)(Math.Asin(vector.Y) / Math.PI + 0.5)); dataStream.Position += mesh.BytesPerVertex - (sizeof(float) * 2); } mesh.UnlockVertexBuffer(); return mesh;}
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement