Propper++
The Propper++ tool in Hammer++ allows you to convert brushwork into model geometry, or merge model geometry together. It serves as a complete replacement for the old Propper tool.
Setup
Before using the tool, the StudioMDL executable must be configured in Tools -> Options -> Build Programs -> MDL executable. This is the compiler that will output new models for the engine.
bin
folder rather than bin/win64
.If you get a command failure later with return code 0x2, this is why.
bin
folder rather than bin/x64
.Basic usage
Brushes
First, select the brushes you want to turn into models. If you want them to be combined into 1 model rather than individual models, you need to group them first, either via the Group tools or by converting them into a brush entity.
Then open Propper++ in the Tools -> Propper++
menu or with Ctrl + Shift + P
keybind.
You'll see a bunch of options. You don't need to worry about most of these except for the Model Path + Name
option at the top.
The name at the end will be the name given to your model. Change it to something appropriate for your model, like mycoolchair
.
Collision will be automatically generated. If you don't want collision, set Entity name as physics mesh to blank.
Press Build + Compile
, and the brushes should now be successfully turned into a model after the compiler finishes.
The old brush geometry will be saved into the Propper
visgroup. This can be used to preserve or recover the original geometry if needed.
Prop scaling
In addition to converting brushes, Propper++ supports conversions or merging with selected models too. Models that are run through Propper++ will have their skins, body groups and scaling baked in. A useful application of this is to quickly scale prop_static entities in games that don't support it, without the old tedious way of decompiling and then recompiling models.
To scale a prop_static uniformly, change it into a prop_dynamic first so you can change the Model Scale, or add the modelscale keyvalue with SmartEdit disabled.
Alternatively, change the Scale
option in the Propper++ menu. You can use negative values here to mirror the prop along an axis.
Merging brushes and models
It's also possible to combine both brushes and models together. To do this, you need to group the selection using the Group tools and then run the tool as normal.
Advanced usage
Editing before compiling
It can be useful to edit the generated model files to add extra options such as $texturegroup or to further edit the meshes. Propper++ supports this by having a two-step process, Build and then Compile.
Usually both steps are run at the same time, but they can be individually run too.
- Build will generate the QC and SMD files for the model.
- Compile sends the generated files to the model compiler and then replaces the selection (if enabled).
Therefore you can press Build-Only, edit the files, and then Compile-Only to send it to the compiler.
Adjusting model origin or illumposition
By default, the generated model's origin and illum position is placed at the center of the selected geometry's bounding box.
If using the Propper++ tool on a brush entity, and the entity has an origin
keyvalue, that value will be used instead.
The illum position can be overriden by specifying a custom lighting origin on any selected model. Propper++ will read the first one it finds and use that as the new $illumposition.
Adding skins
Open your custom QC that was created by Propper++ and add this following code somewhere in the file:
$cdmaterials "models/mycustomfolder" "models/lorem/mycustomfolder2" $texturegroup "skinfamilies" { { "default_texture" } // Skin 0 - Default { "custom_texture_1" } // Skin 1 { "custom_texture_2" } // Skin 2 { "custom_texture_3" } // Skin 3 [...] }
the default (skin 0) should be the default texture and skin 1-32 should be the texture you want to replace.
If you want to replace more textures than 1 texture you can add this following code:
$cdmaterials "models/mycustomfolder" "models/lorem/mycustomfolder2" $texturegroup "skinfamilies" { { "(1)random_texture" "(2)random_texture" [...] } // Random textures that are default { "baggagecarousel04" "helicopter_news2" [...] } // Example { "baggagecarousel04" "acvent05" [...] } // Example { "acunit01" "acvent05" [...] } // Example [...] }
The first column will replace the first default texture (1), while the second column will replace the second texture (2).
After adding the skins, you must press Compile Only to re-compile the model.
Mass conversion
When multiple groups of brushes are selected, each of these will be converted into their own individual models. A warning will appear in the Propper++ menu when mode is active. The model names will have a numerical suffix appended automatically when generating.
Displacements
Displacements can also be converted by Propper++. However unlike brushes, collision will not be built for them. This is intentional as auto-generated collision models for displacements would be very costly and potentially cause server lag (every triangle needs to be its own convex collision piece). If you require collisions for displacements, built a simplified one from brushes and specify it using the Entity name as physics mesh
setting.
Another limitation is that blended textures will also not appear after conversion, as models don't support any vertex blending.
Static prop combine
Propper++ can also be used to combine props for optimization, similar to Static Prop Combine in newer games. A demonstration of the performance gain from merging static props together is shown on the right.
Combining improves performance by reducing draw calls. Draw calls in a nutshell are how the CPU tells your GPU to render something. However, this communication is costly and modern games usually aim to reduce draw calls as much as possible. Every prop in the game will issue a new draw call. If there is multiple materials in a prop, a new draw call is issued for each material. By combining props, draw calls are reduced. Valve claimed that by employing static prop combine in their CS:GO maps (which are very heavy on static prop usage), they gained a 40% performance improvement.
When considering models for merging, make sure to follow these principles:
- You should only merge models that share the same materials, otherwise you will not get any benefit from static prop combine. Different materials still have to issue a new draw call. For example, don't merge a tree with a box, but do merge boxes with other boxes.
- Merge models that are within "clusters" or within vicinity of each other. Merging models will expand the bounding box and make it less likely to be culled when behind areaportals or out of view etc.
- Avoid merging models that have collision and are very spread out from each other, as the physics engine cannot optimize this efficiently. Disable collision meshes if you need to do this, or build simplified ones with tool textured brushes.
- Don't combine across shadows unless the models support per-vertex lighting (i.e. none of the materials have $bumpmap, $normalmap, or $phong.) The combined prop will only sample lighting from its center and this may look awkward across a shadow divide. Alternatively, split the combined prop in half along the shadow boundary so both sides receive correct lighting.
To easily identify clusters of props that you can merge, use the r_colorstaticprops 1
command in-game. Each unique color of a prop means they are issuing a new draw call. After merging props, you will notice that they share the same color (which is good).
If you are encountering bad lighting with the combined props, such as it turning very dark, this may be because of the lighting origin being embedded inside geometry. To fix this, assign a lighting origin entity which is in a more favorable spot.
Lightmapping static props
In the aforementioned branches of the engine, prop_static supports having a lightmap generated by VRAD. However this comes with major limitations:
- Props with overlapping UVs or multiple materials do not get lightmapped correctly.
- Brush geometry converted into models can't be lightmapped correctly due to distorted UVs.
- Materials that use $bumpmap or $phong cannot be lightmapped.
The Lightmap Atlas bypasses all these limitations. It takes the selection of models and/or brushes, and generates a singular non-overlapping UV (an 'atlas') for the whole model, and then projects the textures onto it. It strips away bumpmap and phong as well from the material automatically.
To use this feature, first enable Generate atlas.
Change the width and height of the atlas as desired. This will wildly vary depending on how your geometry looks, how many you are merging etc, so experiment with this. Keep it at a power of 2. Higher resolutions will have better texture quality, but come with bigger file sizes. This is not the size of the lightmap, that is separately defined in the prop_static settings later.
There is 4 formats to choose from: DXT1, DXT5, RGB888, RGBA8888. DXT formats are compressed, the others aren't and hence come with better quality but much bigger file size. DXT1 and RGB888 have no alpha channel which saves file size. You shouldn't use DXT5 or RGBA8888 unless you need the alpha channel preserved (perhaps for something like $envmap masking later).
The padding option is the number of pixels to leave between UV islands in the texture. Increasing this will reduce the possibility of light bleeding but reduce texture quality. As a rule of thumb, the lower the lightmap resolution assigned to the prop, the higher this padding should be set to prevent bleeding.
Finally, the option for bilinear filtering can be turned off for a 'sharper' texture look, however this is subjective and depends on the texture and model. Some look better without this and others don't. Usually you should keep this on.
After building the model, it will now be ready for lightmapping. See the prop_static page for more details. If the texture looks too blurry, increase the atlas resolution or reduce the number of props you are combining together.
Atlas textures are saved to the Material Path + the Model Name if you want to inspect how they look.
Baking blended textures from displacements is currently not supported.
Options
- Model Path + Name
This is the directory and name of the model that will be generated. It is relative to the game's models
directory, so for example with Garry's Mod, "propper/mycoolchair" will generate the model into this location:
.../steamapps/GarrysMod/garrysmod/models/propper/mycoolchair.mdl
Any $vmf
markers will be replaced with the current VMF name. This is useful for organization and to prevent conflicts between maps if they happen to share the same model name.
- Add incremental suffix if already existing MDL name is found
When enabled, if a MDL with the same name as the Model Path + Name is found to already exist, an incremental suffix will be added to the model name (such as _0, _1.. .etc). The suffix will be incremented until a non-conflicting name is found. This is useful to prevent accidentally overriding existing models, as the files would not be recoverable afterwards. Disable this option if you specifically want to override a model with new files (alternatively you can also just delete the existing MDL file).
- Source file directory
This is where the files required for the model compiler will be placed. Similar to above, this can be either relative to the game's directory or you can put in a full path to anywhere on your drive. It supports $vmf
markers as well.
Example with Garry's Mod, "modelsrc/propper/rpassets" will put the source files, which consist of SMD and QC files, in this location:
.../steamapps/GarrysMod/garrysmod/modelsrc/propper/rpassets/[files]
- Material path
This is the directory where converted materials will be placed. Brush materials usually use LightmappedGeneric shaders which are not compatible with models, so Propper++ will convert these shaders into VertexLitGeneric. Like before, it is relative to the game directory but inside the materials
folder, and it supports $vmf
markers. Example with Garry's Mod, "models/propper/rpassets" will generate materials into this location:
.../steamapps/GarrysMod/garrysmod/materials/models/propper/[files]
- Entity name as physics mesh
This allows you to specify what named entity should be used as the collision model. This is useful to make simplified physics models for very complex geometry. Setting this to $self
will use the selected entity as the collision model. Setting it to blank will generate no collision. Entity names will not work when multiple groups of solids are selected.
- Entity classname
If Create entity after compile is enabled, this will replace the selection with a new entity that has the generated model set.
- Surfaceprop
The $surfaceprop for the model. If blank, the texture that covers the most area will be selected automatically as the surface prop. If models are in the selection, it will use the surfaceprop of the most occuring model.
- Scale
The scaling along X, Y and Z for the model. Negative values are supported and will apply a correction to flip faces into the right direction.
- Mass
Overrides mass if not blank. Otherwise, it's calculated automatically by the model compiler with $automass.
- Smoothing angle
Smooths brush vertex normals that are less than this angle apart, similar to -smooth
in VRAD. 0 disables smoothing. Useful for round shapes like cylinders, etc.
- Relocate materials
If enabled, moves brush materials into the folder specified by the Material path. This prevents normal materials from being overriden by the new converted ones.
- Rename shaders
If enabled, LightmappedGeneric, WorldVertexTransition and Water shaders will be renamed to VertexLitGeneric.
- Remove $bumpmap/$phong
If enabled, $bumpmap and $phong will be removed from the converted materials. This allows per-vertex lighting to work on prop_statics, which doesn't work unless both of these are disabled.
- $concave collision
Enables $concave collision models. If disabled, the model compiler will 'shrinkwrap' the model to create a new collision model.
- Remove bad collision faces
Tiny or thin brush faces can cause issues when compiling the collision. This will automatically remove such faces (simply adds $remove2d
to the $collisionmodel)
- Skip nodraw faces
Skips nodraw faces from being added to the visual geometry. They are still added to the collision model though.
- Wait for keypress after compile
If set, waits for a keypress before closing the compiler window output. Useful to check for warnings or errors.
- Create entity after compile
If enabled, this will replace the selection with a new entity that has the generated model set.
- Move selection to 'Propper' visgroup
If enabled, the selection is preserved to the visgroup named 'Propper' after compiling.
- $casttextureshadows
Adds the $casttextureshadows command to the QC. Useful to have transparent shadows working in VRAD (with -textureshadows
) without having to add the model into lights.rad manually.
- Lightmap Atlas
See the advanced usage section above.
Keyvalues
The following KVs are used by Propper++:
- Origin (origin) <origin>
- If there is only one entity in a given group, the origin KV is used to override the output SMD model's origin.
- Pitch Yaw Roll (Y Z X) (angles) <angles>
- Orientation of the output SMD model.
- Name (targetname) <targetname>
- Lighting Origin (lightingorigin) <targetname>
- Set the $illumposition based upon the origin of the defined named entity.
- Model (model) <model path>
- MDL to use.
- Skin (skin) <integer>
- Skin of the MDL to use.
- Bodygroup (body) <integer>
- Bodygroup of the MDL to use.
- Uniform Model Scale (modelscale) <float>
- Scale the output SMD model uniformly on all axes.