Source SDK

Source SDK

80 Bewertungen
$Proceduralbones [SFM - Facepunch archived]
Von мяFunreal
This is the Procedual Bone tutorial by Revzin, from the Facepunch studios. Archived here because Facepunch studio forums closed.
   
Preis verleihen
Favorisieren
Favorisiert
Entfernen
Better alternative
If you use blender, follow this guide instead! No need for SFM anymore.
https://steamcommunity.com/sharedfiles/filedetails/?id=2415253996
What is a procedural bone?
It's a special kind of bone that is animated by the 3D application's coding (this includes DCC apps and game engines). Hair, cloth, ragdolls, jigglebones, biceps or even full body movement like one that GTA IV's Naturalmotion Euphoria devilers -- all these things aren't the hard work of The Animator, they're being calculated by the framework that The Animator uses: 3DS Max's CAT, Euphoria, Bullet physics etc. "Procedural" means "calculated by a terrible lifeless machine like your PC".

I'm going to clarify that 'bones' = 'joints', 'skeleton' = 'armature', 'binded' = 'skinned' = 'weigthed', etc. Every 3D app seems to have its own stupid ideas on how to name the same concept.

Source Engine gives us different kinds of procedurals: ragdoll animation, $ikchain, $jigglebone (a rather dumb system but useful sometimes), and the $proceduralbones. We are going to talk about the latter.

Let's start with an example: you, The Animator, always want the model's wrists/forearms to twist in a predefined way when the hand is begin rotated along the arm axis. Of course, you can animate the twist by hand. But why wouldn't let a computer do it since creative thinking is not involved? Source can do that!

Demo of Procedural bones in action in Valve's Source Engine

The animation is nothing more than the hand bone being rotated. In the first part procedural bones are off which results in nasty twisting of the wrist area. In the second part, procedural bones are on and the engine interpolates the rotation of the wrist based on the position of the forearm and hand bones.
VRD files overview
Source uses special VRD files to set up such procedural bones that are included in the QC file. The Almighty Studiomdl then reads the VRD and stores the data in the MDL file, and the engine picks it up. Of course, the model needs to have these procedural bones, and the mesh must be skinned. Then Source will evaluate the helper's position and rotation based on the specified bones orientation.

In this tutorial, we're going to enable procedural bones on everyone's beloved Femscout. She has her forearms skinned to hlp_forearm bones, and both the hlp_forearm and bip_hand are immediate children of bip_lowerArm (that's a requirement! also, I love this model! that's also a requierment!). You'd need to setup your model's skeleton the same way if you want these procedural things to work.




Donwload the Femscout's source files (tf_femscout_enhanced_src.7z) from MaxOfS2D's website[source.maxofs2d.net]. Open the _femscout_high.qc file, and change the $modelname to something so that our expirements won't mess up the original model.
$modelname "player/hwm/femscout_with_helpers.mdl"

Now navigate to line 48 in the QC file.
... // $include wings.qci // SILLY // $proceduralbones femscout.vrd // BROKEN ...

We're going to 'UNBRAKE' the femscout.vrd file.
Open that femscout.vrd file. What you're going to see won't amuse you at first...
<helper> hlp_forearm_L bip_lowerArm_L bip_lowerArm_L bip_hand_L <basepos> -9.99201e-016 -4.75779 -1.77636e-015 <trigger> 90 -1.09332e-015 3.97569e-016 1.59028e-015 0 0 0 0 0 0 <trigger> 90 0 89 0 0 65 0 0 0 0 <trigger> 90 0 -89 0 0 -65 0 0 0 0 <helper> hlp_forearm_R bip_lowerArm_R bip_lowerArm_R bip_hand_R <basepos> -1.02912e-005 4.75777 -9.37782e-006 <trigger> 90 -1.98785e-016 0 0 0 0 0 0 0 0 <trigger> 90 0 89 0 0 65 0 0 0 0 <trigger> 90 0 -89 0 0 -65 0 0 0 0

That looks like a big WTFy mess of random numbers, but it's actually a simple keyframed animation, 'trigger" being 'keyframe'. Let's take a close look at every line:
<helper> hlp_forearm_L bip_lowerArm_L bip_lowerArm_L bip_hand_L
This line tells Source that
1) we want a procedural bone hlp_forearm_l
2) it's parented to bip_lowerArm_L
3) the coordinate origin is bip_lowerArm_L, too
4) it's going to be controlled by bip_hand_L's rotation

<basepos> -9.99201e-016 -4.75779 -1.77636e-015
That is a 3D translation vector that tells Source the 'rest' translation of the helper bone (hlp_forearm) in the parent bone (bip_lowerArm_L)'s coordinate system is (-9.99201e-016, -4.75779, -1.77636e-015) along X, Y, Z respectively.

<trigger> is essentialy a keyframe! It will make more sense if we brake down the line.
<trigger> 90 -1.09332e-015 3.97569e-016 1.59028e-015 0 0 0 0 0 0

The 1st number "90" is the Angle of influence (AoI)
The 2nd, 3rd and 4th numbers "-1.09332e-015 3.97569e-016 1.59028e-015" is the specified control bone rotation (RotC)
The 5th 6th and 7th numbers "0 0 0" are desired helper rot (axis angles) (RotH)
The 8th 9th and 10th numbers "0 0 0" are desired helper translation from basepos (TransH)

What we're telling the engine is that when control bone rotaton (bip_hand) starts to aprroach RotC (is 'within' AoI) we want the helper to start approaching RotH rotation and TransH translation. The engine will then watch the control's rotation (RotC) and interpolate (lerp if this tells you anything) the helper's transform towards RotH and TransH.

How to visualize the agnle of influence: draw an angle of AoI degrees, bisect it and rotate it around the bisect 180 degrees. You just got a cone! Everything that's inside the cone is within the AoI.

The first trigger/keyframe is the rest position, the model's default state (it approches the default state when you drag the Defaukt slider in SFM). We don't want the helper to be rotated ort translated if everything is OK.

The second is the 'extreme' for the hand twisted left, and the third is the 'extreme' for the right twist. If the hand is twisted right, twist the helper right, and vice versa. Don't move the helper.

Let's take one final look at the left forearm helper definition with comments:
// Declare a procedural bone for left forearm // Bone name Parent bone name Coordinate system origin Control bone <helper> hlp_forearm_L bip_lowerArm_L bip_lowerArm_L bip_hand_L // Declare base translation of the helper (in parent's coordinate system) // X Y Z <basepos> -9.99201e-016 -4.75779 -1.77636e-015 // AoI = Angle of influence // RotC = Specified control bone rotation, // RotH = Desired helper rotation when control bone rotation approaches RotC // TransH = Desired helper translation when control bone rotation approaches RotC // All angles are in XYZ degrees // AoI RotC RotH TransH // 'Rest' position <trigger> 90 -1.09332e-015 3.97569e-016 1.59028e-015 0 0 0 0 0 0 // Left twist extreme <trigger> 90 0 89 0 0 65 0 0 0 0 // Right twist extreme <trigger> 90 -89 0 0 -65 0 0 0 0 0

Hopefully, this explains how this system works.

Exercise: is
<helper> hlp_forearm_R bip_lowerArm_R bip_lowerArm_R bip_hand_R <basepos> -1.02912e-005 4.75777 -9.37782e-006 <trigger> 90 -1.98785e-016 0 0 0 0 0 0 0 0 <trigger> 90 0 89 0 0 65 0 0 0 0 <trigger> 90 0 -89 0 0 -65 0 0 0 0
and this
<helper> hlp_forearm_R bip_lowerArm_R bip_lowerArm_R bip_hand_R <basepos> 0 0 0 <trigger> 90 -1.98785e-016 0 0 0 0 0 -1.02912e-005 4.75777 -9.37782e-006 <trigger> 90 0 89 0 0 65 0 -1.02912e-005 4.75777 -9.37782e-006 <trigger> 90 0 -89 0 0 -65 0 -1.02912e-005 4.75777 -9.37782e-006
the same thing?

Ok, now why this VRD doesn't work, or, as the French people say (in a very loud voice), BROKEN? You've probably guessed already: these numbers aren't for the Femscout's skeleton. (To be precise, femscout.vrd looks like a copypaste of scout.vrd from sourcesdk_content, and their skeletons aren't the same).

Now let's fix these numbers!
How to do it for your model
NOTE: that probably won't interst you if you animate outside the SFM and your system (e. g. a CAT rig) takes care of the procedural bones!

Let's launch the SFM, load a map and spawn a regular HWM Femscout. First of all, we need to read the default position position and rotation of the helper bone. It's exactly waht it says on the tin: ensure the Femscout is in the default pose, select the helper (I've started with the right arm). Don't ever select bip_lowerArm during this tutorial! Double-click here:


You'll see the transform translation values for the helper:


My Femscout reads basepos = -0.0502548, 4.76207, -0.0109539. (Keep in mind your values might be somewhat different (up to 10^(-1)) due to SFM being imprecise).

Now Alt-TAB to the femscout.vrd file (or move your mouse over Notepad++ opened on the second screen if you're a doublescreen Notepad++ badass like me) and copy-paste these values to the corresponding right arm basepos:
<helper> hlp_forearm_R bip_lowerArm_R bip_lowerArm_R bip_hand_R <basepos> -0.0502548 4.76207 -0.0109539

TIP: be in the Motion Editor, and press ESC after you press Ctrl-C, to avoid accidently modifying the transforms.

Now it's the time to create the 'rest' keyframe, since we're already in the rest state.
Select the control bone (bip_hand_R) and write down its rotation values, our RotC. (SFM convinently converts internal quaternions to axis angles as you will see if you double-click 'bip_hand_R - rot'!)
My Femscout reads RotC = -0.116485, -7.10374, 0.923253.


Select the helper and write down its rotation values, our RotH.
My Femscout reads RotH = -26.1455, -71.7396, -60.6497.
The helper isn't translated from the basepos, so, obviously, TransH = 0, 0, 0

We have all values we need to create a rest keyframe:
// AoI RotC RotH TransH // 'Rest' position <trigger> 90 -0.116485 -7.07782 0.923253 -26.1455 -71.739 -60.6497 0 0 0

Now to the left extreme twist. Twist Femscout's bip_hand_R to the right for approx. 90 degrees (don't be too precise, it won't matter). Observe a nasty wrist.


Now rotate the helper (in any needed axis) until the wirst looks good enough:


That's our left extreme position! In the same manner, read the needed values (obvioulsy, your values will be up to 10-15 deg different):
RotC (control bone rot) = -0.288436, 72.9621, 0.610626;
RotH (desired helper rot) = -72.1773, -44.2586, -20.2743;
I didn't move the helper around, so TransH = 0, 0, 0.

Create the keyframe for the left extreme:
// AoI RotC RotH TransH // 'Left extreme' position <trigger> 90 -0.288436 72.9621 0.610626 -72.1773 -44.2586 -20.2743 0 0 0

Now, the right extreme position. Select both the helper and the hand and drag Default to the right. From here, twist the hand 90 deg to the right. Observe a terrible wrist.


As before, fix it by rotating the helper:


For the right twist,
RotC = -179.149, -81.8242, -179.905;
RotH = 81.3212, -37.0352, -157.273;
TransH, as usual, = 0, 0, 0.

Create the final keyframe:
// AoI RotC RotH TransH // 'right extreme' position <trigger> 90 -179.149 -81.8242 -179.905 81.3212 -37.0352 -157.273 0 0 0

And we're done with the right hand! It should look like this now.
// Right forearm helper <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

Repeating the above process for the left hand (that's needed, cause the bone axis usually change orientation in the left and right sides -- and despite this text being very long, the time needed to actually do this stuff is under one minute), we get
// Left forearm helper <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

Uncomment the line #49 in the QC file:
... // $include wings.qci // SILLY $proceduralbones femscout.vrd // BROKEN ...

DONT remove the 'BROKEN' comment. Yet.

Compile the model, spawn it and observe the forearms twisting themselves as you twist the hands (only now shall you remove the famous French saying " // BROKEN" from the QC). Or observe everything being horribly broken and then redo from scratch and pay more attention. The #1 cause of stuff braking is mistaking pos and rot, #2 -- being distracted and accidently not copying a minus from SFM.

This is what you'll get:


The left model is without procedural bones (I didn't hand-edit the helper bones), the right model is with them. The arms are in the same pose. An IK rig was used for posing.

Here's my final VRD file: http://pastebin.com/ZM66ZjM0

Try experimenting with the AoI value.

Thanks to smellyfeetyouhave/Narry Gewman for directing me to look in SSDK 2013 base. The docs were, in a typical Valve fashion, kinda shady, not exactly on the topic and greatly outdated, but a great help nonetheless!

Nota bene: there might be an alternative way to do the same thing via Python IK scripts.
3 Kommentare
мяFunreal  [Autor] 28. Juni 2023 um 11:28 
well this is already outdated. it's way faster and easier to do it via blender.
EmperorFaiz.wav 28. Juni 2023 um 11:21 
This guide is a blessing. No more animating/posing those pesky procedural bones instead of focusing only on posing your model as usual.
(ghoul)RealBasedSociety 19. Mai 2022 um 11:58 
Nice tutorial and explanations.