Vehicle Tutorial Part 2

From Mod Wiki
Revision as of 16:16, 26 November 2007 by MoP (talk | contribs) ('''Related Files''': better download links)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

I'm going to assume you already know how to make a model in LightWave, Maya, or 3D Studio Max. The following tutorial contains specific things to be aware of when modelling vehicles for ET:QW.

Roughing Out The Model

A rough vehicle model with GDF players for scale.

Firstly, and most importantly, don't rush into anything! Modelling a vehicle is a big project, and you need to consider a number of things before you get busy modelling. The last thing you want is to get your model all detailed out in high-poly, then realise that you've forgotten to allow the wheels space to move, or the player doesn't fit inside. This will result in wasted time and effort.

Instead, it makes sense to "rough out" your whole vehicle quickly, using basic shapes and without worrying about texturing or optimisation. Just model enough to make sure that your vehicle will drive well in-game, and be able to be used correctly by players. You can worry about fine details and texturing once it's sure that the design of the vehicle will cause no problems in-game.

Scaling

  • Import an existing ET:QW vehicle and player mesh into your scene to make sure your vehicle is scaled appropriately relative to them.
  • Choose a player animation that will suit your vehicle (eg. set up the Badger driver player animation in EditWorld and export it as an .OBJ file). You can use this to get the size of your seats and position of the steering wheel correct.

Texturing

  • For the time being your vehicle doesn't need a "proper" texture ... you just need a placeholder material to make sure the model will show up in game. Either create your own material and placeholder texture, or use something like textures/concrete/concrete10 for example.


A Basic Vehicle Rig

Once your model is roughed out, you will be ready to add bones (referred to in ETQW as "joints") and rig up your model to work in the game.

Joints

Initial vehicle rig showing the joint positions and names.

Here is a list of the major joints you will need, and what they do. This is the absolute minimum needed to get a 4-wheeled vehicle with no suspension working in game. Further info on adding suspension or a second passenger/gunner can be found in Part 3 of this tutorial. The joint names can be whatever you want, but bear in mind that you will have to refer to them by name in the VScript later, so naming them sensibly can save you a lot of time and avoids confusion.


  • With this setup, all joints should be parented to the origin joint.


Joint Name Description
origin The root joint of the vehicle, it should be at 0,0,0 in the scene.
cam1 Used to position the player's first-person view when sitting inside the vehicle.
driver Used to position the 3D player model within the vehicle.
exit1 The location where the player will try to get out of the vehicle.
front_left_wheel The point that the front left wheel will rotate around.
front_right_wheel The point that the front right wheel will rotate around.
rear_left_wheel The point that the rear left wheel will rotate around.
rear_right_wheel The point that the rear right wheel will rotate around.


  • Important note: all joints should be aligned so that they face forward along the world's X axis, and their local Z axis should point upwards. If not, you may find that your wheels or player view are rotated incorrectly in the game.
    • In 3DS Max, you can make sure that this is correct by creating Bones in the Top viewport and dragging them out to the right. Turn on Grid Snap to make sure the joints are perfectly straight.

Skinning


Skinning (also known as weighting, or binding) is the process of linking your model to the relevant joints. In the case of vehicles it is very straightforward, since nearly all parts of the model are only ever influenced by a single joint. So for example, you would need to bind the whole wheel mesh to the appropriate wheel joint. How you do this will depend on what modelling package you are using.

3ds max

  • Apply a Skin modifier to each of the meshes you want to export (the main body and each of the 4 wheels, for example).
  • In the "Bones" list, click Add, and choose the joint you want the selected mesh to be linked to.
    • In this way, the main body of the vehicle should be linked to the origin joint.
    • Additionally, each wheel should only be linked to its related wheel joint, not anything else.

Maya

  • Bind your meshes to the relevant joints using the Smooth Bind option in the Skin menu.


Exporting


How you export your model and rig to .md5mesh and .md5anim files depends on what modelling package you are using.

3ds max

  • First of all you will need to get Jonathan "BeRSeRKeR" Garcia's maxscript MD5 Exporter, which you can download here.
  • Go to the Maxscript menu and select Run Script... then browse to the location you installed the MD5Exporter.mzp file.
  • A new window should pop up, from here you should set these options:
    • Base frame: 0
    • Start frame: 1
    • End frame: 1
    • Nodes to export: Select all of the mesh parts you want to export. You don't need to select the joints, just the meshes (the main body and 4 wheels, for example).
    • Export options should be set to Export both for now.
    • Ignore the Camera rollout, this is not needed for a vehicle.
  • Now click the Export button and choose a folder to save your MD5 files in - models/vehicles/gdf_buggy/buggy.md5mesh is where I'll put this file.
  • Let the exporter work and save the file (this can take several seconds), soon it will prompt you for a path to save the .md5anim file - just use the same folder and name as before.

That's it, you should now have a working MD5 file that can be read by ETQW!

Maya

That's it, you should now have a working MD5 file that can be read by ETQW!


Related Files


Getting It Into The Game

Now that you have a rough model and a simple rig you are ready to get the vehicle into the game.

Entity Definition

For your new vehicle to be spawned in the game it needs an entityDef describing it. Start out by creating a new file in the def/vehicles subdirectory of your mod. Create the directories if you don't have them already. For this tutorial I am calling the file buggy_tute.def.

The first thing needed is a model definition. This links together the model and any animations it may have into one named model to be referenced by the entity:

model vehicle_buggy_tute {
    mesh                       models/vehicles/gdf_buggy/buggy.md5mesh

    anim base                  models/vehicles/gdf_buggy/buggy.md5anim
    anim ik_pose               models/vehicles/gdf_buggy/buggy.md5anim
    anim initial               models/vehicles/gdf_buggy/buggy.md5anim
    anim idle                  models/vehicles/gdf_buggy/buggy.md5anim
}

Now onto the entityDef. The name you choose for this is the name you will use to spawn the entity. I am using vehicle_buggy_tute:

entityDef vehicle_buggy_tute {

There are base definitions set up for vehicles in base/def/vehicles/base.def, so rather than re-defining all the parameters you might as well just inherit from them. I'm going to use vehicle_base_gdf:

    "inherit"               "vehicle_base_gdf"

There are a large number of key/value pairs that can be set to adjust vehicle behaviour, so for starters I'll describe the bare minimums.

Key Description
spawnclass The code class to use - for most vehicles this should be set to sdVehicle_RigidBody
scriptObject The script to use. The essentials are all defined in vehicle_base, so we don't need to make a new script just yet.
vs_vehicleScript The vscript to point to. I'll make one of these later, called buggy_tute
model The model definition to use. This should be the one we created above.
health The amount of health the vehicle has. I'm using 400 to start with.
input_mode The joystick input mode to use. This selects which set of input CVARs are used to used to translate joystick input to vehicle actions. In this case it should be car
vehicle_control Selects the mode of vehicle control used - in this case it should be wheeled.
info_name This points to a localized string to present to the player to name the vehicle. We can create one of these later, so for now I'll just use game/vec/husky
steering_angle The maximum angle of steering. I'm using 40 to start with.
table_gearforces This points to a table defining the amount of force applied at the wheels at each "gear" speed. I'll be making one of these later, called bugg_tute_gear_forcetable
table_gearspeeds This points to a table defining the speeds at which "gear" forces are applied. I'll be making one of these later, called buggy_tute_gear_speedtable
overdrive_factor The multiplier applied to the wheel speed & force when overdrive (boost) is being used. I'm using 1.5 to start with.
power_curve_scale Adjusts the gear force between gears to act kind of like an engine's torque curve, to help the vehicle go up slopes or maintain speed when hitting a ramp. I'm using 2 to start with.

A few other keys are needed to select some options:

    "option_combat_model"           "1"
    "option_selection_combat_model" "1"
    "option_task_interface"         "1"

The first option sets up a combat model, which is used by bullets so they can hit the actual visible model. The second option sets it so it can be "selected" - this allows it to be locked onto by rockets etc. The final option allows the entity to have missions generated due to it being "spotted" by a radar. TODO: GORDON PLEASE CHECK THIS IS ACCURATE

This leaves us with a very simple, minimal vehicle entityDef. This does not use a lot of features, for example:

  • Command map icon
  • Collision damage
  • Control context CVARs
  • Vehicle HUD elements
  • Engine sounds
  • Climate-based skins
  • Horn sounds
  • Low health warning sounds
  • Tooltip on entering vehicle
  • Decoys

Many things (eg effects) will use the default values, which may not be the most suitable. Some other things won't function properly - for example bullets won't damage it yet. However, there is enough information in the entityDef to get us started.

The completed entityDef:

entityDef vehicle_buggy_tute {
    "inherit"                       "vehicle_base_gdf"

    "option_combat_model"           "1"
    "option_selection_combat_model" "1"
    "option_task_interface"         "1"

    "spawnclass"                    "sdVehicle_RigidBody"
    "scriptObject"                  "vehicle_base"
    "vs_vehicleScript"              "buggy_tute"
    "model"                         "vehicle_buggy_tute"

    "input_mode"                    "car"
    "vehicle_control"               "wheeled"

    "info_name"                     "game/vec/husky"
    "health"                        "400"

    "steering_angle"                "40"

    "table_gearforces"              "buggy_tute_gear_forcetable"
    "table_gearspeeds"              "buggy_tute_gear_speedtable"

    "overdrive_factor"              "1.5"
    "power_curve_scale"             "2"
}


Vehicle Script


The vehicle script file generally contains most of the information to define the behaviour of the vehicle. Start out by making a directory in your mod's directory called vehicles and create a text file in there with the .vscript extension. I named mine buggy_tute.vscript.

Start out by making the definitions we pointed to in the entityDef. The gear speed table and the gear force table are simple and don't take too much explaining so I'll start with those. While ETQW does not use gears in the true sense of the word, it roughly represents the amount of force that the vehicle can apply at the wheels for various speeds, and the speed the vehicle will aim for. In a real-world vehicle the force the vehicle can drive with reduces with each gear, so we generally set up these tables to ramp down the force as the speed ramps up, but you can mess with these to achieve various effects (eg lots of torque at high speed). Here are the tables I'll start out with:

table buggy_tute_gear_speedtable {
    clamp 
 
    {
        20, 30, 50, 80
    }
}
 
table buggy_tute_gear_forcetable {
    clamp

    {
        300000, 200000, 100000, 100000
    }
}

The clamp parameter means that it won't try to extrapolate beyond the range of the table when looking up values.

Now onto the vehicleDef. This does the bulk of the definition. First of all it needs to be named:

vehicleDef "buggy_tute" {


Collision Model


The vehicle needs some sort of physical representation in the world. I've decided that for my model it should weigh somewhere in between the Husky and the Armadillo, so I've chosen 2000kg. Give the vehicle a part to define the shape of it - you can measure your model in your modelling package to try to get the bounds perfect straight away, but I just added a part with a random size ("-100 -100 -100" & "100 100 100") and then tuned the size in-game. To do this you can use the console to turn on g_showCollisionModels and to extend g_maxShowDistance - 1024 should be far enough. You can then spawn your vehicle by typing spawn vehicle_buggy_tute at the console (or whatever you named the entityDef). Enter killTransports to remove all the vehicles in the map, and reloadDecls to load the changes you've made to the definitions. By putting ETQW into windowed mode you can have the vehicle script open in another window, edit the sizing, pop back to ETQW, reloadDecls and spawn a new vehicle. This way you can rapidly tweak the size of the collision. Make the bottom of the collision end at the underside of the body - this gives it some clearance over the ground. A simple box collision model will be enough to start with, you can experiment with compound collision models later to get the size and shape to your liking. This is what I ended up with:

    part {
        "mins"                      "-96 -55 24"
        "maxs"                      "78 55 94"
        "mass"                      "2000"
        "friction"                  "0.4 0.4 0.4"
    }

I also added some friction, so that if the vehicle flips over it doesn't slide forever.


Positions, Views, and Exits


If you spawn this vehicle in the game you'll notice that you can't get into it! This would be a bit of a problem even if it had wheels, so we had better set up a seat for our would-be-drivers. For this you'll need a positionDef. Here is the minimal positionDef I started with:

    positionDef {
        data {
            "joint_attach"  "driver"
            "show_player"   "1"
            "player_anim"   "VehicleBadgerDriver"
        }

        view {
            eyeJoint        "cam1"
            autoCenter

            clamp pitch {
                min         -70
                max         20
            }

            clamp yaw {
                min         -80
                max         80
            }
        }

        view {
            eyeJoint        "cam1"
            type            "smooth_locked"
            thirdPerson
            cameraDistance  240
            cameraHeight    50
        }
    }

This provides a very simple position. It attaches the player at the driver joint, shows the player, and uses the VehicleBadgerDriver animation state machine. It has two views - one is a first person view based on the cam1 joint, which auto-centers. It has pitch and yaw clamps to keep the view within a reasonable range. The other view is a third person view based around the same joint, set back and up from the joint. I haven't named the position or given it a stats name - these won't cause any problems just now, although a warning will be printed on the console. This is not a problem, as the stats only do anything in ranked matches. TODO: REMOVE THE WARNING?

If you spawn your vehicle now you will find that you can get in and look around, but you can't get out - we now need to add an exitDef (or multiple exitDefs) to give the player somewhere to get out of the vehicle. It will not allow the player to get out if there are no valid (unblocked) exits, so it is always a good idea to have several exits. In this case I have only one exit:

    exitDef {
        joint               "exit1"
    }


Wheels


Ok so now we have a vehicle that has physics, and you can get in and out of. Now we need to make it actually go places. In the case of a wheeled vehicle like this we'd obviously want, well, wheels! To start with I'm going to make all the wheels the same, with identical suspension parameters. Maintaining four copies of near-identical settings when tweaking is very error-prone, so I'm going to write a template to define all the common keys between all the four wheels, one for keys common between the front two wheels, and one for keys common between the rear two wheels. Using the parameters to the first template we can make it include the front/rear template too, and set up the names of the joints and surfaces to use. This makes it as simple as just using the template with appropriate front/rear & left/right parameters to define any one wheel. Now, suspension tuning is tricky and can take a lot of fiddling to get it just right. I'll outline some of the methods I use when tuning a new vehicle's suspension, but you'll largely have to experiment to get things the way you like them.

Here is my starting point:

template templates/vehicles/buggy_tute/wheel_behavior {
    parameters< FrontBackParm, LeftRightParm >
    text {
        "name"                         "FrontBackParm LeftRightParm Wheel"
        "surface1"                     "FrontBackParm_LeftRightParm_wheels"
        "joint"                        "FrontBackParm_LeftRightParm_wheel"

        "slowonLeftRightParm"          "1"
 
        "wheelSpinForceThreshhold"     "75000"

        "brakingForce"                 "250000"

        "drive"                        "1"

        "alternateSuspensionModel"     "1"
        "suspensionUpTrace"            "8"
        "suspensionDownTrace"          "20"
        "suspensionRange"              "28"

        "suspensionKCompress"          "100000"
        "suspensionDamping"            "0"

        "suspensionMaxRestVelocity"    "3.5"

        "maxSlip"                      "400"
        "contactFriction"              "0 0.7 0"

        useTemplate templates/vehicles/buggy_tute/wheel_FrontBackParm< "LeftRightParm" >
    }
}

template templates/vehicles/buggy_tute/wheel_front {
    parameters< LeftRightParm >
    text {
        "turn"                         "1"
        "radius"                       "18"
        "footprint"                    "15"
    }
}

template templates/vehicles/buggy_tute/wheel_rear {
    parameters< LeftRightParm >
    text {
        "hasHandbrake"                 "1"
 
        "radius"                       "20"
        "footprint"                    "15"
    }
}

These templates must be defined outside of the vehicleDef - I placed them at the top of the file. Note how it uses the template parameters to construct the joint, surface, and part names. I've set this one up to use the alternate suspension model, which behaves like a simple linear spring & dampener system, as it is somewhat easier to tune and can provide very entertaining results. Its set up for four wheel drive, with the front wheels steering and the rear wheels handbraking. To use the template to create wheels add the following into the vehicleDef:

wheel {
    useTemplate templates/vehicles/buggy_tute/wheel_behavior< "front", "left" >
}

wheel {
    useTemplate templates/vehicles/buggy_tute/wheel_behavior< "front", "right" >
}

wheel {
    useTemplate templates/vehicles/buggy_tute/wheel_behavior< "rear", "left" >
}

wheel {
    useTemplate templates/vehicles/buggy_tute/wheel_behavior< "rear", "right" >
}

If you now spawn your vehicle it should be drive-able! No doubt it will bounce up and down chronically and drive strangely, but its a start.


Wheel Geometry

Firstly you'll want to adjust the amount of suspension travel there is. Start by deciding how far up from the wheel joint you want the bottom of wheel to be able to move, and set suspensionUpTrace to this. It is OK for this to be negative, if you modelled your vehicle with the wheels a long way down. This is basically the vertical offset from the joint's original position that the wheel can go up. Similarly, suspensionDownTrace is the vertical offset from the joint's original position that the wheel can go down. suspensionRange is the distance from the bottom of the movement range over which the suspension will operate - this allows the wheel to still move beyond its range, but the equations used to generate the forces will act as if it had reached the end of its suspensionRange. It is fine to start by setting this to suspensionUpTrace + suspensionDownTrace.

Next you should out the wheel's size - the radius and footprint values set this. radius is pretty self explanatory - its best to measure this one in your modelling package. footprint is the width of the wheel, which should again be measured in your modelling package. In the model I am using the front & rear wheels are different sizes, so I have configured mine to reflect this.


Suspension Tuning

We're almost ready to roughly tune the suspension's spring and dampening constants, but first I'll give a brief explanation of what effect these two have on the vehicle.

The spring compression coefficient, suspensionKCompress, causes a force to be applied that opposes the compression/extension of the suspension. The force is proportional to the amount of compression on the spring. Setting a small value for this will result in "squishy" suspension that allows a lot of vertical movement. Setting a large value will result in "stiff" suspension where only a small amount of movement is possible before large forces are applied.

The damping coefficient, suspensionDamping, causes a force to be applied that opposes the change in compression/extension of the suspension - ie the velocity of the suspension. The force is proportional to the rate of change of the spring's compression. Setting a small value for this will result in bouncy suspension that only slowly resists suspension movement, allowing oscillation to occur. Setting a large value will result in very static feeling suspension that rapidly resists suspension movement, quickly absorbing bumps. Too large a value can result in an "over-damped" situation where it will tend to drastically overcompensate - an example is a vehicle being dropped to the ground and it bouncing up higher than the height it fell from.

Low coefficient values will typically result in a very bouncy, "squishy" ride. This will tend to handle rough terrain well, without bumping and jostling the vehicle too much, but handle smooth terrain badly - bouncing and wobbling too much. High coefficient values will typically result in a very stiff ride, handling smooth terrain well but having a hard time over rough terrain, giving a bumpy and nasty ride. Note that these are very general tips - there is no hard-and-fast rule, and you will need to experiment a lot to find values that satisfy you.

Given the above, I tend to start by making it handle the standing still case. I lower the damping coefficient to zero and spawn a vehicle on flat terrain to see how it sits. I adjust the compression coefficient until the vehicle's body tends to sit at about the height I had imagined. With the damping coefficient at zero it will tend to wobble indefinitely, so now I adjust the damping coefficient until it tends to stabilize after a few oscillations. I normally start with a small value, eg 100, and multiply by 10 until the oscillation disappears. Then I look for values in between that and the previous value until I'm satisfied. For my vehicle this ended up with suspensionKCompress being 300000, and suspensionKCompress being 15000.

I normally find that at this point the vehicle handles pretty decently. Not perfect, but good enough for this stage of development. Depending on the mass of your vehicle you will probably have to adjust the drive force in the force table to get a decent degree of acceleration.


Preventing it from rolling


If your vehicle is anything like mine, you'll probably find that it is all too easy to roll your shiny new car. If you turn on rb_showMass using the console you will see a yellow wireframe sphere somewhere in the middle of your vehicle - this shows where its center of mass (some call it the center of gravity) is. This is quite high, which makes it easier for the vehicle to roll over. There are a few ways to deal with this:

  • Use a point mass to lower the center of mass to a more reasonable place
  • Use several collision parts instead of just one, making the upper one lighter than the lower one - this represents the bulk of the mass of the vehicle being in the lower part of the vehicle (eg the engine, suspension, wheels, the driver, etc)
  • Use an antiroll constraint

I like to use a combination of these methods. The disadvantage of lowering the center of gravity is that it allows less dynamic behaviour, like body rolling and pitching. This behaviour looks really cool, so its a shame to lose it. Antiroll constraints make the vehicle tend to resist rolling over too much. These are very easy to use, but they can look a bit artificial if they are made too obvious. With a bit of tweaking a good combination of the methods can usually be achieved.

This is the combination I settled on:

part {
    "mins"                      "-96 -55 60"
    "maxs"                      "78 55 94"
    "mass"                      "500"
    "friction"                  "0.4 0.4 0.4"
}

part {
    "mins"                      "-96 -55 24"
    "maxs"                      "78 55 60"
    "mass"                      "1500"
    "friction"                  "0.4 0.4 0.4"
}

antiroll {
    "angle_start"               "5"
    "angle_end"                 "30"
    "strength"                  "2"
}


Running people over


At the moment you can probably go prone and crawl under your vehicle, and running prone people over will not kill them. To solve this there is a part called a hurtZone. These are made much the same as a part, but they don't have a mass or friction - they just have a shape. They prevent players from moving through them, and they will cause collision damage to players in their way. Make this big enough to cover the underside of the vehicle.

Here is mine:

    hurtZone {
        "mins"                  "-96 -55 0"
        "maxs"                  "78 55 24"
    }


Click here to continue on to Part 3 of the Vehicle Tutorial