[SlimDX] Mesh utility functions (source included)

This topic is 3039 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

Recommended Posts

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:
public static BoundingSphere ComputeBoundingSphere(Mesh mesh)
{
List<Vector3> verts = new List<Vector3>(mesh.VertexCount);

while (ds.Position < ds.Length)
{
long oldPos = ds.Position;

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);

while (ds.Position < ds.Length)
{
long oldPos = ds.Position;

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[i].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;
ds.Position = oldPos;
}
else                // Spherical mapping with positions
{
ds.Position += elems[posElem].Offset;
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();
}


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.

Share on other sites
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;}