Jump to content

  • Log In with Google      Sign In   
  • Create Account


[SlimDX] Mesh utility functions (source included)


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
2 replies to this topic

#1 Gage64   Members   -  Reputation: 1227

Like
0Likes
Like

Posted 25 September 2009 - 04:38 AM

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[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;
            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.

Sponsor:

#2 Mike.Popoloski   Crossbones+   -  Reputation: 2888

Like
0Likes
Like

Posted 25 September 2009 - 05:29 AM

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

#3 Gage64   Members   -  Reputation: 1227

Like
0Likes
Like

Posted 25 September 2009 - 05:42 AM

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...




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS