Specification: RMF Last edited 1 year ago2023-07-22 00:03:08 UTC

You are viewing an older revision of this wiki page. The current revision may be more detailed and up-to-date. Click here to see the current revision of this page.

RMF Format Specification

An alternative to the MAP format which provides some more features such as saving Visgroups, object grouping, and paths.
All values are stored in little-endian format (least significant byte first), the same format used by x86/x64 CPUs.

Definitions

// Length-prefixed string
typedef struct {
    u_char length;          // The length of the string (max 256)
    char[length] string;    // The ASCII-encoded string
} p_char;

// RGB colour
typedef struct {
    char[3] rgb;      // One byte each for red, green and blue channel
} Color;

// RGBA colour
typedef struct {
    char[4] rgba;     // One byte each for red, green, blue and alpha channel
} RgbaColor;

// 3D Vector
typedef struct {
    float x;
    float y;
    float z;
} Vector;

Overall Structure

typedef struct {
    float version;                      // File format version (2.2 is the most recent version)
    char[3] magic;                      // File format magic number, "RMF" in ASCII encoding
    int visgroup_count;                 // Number of VisGroups
    VisGroup[visgroup_count] visgroups; // VisGroups objects
    World worldspawn;                   // The "CMapWorld" object which contains all other MapObjects as children objects
    Docinfo docinfo;                    // This is optional, the file is still valid if this is not present
} Rmf;

Map Objects

Valve used C++ and object oriented programming as their main style, and this shows with the layout of the map objects. They share the same base structure (which we will call MapObjectBase) and the type of each object is indicated by the first field - the object type.
typedef struct {
    p_char object_type;                // CMapWorld, CMapSolid, CMapBrush, CMapEntity
    int visgroup_id;                   // ID of the VisGroup the object belongs to
    Color color;                       // Editor color of the object
    int child_count;                  // Number of child objects
    MapObject[child_count] brushes;   // List of child objects
} MapObjectBase;
CMapWorld and CMapEntity also have the same layout for entity data, which we'll call EntityData.
typedef struct {
    p_char classname;               // The entity's classname
    char[4] unknown1;               // Unused
    int spawnflags;                 // The entity's spawnflags
    int kv_count;                   // Number of entity's key-value pairs
    KeyValue[kv_count] keyvalues;   // Entity's key-value pairs (properties)
    char[12] unknown2;              // Unused
} EntityData;

World (CMapWorld)

World is the root object of the object tree. There will only ever be one World object in the file.
typedef struct {
    MapObjectBase base_data;
    EntityData entity_data;
    int path_count;                     // Number of path objects
    Path[path_count] paths;             // Path objects (created with the Path tool)
} World;

Brush (CMapSolid)

A brush represents a solid object with textured faces. A brush will never have child objects.
typedef struct {
    MapObjectBase base_data;
    int face_count;         // Number of faces (polygons) making up the brush
    Face[face_count]        // The brush's faces
} Brush;

Entity (CMapEntity)

A entity can be either a point entity or a brush entity. A brush entity will have child objects (groups and solids only), a point entity will not.
typedef struct {
    MapObjectBase base_data;
    EntityData entity_data;
    char[2] unknown1;               // Unused
    Vector origin;                  // The origin if it's a point entity
    char[4] unknown2;               // Unused
} Entity;

Group (CMapGroup)

A group is just a collection of objects, and has no other special properties. A group should always have at least one child object, an empty group is considered invalid and should be discarded.
typedef struct {
    MapObjectBase base_data;
} Group;

Other data types

VisGroup

typedef struct {
    nt_char[128] name;      // VisGroup name
    RgbaColor color;        // Editor color of the VisGroup
    int visgroup_id;        // VisGroup's ID number
    _Bool visible;          // Whether the VisGroup is visible or not
    char[3] unknown;
} VisGroup;

Face

The angle value is what has already been applied to the face and is reflected in the right_axis and down_axis. In other words, any texture projected along right_axis and down_axis will already be rotated. You will mostly only use this to undo the rotation done to the texture.
typedef struct {
    nt_char[260] texture_name;      // Name of the texture applied to the face
    Vector right_axis;              // The texture's projected right axis
    float shift_x;                  // Horizontal shift of the texture
    Vector down_axis;               // The texture's projected down axis
    float shift_y;                  // Vertical shift of the texture
    float angle;                    // The angle of the applied rotation
    float scale_x;                  // Horizontal scale multiplier
    float scale_y;                  // Vertical scale multiplier
    char[16] unknown2;              // Unused
    int vertex_count;               // Number of vertices in the face
    Vector[vertex_count] vertices;  // The vertices that make up the face
    Vector[3] plane_points;         // A triplet of points that define the face plane
} Face;

KeyValue

typedef struct {
    p_char key;         // Key of the property
    p_char value;       // Value of the property
} KeyValue;

Path

Paths are placed using the Path tool in Hammer and offer an alternative to placing path_ entities manually. NOTE: Hammer's method of converting paths to entities is incorrect, and this tool should generally be avoided unless using a custom tool such as hlfix to convert your RMF to .map prior to compilation.
typedef struct {
    nt_char[128] path_name;     // The base name for this path
    nt_char[128] classname;     // Typically path_corner or path_track
    int path_type;              // The direction of the path
    int node_count;             // Number of nodes in the path
    PathNode[node_count] nodes;     // The nodes that make up the path
} Path;
The path_name will be used as the base name for the nodes belonging to this path, i.e. for a path named my_path each node will be named my_path01, my_path02, etc.

The path's direction or path_type can have one of three different values:
0: One way (from first to last node and stops there).
1: Circular (from first to last and back to first again, as an endless loop).
2: Ping pong (from first to last and reverse direction back to first, in an endless loop).

PathNode

typedef struct {
    Vector position;                // The node's position in world coordinates
    int index;                      // The node's index in the path
    nt_char[128] name_override;     // Name to use instead of auto-generated one
    int kv_count;                   // Number of node's key-value pairs
    KeyValue[kv_count] keyvalues;   // Node's key-value pairs (properties)
} PathNode;

Docinfo

typedef struct {
    nt_char[8] docinfo;                 // The string "DOCINFO"
    float camera_version;               // Possibly version number of the camera data - always equal to 0.2
    int active_camera;                // Index of the active camera
    int camera_count;                   // Number of camera objects
    Camera[camera_count];               // Camera objects
} Docinfo;

Camera

typedef struct {
    Vector eye_position;        // The position of the camera in world coordinates
    Vector lookat_position;     // The target position in world coordinates the camera will look at
} Camera;

Sources and further reference

1 Comment

Commented 9 months ago2024-02-08 15:40:38 UTC Comment #105968
I noticed a few things while looking into the various .rmf file versions:
  • VIS group colors appear to be stored as RGB, not RGBA - the 4th byte is always 0 in my test files.
  • In v1.6 and v1.8 files, colors are stored as BGR, not RGB.

You must log in to post a comment. You can login or register a new account.