Dynamically updating vertices of a Xen Model

Developer
May 20, 2011 at 4:54 AM

Hi,

I've looked around quite a bit including the Dynamic Geometry tutorial (#05), and it seems like while Xen allows creating vertex buffers from scratch and and update them on the fly, it doesn't provide anything to do this (i.e. modify the vertices) for a Xen model. The problem is mainly the fact that access to all vertex related data (except for the TryExtractVertexData<T>() function is hidden as private/internal etc.

Without modifying the API, it seems like one would have to recreate the graphics resources by copying the contents from the Xen model and render them as standalone index and vertex buffers. Since that is easier said than done and also pretty messy to deal with than a nicely packaged Xen Model, I'm wondering if I'm perhaps missing something that the API provides via different means?

Thanks in advance

Coordinator
May 21, 2011 at 3:46 PM

Hi.

Unfortunately the second option may be your only one.
It's a limit of the XNA 4 content pipeline - when you ask XNA to build a mesh, it no longer generates raw vertex data (like it did in XNA 3), but it gives you a serialized vertex buffer object instead. All you can do is create that vertex buffer - which is setup to be readonly (and cannot be changed).

Sorry about that :-(

Developer
May 21, 2011 at 7:24 PM

Thanks for the reply.

After I saw how the TryExtractVertexData function worked. I was able to deduce my own (perhaps potentially hacky) solution to get what I wanted. Below is the code I added into Xen to get this going.

In the Vertices class I added a new function (note I had renamed the var "usage" to be "resUsage" to avoid some confusion):

        /// <summary>
        /// Returns the vertex buffer data for every channel used
        /// </summary>
        /// <typeparam name="T">The VertexType of the vertex data</typeparam>
        /// <param name="output">True if the data is available in Vertices class. False if it has to get it from the internal XNA VertexBuffer class</param>
        /// <returns></returns>
        bool IVertices.TryExtractAllVertexData<T>(T[] output)
        {
            if (output == null || output.Length < this.Count)
                throw new ArgumentException("output data array is either null or too small");

            if(resUsage == Graphics.ResourceUsage.Dynamic)
            {
                if (buffer.Data is T[])
                {
                    output = buffer.Data as T[];
                }
                else
                {
                    throw new ArgumentException("Provided buffer is not of proper VertexType");
                }
                return true;
            }
            else
            {
                if (vb == null) throw new InvalidOperationException("Vertex data must be warmed before calling TryExtractAllVertexData");
                vb.GetData<T>(output);
                return false;
            }
        }

Then in GeometryData, I added the following function:
        /// <summary>
        /// Converts the Vertices reference to be a dynamic buffer and returns the contents in "vertexData"
        /// NOTE: There is very little error checking, so use with caution
        /// </summary>
        /// <typeparam name="T">The struct which represents the vertex format</typeparam>
        /// <param name="vertexData">The array which will contain the data. Make it is large enough to hold all of the vertex data</param>
        public void MakeVerticesDynamic<T>(T[] vertexData) where T : struct
        {
            if (!vertices.TryExtractAllVertexData(vertexData))
            {
                vertices = new Vertices<T>(vertexData);
                vertices.ResourceUsage = ResourceUsage.Dynamic;
            }
        }

And finally in the calling code ("outside of Xen code"), I invoke the whole thing via this:

 

            vertData = new ObjVertexFormat[numMeshes][];

            for (int i = 0; i < numMeshes; i++)
            {
                GeometryData geomData = renderObj.Model.ModelData.Meshes[i].Geometry[0];
                vertData[i] = new ObjVertexFormat[geomData.Vertices.Count];
                geomData.Vertices.Warm(game);
                geomData.MakeVerticesDynamic(vertData[i]);
                geomData.Vertices.Warm(game);
            }

At this point, in order to modify the vertex information, the caller can mess around with data vertData, and call SetDirty() on them just like the Dynamic Geometry tutorial does.

For the time being it seems to serve my purpose. That said, I'm open to suggesting on making it more robust, or doing it a better way.

Cheers