Counter-Strike

Counter-Strike

39 ratings
Creating models
By seedee
This guide is aimed at absolute beginners, and explains the complete modeling workflow for Counter-Strike, starting with decompiling, learning the purpose of each file format, moving on to good design principles for this engine, and how to compile a model. You will also learn how to use model viewers. Note that this isn't a general 3D guide, you need to start with an understanding of 3D concepts.
3
2
   
Award
Favorite
Favorited
Unfavorite
Requisites
Introduction
Models are a compiled format made up of different files. This includes the SMD format which contains mesh, skeleton, and animation data, the BMP format for textures, and a compile script using the QC format, which references files used and provides instructions for the compiler.


You can also decompile models, the process is similar and doesn't require a QC file to start from. The decompiler is capable of generating a new QC file from the settings the model was compiled with.


The MDL format is used by various games and engines, which are not cross-compatible. All models for Counter-Strike and other Goldsource engine games use MDL version 10. Other versions include v4-v6 for Half-Life Alpha 0.52, v8 for Quake 2, v53 for Titanfall 2, etc. It's good to understand the difference between these, so you know what is meant when MDL v10 or other versions are mentioned.

T-models
Some models were compiled with their textures separately, which exist as separate MDL files that have the same name except for a t suffix at the end. These don't contain any mesh data, just all of the textures used. There are also sequence MDL files with the same name except for a number n at the end, which contain groups of sequences (animations) used in the actual model.


Many of the original models in the game were compiled this way for optimization purposes to increase framerate and server performance. The model's textures could be unloaded until visible to the player. Servers only needed model data, while texture and animation uploading was deferred until needed. As performance is no longer an issue, compiling models this way is no longer necessary, all it does is waste res entries and can create conflicts with downloadable content.
Setting up Crowbar
Crowbar must be configured first to be used as a decompiler or a wrapper for the compiler. You need to select your game in the dropdown menu at the top. Next, set the compiler to studiomdl.exe from the Sven Co-op SDK you installed from Steam. You can add a model viewer if you want to open a model directly from Crowbar, but it's not important. The actual decompiler is built into Crowbar and doesn't need to be set up.


You can set up a new copy of this configuration for each different compiler you use if you want to experiment with different compilers.
Decompiling a model
Decompiling models will give you a better understanding of the MDL format's structure, and let you edit the mesh, skeleton, and animations directly. The best decompiler to use is Crowbar. There are some other decompilers like various versions of mdldec.exe and Kratisto's Decompiler, but they are very old and have known bugs, such as missing the texture render modes, loss of motion extraction (sliding NPC issues in Half-Life), desync from floor animations, and dead frames which will cause issues with gameplay.

Decompilation process
Go to the decompile tab and select the model (or folder with models) to decompile. You can set the location of the output to preference, and there is only one setting you need to change. Before you decompile, enable "Use non-Valve UV conversion (GoldSource)".


While the Sven Co-op compiler fixes a lot of bugs regarding calculations for UV maps, decompiling may still not be entirely perfect due to the weird math library that Valve's original compiler used, and other poor coding practices (floating-point math is generally inaccurate and the UV coordinates went through different type conversions). The Sven Co-op and Doommusic's compilers fixed the original UV shift bug, but there are still some cases where the decompiler messes up the UVs due to it not aligning with the compiler's math.

The non-Valve UV conversion option solves the problem by adjusting the UV coordinates for cleaner decompilation. High-resolution models generally don't experience this issue, but this option is especially useful when you are decompiling older models featuring low-resolution textures, such as Gearbox models. It's good practice to keep it enabled in any case. After you have decompiled the model, you will see a lot of new files, including an "anims" subfolder for the animation SMDs if you keep the option for it enabled in Crowbar.
Understanding file formats
Mesh and animation data are each exported to their own SMD files. These are plaintext files so it's possible to edit them directly with Notepad. The skeleton is included in both types of SMDs, and the compiler will match bones across SMDs by their names, so every animation SMD will correspond to at least one mesh SMD.

Meshes
Mesh/reference SMDs contain the following data:
  • header — the SMD version
  • nodes — a list of all bones in the skeleton
  • skeleton — position of all bones in the rest pose
  • triangles — vertices and faces in the mesh and their normals

Mesh SMDs are limited to 128 bones, 2048 vertices and vertex normals, and 4080 triangles. If you are creating a higher-poly model, you need to separate parts of it into different SMDs, i.e. the stock and magazine of a v_model will be exported as separate objects, and so will the arms of the v_model. Each object would have a maximum of 2048 vertices and not exceed any other limits.

Another limitation is that vertices must be skinned to one bone only, and you can only have hard vertex weights in your skeleton, so when you are weight painting make sure the brush is set to 1 or 0. While soft weights are not supported, it's possible to replicate the effect with constraints.

Animations
Animation/sequence SMDs contain the following data:
  • header — the SMD version
  • nodes — a list of all bones in the skeleton
  • skeleton — position of all bones in each animation keyframe

Animation SMDs are limited to 512 keyframes. This engine only supports skeletal animations using keyframes, you will have to bake down simulations and other procedural stuff before you can export them. Scaling bones is not possible, you are only limited to translation and rotation. You are also limited to 256 animations in the entire model.

There can only be one animation in each SMD file, so different animations are exported into different files. It's good practice to store your animations in an "anims" subfolder to separate the different SMDs. If they aren't organized properly, you can tell the difference between them based on if they have a triangles section in Notepad.

Textures
All textures need to be exported to the BMP format. One mesh SMD can use multiple textures, and different SMDs can also share the same texture. Textures can also have different render modes, suitable for different use cases and art styles.

Textures are limited to 262144 pixels in the entire image. The maximum resolution for a square texture is 512x512. You can also use other resolutions such as 1024x256 or 2048x128, as long as the total number of pixels does not exceed 262144. Some compilers allow you to go over this pixel limit, but this will cause the game to crash, so make sure your textures stay within this limit. During runtime, Counter-Strike and other games will actually resize textures to 256x256, but this can be fixed with gl_max_size 512.

The width or height of the texture must be divisible by 16. You should still consider creating textures in powers of 2 because older versions of the engine resize non-power of 2 textures during runtime, which destroys texture quality in-game. If you are running an older version, you can disable it with gl_round_down 0.

Textures are limited to indexed color with 8-bit depth (max 256 colors). To optimize palette usage and avoid color banding, it's a good idea to keep within similar color ranges for each different texture. For example, use one texture file for skin tones and another for a different type of color, this allows for a broader range to be represented overall.

QC scripts
The QC file is the script used by the compiler, it contains commands which control the compilation process. The QC files generated during a decompile can be used as a starting point, or check the "Writing a QC file" section for more details.
Creating a mesh
As the scale is different for most 3D editors, it will be helpful to import a random decompiled model to make a comparison. For playermodels and other organic models, remember that since you are only limited to hard vertex weights, it's not recommended to create high poly models without designing them around this limitation, or you will end up with parts deforming at unnatural angles when animating later.

Try to keep plenty of space between loops around deformation areas, dissolving loops if necessary. You can also use the fake soft weight method explained in the next section. If working with higher poly v_models, consider deleting faces on the other side of the model that aren't visible from the player's perspective. This is something you can do after the animation stage.

Origin
v_models will use the origin for the view angle, so set up any cameras in your 3D software based on the origin point. Map props use the origin point for positioning, corresponding to the origin of the entity. The size of your model is only limited by the map's draw distance, you can even simulate Source's 3D skybox effect with large setpiece models that render outside of the skybox. Import your own map into the 3D editor to get a sense of scale before you try creating such a model. Units are based on 3ds Max generic units, while 1 meter in Blender corresponds to 1 unit in the map editor.


Normals
One thing you have to take note of is that the game doesn't render backfaces, make sure that you have your face normals pointing in the right direction or you will see gaps in the mesh.

When creating foliage like trees and bushes, you should apply custom vertex normals to give it more uniform vertex shading. Foliage is generally made with a group of flat planes with a masked transparent leaves texture. Depending on how the plane is oriented, it will not be shaded very well and the flatness of the geometry will be clearly evident:


You can transfer the vertex normals from a hemisphere/dome positioned around the foliage mesh using Transfer Attributes in Maya, or the Data Transfer modifier in Blender. Blender's modifier works in real-time, so you can move the hemisphere around and adjust the effect on the fly. The vertex normals will then point outwards, parallel to the faces.


Note that this method isn't always necessary in the case of palms or other trees with horizontal/down-pointing leaves, where the default vertex shading will look more or less natural. Here's an in-game example of the method done properly:
Rigging a mesh
While vertices can only be skinned to one bone, bones can have more than one vertices skinned to them. Remember to create your skeletons without relying on parts being scaled during the animation phase, as it's not possible to scale bones when animating. You can still create a fake scale effect in your model, for example, 4 bones for each vertex of a plane would be moved outwards to enlarge it. A more complex setup would have a bone for each loop in a torus, these would be moved outwards from the center like a smoke ring effect.

Fake soft weights
While you have to work within 1 or 0 hard weights for bones, you can still replicate soft weights with extra bones and bone rotation constraints in your 3D software. First, create a fake bone that splits the difference between the rotation of the two main bones. Next, you skin the "soft weighted" edge loop to that bone. In the following example, the small green bone has a 40% rotation influence controller on the upper leg edge loop to smooth out the deformation:
Fake soft weight rig

The Counter-Strike developers used a similar technique, with helper bones and some rotation scripts added to the 3ds Max biped. This smooths out body part rotations, where some bones would cut off at certain angles with others for less clipping. These bones are prefixed with --.


They also used 9-way animation blending, so that each weapon would have 9 poses for aiming. 01-03 has a 45-degree up angle, 04-06 has a 0-degree straight angle, 07-09 has a 45-degree down angle. Each group of three has 90-degree turns from left to right.
Creating an animation
Even though you can make complex animations up to 512 keyframes, it's best to limit that to at most 200-300, especially if you have 100+ bones. There is an engine issue where it crashes with the "sequence is greater than 64K" error, so if you get this issue you may have to reduce the frames and lower the framerate to compensate.

Some models have duplicate frames known as dead frames at the start, which were added by old, buggy decompilers. After enough decompiles, these can stack up and cause timing issues for animations, most noticeably on v_models. If you decompile an old model and notice dead frames, you should delete them.

Note that while mapping in Counter-Strike, you can render models on and off but you can't actually trigger animations (play or pause), so animations will always play even when the map entity is not visible. You can get around this by instead exporting each keyframe pose as a separate SMD and compiling them as submodels. You will have to create a new entity for each submodel/keyframe and toggle them on and off in order with a multi_manager. This is actually how the breaking awnings were created in de_dust_2020.

Simulations
The best way to achieve realistic effects, like cloth deformation, debris from a bomb explosion, or an animated ocean, is with simulations. For map props, make sure the entire area of the map covered by the simulation is lit up properly since models take lighting from their origin point. To learn more about baking simulations, click here[the303.org].

Creating a texture — part 1
Before your texture is game-ready you must convert it to an indexed image. Different software use their own methods for color quantization, some of them will convert with severe color banding issues and poor dithering. Use IrfanView for the best result. It does a good job, is faster than Photoshop, and in most cases produces better results.


Open your texture in IrfanView and go to "Image > Decrease Color Depth". Set it to 256 colors and enable "Floyd–Steinberg dithering", also enable "Use best color quality". After you press OK, you will notice it will say 8 BPP on the bottom left. From there you can save the texture to BMP as you would normally. IrfanView also has a bulk convert option that can resize and reduce bit depth for multiple textures at a time.


Handpainting
This is the most common method for creating textures in Counter-Strike, it can be done by itself or as a step after baking. It involves using various techniques such as layer styles, pattern fills, dodge & burn, levels, masking, and layer blending. Click here[the303.org] for a list of walkthroughs that will help you get started.

The way models are unwrapped must be considered if you are going to hand-paint. You should have larger, more continuous UV islands with straight and upright orientations. To make it easier to paint, avoid packing the islands too tightly. Note that this may not be necessary if you're painting in a 3D viewport directly.


Baking
A lot of models created today, especially ports from other games, are done by just taking the diffuse/base color and using that without any further processing. If the texture was originally from an older-era game that you know has been completely handpainted, it should be fine. Otherwise, the texture will look like stone:


To make your lightbakes look good, have a variety of key and accent light sources in the scene to highlight every angle. A proper high-contrast studio HDRI can be helpful when baking. Don't use a flat light source with no variation or your lightbake will be terrible:


Cinema 4D and Marmoset Toolbag are good choices for a lightbake, but it's also possible in Blender. After you finish, you can hand paint manually to touch up any areas that need improvement. Here is an example of how to bake properly:


UV chopping
If you are compiling an existing asset or porting from another game that's using higher-resolution textures (1k or 2k), you should UV chop. Downsizing the high-res texture to 512x512 will wash out the details and contrast, this will look bad in v_models. UV chopping is similar to texture atlasing but in reverse. It involves dividing a large texture into multiple smaller ones, for example, a single 1k texture can be split into four 512x512 textures. Next, the UV islands from each part of the original texture are re-assigned to their new textures respectively, then rescaled and repositioned.

This technique can take a long time and can be harder depending on how tightly packed the UV map is and how long/narrow the UV islands are, but it's the only way to create models with true HD textures. Make sure to take texel density into consideration when UV chopping, you may not need to chop some smaller textures depending on the model. For a full guide, click here[gamebanana.com].
Creating a texture — part 2
Chrome render mode
The chrome render mode is like a matcap (material capture) shader that fakes a whole material including shading and reflection. It can represent glossy surfaces, metals, scope lenses, and other small details. When creating a chrome texture, follow the same method described before but note that chrome textures should always be 64x64 in resolution. The effect also looks best when the texture has a spheroid distortion. For detailed information on chrome, click here[the303.org].


There are two ways to set a texture as chrome. The first method is to prefix the name of the texture with CHROME_, for example, "CHROME_texture.bmp". This will also enable the flatshaded mode on the texture. The second method is to set it as $texrendermode texturename.bmp chrome in the QC file, which won't disable vertex shading.


The following are some good resources for chrome textures:
https://pixologic.com/zbrush/downloadcenter/library/
https://github.com/nidorx/matcaps

Additive render mode
Additive transparency works exactly the same as additive blending in other graphics software, where black parts of the texture are invisible and brighter colors will be visible the most. This effect is suitable for light beams, glass, and holographic effects. You can set a texture as additive with $texrendermode texturename.bmp additive in the QC file. Note that this is not a fullbright mode, the map's lighting still affects this render mode so it won't glow if the model moves into a dark area.


Masked render mode
Masked/alphatest transparency is simple 1-bit opacity, where the last color in the palette will be used for the mask. Follow the same method with IrfanView when creating a masked texture, but you will use different software to swap the index position of the mask color. It's possible to do this in Photoshop, but we will use Wally because it's a free alternative. Make sure you index with IrfanView before importing to Wally to avoid problems with color quantization as previously demonstrated.

First, drag your indexed BMP into the main window. You will see the texture's palette represented on the right. Left-click on the color you want and then go to "Colors > Translate Colors...". It will use the source index from the selected color, you can also check which index a specific color uses in "Colors > Edit Palette...". Set the destination index to 255 and enable "Swap Indexes".


After you click OK, you will then see the color move to the last position in the palette. You can now save the image and compile with $texrendermode texturename.bmp masked in the QC file.


Flatshade render mode
This render mode disables vertex shading on a model. You can use this for models with shading completely baked in, or models created with a specific art style that will benefit from no shading. The QC command is $texrendermode texturename.bmp flatshade. This is also not a fullbright mode, so the map's lighting will still affect the model.

There is also an additional QC command $flags 256 that acts similarly to flatshade, but on the entire model instead of on a per-texture basis. It doesn't completely disable vertex shading but significantly reduces the effect. It was used for flying monsters in Half-Life because the player will be looking up at these models from below.


Combining render modes
You can mix texture render modes for a specific type of material or to create certain effects. The map dome and the golden AK in the following examples use chrome and additive on a secondary mesh layer:
Chrome dome
Golden AK-47
Importing and exporting with Blender
We will be working with Blender in this guide because it's free software, and the Blender Source Tools addon to import and export SMDs. Once you have installed the addon, go to "File > Import > Source Engine (.smd, .vta, .dmx, .qc)". If you import a QC file, you will import all resources used at the same time.

There is a bug with Blender Source Tools that won't import bone orientations correctly for some models, especially those created with Milkshape 3D and fragMOTION. You may notice this if the bones are rotated in different directions, they can be flipped 180 degrees in some cases. When you check the animation pose you will see the mesh deforming incorrectly, which also affects the model after compilation.



If you get this problem you can do the following to fix it. First, import your mesh SMD with the bone flip issue. Next, open the same SMD in a text editor and copy all data from the start until the end of the skeleton block (don't copy the triangles block). Paste that into a new file and save what you have into a brand new SMD. Then you can go back into Blender and import your new SMD. Select the armature, go into pose mode, select all bones, hit Ctrl+A, and click "Pose as Rest Pose". Another workaround involves importing an animation onto a new armature, then deleting that armature and applying the action to the original import.

Lastly, be aware that Blender uses Y-up for the local bone axis. If you import an SMD that was created with Z-up for bones, the bones will be displayed sideways. If you are just animating then it won't be a problem for you, you can leave the skeleton as-is and not mess with it.

Creating your own skeletons using Y-up in Blender will be fine in most cases, but you may have problems with bone controllers, motion extraction, hitboxes and jigglebones. This is because the compiler will use Z-up for bones. The workaround for this is to export and re-import using the FBX format with different bone axis settings. This may not work for all models, so it's worth playing around with the import axis settings to get the correct orientation you need.


If you are still struggling with Blender's idiosyncrasies then it may be worth switching to 3ds Max and using Wall Worm instead, there will be no convoluted setup, and no issues with weight links or bone flipping.

Before exporting, make sure that you have applied transforms to your objects to avoid issues with rotation or scale. Set the "Export Format" option to SMD. Your "Target Up Axis" should be set to Z, and "Target Engine" to GoldSrc. In the source engine exportables section, click on the listed animation and set a subfolder. Click on "Action Filter" and set it to *, this will export all animations at the same time. Make sure the "Implicit Motionless Bone" option is unchecked. Finally, export with "Scene export" to get all the files.


The implicit bone feature creates a new root bone for the skeleton which can cause all sorts of problems when compiling, such as the illegal parent bone replacement error. Note that exporting multiple skeletons is not supported, Blender Source Tools can only export one.
Writing a QC file — part 1
The QC compile script contains a list of commands passed to the compiler. These reference the model's components and properties such as bodygroups for meshes, sequences for animations, events, texture render modes, and more. The file is also case-sensitive, all commands must be in lowercase or you will get errors. There are many more commands you can use and extra command parameters that aren't listed there, click here[the303.org] for a full list of commands.

General commands
You can add comments to a QC file, but they have to start from a new line:
// Valid comment $flags 0 $flags 0 // Invalid comment

The compiler will only accept a single QC file, but you can include others into the main one for more complex models if necessary:
$include ".\model.qc"

The name of the compiled model is defined with $modelname, including the file extension:
$modelname "model.mdl"

The compiler also needs to know the directory to work from. Generally, your SMDs and textures will be located in the same folder as the QC file so you can use a dot path segment:
$cd "." $cdtexture "."

Old compilers used $cliptotextures to stop cutting the texture size to the bounds of the UV map, which caused problems with visible edge lines. The Sven Co-op compiler no longer does this so the command is obsolete, but it's worth mentioning because Crowbar can still generate it with decompiles:
$cliptotextures

You can multiply the scale of the model, this is 1.0 by default:
$scale 1.0

Mesh parts
To define parts in a model, you can use $body. The name of the part is "part1", while "mesh1" is the path to the SMD file used, without the extension.
$body part1 "mesh1" $body part1 "mesh2" $body part1 "mesh3"

The following is a longer way of doing the same thing. If there is only a single mesh in a $bodygroup, it will act the same as $body and be put in the same main part:
$bodygroup "part1" { studio "mesh1" } $bodygroup "part1" { studio "mesh2" } $bodygroup "part1" { studio "mesh3" }

Within a part, you can also declare multiple meshes called submodels. These are used for small variations on a model, for example, the bomb backpack on Counter-Strike playermodels, or unique mesh sets entirely like in the case of gibs.
$bodygroup "part1" { studio "mesh1" studio "mesh2" studio "mesh3" }

Flags
You can define flags that can give the model special effects, such as $flags 4 for a Quake-style blood trail, or the no shadelight $flags 256 discussed earlier. Check the linked page for a full list of flags.
$flags <id>

Animations
Animations are defined with $sequence. You need at least one sequence to compile a model even if it's a static prop. The name of the sequence is "animname", and "anims\animation1" is the path to the SMD file used, without the extension. You can set the fps of the animation, and for looping animations add "loop" and the end.
$sequence "animname" "anims\animation1" fps <fps> [loop] $sequence "animname" "anims\move" fps 30 $sequence "animname" "anims\rotate" fps 20 loop

Animations can also be written out as follows for easier readability:
$sequence "animname" { "anims\animation1" fps 15 loop }

For a full range of motion, playermodels in Counter-Strike blend 9 different poses together within a specific range on an axis. For linear blending you can use X, Y and Z. For rotation use XR, YR and ZR. Don't change the order of the sequences because it will break the playermodel in-game.
$sequence "crouch_aim_carbine" { "leet_anims\crouch_aim_carbine_blend01" "leet_anims\crouch_aim_carbine_blend02" "leet_anims\crouch_aim_carbine_blend03" "leet_anims\crouch_aim_carbine_blend04" "leet_anims\crouch_aim_carbine_blend05" "leet_anims\crouch_aim_carbine_blend06" "leet_anims\crouch_aim_carbine_blend07" "leet_anims\crouch_aim_carbine_blend08" "leet_anims\crouch_aim_carbine_blend09" blend XR -90 90 fps 30 loop }

Playermodels also have activity tags prefixed with ACT_ that mark certain animations to be used when a hitbox is triggered. These are weighted with a number after them, it's always 1 in Counter-Strike. Check the linked page for more information.
$sequence "head" { "leet_anims\head" ACT_DIE_HEADSHOT 1 { event 2001 1 } fps 30 }

Bone attachments
Attachments are used to define the position at which events happen. For playermodels, the attachments are offset from the character's hand bones where the weapon would be. For v_models, you can set the attachment's parent to the bone the weapon is skinned to. If you are working with an existing skeleton that already has a bone for muzzle flash, use that instead.

Most models in Counter-Strike use attachment 0 for muzzle flash and attachment 1 for case ejection. Some weapons have more attachments, by default the USP and M4A1 use attachment 2 for the silencer's muzzle flash:
//Unsilenced muzzle $attachment 0 "bonename" <x> <y> <z> //Case ejection $attachment 1 "bonename" <x> <y> <z> //Silenced muzzle $attachment 2 "bonename" <x> <y> <z>

The Dual elites use four attachments, where attachment 0 and 1 are for the muzzle flash, attachment 2 and 3 are for case ejection:
//Left muzzle $attachment 0 "bonename" <x> <y> <z> //Right muzzle $attachment 1 "bonename" <x> <y> <z> //Left case ejection $attachment 2 "bonename" <x> <y> <z> //Right case ejection $attachment 3 "bonename" <x> <y> <z>

Events
Events are used to trigger interactions that happen during animations, such as a muzzle flash. Events are defined inside sequences, and the position of an event on the model is determined by a bone attachment. The event ID is the type of event, you can also control the frame it triggers at, and set options such as the type of sprite, or the path to a sound file.
$sequence "animname" { "anims\animation1" { event <id> <frame> "options" } fps 30 }

Generally, only event IDs in the 5000 range are used for Counter-Strike, these are client-side events. You can also experiment with other event IDs depending on your design. Check the linked page for a full list:
ID
Type
Options
5001
Muzzle flash on attachment 0
Sprite scale and sprite number
5002
Spark on attachment 0
N/A
5004
Sound effect
Path to WAV file
5011
Muzzle flash on attachment 1
Sprite scale and sprite number
5021
Muzzle flash on attachment 2
Sprite scale and sprite number
5031
Muzzle flash on attachment 3
Sprite scale and sprite number

Case ejection in Counter-Strike doesn't use an event, instead, it's hard-coded to a specific attachment. The spark seems to be locked to attachment 0, it also leaves a trail after model movement. For muzzle flashes, you can set the size and type of the sprite. The first digit controls the size, with 1 being the smallest. The second digit controls the sprite type, you are limited to 0-3 because there are only four. For example, "12" will create a tiny X-shaped sprite whereas "31" will create a large circular sprite:

$sequence "shoot1" { "v_awp_anims\shoot1" { event 5001 0 "31" } { event 5004 15 "weapons/boltup.wav" } { event 5004 19 "weapons/boltpull1.wav" } { event 5004 32 "weapons/boltdown.wav" } fps 35 }
Writing a QC file — part 2
Hitboxes
The compiler automatically creates hitboxes around every bone based on the size of the mesh skinned to it. If you are creating p_models or playermodels from a decompiled skeleton then it's better to keep to the default $hbox values in your generated QC. Make sure the mesh doesn't go outside of the hitboxes or you will get a consistency error on servers that use default models and don't set mp_consistency 0.
$hbox <group> "bonename" <x> <y> <z> <x2> <y2> <z2> $hbox 3 "Bip01 Pelvis" -4.69 -4.44 -6.75 4 5.56 6.75 $hbox 6 "Bip01 L Thigh" 2.66 -3.69 -3.09 18.16 4.88 3.31

Hitboxes have groups that determine the type of hitbox, which will trigger certain animations when hit:
Hitgroup
Type
Activity Tags
0
Generic
N/A
1
Head
ACT_FLINCH_HEAD
ACT_DIE_HEADSHOT
2
Chest
N/A
3
Stomach
ACT_FLINCH_STOMACH
ACT_DIE_GUNSHOT
4
Left Arm
ACT_FLINCH_LEFTARM
5
Right Arm
ACT_FLINCH_RIGHTARM
6
Left Leg
ACT_FLINCH_LEFTLEG
7
Right Leg
ACT_FLINCH_RIGHTLEG

Clipping box
The clipping box controls the extent that the model should be rendered in-game. They were used for some Half-Life monsters like the xen tentacle and the barnacle. Most models in Counter-Strike don't need a clipping box, and v_models don't need one either, so you can compile your model with a collapsed clipping box:
$cbox 0 0 0 0 0 0

The latest Sven Co-op compiler can have problems compiling a clipping box, see the "Fixing errors" section for more details.

Bone controllers
Sometimes the game needs finer control over the movement of a bone when something happens, so it uses bone controllers to achieve this. In Counter-Strike, bone controllers are used in playermodels for the mouth, this is linked to the player's voice chat. You are limited to 8 bone controllers.
$controller Mouth "Bone01" ZR 0 30

Texture render modes
You can set the render mode for a texture using $texrendermode. The name of the texture is the path to the BMP file used, with the extension.
$texrendermode <name> <render mode> $texrendermode texture1.bmp chrome $texrendermode texture2.bmp additive $texrendermode texture3.bmp masked $texrendermode texture4.bmp flatshade
Compiling a model
We will use the compiler from the Sven Co-op SDK for our models. It's the best choice to use because of new QC commands and some additional bug fixes that none of the other compilers have. Valve's original compiler had countless bugs, some of which were discussed in the "Decompiling a model" section, but the worst offender was the UV shift bug that moved the entire UV map away by 1 pixel. There was a new fork of the compiler created by DoomMusic that fixed the UV shift issue. It also automatically padded textures until they were a power of 2, but that compiler is obsolete since the 2019 engine patch that fixed the gl_round_down issue.

The Sven Co-op SDK's compiler no longer cuts the texture to the UV map bounds, and works with models that have UV islands outside of 0-1 space, allowing for tiling and mirroring. It also fixes vertex precision at small scales. The change was due to issues in some models where vertices would get crunched and distorted after compiling. This was most noticeable in v_models with tiny details like scopes or barrel porting:


For a more general example, the cone and Suzanne in the following example are scaled to a very tiny size, the vertices also get distorted after compile:


Compilation process
We will be using Crowbar as a wrapper for the compiler. Go to the compile tab and set the "Game that has the model compiler" option to the correct one. If you haven't set it up then check the "Setting up Crowbar" section for more details. Next, select the QC file(s) to compile. You can set the location of the output to preference. Then click "Compile" and your new model will be created.

Fixing errors
There are a lot of problems you can get when compiling a model, and some that can cause the game to crash. Only the most common ones with the Sven Co-op compiler will be discussed here, for a full list of possible errors, click here[the303.org].

GL_UPLOAD 16
You compiled a model with an incompatible resolution (not divisible by 16). Most of the models from the Half-Life SDK will crash your game when compiled because of this. Instead of fixing the textures, Valve just put a small hack into the compiler which would copy the leading edge and paste it until it was divisible by 16. This hack was removed from the Sven Co-op compiler, you should always manually ensure your textures have a valid resolution.

Illegal parent bone replacement
The skeleton in an animation doesn't match the skeleton in a mesh, it most commonly happens if you modify the skeleton without re-exporting both the mesh and animation SMDs. Sometimes this can happen when a bone has no vertices skinned to it, such bones will be ignored by the compiler. You can override this behavior with the $keepallbones command in the QC file. Another cause for this would be the root bone changing in one of the SMDs if you exported it with Blender Source Tools. Uncheck the "Implicit motionless bone" option before exporting.

Incorrect clipping box
The Sven Co-op compiler can sometimes have problems generating a clipping box for a model. If you still need a clipping box for whatever reason, you can first make a temporary compile with an older version of the compiler[the303.org], and then get the correctly auto-generated cbox value directly from that. Note that this version is before the improved vertex precision update, don't rely on it full-time.
Model viewers
Model viewers are used to preview models outside of the game, but most are capable of making basic changes to models without decompiling. You can replace textures, edit the mesh origin, add events, etc. The best choice for a model viewer is Half-Life Asset Manager because it has the most features, good performance, and regularly receives bug fixes. If you aren't able to run HLAM for whatever reason, then you can try HLMV Standalone which is compatible with more systems.

Other model viewers include the Paranoia 2 model viewer built for Xash3D engine, this one has fewer features than HLAM. There is also Jed's HLVM which is very old and buggy. It doesn't display some models properly and has a memory leak and doesn't shut down properly.

Editing with a model viewer
We will use v_ak47 for demonstration purposes to go through some of the things you can do with a model viewer. This will be a quick recolor of the default texture. When you open a v_model, it will be set to first-person perspective by default but you can use the other camera perspectives in the cameras tab. Note that you can't open T-models or sequence models in a model viewer as it will crash or show an error.


The most common thing people use model viewers for is to replace textures. In the texture tab, you will be able to see a list of all textures used in the model. Click on "Export All Textures" in order to save them.


Before you import, make sure the aspect ratio of the new texture is the same as the old one. If you replace a 256x64 texture with a 512x512 texture, the UV map will be distorted (should have been 512x128 instead, or at most 1024x256).


The texture render mode can also be set. This glove will be replaced with chrome. After you import a chrome texture, make sure you enable the "Chrome" texture type, and "Flatshade" if you want to disable vertex lighting.


In this model, the muzzle flash attachment is slightly off-centered and I will set new coordinates in the attachments tab. The green square is the attachment, and the line drawn from it is towards the coordinates of the parent bone. You can even translate and rotate bones, but this isn't useful in most cases.


HLAM is also capable of adding, deleting and editing events. You can control when and where events happen. In the sequences tab, you will see a list of events, the frame they occur at, the type of event, and the scale option. If you go to the attachments tab, you can select the one you need and click "Highlight Attachment" to see it. Check the "Miscellaneous" section for how it all works.
The size of the muzzle flash sprite will be increased to 3 for each shoot animation.


One other thing you can do is set the bounds of hitboxes, and change the hitgroup depending on your use case. v_models don't seem to use hitboxes for any game purpose, but they are still included for the arms by default.


Finally, the origin of the model can also be shifted with the transformation tab. This tab is hidden by default in the current version of HLAM, you can enable it with Alt+M or find it under "Asset > Dock Widgets > Transformation". It can also be helpful to disable "Show Off-screen Areas" in the model display tab and set your aspect ratio (4/3 for normal or 16/9 for widescreen) to simulate in-game display. Test your new origin during gameplay to avoid clipping/floating arm issues.


Aside from moving the origin, you can also rotate and scale your model. Scaling supports meshes, bones, attachments, and more. Generally you scale everything at the same time but it depends on the model and what you want to achieve.

Make sure you save the model after you finish! The textures went through small hue, contrast, and dodge & burn adjustments in a few places before being upscaled, very basic 20-min recolor but enough for tutorial purposes. This model will still look distorted in some areas, like in front of the magazine, but that's up to the UV map which can't be controlled with the model viewer.

9 Comments
Pookaball 17 Aug, 2024 @ 2:06pm 
pretty comprehensive guide, thanks
seedee  [author] 27 Oct, 2023 @ 7:18am 
I cant think of any that will let you do that. Some people use mdlflip in order to invert, but I would always recommend manually mirroring in modeling software
[GC.SKINS] ElTamales 26 Oct, 2023 @ 11:48pm 
I remember that there is a "$" to invert the v_model, do you know which one it is?
🎲 KROML 🎲 31 Jul, 2023 @ 12:47pm 
Красавец, вовремя забитый хуй - залог успеха. Лайк поставил!
seedee  [author] 31 Jul, 2023 @ 9:22am 
хотел перевести но поленился
🎲 KROML 🎲 31 Jul, 2023 @ 4:35am 
Нихуя не понял, но очень интересно :csgo_headshot:
Zlorak 28 Jul, 2023 @ 9:37pm 
Amazing! I knew modeling was complicated, but not THIS complicated. Kudos for making a guide easy to read, and not overwhelming for beginners. Awarded!
EnSabo4do SKINS 26 Jul, 2023 @ 5:51pm 
such a good guide bro, +rep