How the Mask, Clutter, and CfgSurfaces work together - pennyworth12345/A3_MMSI GitHub Wiki
Preface
The goal of this reference page is to give a detailed description of how the surface and clutter tecnologies work, as well as a start to finish guide on the implementation of clutter.
A common notation used below is Root >> Deeper >> EvenDeeper
, which is used as shorthand to express nested classes, like so
class Root
{
class Deeper
{
class EvenDeeper
{
};
};
};
The examples given below contain comments, which are denoted by
// this is a comment, which is ignored by the program/game, and is used to annotate segments of configs or code
/* this is a block comment, everything between the asterisks is ignored by the program/game */
Overview from mask image to clutter in-game
How mask images are used by Terrain Builder (TB)
The first place to start is the role of the mask image. Mask images are used to tell the game where specific surfaces should be and what texture should be used. Every unique surface type on your terrain starts as its own color on the original mask image. Each of those colors is then defined in your layers.cfg
along with which material (RVMAT) to use. It's essential that the colors used on your mask are exact matches to colors defined in your layers.cfg
. This is because in the event that TB doesn't find a match, it'll find the next "closest" color on the maplegend.png
, which is defined in your layers.cfg
. Below you'll find an example of a common issue when using "brush" tools in image editing programs.
Incorrect | Correct |
---|---|
![]() |
![]() |
For each unique surface on your mask you'll define a material (RVMAT) for it in your layers.cfg
.
class Layers
{
// list of all surfaces on your terrain
// this corresponds to the name of a surface below in Legend >> Colors >> yourtag_someSurfaceName
class yourtag_somegrasssurface
{
// texture doesn’t matter unless you're using
texture = "";
material = "yourtag_yourmap\data\yourtag_somegrasssurface.rvmat";
};
// more of your surfaces
};
class Legend
{
picture = "yourtag_yourmap\source\mapLegend.png";
class Colors
{
// defining what color on your mask corresponds to the surface
// Surface_Name_From_Class_Layers = {{ Red, Green, Blue }};
// matches the name of a surface from the above Layers >> yourtag_someSurfaceName
yourtag_somegrasssurface[] = {{ 23, 243, 69 }}; // bright green
// more of your surfaces
};
};
It's also worth noting that a surface in Legend >> Colors
can have more than one color associated with it. For instance, the following would result in the surface called yourtag_othersurface
to be associated with the colors red (255, 0, 0), green (0, 255, 0), and blue (0, 0, 255) on your mask.
yourtag_othersurface[] = {{ 255, 0, 0}, { 0, 255, 0}, { 0, 0, 255}};
The RVMAT for the surface called yourtag_somegrasssurface.rvmat
contains two "stages",
// this is what your yourtag_yourmap\data\yourtag_somegrasssurface.rvmat might look like
// more stuff before this
class Stage1
{
texture = "yourtag_yourmap\data\yourtag_somegrasssurface_nopx.paa";
// more stuff after this
};
class Stage2
{
texture = "yourtag_yourmap\data\yourtag_somegrasssurface_co.paa";
// more stuff after this
};
When you generate layers in TB, it takes your original satellite, mask, and normal (if you're using a normal map) images and splits them into "tiles". The size of the tile and how many tiles are created is based on your map frame properties. At this point, TB reads the surface RVMATs from your layers.cfg
(e.g. yourtag_somegrasssurface.rvmat
) and creates one or more RVMATs for that specific tile (e.g. p_000-000_l00.rvmat
). The generated tiles (e.g. p_000-000_l00.rvmat
) point to the textures yourtag_somegrasssurface_nopx.paa
and yourtag_somegrasssurface_co.paa
, instead of the original yourtag_somegrasssurface.rvmat
. After you've finished generating layers, the original surface RVMATs, such as yourtag_somegrasssurface.rvmat
, are no longer used.
The colors you used on your original mask and layers.cfg
, as well as the names for surfaces you used within your layers.cfg
, aren't used anywhere passed this point. When the game loads p_000-000_l00.rvmat
, it only knows about the textures yourtag_somegrasssurface_nopx.paa
and yourtag_somegrasssurface_co.paa
. It knows nothing about what color yourtag_somegrasssurface
was on your original mask, or was it was called in your layers.cfg
.
Before going into further detail about how CfgSurfaces
, CfgSurfaceCharacters
, and Clutter
config entries work together, here is an image that summarizes their connections.
Class CfgSurfaces
You define properties for that surface in CfgSurfaces
, for example:
class CfgSurfaces
{
class Default;
class yourtag_somegrasssurface: Default
{
files = "yourtag_somegrasssurface_*";
character = "yourtag_somegrasssurface_character";
soundEnviron = "grass";
soundHit = "soft_ground";
impact = "hitGroundSoft";
};
//more surfaces
};
With regards to getting clutter to work, the important things to note here are files
and character
.
- The
files
corresponds to the name of the textures used in your surface's RVMAT (yourtag_somegrasssurface.rvmat
), in this caseyourtag_somegrasssurface_co.paa
, oryourtag_somegrasssurface_nopx.paa
. This is why it's important you tag the name of your textures, if you don't, you could easily mess with vanilla or third party terrains if you are using the same texture or surface names. - The
character
corresponds to an entry in yourCfgSurfaceCharacters
. In this example, the character is defined asyourtag_somegrasssurface_character
. If you'd like a surface to have no clutter then you can usecharacter = "EMPTY";
, which is a "character" defined by BI that has no clutter.
For more information on what each property within CfgSurfaces
does, see the CfgSurfaces Config Reference on the official wiki.
Class CfgSurfaceCharacters
The properties of a character
in CfgSurfaces
is defined in CfgSurfaceCharacters
. You should notice that in the example below, the entry for yourtag_somegrasssurface_character
in CfgSurfaceCharacters
matches the character
that was defined in the CfgSurfaces
shown above.
class CfgSurfaceCharacters
{
class yourtag_somegrasssurface_character
{
probability[] = {0.2, 0.4};
names[] = {"yourtag_grassgreen_c", "yourtag_thistlethorngreen_c"};
};
//more surface characters
};
Each index in the names[]
corresponds to an index in the probability[]
. The sum of all values in the probability[]
must be <= 1. To clarify the above, here is a small table just to demonstrate which name corresponds to which probability from the example above.
Name | Probability |
---|---|
yourtag_grassgreen_c | 0.2 |
yourtag_thistlethorngreen_c | 0.4 |
So now your character is defined, but the game needs to know which models to actually place on your terrain. The entries from names[]
point to classes defined in CfgWorlds >> yourtag_yourmap >> clutter
.
Class Clutter
You define the actual clutter models that will be used in CfgWorlds >> yourtag_yourmap >> clutter
.
class CfgWorlds
{
class someBaseTerrain;
class yourtag_yourmap: someBaseTerrain
{
//lots of stuff before this
class DefaultClutter;
class clutter
{
class yourtag_grassgreen_c: DefaultClutter
{
model = "A3\plants_f\Clutter\c_Grass_Green.p3d";
// more config entries here for your clutter
};
class yourtag_thistlethorngreen_c: DefaultClutter
{
model = "A3\plants_f\Clutter\c_StrThornGreen.p3d";
// more config entries here for your clutter
};
};
};
};