So, first of all, let's say you're trying to resize a cuboid (and it will stay a cuboid). The correct solution in this case isn't to modify vertices or even to use bones. What you want to do is use a model matrix that specifies a scale. You can then scale your model on each axis to place the vertices where you want them.
But, let's say that you're sure that you need to modify individual vertices. And let's say that you want to move them independently and arbitrarily - in which case bones is not a good solution. (Bones would be good if you want to move parts of the model around - you don't want to have one bone per vertex!)
First, let's cover the easy bit: How do you identify which vertices belong to a face? You simply save the indices of those vertices when you construct your cuboid. For each face store a list of indices (List<int>
or int[]
).
Now let's look at how to actually maintain and draw your vertex buffer.
It's worth considering, at this point, that most people (including myself) are answering this question with an eye for performance. But if all you have is a cuboid - you could do just about anything and not suffer performance issues.
There are three "kinds" of vertex buffers in XNA. There's VertexBuffer
, DynamicVertexBuffer
and the various DrawUserPrimitves
methods. I will give a very quick overview of how they work:
VertexBuffer
allocates a section of GPU memory that the GPU reads vertices from. The GPU does some crazy pipelining stuff - it's possible for it to be drawing a frame long after you've started to draw the next one. So if you modify a VertexBuffer
that is still being used to draw a previous frame, you stall the pipeline while the GPU finishes using the old data. Basically: Do not modify a VertexBuffer
that is in use, if you can avoid it.
DynamicVertexBuffer
works around this problem at the expense of GPU memory (and maybe some performance). Basically it allows the GPU to store several copies of your vertex buffer. That means that an older frame can use one copy, while your changes get applied to a different copy. There is also a DynamicIndexBuffer
.
DrawUserPrimitves
allows the GPU to assume that the vertex buffer won't get modified while it's still in use. The downside is that you are creating a new one each frame and must send the data for it to the GPU each frame. There is also an advantage, in some cases with small primitive counts, in that your data can be batched up with the the draw command, rather than copied separately.
Finally you must actually get your data to the GPU. I have a few important pieces of advice here.
First of all, you should never pull data back from the GPU, if you can avoid it. In XNA, that's the GetData
method. It can potentially cause the GPU to pipeline flush and you're stalling your own program on the CPU, while it waits for the slow transfer of data from the GPU.
Given the above, you should keep a copy of your vertex data as an array (of your vertex type, eg: VertexPositionColor
) in main memory. You can use this directly, modifying vertex positions (no need for a separate array of just the positions). Select elements in the array with the indicies you stored earlier.
Then simply use your array of vertex data with DynamicVertexBuffer.SetData
(probably better - especially if you only change a section of the vertex data) before drawing the buffer, or with DrawUserIndexedPrimitives
(probably easier).
If you do use SetData
, for best performance you should try to minimise how many writes you do. For example, if you modify the face of a cuboid you only change 4 vertices. But those vertices may be spread out in the buffer - it is likely to be faster to set all 8 vertices of the cuboid, than to set 4 of them one at a time.
And, finally, it's worth pointing out that, no matter what option you choose, your data still has to get onto the GPU somehow! Using bones will not solve this problem. Bone transforms aren't exactly small - they get sent each frame (like DrawUserPrimitves
) - so it's very possible for vertex data itself to be smaller if the number of bones is similar to the number of vertices. People are suggesting bones because it means you don't have to modify a VertexBuffer
- but as you can see from my answer, there are alternatives.