[SlimDX] Mesh utility functions (source included)

Started by
1 comment, last by Gage64 14 years, 6 months ago
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);
    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();
}

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.
Advertisement
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;}
Mike Popoloski | Journal | SlimDX
Thanks. I'm still not familiar with the C# 3.0 features but that does look pretty nice.

Also, I somehow managed to miss the D3DX class...

This topic is closed to new replies.

Advertisement