Problem
In the previous tutorial, you can access the positions of all vertices of the model relative to the initial position of the model. However, if you want to apply an animation to a part of the model, such as rotating a person's arm, you also want to change the positions of the arm, hand, and finger. It is impossible to use the results of the previous tutorial, because the vector3 set does not contain information about which part of the model vector3 belongs, and only contains information relative to the initial position of the model.
Solution
The independent transformation part of the model is stored in modelmeshe. Modelmesh has a tag attribute. You can store useful information in tags. In this tutorial, you will store the location information of all the vertices of modelmesh In the tag attribute.
You will store the vertex position information relative to the initial position of modelmesh (instead of the initial position relative to the model in the previous tutorial ). When xNaProgramWhen updating the bone matrix and calculating the absolute matrix (see tutorial 4-9), you can use the same absolute bone matrix transformation position as the rendering model to obtain the vertex position relative to the initial position of the model. Compared with the previous tutorial, the biggest benefit of this method is that this time you will handle the current transformation of the independent part. Working principle this tutorial is extended based on the previous tutorial. You still need to traverse the entire structure of the model, but this time, you do not need to store all vertices in a large array, and you will generate an independent array for each node.
After traversing the entire model and storing the array, you use the default model processor to generate a modelcontent from the node. Finally, you traverse all modelmeshcontents in modelcontent and store each array in the tag attribute of the corresponding modelmeshcontent.
Note:The modelcontent object is the output of the content processor and is then written into a binary file. When the program runs, the file is loaded from the disk and the object is converted to a model.
The starting part of the processor is as follows:Code:
Public override modelcontent process (nodecontent input, contentprocessorcontext context) {list <vector3 []> modelvertices = new list <vector3 []> (); modelvertices = inputs (input, modelvertices );}
You have created a set to store an independent vector3 array, and then pass the set and root node to the addmodelmeshvertexarraytolist method.
This method first calls itself and passes its subnode, so that the array of the subnode can be added to the set.
After the arrays of all child nodes are added to the set, the addmodelmeshvertexarraytolist method checks whether the current node contains vertices. If any, create a vector3 array containing all vertices and add it to the set.
Private list <vector3 []> addmodelmeshvertexarraytolist (nodecontent node, list <vector3 []> modelvertices) {foreach (nodecontent child in node. children) modelvertices = addmodelmeshvertexarraytolist (child, modelvertices); meshcontent mesh = node as meshcontent; If (mesh! = NULL) {list <vector3> nodevertices = new list <vector3> (); foreach (geometrycontent GEO in mesh. geometry) {foreach (INT index in GEO. indices) {vector3 vertex = GEO. vertices. positions [Index]; nodevertices. add (vertex) ;}} modelvertices. add (nodevertices. toarray ();} return modelvertices ;}
Note:Unlike the previous tutorial, this location is not changed. This is because you want to store the vertex position relative to the initial position of modelmesh, rather than the initial position relative to the model. You can use the absolute matrix of modelmesh to convert them later.
Note:The addmodelmeshvertexarraytolist method differs from the addverticestolist method in the previous tutorial in that you first add the data of the subnode to the set before adding the data of the current node. In the previous tutorial, the data of the current node is added before the data of the subnode is added, which may be more intuitive. However, it is important to save the arrays of modelmesh in the same order as the modelmesh is saved to the model, so that you can easily load the array at the end of the process method.
Finally, add the following code to the end of the process method:
Modelcontent usualmodel = base. Process (input, context); int I = 0; foreach (modelmeshcontent mesh in usualmodel. Meshes) mesh. Tag = modelvertices [I ++]; return usualmodel;
With arrays, you can use the default model processor to create a default modelcontent object from the node. In each modelmeshes, you store the correct array containing vector3.
However, do not forget that some modelmeshe consists of more than one modelmeshpart. Each modelmeshpart has its own nodecontent. For each modelmeshpart, The addmodelmeshvertexarraytolist method adds the vector3 array to the set. To complete the code, replace the previous code with the following code:
Modelcontent usualmodel = base. process (input, context); int I = 0; foreach (modelmeshcontent mesh in usualmodel. meshes) {list <vector3> modelmeshvertices = new list <vector3> (); foreach (modelmeshpartcontent part in mesh. meshparts) {modelmeshvertices. addrange (modelvertices [I ++]);} mesh. tag = modelmeshvertices. toarray ();} return usualmodel;
For all modelmeshparts belonging to a modelmesh, the code above adds all vector3 to a set, which is converted to an array and stored in the tag attribute of modelmesh.
Note:Because all modelmeshparts of a modelmesh use the same absolute transformation matrix, You can process all vector3 at a time and still ensure that the animation is correctly processed (see tutorial 4-9 ). You may also want to store the vertex of each modelmeshpart In the tag attribute of modelmeshpart. For example, you can create a better surround ball to process fast collision detection.
Finally, modelcontent is returned, which is ready to be serialized as a binary file.
When you use a custom model processor to import a model, you can find an array in the tag attribute of each modelmesh of the model, this array contains three vector3 components of each triangle of modelmesh. Load the model in the loadcontent method:
Mymodel = content. Load <model> ("tank"); modeltransforms = new matrix [mymodel. Bones. Count];
Now the tag attribute of modelmesh for each model contains a set of vector3. You can use the compiler to access the data stored in the array:
Vector3 [] modelvertices = (vector3 []) mymodel. meshes [0]. Tag; system. Diagnostics. Debugger. Break ();
A breakpoint is placed in the last line of code. You can observe the content of vector3.
Code
The following is a custom content pipeline:
Namespace vector3pipeline {[contentprocessor] public class modelvector3processor: modelprocessor {public override modelcontent process (nodecontent input, contentprocessorcontext context) {list <vector3 []> modelvertices = new list <vector3 []> (); modelvertices = addmodelmeshvertexarraytolist (input, modelvertices); modelcontent usualmodel = base. process (input, context); int I = 0; foreach (modelmesh Content mesh in usualmodel. meshes) {list <vector3> modelmeshvertices = new list <vector3> (); foreach (modelmeshpartcontent part in mesh. meshparts) {modelmeshvertices. addrange (modelvertices [I ++]);} mesh. tag = modelmeshvertices. toarray ();} return usualmodel;} private list <vector3 []> addmodelmeshvertexarraytolist (nodecontent node, list <vector3 []> modelvertices) {foreach (nodecontent child in node. Children) modelvertices = addmodelmeshvertexarraytolist (child, modelvertices); meshcontent mesh = node as meshcontent; If (mesh! = NULL) {list <vector3> nodevertices = new list <vector3> (); foreach (geometrycontent GEO in mesh. geometry) {foreach (INT index in GEO. indices) {vector3 vertex = GEO. vertices. positions [Index]; nodevertices. add (vertex) ;}} modelvertices. add (nodevertices. toarray ();} return modelvertices ;}}}