Vehicles (modeling): Difference between revisions

From Valve Developer Community
Jump to navigation Jump to search
(Undo revision 208722 by G3ST3RR (talk))
(major cleanup/gauge animation info)
Line 3: Line 3:
}}
}}
[[Category:Modeling]]
[[Category:Modeling]]
This page describes how to set up a model for use as a functional vehicle, with basic animations, and optionally, gauge animations.
=SMD prerequisites=
==Reference model==
Your [[Reference model|reference model]] at minimum needs the following bones in order to work:
* suspension front left -> steering front left -> wheel front left
* suspension front right -> steering front right -> wheel front right
* suspension rear left -> wheel rear left
* suspension rear right -> wheel rear right
* view, located where the driver's view should be
Optionally:
* steering wheel
* gauge needle(s)
All of which should be children of a static root bone, and should follow the above hierarchy. You may name them anything, as long as you edit the following code examples accordingly.
Your respective meshes need to be rigged to their respective bones.
==Collision model==
==Collision model==


All vehicles need a physics collision model. Your [[Qc|.QC]] needs a section like this (see [[Physics and Ragdolls]] for information on how to build the SMD for the collision model).
All vehicles need physical models, so you need to set up a collision model. See [[$collisionmodel]].
 
To get started, you can simply use a copy of the reference model of your vehicle, but make sure to remove the wheels from the model, as wheel collisions aren't defined in the collision model itself, but at runtime, based on the entire compiled model's wheel attachments. Note that doing this will give your model a convex collision model.
 
If you have the time for it, modelling a low-poly version of the vehicle specifically for use as a collision model can be useful, both for optimization, and because it can be concave. See [[Collision mesh]].
 
==Basic animations==
 
Vehicles require a few basic animations in order to work properly.
 
* [[Studiomdl Data#Types|An animation SMD]] with only the suspension bones animated. This may be 2 frames (fully compressed, fully extended). This sequence determines the suspension travel each wheel has.
* An animation SMD with only the wheel bones individually spinning one full revolution. This may be 9 frames, and may need a linear animation curve, defined before the animation is exported in the model editor you use.
* An animation SMD with the steering of the wheels, with only the steering bones (and the steering wheel bone if it exists) and not the wheel bones animated. This may be just 3 frames (left, centered, right). Be sure to note the exact angle that the wheels turn to and make it consistent. You’ll need to put that angle into the vehicle’s script file so that the physics will match the animation.
 
Note that with just 3 frames, if you animate a steering wheel, it can't turn further than 90 degrees one way without breaking the centered position, which might be unsuitable for some models. See [[#Making the steering wheel turn further than 180 degrees|Making the steering wheel turn further than 180 degrees]].


<pre>
=QC setup=
$collisionmodel "digger_physbox.smd"
{
    // Mass in kilograms
    $Mass 1600
}
</pre>


To get started, you can simply use a copy of render model of the vehicle but make sure to remove the wheels. The actual collision model will be the convex hull of the render model in that case. You can visualize it by placing your vehicle in a level and using the command <code>vcollide_wireframe 1</code> in the console.
==Attachments==


==Wheel Collisions==
===Wheels===
You need to set up [[$attachment|attachments]] for all four wheels, to define where they are located in the model.


Next, we’ll set up the wheels. For best results, you’ll want to place attachment points in the [[Qc|.QC]] file for each wheel.
$attachment "wheel_fl" "<wheel front left bone>" 0 0 8
$attachment "wheel_fr" "<wheel front right bone>" 0 0 -8
$attachment "wheel_rl" "<wheel rear left bone>" 0 0 8
$attachment "wheel_rr" "<wheel rear right bone>" 0 0 –8


$attachment wheel_fl "Dummy04" 0 0 8
You '''must''' name the attachments as above. You may however name their respective bones anything, and you may place the $attachment lines anywhere in your .QC.
$attachment wheel_fr "Dummy03" 0 0 -8
$attachment wheel_rl "Dummy01" 0 0 8
$attachment wheel_rr "Dummy02" 0 0 –8


There are four wheels supported at this time. They are named with two letter codes:
Only four wheels may be simulated in one vehicle. Their attachments are named with two letter codes:
*<code>fl</code> "front left"
*<code>fl</code> "front left"
*<code>fr</code> "front right"
*<code>fr</code> "front right"
Line 32: Line 65:
*<code>rr</code> "rear right"
*<code>rr</code> "rear right"


You '''must''' name the attachments as above. It does not matter what order you add them to the [[Qc|.QC]] file however.
One important detail is that the wheels are simulated as spheres. For this reason, you may want to pull the attachments slightly inwards. The origin of the wheel bones is actually the center of the wheel graphically, but in order to avoid having the spherical wheel collisions poke out from the sides of the vehicle (thus brushing against things next to it), you might want to pull the attachments in by a bit.
 
For example, this would place the wheel sphere exactly over its graphical representation:
 
$attachment "wheel_fl" "<wheel front left bone>" 0 0 0
 
While this adjusts it inwards by 8 inches:
 
$attachment "wheel_fl" "<wheel front left bone>" 0 0 8
 
The amount to pull them in depends on the size of the wheels and the collision model of the vehicle. In this case, the wheels have a radius of about 22.5 inches and I’ve pulled them in by 8 inches. Using this ratio is a good starting point.


One important detail is that the wheels are modeled as spheres. For this reason, the wheels are pulled under the vehicle a bit. The origin of the wheel bones is actually the center of the wheel graphically, but in order to avoid having the spherical wheels poke out from under the car (thus brushing against things next to the car), each attachment is pulled in a bit.
===Driver's view===


For example, this would place the wheel sphere exactly over its graphical representation:
This line defines where your view is going to be when driving.


  $attachment wheel_fl "Dummy04" 0 0 0
  $attachment "vehicle_driver_eyes" "<view bone>" 0 0 0


This is adjusted in by 8 inches.
As with the wheel attachments, this attachment '''must''' be named <code>vehicle_driver_eyes</code>.


$attachment wheel_fl "Dummy04" 0 0 8
===Miscellaneous attachments===


The amount to pull them in depends on the size of the wheels and the collision model of the car. In this case, the wheels have a radius of about 22.5 inches and I’ve pulled them in by 8 inches. Using this ratio is a good starting point. You can visualize the wheels using "ent_bbox prop_vehicle" in the console if you build a map with your vehicle as a [[prop_vehicle]].
These are some miscellaneous attachments that might be required, with approximate positions that you might have to tweak.


==Vehicle Animations==
$attachment "vehicle_feet_passenger0" "<view bone>" 0 0.00 -27.00
[[File:Car_skeleton.PNG|thumb|right|400px|'''Your vehicle skeleton might look something like this.''' - Click to enlarge.]]
$attachment "vehicle_engine" "<root bone>" 40.00 0.00 17.00


The vehicle requires a few animations.
The <code>vehicle_feet_passenger0</code> attachment determines where the driver's third person player model is located. It doesn't need to be based on the view bone - you can instead set up an individual bone for it.


# A sequence with the steering of the wheels. This can be only 3 frames (left, center, right) if you’d like. Be sure to note the exact angle that the wheels turn and make it consistent. You’ll need to put that angle into the vehicle’s script file so that the physics will match the animation.
==Pose parameters==
# A sequence with the suspension animated. This can be only 2 frames (fully compressed, fully extended). This sequence determines the amount of travel each wheel has. So if you want to have a car be angled/jacked up, you’ll need to make the back wheels extend further than the front wheels.
# A sequence with all of the wheels spinning one revolution.


Because steering, suspension and wheel rotation are all animation you can map them to anything. You can animated flexing dune-buggy suspensions or counter rotating wheel pieces, etc. These won’t be simulated by the physics system, but they will appear to be simulated because they will be precisely coordinated with the simulation. You’ll need the following pose parameters to map the simulation onto the animation.
You need the following [[$poseparameter]] lines to allow the physics to interact with your model.


This controls the steering. –1 is left, +1 is right.
This line is for the steering. –1 is left, +1 is right.


  $poseparameter "vehicle_steer" -1 1
  $poseparameter "vehicle_steer" -1 1


These control the height of each wheel (flex the suspension). 0 is compressed, 1 is extended.
These lines are for the suspension states of each wheel. 0 is compressed, 1 is extended.


  $poseparameter "vehicle_wheel_fl_height" 0 1
  $poseparameter "vehicle_wheel_fl_height" 0 1
Line 68: Line 109:
  $poseparameter "vehicle_wheel_rr_height" 0 1
  $poseparameter "vehicle_wheel_rr_height" 0 1


These control the rotation of each wheel. +/- 180 degrees from neutral/base rotation.
These lines are for the rotation of each wheel. +/- 180 degrees from base rotation.


  $poseparameter "vehicle_wheel_fl_spin" -180 180 wrap
  $poseparameter "vehicle_wheel_fl_spin" -180 180 wrap
Line 75: Line 116:
  $poseparameter "vehicle_wheel_rr_spin" -180 180 wrap
  $poseparameter "vehicle_wheel_rr_spin" -180 180 wrap


Here’s an example [[Qc|.QC]] file that sets up the pose parameters.
All of the above should not be renamed.
 
==Animations==
 
Here’s an example .QC snippet that sets up animations for the pose parameters, including a steering wheel in the steering animation.


<pre>
<pre>
$animation neutral "digger_idle" frames 0 0
$animation neutral "<reference model SMD>" frames 0 0


$weightlist front_wheels { "Dummy03" 1.0 "Dummy04" 1.0 }
// steering
$animation turn_left "digger_turn" frame 0 0 subtract neutral 0 weightlist front_wheels
$weightlist front_wheels { "<steering front left bone>" 1.0 "<steering front right bone>" 1.0 "<steering wheel bone>" 1.0 }
$animation turn_right "digger_turn" frame 2 2 subtract neutral 0 weightlist front_wheels
$animation turn_left "<steering animation SMD>" frame 0 0 subtract neutral 0 weightlist front_wheels
$animation turn_right "<steering animation SMD>" frame 2 2 subtract neutral 0 weightlist front_wheels
$sequence turning { turn_left turn_right blend vehicle_steer -1 1 } weightlist front_wheels delta autoplay
$sequence turning { turn_left turn_right blend vehicle_steer -1 1 } weightlist front_wheels delta autoplay
// front left
$weightlist wheel_fl { "<steering front left bone>" 1.0  "<suspension front left bone>" 1.0 "<wheel front left bone>" 0.0 }
$animation wheel_fl_low "<suspension animation SMD>" frame 0 0 subtract neutral 0 weightlist wheel_fl
$animation wheel_fl_high "<suspension animation SMD>" frame 2 2 subtract neutral 0 weightlist wheel_fl
$sequence wheel_fl_suspension { wheel_fl_low wheel_fl_high blend "vehicle_wheel_fl_height" 0 1.0 } weightlist wheel_fl delta autoplay
$weightlist wheel_fl_spin { "<wheel front left bone>" 1.0 }
$animation wheel_fl_spin0 "<wheelspin animation SMD>" frame 0 0 subtract neutral 0 weightlist wheel_fl_spin
$animation wheel_fl_spin120 "<wheelspin animation SMD>" frame 3 3 subtract neutral 0 weightlist wheel_fl_spin
$animation wheel_fl_spin240 "<wheelspin animation SMD>" frame 6 6 subtract neutral 0 weightlist wheel_fl_spin
$sequence wheel_fl_spin { wheel_fl_spin0 wheel_fl_spin120 wheel_fl_spin240 wheel_fl_spin0 blendwidth 4 blend "vehicle_wheel_fl_spin" -180 180 } weightlist wheel_fl_spin delta autoplay


// front right
// front right
$weightlist wheel_fr { "Dummy03" 1.0 }
$weightlist wheel_fr { "<steering front right bone>" 1.0  "<suspension front right bone>" 1.0 "<wheel front right bone>" 0.0 }
$animation wheel_fr_low "digger_suspension" frame 0 0 subtract neutral 0 weightlist wheel_fr
$animation wheel_fr_low "<suspension animation SMD>" frame 0 0 subtract neutral 0 weightlist wheel_fr  
$animation wheel_fr_high "digger_suspension" frame 2 2 subtract neutral 0 weightlist wheel_fr
$animation wheel_fr_high "<suspension animation SMD>" frame 2 2 subtract neutral 0 weightlist wheel_fr  
$sequence wheel_fr_suspension { wheel_fr_low wheel_fr_high blend "vehicle_wheel_fr_height" 0 1.0 } weightlist wheel_fr delta autoplay
$sequence wheel_fr_suspension { wheel_fr_low wheel_fr_high blend "vehicle_wheel_fr_height" 0 1.0 } weightlist wheel_fr delta autoplay


$animation wheel_fr_spin0 "digger_wheelspin" frame 0 0 subtract neutral 0 weightlist wheel_fr
$weightlist wheel_fr_spin { "<wheel front right bone>" 1.0 }
$animation wheel_fr_spin120 "digger_wheelspin" frame 3 3 subtract neutral 0 weightlist wheel_fr
$animation wheel_fr_spin0 "<wheelspin animation SMD>" frame 0 0 subtract neutral 0 weightlist wheel_fr_spin
$animation wheel_fr_spin240 "digger_wheelspin" frame 6 6 subtract neutral 0 weightlist wheel_fr
$animation wheel_fr_spin120 "<wheelspin animation SMD>" frame 3 3 subtract neutral 0 weightlist wheel_fr_spin
$sequence wheel_fr_spin { wheel_fr_spin0 wheel_fr_spin120 wheel_fr_spin240 wheel_fr_spin0 blendwidth 4 blend "vehicle_wheel_fr_spin" -180 180 } weightlist wheel_fr delta autoplay
$animation wheel_fr_spin240 "<wheelspin animation SMD>" frame 6 6 subtract neutral 0 weightlist wheel_fr_spin
$sequence wheel_fr_spin { wheel_fr_spin0 wheel_fr_spin120 wheel_fr_spin240 wheel_fr_spin0 blendwidth 4 blend "vehicle_wheel_fr_spin" -180 180 } weightlist wheel_fr_spin delta autoplay
 
 
// rear left
$weightlist wheel_rl { "<suspension rear left bone>" 1.0 "<wheel rear left bone>" 0.0 }
$animation wheel_rl_low "<suspension animation SMD>" frame 0 0 subtract neutral 0 weightlist wheel_rl
$animation wheel_rl_high "<suspension animation SMD>" frame 2 2 subtract neutral 0 weightlist wheel_rl
$sequence wheel_rl_suspension { wheel_rl_low wheel_rl_high blend "vehicle_wheel_rl_height" 0 1.0 } weightlist wheel_rl delta autoplay
 
$weightlist wheel_rl_spin { "<wheel rear left bone>" 1.0 }
$animation wheel_rl_spin0 "<wheelspin animation SMD>" frame 0 0 subtract neutral 0 weightlist wheel_rl_spin
$animation wheel_rl_spin120 "<wheelspin animation SMD>" frame 3 3 subtract neutral 0 weightlist wheel_rl_spin
$animation wheel_rl_spin240 "<wheelspin animation SMD>" frame 6 6 subtract neutral 0 weightlist wheel_rl_spin
$sequence wheel_rl_spin { wheel_rl_spin0 wheel_rl_spin120 wheel_rl_spin240 wheel_rl_spin0 blendwidth 4 blend "vehicle_wheel_rl_spin" -180 180 } weightlist wheel_rl_spin delta autoplay


// front left
$weightlist wheel_fl { "Dummy04" 1.0 }
$animation wheel_fl_low "digger_suspension" frame 0 0 subtract neutral 0 weightlist wheel_fl
$animation wheel_fl_high "digger_suspension" frame 2 2 subtract neutral 0 weightlist wheel_fl
$sequence wheel_fl_suspension { wheel_fl_low wheel_fl_high blend "vehicle_wheel_fl_height" 0 1.0 } weightlist wheel_fl delta autoplay
$animation wheel_fl_spin0 "digger_wheelspin" frame 0 0 subtract neutral 0 weightlist wheel_fl
$animation wheel_fl_spin120 "digger_wheelspin" frame 3 3 subtract neutral 0 weightlist wheel_fl
$animation wheel_fl_spin240 "digger_wheelspin" frame 6 6 subtract neutral 0 weightlist wheel_fl
$sequence wheel_fl_spin { wheel_fl_spin0 wheel_fl_spin120 wheel_fl_spin240 wheel_fl_spin0 blendwidth 4 blend "vehicle_wheel_fl_spin" -180 180 } weightlist wheel_fl delta autoplay


// rear right
// rear right
$weightlist wheel_rr { "Dummy02" 1.0 }
$weightlist wheel_rr { "<suspension rear right bone>" 1.0 "<wheel rear right bone>" 0.0 }
$animation wheel_rr_low "digger_suspension" frame 0 0 subtract neutral 0 weightlist wheel_rr
$animation wheel_rr_low "<suspension animation SMD>" frame 0 0 subtract neutral 0 weightlist wheel_rr  
$animation wheel_rr_high "digger_suspension" frame 2 2 subtract neutral 0 weightlist wheel_rr
$animation wheel_rr_high "<suspension animation SMD>" frame 2 2 subtract neutral 0 weightlist wheel_rr  
$sequence wheel_rr_suspension { wheel_rr_low wheel_rr_high blend "vehicle_wheel_rr_height" 0 1.0 } weightlist wheel_rr delta autoplay
$sequence wheel_rr_suspension { wheel_rr_low wheel_rr_high blend "vehicle_wheel_rr_height" 0 1.0 } weightlist wheel_rr delta autoplay
$animation wheel_rr_spin0 "digger_wheelspin" frame 0 0 subtract neutral 0 weightlist wheel_rr
$animation wheel_rr_spin120 "digger_wheelspin" frame 3 3 subtract neutral 0 weightlist wheel_rr
$animation wheel_rr_spin240 "digger_wheelspin" frame 6 6 subtract neutral 0 weightlist wheel_rr
$sequence wheel_rr_spin { wheel_rr_spin0 wheel_rr_spin120 wheel_rr_spin240 wheel_rr_spin0 blendwidth 4 blend "vehicle_wheel_rr_spin" -180 180 } weightlist wheel_rr delta autoplay


// rear left
$weightlist wheel_rr_spin { "<wheel rear right bone>" 1.0 }  
$weightlist wheel_rl { "Dummy01" 1.0 }
$animation wheel_rr_spin0 "<wheelspin animation SMD>" frame 0 0 subtract neutral 0 weightlist wheel_rr_spin
$animation wheel_rl_low "digger_suspension" frame 0 0 subtract neutral 0 weightlist wheel_rl
$animation wheel_rr_spin120 "<wheelspin animation SMD>" frame 3 3 subtract neutral 0 weightlist wheel_rr_spin
$animation wheel_rl_high "digger_suspension" frame 2 2 subtract neutral 0 weightlist wheel_rl
$animation wheel_rr_spin240 "<wheelspin animation SMD>" frame 6 6 subtract neutral 0 weightlist wheel_rr_spin
$sequence wheel_rl_suspension { wheel_rl_low wheel_rl_high blend "vehicle_wheel_rl_height" 0 1.0 } weightlist wheel_rl delta autoplay
$sequence wheel_rr_spin { wheel_rr_spin0 wheel_rr_spin120 wheel_rr_spin240 wheel_rr_spin0 blendwidth 4 blend "vehicle_wheel_rr_spin" -180 180 } weightlist wheel_rr_spin delta autoplay
$animation wheel_rl_spin0 "digger_wheelspin" frame 0 0 subtract neutral 0 weightlist wheel_rl
$animation wheel_rl_spin120 "digger_wheelspin" frame 3 3 subtract neutral 0 weightlist wheel_rl
$animation wheel_rl_spin240 "digger_wheelspin" frame 6 6 subtract neutral 0 weightlist wheel_rl
$sequence wheel_rl_spin { wheel_rl_spin0 wheel_rl_spin120 wheel_rl_spin240 wheel_rl_spin0 blendwidth 4 blend "vehicle_wheel_rl_spin" -180 180 } weightlist wheel_rl delta autoplay
</pre>
</pre>


==Driver's View==
If you don't want an animated steering wheel, simply remove its bone from the <code>front_wheels</code> $weightlist.
 
You might need to include a basic idle animation, like this:


If the vehicle is drivable, you’ll also need to place an attachment point for the driver’s eyes.
$sequence "idle" "<reference model SMD>"  activity ACT_IDLE -1 loop fps 30


$attachment vehicle_driver_eyes "view" 0 0 0
=Miscellaneous=


It must be named <code>vehicle_driver_eyes</code>.
==Model origin note==


==Quick reference==
If your model is pivoted by 90 to 180 degrees relative to the direction it actually drives while its animations are as they should be, to easily fix it, try using an [[$origin]] line like this:


As a quick animation reference of what needs to be animated, you'll need the following:
  $origin 0 0 0 90
  -suspension - 3 frames - frame0: lowest position of wheels; frame1: normal position of wheel; frame2: highest position of wheels
-turn - 3 frames - frame0: turn left; frame1: centered; frame2: turn right
-spin - 9 frames - frame0: wheels at 0 degrees; frame9: wheels spun forwards 360 degrees


If you have a mounted weapon (only takes into account 1 weapon):
==Gauge animations==
-aim - 9 frames - frame0: weapon pointed left and aimed down; frame1: weapon centered and aimed down; frame2: weapon pointed right and aimed down; frame3: weapon pointed left and centered aim; frame4: weapon centered and centered aim; frame5: weapon pointed right and centered aim; frame6: weapon pointed left and aimed up; frame7: weapon centered and aimed up; frame8: weapon pointed right and aimed up


These animations are only what are needed for a basic driveable vehicle. Additional animations can be seen in sourcesdk_content\hl2\modelsrc\Buggy
For either method, use this $poseparameter line:


==Scripting==
$poseparameter "vehicle_guage" 0 1


Each vehicle requires a script to function properly. Nearly all the vehicle traits can be controlled via the script. A vehicle script which lacks a definition for any variable will simply default it's value based on the hard code. A variable placed in the wrong block will cause a fatal crash.
===Simple===


The script is broken down into separate blocks defining each set of traits.
For a simple animation with a single gauge needle, like in the Half-Life 2 buggy:


<pre>
<pre>
"vehicle"
$weightlist speedo { "<gauge needle bone>" 1.0 }
{
$animation slow "<gauge animation SMD>" frame 0 0 subtract neutral 0 weightlist speedo
"body"
$animation mid "<gauge animation SMD>" frame 1 1 subtract neutral 0 weightlist speedo
{
$animation fast "<gauge animation SMD>" frame 2 2 subtract neutral 0 weightlist speedo
"massCenterOverride" "0 0 0"
$sequence speedometer { slow mid fast blend vehicle_guage 0 1 } weightlist speedo delta autoplay
"massoverride" "800"
"addgravity" "0.33"
}
Rest goes here...
}
</pre>
</pre>


This will define the basics of a body set up for the vehicle. Remember that if any of these were missing, they would default automatically. These settings are copied directly from the Source SDK's <code>jeep_text.txt</code>.
You may need to give the animation a linear curve.


'''massCenterOverride''' will adjust the center of gravity in relation to the vehicle's local origin (as defined in the .MDL).
===Advanced===


Mass and Gravity are distinctly different. Mass will cause the vehicle to become heavier, whereas Gravity will create downward force. You will need to compensate with Horsepower in either case.
For an animation with two gauge needles moving independently, as in a traditional car's gauge cluster:
Mass is evident at all times, whereas Gravity is most apparant during uphill movement. Flat ground movement is unaffected.


Vehicles are overdetermined systems. There are many parameters that interact, so tuning them (especially randomly) can be difficult.
<pre>
$weightlist speedo { "<gauge needle speedo bone>" 1.0 "<gauge needle tacho bone>" 1.0 }
$animation speedo1 "<gauge animation SMD>" frame 0 0 subtract neutral 0 weightlist speedo
$animation speedo2 "<gauge animation SMD>" frame 1 1 subtract neutral 0 weightlist speedo
$animation speedo3 "<gauge animation SMD>" frame 2 2 subtract neutral 0 weightlist speedo
$animation speedo4 "<gauge animation SMD>" frame 3 3 subtract neutral 0 weightlist speedo
$animation speedo5 "<gauge animation SMD>" frame 4 4 subtract neutral 0 weightlist speedo
$animation speedo6 "<gauge animation SMD>" frame 5 5 subtract neutral 0 weightlist speedo
$animation speedo7 "<gauge animation SMD>" frame 6 6 subtract neutral 0 weightlist speedo
$animation speedo8 "<gauge animation SMD>" frame 7 7 subtract neutral 0 weightlist speedo
$animation speedo9 "<gauge animation SMD>" frame 8 8 subtract neutral 0 weightlist speedo
$animation speedo10 "<gauge animation SMD>" frame 9 9 subtract neutral 0 weightlist speedo
$animation speedo11 "<gauge animation SMD>" frame 10 10 subtract neutral 0 weightlist speedo
$animation speedo12 "<gauge animation SMD>" frame 11 11 subtract neutral 0 weightlist speedo
$animation speedo13 "<gauge animation SMD>" frame 12 12 subtract neutral 0 weightlist speedo
$animation speedo14 "<gauge animation SMD>" frame 13 13 subtract neutral 0 weightlist speedo
$animation speedo15 "<gauge animation SMD>" frame 14 14 subtract neutral 0 weightlist speedo
$animation speedo16 "<gauge animation SMD>" frame 15 15 subtract neutral 0 weightlist speedo
$animation speedo17 "<gauge animation SMD>" frame 16 16 subtract neutral 0 weightlist speedo
$animation speedo18 "<gauge animation SMD>" frame 17 17 subtract neutral 0 weightlist speedo
$animation speedo19 "<gauge animation SMD>" frame 18 18 subtract neutral 0 weightlist speedo
$animation speedo20 "<gauge animation SMD>" frame 19 19 subtract neutral 0 weightlist speedo
$sequence speedometer { speedo1 speedo2 speedo3 speedo4 speedo5 speedo6 speedo7 speedo8 speedo9 speedo10 speedo11 speedo12 speedo13 speedo14 speedo15 speedo16 speedo17 speedo18 speedo19 speedo20 blend vehicle_guage 0 1 } blendwidth 20 weightlist speedo delta autoplay
</pre>


The '''offset''' is the distance from the origin to the axle. The '''wheeloffset''' is the distance from that point to each wheel. This get symmetrically reflected +/- around the axle origin for alternating wheels (which means only supports two wheels per axle).  
In this example, 20 frames were animated, but you can use as many as you want, as long as you change the code to account for differences in frame amounts.


{{note|If you use wheel attachments on your model ('''wheel_fl''', etc...), then the vehicle code will compute these values for you, so it is not necessary to have them in your script.}}
== Making the steering wheel turn further than 180 degrees ==


'''keepuprighttorque''' is not hooked up to anything.
Because of how animation SMDs work, if a bone rotates over 180 degrees over one frame, its rotation direction will appear to be the opposite of what you might've intended. To avoid this, you can animate in a way so that there's no more than 180 degrees of rotation in individual frames.


'''tiltforce''' is the number of gravities to apply to the car when it rolls past a certain hardcoded limit.
This setup allows for about 2.5 proper revolutions of the steering wheel, when turning one way:


'''tiltforceheight''' is an offset in the car's local space to move that additional gravity.
<pre>
 
$weightlist front_wheels { "<steering front left bone>" 1.0 "<steering front right bone>" 1.0 "<steering wheel bone>" 1.0 }
Together these two parameters attempt to simulate a different mass center when the car is rolling.
$animation steer1 "<steering animation SMD>" frame 0 0 subtract neutral 0 weightlist front_wheels
'''countertorque''' is a scale modulates the torque at the wheels acting on the body.
$animation steer2 "<steering animation SMD>" frame 1 1 subtract neutral 0 weightlist front_wheels
$animation steer3 "<steering animation SMD>" frame 2 2 subtract neutral 0 weightlist front_wheels
$animation steer4 "<steering animation SMD>" frame 3 3 subtract neutral 0 weightlist front_wheels
$animation steer5 "<steering animation SMD>" frame 4 4 subtract neutral 0 weightlist front_wheels
$animation steer6 "<steering animation SMD>" frame 5 5 subtract neutral 0 weightlist front_wheels // center frame
$animation steer7 "<steering animation SMD>" frame 6 6 subtract neutral 0 weightlist front_wheels
$animation steer8 "<steering animation SMD>" frame 7 7 subtract neutral 0 weightlist front_wheels
$animation steer9 "<steering animation SMD>" frame 8 8 subtract neutral 0 weightlist front_wheels
$animation steer10 "<steering animation SMD>" frame 9 9 subtract neutral 0 weightlist front_wheels
$animation steer11 "<steering animation SMD>" frame 10 10 subtract neutral 0 weightlist front_wheels
$sequence turning { steer1 steer2 steer3 steer4 steer5 steer6 steer7 steer8 steer9 steer10 steer11 blend vehicle_steer -1 1 } blendwidth 11 weightlist front_wheels delta autoplay
</pre>


Wheel mass affects the car body through the suspension.
When creating your own animation for such a setup, make sure to have an even amount of frames for both turning directions, with a center frame in the middle.


'''massCenterOverride''' is very useful for preventing roll. Keep in mind that the car body is completely rigid and has uniform mass distribution. This is a poor model for real cars since they have most of their mass at the bottom (suspension) and very little in the cabin since it's mostly empty space. So generally you'll want to adjust this a little bit to avoid having the mass center go to the center of the car's collision shape. You can also artifically push this through the ground if you want a more hacked way to keep the car from rolling.
As with the above advanced gauge setup, it's possible to have more or less than 10 frames.


== See also ==
=See also=
* [[Qc|QC files]]
* [[Vehicles (Scripting Parameters)]]
* [[prop_vehicle]]
* [[prop_vehicle]]
* [[.QC Commands]]
* [[QC|.QC files]]

Revision as of 14:50, 6 May 2017

Template:Otherlang2

This page describes how to set up a model for use as a functional vehicle, with basic animations, and optionally, gauge animations.

SMD prerequisites

Reference model

Your reference model at minimum needs the following bones in order to work:

  • suspension front left -> steering front left -> wheel front left
  • suspension front right -> steering front right -> wheel front right
  • suspension rear left -> wheel rear left
  • suspension rear right -> wheel rear right
  • view, located where the driver's view should be

Optionally:

  • steering wheel
  • gauge needle(s)

All of which should be children of a static root bone, and should follow the above hierarchy. You may name them anything, as long as you edit the following code examples accordingly.

Your respective meshes need to be rigged to their respective bones.

Collision model

All vehicles need physical models, so you need to set up a collision model. See $collisionmodel.

To get started, you can simply use a copy of the reference model of your vehicle, but make sure to remove the wheels from the model, as wheel collisions aren't defined in the collision model itself, but at runtime, based on the entire compiled model's wheel attachments. Note that doing this will give your model a convex collision model.

If you have the time for it, modelling a low-poly version of the vehicle specifically for use as a collision model can be useful, both for optimization, and because it can be concave. See Collision mesh.

Basic animations

Vehicles require a few basic animations in order to work properly.

  • An animation SMD with only the suspension bones animated. This may be 2 frames (fully compressed, fully extended). This sequence determines the suspension travel each wheel has.
  • An animation SMD with only the wheel bones individually spinning one full revolution. This may be 9 frames, and may need a linear animation curve, defined before the animation is exported in the model editor you use.
  • An animation SMD with the steering of the wheels, with only the steering bones (and the steering wheel bone if it exists) and not the wheel bones animated. This may be just 3 frames (left, centered, right). Be sure to note the exact angle that the wheels turn to and make it consistent. You’ll need to put that angle into the vehicle’s script file so that the physics will match the animation.

Note that with just 3 frames, if you animate a steering wheel, it can't turn further than 90 degrees one way without breaking the centered position, which might be unsuitable for some models. See Making the steering wheel turn further than 180 degrees.

QC setup

Attachments

Wheels

You need to set up attachments for all four wheels, to define where they are located in the model.

$attachment "wheel_fl" "<wheel front left bone>" 0 0 8
$attachment "wheel_fr" "<wheel front right bone>" 0 0 -8
$attachment "wheel_rl" "<wheel rear left bone>" 0 0 8
$attachment "wheel_rr" "<wheel rear right bone>" 0 0 –8

You must name the attachments as above. You may however name their respective bones anything, and you may place the $attachment lines anywhere in your .QC.

Only four wheels may be simulated in one vehicle. Their attachments are named with two letter codes:

  • fl "front left"
  • fr "front right"
  • rl "rear left"
  • rr "rear right"

One important detail is that the wheels are simulated as spheres. For this reason, you may want to pull the attachments slightly inwards. The origin of the wheel bones is actually the center of the wheel graphically, but in order to avoid having the spherical wheel collisions poke out from the sides of the vehicle (thus brushing against things next to it), you might want to pull the attachments in by a bit.

For example, this would place the wheel sphere exactly over its graphical representation:

$attachment "wheel_fl" "<wheel front left bone>" 0 0 0

While this adjusts it inwards by 8 inches:

$attachment "wheel_fl" "<wheel front left bone>" 0 0 8

The amount to pull them in depends on the size of the wheels and the collision model of the vehicle. In this case, the wheels have a radius of about 22.5 inches and I’ve pulled them in by 8 inches. Using this ratio is a good starting point.

Driver's view

This line defines where your view is going to be when driving.

$attachment "vehicle_driver_eyes" "<view bone>" 0 0 0

As with the wheel attachments, this attachment must be named vehicle_driver_eyes.

Miscellaneous attachments

These are some miscellaneous attachments that might be required, with approximate positions that you might have to tweak.

$attachment "vehicle_feet_passenger0" "<view bone>" 0 0.00 -27.00
$attachment "vehicle_engine" "<root bone>" 40.00 0.00 17.00

The vehicle_feet_passenger0 attachment determines where the driver's third person player model is located. It doesn't need to be based on the view bone - you can instead set up an individual bone for it.

Pose parameters

You need the following $poseparameter lines to allow the physics to interact with your model.

This line is for the steering. –1 is left, +1 is right.

$poseparameter "vehicle_steer" -1 1

These lines are for the suspension states of each wheel. 0 is compressed, 1 is extended.

$poseparameter "vehicle_wheel_fl_height" 0 1
$poseparameter "vehicle_wheel_fr_height" 0 1
$poseparameter "vehicle_wheel_rl_height" 0 1
$poseparameter "vehicle_wheel_rr_height" 0 1

These lines are for the rotation of each wheel. +/- 180 degrees from base rotation.

$poseparameter "vehicle_wheel_fl_spin" -180 180 wrap
$poseparameter "vehicle_wheel_fr_spin" -180 180 wrap
$poseparameter "vehicle_wheel_rl_spin" -180 180 wrap
$poseparameter "vehicle_wheel_rr_spin" -180 180 wrap

All of the above should not be renamed.

Animations

Here’s an example .QC snippet that sets up animations for the pose parameters, including a steering wheel in the steering animation.

$animation neutral "<reference model SMD>" frames 0 0

// steering
$weightlist front_wheels { "<steering front left bone>" 1.0 "<steering front right bone>" 1.0 "<steering wheel bone>" 1.0 }
$animation turn_left "<steering animation SMD>" frame 0 0 subtract neutral 0 weightlist front_wheels
$animation turn_right "<steering animation SMD>" frame 2 2 subtract neutral 0 weightlist front_wheels
$sequence turning { turn_left turn_right blend vehicle_steer -1 1 } weightlist front_wheels delta autoplay


// front left
$weightlist wheel_fl { "<steering front left bone>" 1.0  "<suspension front left bone>" 1.0 "<wheel front left bone>" 0.0 }
$animation wheel_fl_low "<suspension animation SMD>" frame 0 0 subtract neutral 0 weightlist wheel_fl 
$animation wheel_fl_high "<suspension animation SMD>" frame 2 2 subtract neutral 0 weightlist wheel_fl 
$sequence wheel_fl_suspension { wheel_fl_low wheel_fl_high blend "vehicle_wheel_fl_height" 0 1.0 } weightlist wheel_fl delta autoplay

$weightlist wheel_fl_spin { "<wheel front left bone>" 1.0 } 
$animation wheel_fl_spin0 "<wheelspin animation SMD>" frame 0 0 subtract neutral 0 weightlist wheel_fl_spin 
$animation wheel_fl_spin120 "<wheelspin animation SMD>" frame 3 3 subtract neutral 0 weightlist wheel_fl_spin 
$animation wheel_fl_spin240 "<wheelspin animation SMD>" frame 6 6 subtract neutral 0 weightlist wheel_fl_spin 
$sequence wheel_fl_spin { wheel_fl_spin0 wheel_fl_spin120 wheel_fl_spin240 wheel_fl_spin0 blendwidth 4 blend "vehicle_wheel_fl_spin" -180 180 } weightlist wheel_fl_spin delta autoplay


// front right
$weightlist wheel_fr { "<steering front right bone>" 1.0  "<suspension front right bone>" 1.0 "<wheel front right bone>" 0.0 }
$animation wheel_fr_low "<suspension animation SMD>" frame 0 0 subtract neutral 0 weightlist wheel_fr 
$animation wheel_fr_high "<suspension animation SMD>" frame 2 2 subtract neutral 0 weightlist wheel_fr 
$sequence wheel_fr_suspension { wheel_fr_low wheel_fr_high blend "vehicle_wheel_fr_height" 0 1.0 } weightlist wheel_fr delta autoplay

$weightlist wheel_fr_spin { "<wheel front right bone>" 1.0 }
$animation wheel_fr_spin0 "<wheelspin animation SMD>" frame 0 0 subtract neutral 0 weightlist wheel_fr_spin 
$animation wheel_fr_spin120 "<wheelspin animation SMD>" frame 3 3 subtract neutral 0 weightlist wheel_fr_spin
$animation wheel_fr_spin240 "<wheelspin animation SMD>" frame 6 6 subtract neutral 0 weightlist wheel_fr_spin
$sequence wheel_fr_spin { wheel_fr_spin0 wheel_fr_spin120 wheel_fr_spin240 wheel_fr_spin0 blendwidth 4 blend "vehicle_wheel_fr_spin" -180 180 } weightlist wheel_fr_spin delta autoplay


// rear left
$weightlist wheel_rl { "<suspension rear left bone>" 1.0 "<wheel rear left bone>" 0.0 }
$animation wheel_rl_low "<suspension animation SMD>" frame 0 0 subtract neutral 0 weightlist wheel_rl 
$animation wheel_rl_high "<suspension animation SMD>" frame 2 2 subtract neutral 0 weightlist wheel_rl 
$sequence wheel_rl_suspension { wheel_rl_low wheel_rl_high blend "vehicle_wheel_rl_height" 0 1.0 } weightlist wheel_rl delta autoplay

$weightlist wheel_rl_spin { "<wheel rear left bone>" 1.0 }
$animation wheel_rl_spin0 "<wheelspin animation SMD>" frame 0 0 subtract neutral 0 weightlist wheel_rl_spin 
$animation wheel_rl_spin120 "<wheelspin animation SMD>" frame 3 3 subtract neutral 0 weightlist wheel_rl_spin 
$animation wheel_rl_spin240 "<wheelspin animation SMD>" frame 6 6 subtract neutral 0 weightlist wheel_rl_spin 
$sequence wheel_rl_spin { wheel_rl_spin0 wheel_rl_spin120 wheel_rl_spin240 wheel_rl_spin0 blendwidth 4 blend "vehicle_wheel_rl_spin" -180 180 } weightlist wheel_rl_spin delta autoplay


// rear right
$weightlist wheel_rr { "<suspension rear right bone>" 1.0 "<wheel rear right bone>" 0.0 }
$animation wheel_rr_low "<suspension animation SMD>" frame 0 0 subtract neutral 0 weightlist wheel_rr 
$animation wheel_rr_high "<suspension animation SMD>" frame 2 2 subtract neutral 0 weightlist wheel_rr 
$sequence wheel_rr_suspension { wheel_rr_low wheel_rr_high blend "vehicle_wheel_rr_height" 0 1.0 } weightlist wheel_rr delta autoplay

$weightlist wheel_rr_spin { "<wheel rear right bone>" 1.0 } 
$animation wheel_rr_spin0 "<wheelspin animation SMD>" frame 0 0 subtract neutral 0 weightlist wheel_rr_spin 
$animation wheel_rr_spin120 "<wheelspin animation SMD>" frame 3 3 subtract neutral 0 weightlist wheel_rr_spin 
$animation wheel_rr_spin240 "<wheelspin animation SMD>" frame 6 6 subtract neutral 0 weightlist wheel_rr_spin 
$sequence wheel_rr_spin { wheel_rr_spin0 wheel_rr_spin120 wheel_rr_spin240 wheel_rr_spin0 blendwidth 4 blend "vehicle_wheel_rr_spin" -180 180 } weightlist wheel_rr_spin delta autoplay

If you don't want an animated steering wheel, simply remove its bone from the front_wheels $weightlist.

You might need to include a basic idle animation, like this:

$sequence "idle" "<reference model SMD>"  activity ACT_IDLE -1 loop fps 30

Miscellaneous

Model origin note

If your model is pivoted by 90 to 180 degrees relative to the direction it actually drives while its animations are as they should be, to easily fix it, try using an $origin line like this:

$origin 0 0 0 90

Gauge animations

For either method, use this $poseparameter line:

$poseparameter "vehicle_guage" 0 1

Simple

For a simple animation with a single gauge needle, like in the Half-Life 2 buggy:

$weightlist speedo { "<gauge needle bone>" 1.0 }
$animation slow "<gauge animation SMD>" frame 0 0 subtract neutral 0 weightlist speedo
$animation mid "<gauge animation SMD>" frame 1 1 subtract neutral 0 weightlist speedo
$animation fast "<gauge animation SMD>" frame 2 2 subtract neutral 0 weightlist speedo
$sequence speedometer { slow mid fast blend vehicle_guage 0 1 } weightlist speedo delta autoplay

You may need to give the animation a linear curve.

Advanced

For an animation with two gauge needles moving independently, as in a traditional car's gauge cluster:

$weightlist speedo { "<gauge needle speedo bone>" 1.0 "<gauge needle tacho bone>" 1.0 }
$animation speedo1 "<gauge animation SMD>" frame 0 0 subtract neutral 0 weightlist speedo
$animation speedo2 "<gauge animation SMD>" frame 1 1 subtract neutral 0 weightlist speedo
$animation speedo3 "<gauge animation SMD>" frame 2 2 subtract neutral 0 weightlist speedo
$animation speedo4 "<gauge animation SMD>" frame 3 3 subtract neutral 0 weightlist speedo
$animation speedo5 "<gauge animation SMD>" frame 4 4 subtract neutral 0 weightlist speedo
$animation speedo6 "<gauge animation SMD>" frame 5 5 subtract neutral 0 weightlist speedo
$animation speedo7 "<gauge animation SMD>" frame 6 6 subtract neutral 0 weightlist speedo
$animation speedo8 "<gauge animation SMD>" frame 7 7 subtract neutral 0 weightlist speedo
$animation speedo9 "<gauge animation SMD>" frame 8 8 subtract neutral 0 weightlist speedo
$animation speedo10 "<gauge animation SMD>" frame 9 9 subtract neutral 0 weightlist speedo
$animation speedo11 "<gauge animation SMD>" frame 10 10 subtract neutral 0 weightlist speedo
$animation speedo12 "<gauge animation SMD>" frame 11 11 subtract neutral 0 weightlist speedo
$animation speedo13 "<gauge animation SMD>" frame 12 12 subtract neutral 0 weightlist speedo
$animation speedo14 "<gauge animation SMD>" frame 13 13 subtract neutral 0 weightlist speedo
$animation speedo15 "<gauge animation SMD>" frame 14 14 subtract neutral 0 weightlist speedo
$animation speedo16 "<gauge animation SMD>" frame 15 15 subtract neutral 0 weightlist speedo
$animation speedo17 "<gauge animation SMD>" frame 16 16 subtract neutral 0 weightlist speedo
$animation speedo18 "<gauge animation SMD>" frame 17 17 subtract neutral 0 weightlist speedo
$animation speedo19 "<gauge animation SMD>" frame 18 18 subtract neutral 0 weightlist speedo
$animation speedo20 "<gauge animation SMD>" frame 19 19 subtract neutral 0 weightlist speedo
$sequence speedometer { speedo1 speedo2 speedo3 speedo4 speedo5 speedo6 speedo7 speedo8 speedo9 speedo10 speedo11 speedo12 speedo13 speedo14 speedo15 speedo16 speedo17 speedo18 speedo19 speedo20 blend vehicle_guage 0 1 } blendwidth 20 weightlist speedo delta autoplay

In this example, 20 frames were animated, but you can use as many as you want, as long as you change the code to account for differences in frame amounts.

Making the steering wheel turn further than 180 degrees

Because of how animation SMDs work, if a bone rotates over 180 degrees over one frame, its rotation direction will appear to be the opposite of what you might've intended. To avoid this, you can animate in a way so that there's no more than 180 degrees of rotation in individual frames.

This setup allows for about 2.5 proper revolutions of the steering wheel, when turning one way:

$weightlist front_wheels { "<steering front left bone>" 1.0 "<steering front right bone>" 1.0 "<steering wheel bone>" 1.0 }
$animation steer1 "<steering animation SMD>" frame 0 0 subtract neutral 0 weightlist front_wheels
$animation steer2 "<steering animation SMD>" frame 1 1 subtract neutral 0 weightlist front_wheels
$animation steer3 "<steering animation SMD>" frame 2 2 subtract neutral 0 weightlist front_wheels
$animation steer4 "<steering animation SMD>" frame 3 3 subtract neutral 0 weightlist front_wheels
$animation steer5 "<steering animation SMD>" frame 4 4 subtract neutral 0 weightlist front_wheels
$animation steer6 "<steering animation SMD>" frame 5 5 subtract neutral 0 weightlist front_wheels // center frame
$animation steer7 "<steering animation SMD>" frame 6 6 subtract neutral 0 weightlist front_wheels
$animation steer8 "<steering animation SMD>" frame 7 7 subtract neutral 0 weightlist front_wheels
$animation steer9 "<steering animation SMD>" frame 8 8 subtract neutral 0 weightlist front_wheels
$animation steer10 "<steering animation SMD>" frame 9 9 subtract neutral 0 weightlist front_wheels
$animation steer11 "<steering animation SMD>" frame 10 10 subtract neutral 0 weightlist front_wheels
$sequence turning { steer1 steer2 steer3 steer4 steer5 steer6 steer7 steer8 steer9 steer10 steer11 blend vehicle_steer -1 1 } blendwidth 11 weightlist front_wheels delta autoplay

When creating your own animation for such a setup, make sure to have an even amount of frames for both turning directions, with a center frame in the middle.

As with the above advanced gauge setup, it's possible to have more or less than 10 frames.

See also