XNA Creators Club Online
Page 1 of 1 (7 items)
Sort Posts: Previous Next

Custom processing to get edge data

Last post 17/04/2008 20:18 by DoppleX. 6 replies.
  • 16/04/2008 18:20

    Custom processing to get edge data


    I’m afraid that I’m totally lost trying to make a custom model processor.  I apologize in advance for the long post – I have a *lot* of questions about this (and I’m probably making the questions worse by looking at too many different sets of sample code at once)

    Since the rest of this is long, I’m adding a short list of isolated questions to the top to make it easier to answer!

    • Is a custom model processor mostly responsible for taking in NodeData and outputting its custom type?  Or is it for writing its data in XNB form?
    • What is XNB anyway?  My impression is that it’s sort of a generic “compiled” way of storing content post-buildtime.  Is this accurate?
    • If all I’m doing is adding a list of edges to the standard ModelMeshPart class, is there a way to easily handle reading and writing my extra data from the XNB file?  (Each entry in the list is four vertices, each with an associated normal vector – although I may also want to precalculate the face normal of the two triangles on the edge and include that in the data.)
    • Should I be adding a new class for handling the list of edges?  If I do, does it make writing to the XNB file more complicated?
    • Is there an easy way of traversing the triangles in a mesh during content processing?  Or do I need to reconstruct them from lists of vertices and indices?


    What I’m trying to do is add a custom Model class that has a list of edges that I can process for silhouette detection (initially, I was going to try to read the vertexbuffer of the model at load-time, but after looking around it seemed clear that it would be better to implement this as a build-time processing.)

    I’m not entirely sure where it should fit though – I’m not even really sure if it’s a custom processor or an importer – or for that matter, what the difference between the two is.

    What I do know is that all the data I need is already in the file (it’s just triangle data, after all) – and that I only really want to have to implement code for finding edges and glomming that data on to the rest of the model data (or even extending ModelMeshPart and storing the edge data per-part, which might be more optimal) – and have the rest of the pipeline continue on its merry way.

    I don’t know if that’s actually possible, though.


    At a high level, I *think* that what I need to do is take the glob of data representing my geometry – in NodeContent form, it appears – and write a processor that will return a custom class – which I’d like to be the basic Model class plus an extra data structure.

    (I’m mostly looking at the CustomModelSample for guidance while I do this, btw – in the event that this isn’t the best thing to work off of, I’m going to be even further off than I am already)

    So…  If I need to put together a “shopping list” of things I need to do in order to make this happen, here’s what I’m seeing in the sample that I need to implement:

    • 1. A CustomModelProcessor class –   Takes NodeContent and magically turns it into CustomModelContent.  Doesn’t really care how.
    • 2. A CustomModelContent class –  This is the part of the thing I don’t understand!  While the class is custom, it appears to be using standard ModelPart for representing the meshes and writing them to a XNB file.  I’m almost sure that I need to change this - but the stuff I have to do on the other end seems quite confusing.   After looking at the Sample class, I *think* that what I need to do is extend the ModelMeshPart class to handle my data (since it should be more efficient to handle the edges per-part).
    • 3. CustomModelReader and CustomModel classes gameside – hopefully also just extending the existing classes and properly handling reading the extra data.

    How far off base am I?  I think I understand it a bit better just having had to write this down, but there’s still a lot about the content pipeline that seems quite confusing.

    Thanks in advance!

    -Dan

  • 16/04/2008 19:42 In reply to

    Re: Custom processing to get edge data - help!

    One or two other questions...

    Right now, I'm working on trying to extract my edge/triangle data during the content processing - and it's so cumbersome that I almost feel like I have to be going about this in the completely wrong way.

    Right now, what I'm doing (or trying to do) for each GeometryContent object is creating the vertex Buffer from the VertexContent, cycling through the VertexElement[] generated from that to find the VertexElements for Normal and Position.

    I'm then planning on using those to guide reads into the vertex buffer based on the indices in the GeometryContent.

    I have to be doing something wrong, right?  Is there a way to access my Vertex data before it gets all Vertex-Buffery so I don't need some kind of secret decoder ring to directly read the buffer?
  • 17/04/2008 9:33 In reply to

    Re: Custom processing to get edge data - help!

    Figured out some of my questions from the second post.

    The actual vertex locations for a piece of geometry content aren't directly accessible through GeometryContent - they're actually in the MeshContent object and I need to use the VertexContent object to match the indices given by geometry.Indices to the corresponding indices in the parent MeshContent...

    And the normal data is in a totally seperate array in the VertexContent object..   I'm not sure if I index into it with the indices from geometryContent or whether I have to go through the indirect indices in the VertexContent again...

    At least that's my current understanding.   Is this accurate?

    I think I'm finally getting close to being able to put things together, at least - which brings up a different question:  Since this runs at build time rather than run time, how do I go about debugging it?
  • 17/04/2008 15:02 In reply to

    Re: Custom processing to get edge data - help!

    Answer
    Reply Quote
    DoppleX:

    • Is a custom model processor mostly responsible for taking in NodeData and outputting its custom type?  Or is it for writing its data in XNB form?


    The model processor converts NodeData to some other type.

    If this type is not already supported, you must then also provide a ContentTypeWriter and ContentTypeReader pair for reading/writing that custom type to XNB format. We already have ContentTypeWriters and readers for built in types such as Model, and also for lists and collections, so if your processor output for instance a List<MyType>, you would only need to implement a ContentTypeWriter for MyType, rather than for the entire List<MyType> class. Our built in writer would handle the List collection, and then call into your custom writer when it came to write out the object data for each instance in the list.


    DoppleX:

    • What is XNB anyway?  My impression is that it’s sort of a generic “compiled” way of storing content post-buildtime.  Is this accurate?


    Exactly. XNB format is basically just whatever binary data the ContentTypeWriter decided to save into it, prefixed with a standard header that indicates what kind of object the file contains, and which ContentTypeReader should be used to load it.


    DoppleX:

    • If all I’m doing is adding a list of edges to the standard ModelMeshPart class, is there a way to easily handle reading and writing my extra data from the XNB file?  (Each entry in the list is four vertices, each with an associated normal vector – although I may also want to precalculate the face normal of the two triangles on the edge and include that in the data.)


    Depending on how typesafe you want to be, if you make a custom MyData class that contains your four vertices, four normals, etc, you would also have to make a writer and reader for that class. Alternatively it seems like you could just use an array of Vector3 here, and use the first four elements for your vertices, next four for normals, etc, in which case our built-in array and vector writers would know how to handle this data without you writing any custom serialization code at all.

    If you make your processor by deriving from the built-in ModelProcessor and overriding the Process method, you can call the base.Process to get the output ModelContent, then attach your custom data to the Tag property of whichever objects you are interested in within the output data tree. We have a builtin writer for ModelContent data, which will automatically save out whatever objects you attach to the Tag. If those are of a type that we already know how to save (for instance an array or list of vectors) it can do that automatically, otherwise it will try to look up and call into a type writer for whatever custom object type it finds in the tag.

    The "picking with triangle accuracy" sample is probably the best example of attaching custom data to a model tag in this way.


    DoppleX:

    • Is there an easy way of traversing the triangles in a mesh during content processing?  Or do I need to reconstruct them from lists of vertices and indices?


    You'll have to reconstruct them: we don't have any direct representation for edge data in the mesh object model. This is an area I'd love to get feedback about (via the Connect site) if you have any particular ideas for helpers you'd find useful.


    DoppleX:

    The actual vertex locations for a piece of geometry content aren't directly accessible through GeometryContent - they're actually in the MeshContent object and I need to use the VertexContent object to match the indices given by geometry.Indices to the corresponding indices in the parent MeshContent...


    You got it. We actually built in a shortcut for doing this, since it is such a common pattern: you can use the values from geometry.Indices to look up into geometry.Vertices.Positions (which will internally follow the backlink and pull the right position vector out of the parent MeshContent for you).


    DoppleX:

    And the normal data is in a totally seperate array in the VertexContent object..   I'm not sure if I index into it with the indices from geometryContent or whether I have to go through the indirect indices in the VertexContent again...


    You can use the values from geometry.Indices to index into geometry.Vertices.Channels[VertexChannelNames.Normal()].


    DoppleX:

    I think I'm finally getting close to being able to put things together, at least - which brings up a different question:  Since this runs at build time rather than run time, how do I go about debugging it?


    If you have the full version of Visual Studio 2005, see the posts from Stephen and jwatte in this thread. If you are using C# Express, see here.

    XNA Framework Developer - blog - homepage
  • 17/04/2008 16:01 In reply to

    Re: Custom processing to get edge data - help!

    Thanks Shawn, those answers are a huge help.

    I'm actually rethinking part of my approach here, though I'm not sure if the reimagined version will actually work.  I still need to process the triangles and edges in the mesh - but I may be able to avoid needing to write custom ModelContent classes.

    Rather than actually sending extra data for a custom Model class for me to write all sorts of extra processing for - which was going to inevitably be CPU intensive at runtime - I think that I've worked out a way to do everything I need on the GPU Vertex Shader (and just brute force the processing).

    Assuming I don't hit a dead end with my "do it all on the GPU" idea, I was thinking perhaps the correct approach is to:
    1.) Generate a Vertex Buffer, Index buffer, and all of the other parts (VertexElements,  for the original meshpart from my GeometryContent
    2.) Possibly copy the GeometryContent object so that the changes I'm about to make to the Vertex Channels and Indices don't screw up my original meshpart.
    3.) Add several channels to my VertexContent object
    I think the syntax for this is:
    myVertex.Channels.Add<Vector3>("P1Pos", new Vector3(-9999.0f, -9999.0f, -9999.0f));
    (To initialize all existing vertices to (-9999.0f, -9999.0f, -9999.0f) in this channel)
    (EDIT:  This doesn't seem to work.  I need to create an IEnumerable to set the default, but I'm not sure how that would work.)


    Do this for positions and normals for the two different adjacent points, and also add two channels for the edge normals of each bordering triangle.


    4.) do a geometry.Indices.Clear(); then iterate through all edges in the meshpart, and add a vertex to create a degenerate triangle on each edge.

    I think I do this with:              geometry.Vertices.Add(geometry.Vertices.VertexCount);
    To add a vertex to the end of the list.
    Set the channels on the vertex appropriately.
    Then (starting from an empty index buffer) to add the triangle do:
    geometry.Indices.Addrange(1,2,x, 2, 1, x); to add two triangles to my mesh.

    When i finish, use the exact same process used to build the vertexbuffer for the first meshpart for this second one.

    a. At this point, either just add the additional modelMeshPart to the model as a regular additional modelMeshPart - which would allow me to keep my output as ModelContent

    OR

    b. Implement a CustomModelContent class that has two sets of geometry buffers per Mesh Part.  (Probably the better long term solution, though the first approach is probably better for trying to get a proof of concept).

    Approach b would require me to write code for writing to the XNB file - but the CustomModelContent sample already has code for reading the VertexBuffer and IndexBuffer, so it isn't a huge problem.

    Overall, I have a feeling that this is a much better approach than the one I was going to take.  Not only am I offloading a ton of what would have been CPU-intensive heavy lifting to the Vertex Shader, I'm also no longer sending in vertex data each frame from the CPU.

    It occurs to me that I could even unify everything into a single Vertex Buffer, and just store two seperate index buffers, which would probably be the most optimal approach...

    Thanks again for the help - very, very useful.  I'll keep an eye out for helper functions that would be useful when I actually write my geometry processing.

    -Dan
  • 17/04/2008 19:17 In reply to

    Re: Custom processing to get edge data - help!

    DoppleX:

    I think the syntax for this is:
    myVertex.Channels.Add<Vector3>("P1Pos", new Vector3(-9999.0f, -9999.0f, -9999.0f));
    (To initialize all existing vertices to (-9999.0f, -9999.0f, -9999.0f) in this channel)
    (EDIT:  This doesn't seem to work.  I need to create an IEnumerable to set the default, but I'm not sure how that would work.)


    You need to pass in a collection of Vector3 with an entry for each vertex in your geometry: this won't automatically replicate a single value over all the vertices. A List<Vector3> or Vector3[] should do the trick.

    DoppleX:

    Overall, I have a feeling that this is a much better approach than the one I was going to take.  Not only am I offloading a ton of what would have been CPU-intensive heavy lifting to the Vertex Shader, I'm also no longer sending in vertex data each frame from the CPU.


    Sounds good! I'm curious now as to what on earth this effect is going to do :-)
    XNA Framework Developer - blog - homepage
  • 17/04/2008 20:18 In reply to

    Re: Custom processing to get edge data - help!

    Well, the starting point is going to be an implementation of image-space silhouette detection and rendering (for a cartoon rendered graphics system) on the GPU which vaguely resembles the "edge-buffer" concept, but which is really pretty much just brute forcing things onto the GPU

    I have plans past that - which is where the additional data I'm putting into my vertex channels comes in (and that data may change as I experiment) - but I'm honestly not sure whether those plans will actually yield anything worthwhile yet.  (Or if they do, what kind of performance hit it will cause)

    Should it work - or even just come close enough to demonstrate the intended effect - I will of course post details.  :-)
Page 1 of 1 (7 items) Previous Next