Project 12.2: Skin Weight Info


Project 12.2: Skin Weight Info

Introduction

When writing out files for consumption by the computer, rather than by human eyes, different considerations need to be accounted for. As with MEL scripts, the whitespace and formatting of the file is largely irrelevant to the application. This is not to say that these qualities should be ignored. Formatting a file so it can easily be comprehended by a human user allows us to exploit the external file, perhaps bringing in data from an external program like Microsoft Excel, or to manually debug the file. In this project, we will create an external file format that stores the skinCluster information of all selected meshes, ignoring component numbers , allowing the exporting of skinning data regardless of history changes that might occur after the export. Both our import and export routines will deal with vertices according to explicit world space locations. By doing so, we counter any component renumbering that might occur due to the actions of the user, and overcomes the flaw of using images to store weight info, as importing that information is reliant on UV co-ordinates.

Design

Our tool is by necessity made of two procedures. The first writes out the file, and is shown in Flowchart 12.2. The second script, which reads in the skinning data is shown in Flowchart 12.3, page 436.

click to expand
Flowchart 12.2: The procedure to write out the file.
click to expand
Flowchart 12.3: The flowchart of the procedure that reads in skinning data.

There are a couple of issues related to the design of which we should be aware. First, we are relying on the user selecting the skin objects in the same order, and that none of the joints will be renamed . Although this is a slight hindrance to the end user, it could be remedied by a clever UI, if we so desired. For now, we will simply construct the script and leave the UI for later.

We also need to develop a format for our file. First, we determine what information we need to store.

  • Mesh name

  • Skin cluster name

  • Skin cluster influences

  • Component identifier (component location)

  • Influence/ component relationship

While the name of the mesh and skin cluster are largely irrelevant, they will help for matters of bookkeeping and error checking. To store each of these pieces of information, we will head each section with an appropriate labeling string. In Example 12.10, we layout a dummy file structure.

Example 12.10: Our desired file layout.

 [mesh]  meshName  [skincluster]  skinclusterName  [influences]  influence influence influence influence   influence influence . . .  [components]  x.xxxxxx y.yyyyyy z.zzzzzz   influence_value   influence_value   influence_value   . . .   x.xxxxxx y.yyyyyy z.zzzzzz   influence_value   influence_value   influence_value   . . .  [mesh]  meshName  [skincluster]  skinclusterName   . . .  

We optimize readability while still retaining an effective and clean format for reading the data into the computer. By bracketing the header labels, we can easily find them using the substring command and a conditional statement to compare the first character of the retrieved word to the character [ to find the headers.

Implementation

We begin our script simply enough by declaring our write-out procedure. In Example 12.11 we start, including a fileDialog to choose the location of the file.

Example 12.11: The start of our procedure, finding the file to write to.

 global proc string skinDataOut ()     {  // open a file dialog to name the file.  string $fileName = `fileDialog` ;  // Open the file for writing and capture the file   // identifier for later use  int $fileID = `fopen $fileName "w"`;     return $fileName;     fclose $fileID;     } 
On the CD  

The text for this script is found on the companion CD-ROM as Chapter_12/project_09/part_01/v01/skinDataOut.mel.

Next, we begin gathering data from the scene we need to create our file. We get the user s selection list, and parse the history of each object in that list, looking for skinCluster nodes. In Example 12.12, we can begin to gather data, format it, and write it out to a file.

Example 12.12: Finding the data for each of skinClusters and writing it out to the file.

 global proc string skinDataOut ()     {  // open a file dialog to name the file.  string $fileName = `fileDialog` ;  // Open the file for writing and capture the file   // identifier for later use  int $fileID = `fopen $fileName "w"`;  // Gather user selection  string $selList[] = `ls selection`;  // parse selection list  for ($obj in $selection)         {         string $hisList[] = `listHistory $obj`);         string $sknClstr;         for ($aNode in $hisList)             if (`nodeType $aNode` == "skinCluster")                 $sknClstr = $aNode;  // add mesh header and mesh name  fprint $fileID "[mesh]\n";          fprint $fileID ($obj + "\n\n");  // add skin header and cluster name  fprint $fileID " [skincluster]\n";          fprint $fileID (" " + $sknClstr + "\n\n");         }     return $fileName;     fclose $fileID;     } 
On the CD  

The text for this script is found on the companion CD-ROM as Chapter_12/project_09/part_01/v02/skinDataOut.mel.

Next, we can go and retrieve the names of the influences associated with each skin cluster, using the skinCluster command. To format the listing of influences so there are only four per line, we use an integer variable with a conditional statement to add a new line character and indentation when appropriate. We see this used in Example 12.13.

Example 12.13: Using a simple conditional statement to add text formatting.

  // parse selection list  for ($obj in $selection)         {         string $hisList[] = `listHistory $obj`);         string $sknClstr;         for ($aNode in $hisList)             if (`nodeType $aNode` == "skinCluster")                 $sknClstr = $aNode;  // add mesh header and mesh name  fprint $fileID "[mesh]\n";          fprint $fileID ($obj + "\n\n");  // add skin header and cluster name  fprint $fileID " [skincluster]\n";          fprint $fileID (" " + $sknClstr + "\n\n");  // get a list of all the influences used by skin  string $infList[] = `skinCluster query $sknClstr`;  // add the influences  fprint $fileID "  [influences]\n  ";         int $n = 1;         for ($aInf in $infList)             {             fprint $fileID $aInf;             $n++;             if ($n > 4)                 {                 fprint $fileID "\n  ";                 $n = 1;                 }             else                 fprint $fileID " ";             }         } 
On the CD  

The text for this script is found on the companion CD-ROM as Chapter_12/project_09/part_01/v03/skinDataOut.mel.

The last piece of our file is to write out the vertex designations. Unfortunately, this has two problems. Although we are using the vertex locations in world space as the identifier in our file, internally we must query the weighting values using their component index. Because a user might have placed history after the skin cluster node and changed the component numbers, our actual query might be faulty. There is, unfortunately , no way to avoid this; it simply must be documented with the tool. The second problem relates to one of floating-point precision. In Example 12.14, we create a variable holding a relatively precise floating-point number, and then print it out.

Example 12.14: Demonstration of the lack of precision of printing out floating-point values.

 float $floatTest = 0.1234567890123 ;     print $floatTest ;  // Result : 0.123456  

We get six places of floating-point precision even when our floating point contains whole number values, seen in Example 12.15.

Example 12.15: Precision is independent of the integer value.

 float $floatTest = 1234.1234567890123 ;     print $floatTest ;  // Result : 1234.123456  

We start to lose important detail at this point. AliasWavefront s suggestion is to convert the float value to a string, seen in Example 12.16.

Example 12.16: Conversion to a string adds precision to numbers, but is limited to 11 characters .

 float $floatTest = 0.1234567890123 ;     string $floatString = $floatTest ;     print $floatString ;  // Result : 0.123456789  

This provides slightly more precision, unless we start dealing with floats above an absolute value of 1000. In Example 12.17, we see how we begin to get major data loss.

Example 12.17: The 11 characters include the integer values of the number.

 float $floatTest = 123456789.1234567890123 ;     string $floatString = $floatTest ;     print $floatString ;  // Result : 123456789.1  

This could cause problems as we progress, not only on this script but as we progress into increasingly detailed file writing and scripts that might require extreme amounts of precision and detail. This is why we have created a custom accessory procedure,  preciseFloatString.mel , seen in Example 12.18.

Example 12.18: A script that gives a workaround to this issue.

 global proc string preciseFloatString (int $precision,                                             float $value)     {     string $convertedString = ".";  // get only the number to the left of the decimal point  int $baseInteger = $value;  // get only the number to the right of the decimal point  $value = abs($value - $baseInteger);     // build new decimal value     while (`size $convertedString` < ($precision + 1))         {  // 0.123 becomes 1.23  $value = ($value * 10);         // get only the number to the left of the decimal point         int $digitToAddToString = $value;  // build string  $convertedString = ($convertedString                                 + $digitToAddToString);  // get only the number to the right of the decimal point  $value = abs($value - $digitToAddToString);         }  // add original integer  $convertedString = ($baseInteger + $convertedString);     return $convertedString; } 
On the CD  

The text for this script is found on the companion CD-ROM as bonusscripts/preciseFloatString.mel.

Now, we can use this in our file write out string to construct extremely precise number strings to write to our file. When read back in, these numbers will retain all their precision, converted back to floats.

Example 12.19: Use of the preciseFloatString script in our file writeout script.

  // find number of vertices  $numVerts = `polyEvaluate -vertex $each`;  // add the component information  fprint $fileID "\n\n [components]\n "             {             float $pos[] = `xform query                             transform worldspace                             ($each + ".vtx[" + $n + "]")`;             fprint $fileID `preciseFloatString 15 $pos[0]`;             fprint $fileID " " ;              fprint $fileID `preciseFloatString 15 $pos[1]`;             fprint $fileID " " ;              fprint $fileID `preciseFloatString 15 $pos[2]`;             fprint $fileID "\n" ;             for ($aInf in $infList)                 {                 float $weight = `skinPercent                                 -transform $aInf                                 -query                                 $sknClstr                                 ($each + ".vtx["                                 + $n + "]")`;                  fprint $fileID " " ;                  fprint $fileID                     `preciseFloatString 15 $weight`;                 fprint $fileID "\n" ;                 }             } 
On the CD  

The text for this script is found on the companion CD-ROM as Chapter_12/project_09/part_01/final/skinDataOut.mel.

Now, we can run the script to export out a file from a valid scene. Next, we have to create the script to read our data back in. See Example 12.20.

Example 12.20: The procedure to read the data back in.

 global proc string skinDataIn ()     {  // open a file dialog to name the file.  string $fileName = `fileDialog` ;  // Open the file for writing and capture the file   // identifier for later use  int $fileID = `fopen $fileName "r"`;  // Get the first line of the file, and declare an   // array to hold the entirety of the file  string $word = `fgetword $fileID`;     string $fileContents[];  // continue as long as fgetline returns info  while (`feof $fileID` == 0)         {         $fileContents[`size $fileContents`] = $word;         $word = `fgetword $fileID`;         }  // close out the file  fclose $fileID;  // test read data  for ($eachItem in $fileContents)         print ($eachItem + "\n");     } 
On the CD  

The text for this script is found on the companion CD-ROM as Chapter_12/project_09/part_02/v01/skinDataIn.mel.

This successfully reads in the file, and prints it out to the Script Editor. We now need to parse the data. In Example 12.21, we begin searching the read in data for headers by checking each string to see if it begins with [ .

Example 12.21: Code used to parse the data to find the section headers.

  // test read data  for ($eachItem in $fileContents)         if (`substring $eachItem 1 1` == "[")             print ($eachItem + "\n"); 
On the CD  

The text for this script is found on the companion CD-ROM as Chapter_12/project_09/part_02/v02/skinDataIn.mel.

While this allows us to very quickly find the section headers, it does not let us find the data held in any logical manner. What we need to do is combine both the data and a call to a procedure to handle the compiled data. In Example 12.22, we parse the array, sorting data into separate arrays that are then passed to a separate procedure.

Example 12.22: The code to sort the data into appropriate variable arrays.

 $n = 0     while ($n < `size $fileContents`)         {         vector $vrt;         string $influences[];         float $weights;             {             while ($fileContents[$n] != "[influences]")                 $n++ ;             while (`substring $fileContents[++$n] 1 1`                         != "[")                 $influences[`size $influences`] =                             $fileContents[$n] ;         if ($fileContents[$n] == "[components]")             {             while (`substring $fileContents[$n] 1 1`                         != "[")                 {                 $vrt = << $fileContents[++$n],                             $fileContents[++$n],                             $fileContents[++$n] >> ;  // read in one number for each influence  for ($i=0;$i<`size $influences`;$i++)                     $weights[`size $weights`] =                                 $fileContents[++$n];  // Separate editing procedure  skinReadEdit $vrt $influences $weights;  // clear out weights to hold fresh date  clear $weights;                 }             }             }         } 
On the CD  

The text for this script is found on the companion CD-ROM as Chapter_12/project_09/part_02/final/skinDataIn.mel.

This is possibly some of the most complicated code we have yet created. Flowchart 12.4 illustrates how this read-in code functions.

click to expand
Flowchart 12.4: A diagram to clarify the code used to parse our data.
On the CD  

Although it is a lengthy process to execute, this method produces very good results, although some tweaking of tolerances in the skinReadEdit procedure, found on the companion CD-ROM under Chapter_12/project_09/skinReadEdit.mel.

Project Conclusion and Review

As we have seen, it is more than possible to create files that are easily readable by both human and computer. There are a vast number of file formats from which to glean ideas. For example, we could have used HTML as a basis, and used [mesh] and [/mesh] to bracket each object. We could have put a header into the file to let us know how many objects exist. The robustness and structure of a file format is a question of how much time can be dedicated to it and what its primary purpose is. We have also learned that occasionally, data errors might not be the fault of the code, but the way Maya handles data, and we must be prepared to react to these issues.

Project Script Review

skinDataOut.mel

 global proc string skinDataOut ()     {  // open a file dialog to name the file.  string $fileName = `fileDialog` ;  // ensure we create a .skin file  string $splitFile[] = `dirName $fileName`;     $fileName = ($splitFile[0] + $splitFile[1] + ".skin")  // Open the file for writing and capture the file   // identifier for later use  int $fileID = `fopen $fileName "w"`;  // Gather user selection  string $selList[] = `ls selection`;  // parse selection list  for ($obj in $selection)         {         string $hisList[] = `listHistory $obj`);         string $sknClstr;         for ($aNode in $hisList)             if (`nodeType $aNode` == "skinCluster")                 $sknClstr = $aNode;  // add mesh header and mesh name  fprint $fileID "[mesh]\n";          fprint $fileID ($obj + "\n\n");  // add skin header and cluster name  fprint $fileID " [skincluster]\n";          fprint $fileID (" " + $sknClstr + "\n\n");  // get a list of all the influences used by skin  string $infList[] = `skinCluster query $sknClstr`;  // add the influences  fprint $fileID "  [influences]\n  ";         int $n = 1;         for ($aInf in $infList)             {             fprint $fileID $aInf;             $n++;             if ($n > 4)                 {                 fprint $fileID "\n  ";                 $n = 1;                 }             else                 fprint $fileID " ";             }  // find number of vertices  $numVerts = `polyEvaluate -vertex $each`;  // add the component information  fprint $fileID "\n\n [components]\n "         for ($n = 0; $n< $numVerts; $n++)             {             float $pos[] = `xform query                             transform worldspace                             ($each + ".vtx[" + $n + "]")`;             fprint $fileID `preciseFloatString 15 $pos[0]`;             fprint $fileID " " ;              fprint $fileID `preciseFloatString 15 $pos[1]`;             fprint $fileID " " ;              fprint $fileID `preciseFloatString 15 $pos[2]`;             fprint $fileID "\n" ;             for ($aInf in $infList)                 {                 float $weight = `skinPercent                                 -transform $aInf                                 -query                                 $sknClstr                                 ($each + ".vtx["                                 + $n + "]")`;                  fprint $fileID " " ;                  fprint $fileID                     `preciseFloatString 15 $weight`;                 fprint $fileID "\n" ;                 }             }         }     return $fileName;     fclose $fileID;     } 

skinDataIn.mel

 global proc string skinDataIn ()     {  // open a file dialog to name the file.  string $fileName = `fileDialog` ;  // Open the file for writing and capture the file   // identifier for later use  int $fileID = `fopen $fileName "r"`;  // Get the first line of the file, and declare an   // array to hold the entirety of the file  string $word = `fgetword $fileID`;     string $fileContents[];  // continue as long as fgetline returns info  while (`feof $fileID` == 0)         {         $fileContents[`size $fileContents`] = $word;         $word = `fgetword $fileID`;         }  // close out the file  fclose $fileID;     $n = 0     while ($n < `size $fileContents`)         {         vector $vrt;         string $influences[];         float $weights;         if ($fileContents[$n] == "[mesh]")             {             while ($fileContents[$n] != "[influences]")                 $n++ ;             while (`substring $fileContents[++$n] 1 1`                         != "[")                 $influences[`size $influences`] =                             $fileContents[$n] ;         if ($fileContents[$n] == "[components]")             {             while (`substring $fileContents[$n] 1 1`                         != "[")                 {                 $vrt = << $fileContents[++$n],                             $fileContents[++$n],                             $fileContents[++$n] >> ;  // read in one number for each influence  for ($i=0;$i<`size $influences`;$i++)                     $weights[`size $weights`] =                                 $fileContents[++$n];  // Separate editing procedure  skinReadEdit $vrt $influences $weights;  // clear out weights to hold fresh date  clear $weights;                 }             }             }         }     } 



The MEL Companion
The MEL Companion: Maya Scripting for 3D Artists (Charles River Media Graphics)
ISBN: 1584502754
EAN: 2147483647
Year: 2003
Pages: 101

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net