$proceduralbones: Difference between revisions
|  (→VRD files:  mentioned that a maximum of 32 triggers can be used. Also reworded the somewhat confusing description. What is "a discrere rotation" and why is it used twice in the same sentence...) | Trigger hurt (talk | contribs)  m (Fix a typo, re-word the self-collision example with a more clear hypothetical scenario, clarify the unusual end-of-line behavior for <trigger>) | ||
| (5 intermediate revisions by 4 users not shown) | |||
| Line 1: | Line 1: | ||
| {{LanguageBar}} | {{LanguageBar}} | ||
| = | {{toc-right}} | ||
| {{this is a|QC command|name=$proceduralbones}} It uses a special definition file (VRD) to tell the engine to automatically animate certain bones procedurally/at runtime. | |||
| == Usage == | |||
| <source lang=php> | |||
| $proceduralbones "<file name>.vrd" | |||
| </source> | |||
| {{bug|While the quotation marks are optional provided the file has no spaces in its name, the file extension is NOT optional. Code exists in StudioMDL to read an older, unsupported format if the .vrd file extension is excluded.|tested={{src13}}}} | |||
| {{bug|StudioMDL will not throw an error if the file specified does not exist.|tested={{src13}}}} | |||
| VRD files work similarly to driven keys in Maya, or transform modifiers in Blender. There are two types of procedural bone: | |||
| : '''Helper bones''', which consist of a number of keyframes (called "triggers") that tell the engine which angles it should look out for, and as the target bone approaches an angle, its trigger will be blended in proportionally. | |||
| : '''Aim constraints''', which will always point a bone towards a specified attachment point on the model. | |||
| {{tip|VRD files support C-style comments, tabs, spaces, etc. but do NOT support multi-line comments.}} | |||
| == | == Guides == | ||
| * This [https://steamcommunity.com/sharedfiles/filedetails/?id=1776518541 Facepunch guide] (backed up on steam) describes the process of crafting a VRD file, using [[Source Filmmaker]]. | |||
| * This [https://steamcommunity.com/sharedfiles/filedetails/?id=3485026924 Steam Guide] shows how to do this in entirely in [[Blender]], with the help of an addon. | |||
| == VRD file syntax & parameters == | |||
| ; <code><helper> HelperName HelperParent DriverParent DriverName</code> | |||
| : {{note|This marks the beginning of a new procedural bone, and ends the previous one, making it mutually exclusive with <code><aimconstraint></code>.}} | |||
| : <code>HelperName</code> - The name of the helper bone being driven | |||
| : <code>HelperParent</code> - The name of the helper bone's parent | |||
| : <code>DriverParent</code> - The name of the driver bone's parent | |||
| : <code>DriverName</code> - The name of the bone that will be driving <code>HelperName</code> | |||
| :: {{note|The parent bones '''must''' match their respective bones' parents. The compile will fail if they do not match.}} | |||
| <!-- Side note, no idea why this is required. StudioMDL compares them to the real skeleton anyway & will throw an error if it's wrong, and since the parents aren't allowed to deviate + there can only be one parent per bone, this seems redundant... --> | |||
| :: May have up to 32 <code><trigger></code> definitions, each of which define a pose to blend <code>HelperName</code> towards based on the rotation of <code>DriverName</code> within its <code>AngleOfInfluence</code>. | |||
| :: In effect, each <code><trigger></code> is a unique pose that <code>HelperName</code> will translate to whenever <code>DriverName</code> approaches the <code><trigger></code>'s defined watch angle. | |||
| :: Example: <source><helper>   hlp_forearm_L   bip_lowerArm_L   bip_lowerArm_L   bip_hand_L</source> | |||
| ; <code><aimconstraint> HelperName HelperParent TargetAttachment</code> | |||
| : {{note|This marks the beginning of a new procedural bone, and ends the previous one, making it mutually exclusive with <code><helper></code>.}} | |||
| : <code>HelperName</code> - The name of the helper bone being driven | |||
| : <code>HelperParent</code> - The name of the helper bone's parent | |||
| : <code>TargetAttachment</code> - The name of the [[$attachment]] that <code>HelperName</code> will always point towards | |||
| :: Causes <code>HelperName</code> to always face <code>TargetAttachment</code>. The angle it faces can be modified with <code><aimvector></code> and <code><upvector></code>. | |||
| :: Example: <source><aimconstraint>   hlp_piston_R   hlp_lowerArm_R   attach_upperArm_piston_R</source> | |||
| :: {{bug|The lexer suggests <code>TargetAttachment</code> should also accept a bone, but no code implements this - using a bone here will throw an error.|tested={{src13}}}} | |||
| :: {{bug|The <code>TargetAttachment</code> will appear to visibly lag behind by one frame depending on how much movement occurs, which will have more or less impact depending on the client's FPS.|tested={{src13}}}} | |||
| {{tip| | ; <code><basepos> <[[float]]|X> <[[float]]|Y> <[[float]]|Z></code> | ||
| :: Used to declare the starting position of a procedural bone, relative to its parent. | |||
| <!-- Side note, why is this even necessary? StudioMDL has to access the parent anyway to compare and see if it's wrong, and you probably don't want to change the base position of this bone from where you specifically defined it to be. It's like this system was designed to be unintuitive... --> | |||
| :: For <code><helper></code>, this is not necessarily required, as each <code><trigger></code> also includes a location. If specified anyway, all <code><trigger></code> locations will become relative to this one. | |||
| :: For <code><aimconstraint></code>, this is basically a necessity, as there is no other way to specify where the bone should be. | |||
| :: Example: <source><basepos> 15.003496 0.0043543 3.9911634</source> | |||
| :: {{tip|To avoid using external tools or scripts, you can find the correct coordinates by first exporting your model in its reference position as an SMD file, then load this file into any text editor of your choosing. Locate the desired bone in the <code>nodes</code> list; to the left of the name will be its ID, used in the <code>skeleton</code> section of this file. Find <code>time 0</code> (the first and ideally only frame of animation), and look for the bone ID on the left - the three numbers following this ID are the base pose of this bone relative to its parent.}} | |||
| == | ; <code><trigger> AngleOfInfluence DriverRotation_XYZ HelperRotation_XYZ HelperLocation_XYZ</code> | ||
| : <code>AngleOfInfluence</code> - A single number representing a cone of influence around this trigger (beyond the cone = 0%, center of the cone = 100%) | |||
| : <code>DriverRotation_XYZ</code> - A 3D vector representing the rotation of the driver bone (relative to its parent) that represents the center of this trigger's cone | |||
| : <code>HelperRotation_XYZ, HelperLocation_XYZ</code> - A pair of 3D vectors that respectively represent rotation & location of this helper bone as the driver approaches the center of this trigger's cone | |||
| :: Used by <code><helper></code> to declare a specific rotation being watched, and the resulting rotation & location of the helper when the watched bone approaches this rotation. | |||
| :: There can be up to 32 <code><trigger></code> lines per <code><helper></code>, and all rotations are Euler rotations expressed in degrees. | |||
| :: The angle of influence refers to how far the bone must be from this rotation for this <code><trigger></code> to have fully faded out; as it approaches <code>DriverRotation_XYZ</code>, the procedural bone will approach <code>HelperRotation_XYZ</code> and <code>HelperLocation_XYZ</code> | |||
| :: Example: <source><trigger>   60   -26.5650735797   -14.4773829758   -26.5652454670      -26.5650162839   -14.4773829758   -26.5651881712      0.000000000   0.000000000   0.000000000</source> | |||
| :: {{Warning|The same trick above works here too, the three numbers after the bone ID and position are the rotation, but the rotations are in '''RADIANS''' - convert to '''degrees''' before using them in a <code><trigger></code>}} | |||
| ; <code><aimvector> <[[float]]|X> <[[float]]|Y> <[[float]]|Z></code> | |||
| :: Used by <code><aimconstraint></code> to declare the direction a <code><aimconstraint></code> will face as it tracks its <code>TargetAttachment</code>. | |||
| :: This vector is normalized, but does support any arbitrary orientation. Most commonly, bones will face a particular axis. | |||
| :: Example: <source><aimvector> 0 0 -1</source> (causes the bone to face -Z, or Z always faces opposite the <code>TargetAttachment</code>) | |||
| < | ; <code><upvector> <[[float]]|X> <[[float]]|Y> <[[float]]|Z></code> | ||
| < | :: Used to clarify which axis on the <code>TargetAttachment</code> is considered "up". | ||
| < | :: Example: <source><upvector> 0 1 0</source> (causes the bone to rotate with the attachment if it rotates along its local Y axis) | ||
| < | |||
| < | |||
| < | |||
| ; <code><display><[[float]]|X> <[[float]]|Y> <[[float]]|Z> <[[float]]|distance></code> | |||
| :: Used by the Softimage Mod Tool to store the attributes for procedural bone's appearance in Mod Tool's viewport, but has no purpose outside of it. | |||
| :: Example: <source><display> -12.64333 4.33348 0.63611 4.45</source> | |||
| ; <code><rotateaxis> <[[float]]|X> <[[float]]|Y> <[[float]]|Z></code> | |||
| :: Used to specify a '''pre'''-multiplied angle for all <code><trigger></code> definitions. | |||
| :: Example: <source><rotateaxis> 0.0004010705   -119.9998795417   89.9996375014</source> | |||
| :: {{note|This may have been meant to compliment <code><basepos></code> in setting a default angle, but no retail game ever uses it.}} | |||
| < | ; <code><jointorient> <[[float]]|X> <[[float]]|Y> <[[float]]|Z></code> | ||
| < | :: Used to specify a '''post'''-multiplied angle for all <code><trigger></code> definitions. | ||
| < | :: Example: <source><jointorient> 56.3101202181   -154.3411936127   16.1020048039</source> | ||
| :: {{note|This may have been meant to compliment <code><basepos></code> in setting a default angle, but no retail game ever uses it.}} | |||
| == | == Common applications == | ||
| [[File:Valve_biped_forearm_with_twist_bones.png|thumb|240px|The forearm of a full Valve biped skeleton. Note the ulna and wrist bones, both of which are "twist" bones that will be procedurally driven by the rotation of the hand bone.]] | |||
| The most common use case of procedural bones is to automatically rotate forearms, upper arms, thighs, and shins of character models. In the diagram to the right, the mesh is weighted to both a twist bone placed in the middle of the forearm (''ValveBiped.Bip01_L_Ulna''), and a twist bone located on the wrist (''ValveBiped.Bip01_L_Wrist''). Rather than require the animator to pose the hand, wrist, and ulna all together (which would not animate in the ragdoll anyway), the ulna & wrist are given procedural bone definitions that allow them to automatically rotate with the hand. | |||
| === Corrective bones === | |||
| In areas where rotating weighted bones causes a noticeable loss in mesh volume, such as on the exteriors of elbows & knees, a procedural bone can be used to better maintain the volume. By adding a helper in the same position as the elbow/knee and weighting the vertices nearest the apex of the joint to it, the mesh can be made to deform in a way that doesn't noticeably shrink. | |||
| In {{tf2}}, the Engineer's character model uses a pair of procedural bones to keep his knee pads from deforming poorly when his knees bend, by watching the angle of the knee & rotating about half the distance to both keep the knee pads in the correct orientation and avoid visible shrinking. | |||
| < | === Corrective flexes === | ||
| < | In some engine branches<sup>†</sup> that support <code>[[$boneflexdriver]]</code>, it is possible to use procedural bones to automatically fade in and fade out flexes<sup>‡</sup>. This technique lends itself most to applying local corrective or limited delta mush shapes to parts of a mesh that experience low-quality deformations, typically when put into extreme poses. Care must be taken when using this technique, since each corrective flex takes up a flexcontroller slot and requires a corresponding bone in the model's skeleton to drive it. This can quickly add up, counting against the MDL flexcontroller limit (usually 96 or 128) and [[Skeleton#Bone_count_limit|bone limit]] (usually 128) and reducing the number of normal flexes and bones available to the model. | ||
| </ | {{note|<sup>†</sup> Deviates from official spec, but tested and working in: {{hl2}}, {{css}}, {{tf2}}, {{portal}}, {{gmod}}}} | ||
| {{warning|<sup>‡</sup> With additional requirements. Refer to section [[$proceduralbones#Driving_Flexes_with_Procedural_Bones|Driving Flexes with Procedural Bones]] at the end of this article.}} | |||
| === Self-collision approximation === | |||
| While not common, there are situations where character props may noticeably intersect with a character due to being located on or near a joint; for example, the sleeves of a suit jacket. In this scenario, the wrists of the character may noticeably intersect with the jacket as they attempt to handle equipment. While it is possible to manually animate this wherever it comes up, doing so will not account for aim matrices applied to the character, nor will it apply to their ragdoll. Weighting the end of the sleeve to the hand would likely not look very good either, since parts of the sleeve that don't need to move at a given moment will still move to follow the hand. This can be corrected for with a few procedural bones placed around the wrist, all of which watch the rotation of the hand; the helpers could flex out of the way of the hand automatically, which would not only look better during a ragdoll, but would save the animator the effort of needing to account for this with every new animation added. | |||
| === Mechanical armatures === | |||
| It's desirable for a mechanical linkage to always point in the correct direction, for instance a piston that connects a lower armature to an upper armature. While you can manually animate this, it will neither work with a ragdoll nor during transitions of different animations. Doing this precisely with a series of triggers will usually introduce small blending artifacts as the engine attempts to lerp the piston to the correct orientation, which may look out of place in this sort of precision application. With an aim constraint, this piston could be guaranteed to always aim at the correct place on the upper armature from its position on the lower armature. | |||
| == VRD file examples == | |||
| === HWM Femscout forearm twists === | |||
| {{note|Source Filmmaker was used to set up this VRD file, but you can use any modeling software that supports Source models.}} | |||
| <source lang=xml> | <source lang=xml> | ||
| <helper> hlp_forearm_L   bip_lowerArm_L   bip_lowerArm_L   bip_hand_L | <helper> hlp_forearm_L   bip_lowerArm_L   bip_lowerArm_L   bip_hand_L | ||
| Line 155: | Line 128: | ||
| <trigger> 90 	-157.919 -89.6281 158.827 		0.101044 -61.3622 -0.505184 		0 0 0 | <trigger> 90 	-157.919 -89.6281 158.827 		0.101044 -61.3622 -0.505184 		0 0 0 | ||
| <trigger> 90 	-0.905463 81.1157 0.0128013 	-1.79037e-05 49.6029 -0.0109647 	0 0 0 | <trigger> 90 	-0.905463 81.1157 0.0128013 	-1.79037e-05 49.6029 -0.0109647 	0 0 0 | ||
| <helper> hlp_forearm_R   bip_lowerArm_R   bip_lowerArm_R   bip_hand_R | |||
| <basepos> -0.0502548 4.76207 -0.0109539 | |||
| <trigger> 90 	-0.116485 -7.07782 0.923253 	-26.1455 -71.739 -60.6497 		0 0 0 | |||
| <trigger> 90 	-0.288436 72.9621 0.610626 		-72.1773 -44.2586 -20.2743 		0 0 0 | |||
| <trigger> 90 	-179.149 -81.8242 -179.905 		81.3212 -37.0352 -157.273 		0 0 0 | |||
| </source> | </source> | ||
| This model with source files is available [http://source.maxofs2d.net/index.php?dir=models%2Ftf_femscout_enhanced%2F here] on MaxOfS2D's website. | |||
| === Crossbow string === | |||
| <source lang=xml> | |||
| <aimconstraint>	hlp_bow_L		hlp_bowflex_L	attach_string_L | |||
| <basepos>       15.070189000 0.000035000 -5.834238000 | |||
| <aimvector>		-1 0 0 // Causes hlp_bow_L to face away from attach_string_L on the X axis | |||
| <upvector>		0 1 0 | |||
| <aimconstraint>	hlp_bow_R		hlp_bowflex_R	attach_string_R | |||
| <basepos>       15.069914000 0.000015000 -5.834859000 | |||
| <aimvector>		-1 0 0 // Causes hlp_bow_R to face away from attach_string_R on the X axis | |||
| <upvector>		0 1 0 | |||
| </source> | |||
| ===  | === Detailed right wrist created from an SMD animation === | ||
| <source lang=xml> | <source lang=xml> | ||
| <trigger> 90 -6. | <helper>        hlp_wrist_R     bip_lowerArm_R  bip_lowerArm_R  bip_hand_R | ||
| <basepos>       8.643163000 -0.000000000 0.000021000 | |||
| <trigger>       60      0.0001718873    150.0000642864  -90.0000958676  0.0001145916    150.0000642864  -90.0001531634  0.000000000     0.000000000     0.000000000      // time 0 | |||
| <trigger>       60      0.0001718873    -149.9998923990 -90.0002677549  0.0001145916    -149.9998923990 -90.0003250507  0.000000000     0.000000000     0.000000000      // time 1 | |||
| <trigger>       60      -52.0251407557  -89.9998666845  -38.2358227960  -48.7844914664  -89.9998666845  -38.7109193998  0.000000000     0.000000000     0.000000000      // time 2 | |||
| <trigger>       60      -0.0001145916   -29.9999555615  -90.0001531634  -0.0001145916   -29.9999555615  -90.0002104591  0.000000000     0.000000000     0.000000000      // time 3 | |||
| <trigger>       60      -0.0001145916   30.0001274488   -90.0002104591  -0.0001145916   30.0001274488   -90.0002677549  0.000000000     0.000000000     0.000000000      // time 4 | |||
| <trigger>       60      153.4350608597  14.4776121589   26.5649016923   164.0022360669  26.1849160826   61.2785237386   -0.209301000    -0.381664000    0.031250000      // time 5 | |||
| <trigger>       60      -153.4347743808 -14.4773829758  26.5648443966   -171.5899734436 -29.0682115950  68.3158205615   -0.189188000    -0.685485000    -0.609611000     // time 6 | |||
| <trigger>       60      -89.9999239803  -29.9998982657  -0.0002291831   -90.0125290517  -70.4192059232  0.4019298933    -0.103786000    -0.028785000    -0.800817000     // time 7 | |||
| <trigger>       60      -26.5650735797  -14.4773829758  -26.5652454670  -10.9441241406  -28.1016317947  -66.4090170193  -0.138767000    0.601700000     -0.401906000     // time 8 | |||
| <trigger>       60      26.5651308755   14.4776121589   -26.5651881712  14.4604807209   26.7658952869   -68.4830096461  -0.374143000    0.995121000     0.171281000      // time 9 | |||
| <trigger>       60      26.5651308755   165.5226941678  -26.5651881712  2.8720655397    150.2507333217  -86.7703900722  -0.108041000    0.564029000     -0.252821000     // time 10 | |||
| <trigger>       60      -26.5651308755  -165.5223503931 -26.5652454670  3.3628038912    -149.6761139490 -92.8659161673  -0.211483000    0.771767000     0.567706000      // time 11 | |||
| <trigger>       60      -90.0001531634  -149.9998923990 -0.0001145916   -86.2042504748  -94.3424857011  -7.0959104054   -0.224750000    0.172373000     0.564973000      // time 12 | |||
| <trigger>       60      26.5649589881   -14.4775548631  -153.4351181555 3.6163950113    -29.7311237640  -95.1886297730  -0.084996000    -0.475180000    0.216209000      // time 13 | |||
| <trigger>       60      -26.5651881712  14.4774402716   -153.4351181555 -5.6986955262   28.7843110076   -111.9647767186 -0.068011000    0.295446000     -0.111653000     // time 14 | |||
| <trigger>       60      -119.9998795417 29.9999555615   89.9999239803   16.3264896680   144.2979310134  -91.0288988845  0.029360000     -0.468964000    -0.863318000     // time 15 | |||
| <trigger>       60      -119.9998222460 -30.0000701530  89.9996947971   -158.5945458049 -15.7165760951  87.5142038819   -0.044148000    0.546487000     -0.608450000     // time 16 | |||
| <trigger>       60      7.7956064648    -89.9998666845  -38.1285650968  30.3528975633   -87.9546938348  -100.1075870357 -0.292867000    0.814450000     -0.029658000     // time 17 | |||
| <trigger>       60      59.9998538272   -29.9998982657  -90.0001531634  19.9532552154   -29.4507755149  -90.1107913136  -0.195600000    0.410349000     0.696194000      // time 18 | |||
| <trigger>       60      59.9997965314   30.0000701530   -90.0002104591  19.3298771343   19.5167759671   -88.2561842266  -0.296514000    -0.591582000    0.830669000      // time 19 | |||
| <trigger>       60      120.0001660206  29.9999555615   89.9999239803   156.9124563099  22.1676797978   88.3612073904   -0.348013000    0.475677000     0.507207000      // time 20 | |||
| <trigger>       60      120.0002233164  -30.0000701530  89.9996947971   -18.0940644660  -137.9124309783 -92.6856636449  -0.288719000    -0.342254000    0.879168000      // time 21 | |||
| <trigger>       60      -112.4273255466 -89.9998666845  -38.0887445300  -33.8569355510  -95.9927187426  -78.7788893373  -0.413648000    -0.655860000    -0.128256000     // time 22 | |||
| <trigger>       60      -60.0000257145  -29.9999555615  -90.0001531634  -21.5571359713  -30.0129617034  -90.0030179523  -0.272500000    -0.344648000    -0.597419000     // time 23 | |||
| <trigger>       60      -60.0000830103  30.0001274488   -90.0002104591  -19.7873839337  32.3326959286   -89.5132536291  -0.207348000    0.373764000     -0.711022000     // time 24 | |||
| </source> | </source> | ||
| == | == Miscellaneous == | ||
| For symmetrical models (such as humans) it's very common for left/right bones to have completely different local rotations & locations. As such, copying & pasting the procedural bone data from one side to the other will typically not work. | |||
| A procedural bone '''cannot''' be controlled by more than one driver bone, but one driver bone can be used to control many procedural bones. | |||
| While parsing a <code><trigger></code>, all characters after the 10th space-delineated number are ignored. This technically means that comments placed at the end of this line do not actually need to be prefixed with <code>//</code>, though doing so would be visually inconsistent with every other kind of comment in the file & likely break syntax highlighters, so it would be best practice to begin the comment normally regardless. | |||
| <source lang=xml> | <source lang=xml> | ||
| < | // Not sure why you'd want to do it this way, as it will probably break syntax highlighters, but it technically does work. | ||
| <trigger> 90  -6.75 1.59 3.18  0 0 0  0 0 0   this text is ignored for some reason, so it doesn't even need to begin with two forward slashes | |||
| </source> | </source> | ||
| == Driving Flexes with Procedural Bones == | == Driving Flexes with Procedural Bones == | ||
| Using <code>$proceduralbones</code> to automatically drive a <code>$boneflexdriver</code> allows for advanced deformation techniques, such as automatic corrective shapes, but this functionality is not officially supported. As such, retail releases of StudioMDL refuse to compile models that use <code>$boneflexdriver</code> on any procedural bone. To fix this, StudioMDL may be directly patched using a hex editor, thus allowing it to skip the early exit and successfully compile the model. | |||
| {{warning|Modified instances of StudioMDL often produce models that deviate from the format expected by the engine - this may cause issues on specific games or engine branches.}} | |||
| ===  | === Engine-specific patches === | ||
| * '''{{src13|4}} branch''' | |||
| : ''Includes most games where the model format's header begins with <code>IDST0</code>, including {{Half-Life 2|4}}, {{Counter-Strike: Source|4}}, {{Portal|4}}, {{Team Fortress 2|4}}, and {{gmod|4}}.'' | |||
| * '''{{src13|4}} branch  | |||
| : '' | |||
| : Search for the unique byte sequence <code>04 74 25 8D 4F</code>, then change the byte <code>74</code> to <code>EB</code>. | : Search for the unique byte sequence <code>04 74 25 8D 4F</code>, then change the byte <code>74</code> to <code>EB</code>. | ||
| * '''{{as|4}} | * '''{{as|4}}''' | ||
| : Search for the unique byte sequence <code>04 74 25 8B</code>, then change the byte <code>74</code> to <code>EB</code>. | : Search for the unique byte sequence <code>04 74 25 8B</code>, then change the byte <code>74</code> to <code>EB</code>. | ||
| * '''{{sfm|4}} | * '''{{sfm|4}}''' | ||
| : Search for the unique byte sequence <code>04 74 1C 8B 56 5C</code>, then change the byte <code>74</code> to <code>EB</code>. | : Search for the unique byte sequence <code>04 74 1C 8B 56 5C</code>, then change the byte <code>74</code> to <code>EB</code>. | ||
| * '''{{Portal 2|4}} | * '''{{Portal 2|4}}''' | ||
| : Search for the unique byte sequence <code>04 74 28 8B</code>, then change the byte <code>74</code> to <code>EB</code>. | : Search for the unique byte sequence <code>04 74 28 8B</code>, then change the byte <code>74</code> to <code>EB</code>. | ||
| {{todo|Determine the patch location for other notable games, including {{l4d}} and {{l4d2}}}} | {{todo|Determine the patch location for other notable games, including {{l4d}} and {{l4d2}}}} | ||
| ===  | === Important QC modification === | ||
| In your model's [[QC]] file, you '''must''' specify <code>[[$bonemerge]]</code> on every bone that you make procedural. Refer to your VRD file, and add <code>$bonemerge</code> line(s) to your QC for the <code>ProceduralBone</code> of every <code><helper></code> line. The <code>DriverBone</code>s do not need <code>$bonemerge</code>. | In your model's [[QC]] file, you '''must''' specify <code>[[$bonemerge]]</code> on every bone that you make procedural. Refer to your VRD file, and add <code>$bonemerge</code> line(s) to your QC for the <code>ProceduralBone</code> of every <code><helper></code> line. The <code>DriverBone</code>s do not need <code>$bonemerge</code>. | ||
| The reasoning behind the requirement of the seemingly unrelated <code>$bonemerge</code> flag is not yet fully understood. | The reasoning behind the requirement of the seemingly unrelated <code>$bonemerge</code> flag is not yet fully understood. | ||
| ===  | === Confirmation === | ||
| Once the model has been compiled, you can test it in HLMV to verify that everything works as intended. The skeletal animation should now be able to drive the desired procedural bone(s), which in turn drive the flexcontroller(s). | |||
Latest revision as of 18:37, 30 October 2025
$proceduralbones  is a   QC command  available in all  Source games. It uses a special definition file (VRD) to tell the engine to automatically animate certain bones procedurally/at runtime.
 Source games. It uses a special definition file (VRD) to tell the engine to automatically animate certain bones procedurally/at runtime.
Usage
$proceduralbones "<file name>.vrd"
VRD files work similarly to driven keys in Maya, or transform modifiers in Blender. There are two types of procedural bone:
- Helper bones, which consist of a number of keyframes (called "triggers") that tell the engine which angles it should look out for, and as the target bone approaches an angle, its trigger will be blended in proportionally.
- Aim constraints, which will always point a bone towards a specified attachment point on the model.
 Tip:VRD files support C-style comments, tabs, spaces, etc. but do NOT support multi-line comments.
Tip:VRD files support C-style comments, tabs, spaces, etc. but do NOT support multi-line comments.
Guides
- This Facepunch guide (backed up on steam) describes the process of crafting a VRD file, using Source Filmmaker.
- This Steam Guide shows how to do this in entirely in Blender, with the help of an addon.
VRD file syntax & parameters
- <helper> HelperName HelperParent DriverParent DriverName
 Note:This marks the beginning of a new procedural bone, and ends the previous one, making it mutually exclusive with Note:This marks the beginning of a new procedural bone, and ends the previous one, making it mutually exclusive with- <aimconstraint>.
- HelperName- The name of the helper bone being driven
- HelperParent- The name of the helper bone's parent
- DriverParent- The name of the driver bone's parent
- DriverName- The name of the bone that will be driving- HelperName Note:The parent bones must match their respective bones' parents. The compile will fail if they do not match. Note:The parent bones must match their respective bones' parents. The compile will fail if they do not match.
- May have up to 32 <trigger>definitions, each of which define a pose to blendHelperNametowards based on the rotation ofDriverNamewithin itsAngleOfInfluence.
- In effect, each <trigger>is a unique pose thatHelperNamewill translate to wheneverDriverNameapproaches the<trigger>'s defined watch angle.
- Example: <helper> hlp_forearm_L bip_lowerArm_L bip_lowerArm_L bip_hand_L 
 
- <aimconstraint> HelperName HelperParent TargetAttachment
 Note:This marks the beginning of a new procedural bone, and ends the previous one, making it mutually exclusive with Note:This marks the beginning of a new procedural bone, and ends the previous one, making it mutually exclusive with- <helper>.
- HelperName- The name of the helper bone being driven
- HelperParent- The name of the helper bone's parent
- TargetAttachment- The name of the $attachment that- HelperNamewill always point towards- Causes HelperNameto always faceTargetAttachment. The angle it faces can be modified with<aimvector>and<upvector>.
- Example: <aimconstraint> hlp_piston_R hlp_lowerArm_R attach_upperArm_piston_R 
 
- Causes 
- <basepos> <float|X> <float|Y> <float|Z>
- 
- Used to declare the starting position of a procedural bone, relative to its parent.
- For <helper>, this is not necessarily required, as each<trigger>also includes a location. If specified anyway, all<trigger>locations will become relative to this one.
- For <aimconstraint>, this is basically a necessity, as there is no other way to specify where the bone should be.
- Example: <basepos> 15.003496 0.0043543 3.9911634 
 Tip:To avoid using external tools or scripts, you can find the correct coordinates by first exporting your model in its reference position as an SMD file, then load this file into any text editor of your choosing. Locate the desired bone in the Tip:To avoid using external tools or scripts, you can find the correct coordinates by first exporting your model in its reference position as an SMD file, then load this file into any text editor of your choosing. Locate the desired bone in the- nodeslist; to the left of the name will be its ID, used in the- skeletonsection of this file. Find- time 0(the first and ideally only frame of animation), and look for the bone ID on the left - the three numbers following this ID are the base pose of this bone relative to its parent.
 
- <trigger> AngleOfInfluence DriverRotation_XYZ HelperRotation_XYZ HelperLocation_XYZ
- AngleOfInfluence- A single number representing a cone of influence around this trigger (beyond the cone = 0%, center of the cone = 100%)
- DriverRotation_XYZ- A 3D vector representing the rotation of the driver bone (relative to its parent) that represents the center of this trigger's cone
- HelperRotation_XYZ, HelperLocation_XYZ- A pair of 3D vectors that respectively represent rotation & location of this helper bone as the driver approaches the center of this trigger's cone- Used by <helper>to declare a specific rotation being watched, and the resulting rotation & location of the helper when the watched bone approaches this rotation.
- There can be up to 32 <trigger>lines per<helper>, and all rotations are Euler rotations expressed in degrees.
- The angle of influence refers to how far the bone must be from this rotation for this <trigger>to have fully faded out; as it approachesDriverRotation_XYZ, the procedural bone will approachHelperRotation_XYZandHelperLocation_XYZ
- Example: <trigger> 60 -26.5650735797 -14.4773829758 -26.5652454670 -26.5650162839 -14.4773829758 -26.5651881712 0.000000000 0.000000000 0.000000000 
 Warning:The same trick above works here too, the three numbers after the bone ID and position are the rotation, but the rotations are in RADIANS - convert to degrees before using them in a Warning:The same trick above works here too, the three numbers after the bone ID and position are the rotation, but the rotations are in RADIANS - convert to degrees before using them in a- <trigger>
 
- Used by 
- <aimvector> <float|X> <float|Y> <float|Z>
- 
- Used by <aimconstraint>to declare the direction a<aimconstraint>will face as it tracks itsTargetAttachment.
- This vector is normalized, but does support any arbitrary orientation. Most commonly, bones will face a particular axis.
- Example: (causes the bone to face -Z, or Z always faces opposite the<aimvector> 0 0 -1 TargetAttachment)
 
- Used by 
- <upvector> <float|X> <float|Y> <float|Z>
- 
- Used to clarify which axis on the TargetAttachmentis considered "up".
- Example: (causes the bone to rotate with the attachment if it rotates along its local Y axis)<upvector> 0 1 0 
 
- Used to clarify which axis on the 
- <display><float|X> <float|Y> <float|Z> <float|distance>
- 
- Used by the Softimage Mod Tool to store the attributes for procedural bone's appearance in Mod Tool's viewport, but has no purpose outside of it.
- Example: <display> -12.64333 4.33348 0.63611 4.45 
 
- <rotateaxis> <float|X> <float|Y> <float|Z>
- 
- Used to specify a pre-multiplied angle for all <trigger>definitions.
- Example: <rotateaxis> 0.0004010705 -119.9998795417 89.9996375014 
 Note:This may have been meant to compliment Note:This may have been meant to compliment- <basepos>in setting a default angle, but no retail game ever uses it.
 
- Used to specify a pre-multiplied angle for all 
- <jointorient> <float|X> <float|Y> <float|Z>
- 
- Used to specify a post-multiplied angle for all <trigger>definitions.
- Example: <jointorient> 56.3101202181 -154.3411936127 16.1020048039 
 Note:This may have been meant to compliment Note:This may have been meant to compliment- <basepos>in setting a default angle, but no retail game ever uses it.
 
- Used to specify a post-multiplied angle for all 
Common applications
The most common use case of procedural bones is to automatically rotate forearms, upper arms, thighs, and shins of character models. In the diagram to the right, the mesh is weighted to both a twist bone placed in the middle of the forearm (ValveBiped.Bip01_L_Ulna), and a twist bone located on the wrist (ValveBiped.Bip01_L_Wrist). Rather than require the animator to pose the hand, wrist, and ulna all together (which would not animate in the ragdoll anyway), the ulna & wrist are given procedural bone definitions that allow them to automatically rotate with the hand.
Corrective bones
In areas where rotating weighted bones causes a noticeable loss in mesh volume, such as on the exteriors of elbows & knees, a procedural bone can be used to better maintain the volume. By adding a helper in the same position as the elbow/knee and weighting the vertices nearest the apex of the joint to it, the mesh can be made to deform in a way that doesn't noticeably shrink.
In  , the Engineer's character model uses a pair of procedural bones to keep his knee pads from deforming poorly when his knees bend, by watching the angle of the knee & rotating about half the distance to both keep the knee pads in the correct orientation and avoid visible shrinking.
, the Engineer's character model uses a pair of procedural bones to keep his knee pads from deforming poorly when his knees bend, by watching the angle of the knee & rotating about half the distance to both keep the knee pads in the correct orientation and avoid visible shrinking.
Corrective flexes
In some engine branches† that support $boneflexdriver, it is possible to use procedural bones to automatically fade in and fade out flexes‡. This technique lends itself most to applying local corrective or limited delta mush shapes to parts of a mesh that experience low-quality deformations, typically when put into extreme poses. Care must be taken when using this technique, since each corrective flex takes up a flexcontroller slot and requires a corresponding bone in the model's skeleton to drive it. This can quickly add up, counting against the MDL flexcontroller limit (usually 96 or 128) and bone limit (usually 128) and reducing the number of normal flexes and bones available to the model.
 Warning:‡ With additional requirements. Refer to section Driving Flexes with Procedural Bones at the end of this article.
Warning:‡ With additional requirements. Refer to section Driving Flexes with Procedural Bones at the end of this article.Self-collision approximation
While not common, there are situations where character props may noticeably intersect with a character due to being located on or near a joint; for example, the sleeves of a suit jacket. In this scenario, the wrists of the character may noticeably intersect with the jacket as they attempt to handle equipment. While it is possible to manually animate this wherever it comes up, doing so will not account for aim matrices applied to the character, nor will it apply to their ragdoll. Weighting the end of the sleeve to the hand would likely not look very good either, since parts of the sleeve that don't need to move at a given moment will still move to follow the hand. This can be corrected for with a few procedural bones placed around the wrist, all of which watch the rotation of the hand; the helpers could flex out of the way of the hand automatically, which would not only look better during a ragdoll, but would save the animator the effort of needing to account for this with every new animation added.
Mechanical armatures
It's desirable for a mechanical linkage to always point in the correct direction, for instance a piston that connects a lower armature to an upper armature. While you can manually animate this, it will neither work with a ragdoll nor during transitions of different animations. Doing this precisely with a series of triggers will usually introduce small blending artifacts as the engine attempts to lerp the piston to the correct orientation, which may look out of place in this sort of precision application. With an aim constraint, this piston could be guaranteed to always aim at the correct place on the upper armature from its position on the lower armature.
VRD file examples
HWM Femscout forearm twists
 Note:Source Filmmaker was used to set up this VRD file, but you can use any modeling software that supports Source models.
Note:Source Filmmaker was used to set up this VRD file, but you can use any modeling software that supports Source models.<helper> hlp_forearm_L   bip_lowerArm_L   bip_lowerArm_L   bip_hand_L
<basepos> -0.0003 -4.7578 -0.000685692
<trigger> 90 	-0.140879 -7.0729 0.923253 		2.05439e-06 -0.00011566 -0.0109454 	0 0 0
<trigger> 90 	-157.919 -89.6281 158.827 		0.101044 -61.3622 -0.505184 		0 0 0
<trigger> 90 	-0.905463 81.1157 0.0128013 	-1.79037e-05 49.6029 -0.0109647 	0 0 0
<helper> hlp_forearm_R   bip_lowerArm_R   bip_lowerArm_R   bip_hand_R
<basepos> -0.0502548 4.76207 -0.0109539
<trigger> 90 	-0.116485 -7.07782 0.923253 	-26.1455 -71.739 -60.6497 		0 0 0
<trigger> 90 	-0.288436 72.9621 0.610626 		-72.1773 -44.2586 -20.2743 		0 0 0
<trigger> 90 	-179.149 -81.8242 -179.905 		81.3212 -37.0352 -157.273 		0 0 0
This model with source files is available here on MaxOfS2D's website.
Crossbow string
<aimconstraint>	hlp_bow_L		hlp_bowflex_L	attach_string_L
<basepos>       15.070189000 0.000035000 -5.834238000
<aimvector>		-1 0 0 // Causes hlp_bow_L to face away from attach_string_L on the X axis
<upvector>		0 1 0
<aimconstraint>	hlp_bow_R		hlp_bowflex_R	attach_string_R
<basepos>       15.069914000 0.000015000 -5.834859000
<aimvector>		-1 0 0 // Causes hlp_bow_R to face away from attach_string_R on the X axis
<upvector>		0 1 0
Detailed right wrist created from an SMD animation
<helper>        hlp_wrist_R     bip_lowerArm_R  bip_lowerArm_R  bip_hand_R
<basepos>       8.643163000 -0.000000000 0.000021000
<trigger>       60      0.0001718873    150.0000642864  -90.0000958676  0.0001145916    150.0000642864  -90.0001531634  0.000000000     0.000000000     0.000000000      // time 0
<trigger>       60      0.0001718873    -149.9998923990 -90.0002677549  0.0001145916    -149.9998923990 -90.0003250507  0.000000000     0.000000000     0.000000000      // time 1
<trigger>       60      -52.0251407557  -89.9998666845  -38.2358227960  -48.7844914664  -89.9998666845  -38.7109193998  0.000000000     0.000000000     0.000000000      // time 2
<trigger>       60      -0.0001145916   -29.9999555615  -90.0001531634  -0.0001145916   -29.9999555615  -90.0002104591  0.000000000     0.000000000     0.000000000      // time 3
<trigger>       60      -0.0001145916   30.0001274488   -90.0002104591  -0.0001145916   30.0001274488   -90.0002677549  0.000000000     0.000000000     0.000000000      // time 4
<trigger>       60      153.4350608597  14.4776121589   26.5649016923   164.0022360669  26.1849160826   61.2785237386   -0.209301000    -0.381664000    0.031250000      // time 5
<trigger>       60      -153.4347743808 -14.4773829758  26.5648443966   -171.5899734436 -29.0682115950  68.3158205615   -0.189188000    -0.685485000    -0.609611000     // time 6
<trigger>       60      -89.9999239803  -29.9998982657  -0.0002291831   -90.0125290517  -70.4192059232  0.4019298933    -0.103786000    -0.028785000    -0.800817000     // time 7
<trigger>       60      -26.5650735797  -14.4773829758  -26.5652454670  -10.9441241406  -28.1016317947  -66.4090170193  -0.138767000    0.601700000     -0.401906000     // time 8
<trigger>       60      26.5651308755   14.4776121589   -26.5651881712  14.4604807209   26.7658952869   -68.4830096461  -0.374143000    0.995121000     0.171281000      // time 9
<trigger>       60      26.5651308755   165.5226941678  -26.5651881712  2.8720655397    150.2507333217  -86.7703900722  -0.108041000    0.564029000     -0.252821000     // time 10
<trigger>       60      -26.5651308755  -165.5223503931 -26.5652454670  3.3628038912    -149.6761139490 -92.8659161673  -0.211483000    0.771767000     0.567706000      // time 11
<trigger>       60      -90.0001531634  -149.9998923990 -0.0001145916   -86.2042504748  -94.3424857011  -7.0959104054   -0.224750000    0.172373000     0.564973000      // time 12
<trigger>       60      26.5649589881   -14.4775548631  -153.4351181555 3.6163950113    -29.7311237640  -95.1886297730  -0.084996000    -0.475180000    0.216209000      // time 13
<trigger>       60      -26.5651881712  14.4774402716   -153.4351181555 -5.6986955262   28.7843110076   -111.9647767186 -0.068011000    0.295446000     -0.111653000     // time 14
<trigger>       60      -119.9998795417 29.9999555615   89.9999239803   16.3264896680   144.2979310134  -91.0288988845  0.029360000     -0.468964000    -0.863318000     // time 15
<trigger>       60      -119.9998222460 -30.0000701530  89.9996947971   -158.5945458049 -15.7165760951  87.5142038819   -0.044148000    0.546487000     -0.608450000     // time 16
<trigger>       60      7.7956064648    -89.9998666845  -38.1285650968  30.3528975633   -87.9546938348  -100.1075870357 -0.292867000    0.814450000     -0.029658000     // time 17
<trigger>       60      59.9998538272   -29.9998982657  -90.0001531634  19.9532552154   -29.4507755149  -90.1107913136  -0.195600000    0.410349000     0.696194000      // time 18
<trigger>       60      59.9997965314   30.0000701530   -90.0002104591  19.3298771343   19.5167759671   -88.2561842266  -0.296514000    -0.591582000    0.830669000      // time 19
<trigger>       60      120.0001660206  29.9999555615   89.9999239803   156.9124563099  22.1676797978   88.3612073904   -0.348013000    0.475677000     0.507207000      // time 20
<trigger>       60      120.0002233164  -30.0000701530  89.9996947971   -18.0940644660  -137.9124309783 -92.6856636449  -0.288719000    -0.342254000    0.879168000      // time 21
<trigger>       60      -112.4273255466 -89.9998666845  -38.0887445300  -33.8569355510  -95.9927187426  -78.7788893373  -0.413648000    -0.655860000    -0.128256000     // time 22
<trigger>       60      -60.0000257145  -29.9999555615  -90.0001531634  -21.5571359713  -30.0129617034  -90.0030179523  -0.272500000    -0.344648000    -0.597419000     // time 23
<trigger>       60      -60.0000830103  30.0001274488   -90.0002104591  -19.7873839337  32.3326959286   -89.5132536291  -0.207348000    0.373764000     -0.711022000     // time 24
Miscellaneous
For symmetrical models (such as humans) it's very common for left/right bones to have completely different local rotations & locations. As such, copying & pasting the procedural bone data from one side to the other will typically not work.
A procedural bone cannot be controlled by more than one driver bone, but one driver bone can be used to control many procedural bones.
While parsing a <trigger>, all characters after the 10th space-delineated number are ignored. This technically means that comments placed at the end of this line do not actually need to be prefixed with //, though doing so would be visually inconsistent with every other kind of comment in the file & likely break syntax highlighters, so it would be best practice to begin the comment normally regardless.
// Not sure why you'd want to do it this way, as it will probably break syntax highlighters, but it technically does work.
<trigger> 90  -6.75 1.59 3.18  0 0 0  0 0 0   this text is ignored for some reason, so it doesn't even need to begin with two forward slashes
Driving Flexes with Procedural Bones
Using $proceduralbones to automatically drive a $boneflexdriver allows for advanced deformation techniques, such as automatic corrective shapes, but this functionality is not officially supported. As such, retail releases of StudioMDL refuse to compile models that use $boneflexdriver on any procedural bone. To fix this, StudioMDL may be directly patched using a hex editor, thus allowing it to skip the early exit and successfully compile the model.
 Warning:Modified instances of StudioMDL often produce models that deviate from the format expected by the engine - this may cause issues on specific games or engine branches.
Warning:Modified instances of StudioMDL often produce models that deviate from the format expected by the engine - this may cause issues on specific games or engine branches.Engine-specific patches
 Source 2013 branch Source 2013 branch
- Includes most games where the model format's header begins with IDST0, including Half-Life 2, Half-Life 2, Counter-Strike: Source, Counter-Strike: Source, Portal, Portal, Team Fortress 2, and Team Fortress 2, and Garry's Mod. Garry's Mod.
- Search for the unique byte sequence 04 74 25 8D 4F, then change the byte74toEB.
- Search for the unique byte sequence 04 74 25 8B, then change the byte74toEB.
- Search for the unique byte sequence 04 74 1C 8B 56 5C, then change the byte74toEB.
- Search for the unique byte sequence 04 74 28 8B, then change the byte74toEB.
Important QC modification
In your model's QC file, you must specify $bonemerge on every bone that you make procedural. Refer to your VRD file, and add $bonemerge line(s) to your QC for the ProceduralBone of every <helper> line. The DriverBones do not need $bonemerge.
The reasoning behind the requirement of the seemingly unrelated $bonemerge flag is not yet fully understood.
Confirmation
Once the model has been compiled, you can test it in HLMV to verify that everything works as intended. The skeletal animation should now be able to drive the desired procedural bone(s), which in turn drive the flexcontroller(s).

































