Specification: JMF Last edited 1 month ago2024-03-12 13:03:49 UTC

Jackhammer Map Format is a format created for the Jackhammer/JACK editor. It's very similar to the RMF format, but supports objects being part of multiple VisGroups.

Definitions

// Types
char: 8-bit ASCII character (uint8_t)
byte: unsigned 8-bit integer (uint8_t)
float: 32-bit single-precision floating point value
int32: signed 32-bit integer (int32_t)
int16: signed 16-bit integer (int16_t)

// Length-prefixed string
typedef struct {
    int32 length;           // The length of the string
    char[length] string;    // The ASCII-encoded string
} p_char;

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

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

// 2D Vector
typedef struct {
    float x;
    float y;
} Vector2;

// Editor flags
enum JmfFlags
{
    None =           0x00,
    PointBased =     0x01,   // Or maybe 'HasOrigin'?
    Selected =       0x02,
    Hidden =         0x08,
    RenderMode =     0x10,   // Any other render mode than 'normal'
    IsWorld =        0x20,   // worldspawn entity
    WeaponOrItem =   0x40,   // weapon* and item_* entities
    PathEntity =     0x80,   // path* entities
    Unknown =      0x8000,   // Unknown purpose, appears to be related to models, may have been introduced in file format v122
}

Overall structure

typedef struct {
    char[4] magic;                      // File format magic number, "JHMF" in ASCII encoding
    int32 version;                      // File format version (only version 121 is documented here)
    int32 ep_count;                     // Number of recent export paths
    p_char[ep_count] export_paths;      // Recent export paths
    int32 group_count;                  // Number of groups in the map
    Group[group_count] groups;          // Group objects
    int32 visgroup_count;               // Number of VisGroups
    VisGroup[visgroup_count] visgroups; // VisGroups objects
    Vector3 cordon_min;                 // Minimum corner of the Cordon box
    Vector3 cordon_max;                 // Maximum corner of the Cordon box
    int32 camera_count;                 // Number of Camera objects
    Camera[camera_count] cameras;       // Camera objects
    int32 path_count;                   // Number of path objects
    Path[path_count] paths;             // Path objects (created with the Path tool)
    Entity[] entities;                  // All entities including worldspawn, read until the end of the file
} Jmf;
All world brushes are stored within the worldspawn entity. The entities array is read until the end of file.

Other data types

Group

typedef struct {
    int32 group_id;         // The ID number of this group
    int32 group_parent_id;  // ID of the group this group is nested within
    int32 flags;            // Editor flags
    int32 count;            // Number of objects in the group
    Color color;            // Editor color of the object
} Group;

VisGroup

typedef struct {
    p_char name;        // VisGroup name
    int32 visgroup_id;  // VisGroup's ID number
    Color color;        // Editor color of the VisGroup
    byte visible;       // Whether the VisGroup is visible or not
} VisGroup;

Camera

typedef struct {
    Vector3 eye_position;       // The position of the camera in world coordinates
    Vector3 lookat_position;    // The target position in world coordinates the camera will look at
    int32 flags;                // Editor flags
    Color color;                // Editor color of the Camera
} Camera;

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 J.A.C.K and offer an alternative to placing path_ entities manually. It works similar to the Path tool in Hammer although can offer a "preview" in the 3D viewport of a camera travelling along the path with the speed specified by each node. One can read more about this in the J.A.C.K VDKManual.
typedef struct {
    p_char classname;           // Typically path_corner or path_track
    p_char path_name;           // The base name for this path
    int32 path_type;            // The direction of the path
    int32 flags;                // Editor flags
    Color color;                // Editor color of the Path
    int32 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_path, my_path_1, 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 {
    p_char name_override;           // Name to use instead of auto-generated one
    p_char fire_on_pass;            // Trigger this target when this node has been reached
    Vector3 position;               // The node's position in world coordinates
    Vector3 angles;                 // The node's angles
    int32 flags;                    // Entity spawnflags
    int32 kv_count;                 // Number of node's key-value pairs
    KeyValue[kv_count] keyvalues;   // Node's key-value pairs (properties)
} Node;

Entity

typedef struct {
    p_char[] classname;                 // The entity's classname
    Vector3 origin;                     // The origin if it's a point entity
    int32 flags;                        // Editor flags
    int32 group_id;                     // ID of the group this entity belongs to
    int32 root_group_id;                // The top-most group this entity is nested within
    Color color;                        // Editor color of the Path
    p_char[13] special_keys;            // The names of various special attributes used by J.A.C.K
    int32 sp_spawnflags;                // Entity spawnflags
    Vector3 sp_angles;
    int32 sp_rendering;
    Color sp_fx_color;
    int32 sp_rendermode;
    int32 sp_render_fx;
    int16 sp_body;
    int16 sp_skin;
    int32 sp_sequence;
    float sp_framerate;
    float sp_scale;
    float sp_radius;
    byte[28] unknown;
    int32 kv_count;                     // Number of entity's key-value pairs
    KeyValue[kv_count] keyvalues;       // Entity's key-value pairs (properties)
    int32 vg_count;                     // Number of VisGroups
    int32[vg_count] visgroup_ids;       // The ID numbers of the VisGroups the entity belongs to
    int32 brush_count;                  // Number of brushes
    Solid[brush_count] brushes;         // The brushes making up this entity (empty if point entity)
} Entity;
The special attributes section is a bit of a mystery. The fields and their data types here just reflect how they're described in MESS and should not be taken as a definite guide for parsing this part of the file. All these values are duplicated in the entity keyvalues, so it seems fairly safe to simply skip over these fields.

Solid

typedef struct {
    int32 mesh_count;                   // Number of meshes (e.g. Quake III/Volatile patches)
    int32 flags;                        // Editor flags
    int32 group_id;                     // ID of the group this entity belongs to
    int32 root_group_id;                // The top-most group this entity is nested within
    Color color;                        // Editor color of the Path
    int32 vg_count;                     // Number of VisGroups
    int32[vg_count] visgroup_ids;       // The ID numbers of the VisGroups the entity belongs to
    int32 face_count;                   // Number of faces (polygons) making up the brush
    Face[face_count] faces;             // The brush's faces
    Mesh[mesh_count] meshes;            // The brush's meshes
} Brush;

Surface properties

Common struct shared by faces and meshes.
typedef struct {
    Vector3 right_axis;             // The texture's projected right axis
    float shift_x;                  // Horizontal shift of the texture
    Vector3 down_axis;              // The texture's projected down axis
    float shift_y;                  // Vertical shift of the texture
    Vector2 scale;                  // Horizontal and vertical scale multiplier
    float angle;                    // The angle of the applied rotation
    int32 texture_alignment;        // Flags: 0x01 = world-aligned, 0x02 = face-aligned
    byte[12] unknown;
    int32 surface_flags;            // Content flags for Quake 2 maps
    char[64] texture_name;          // Name of the texture applied to the face
} SurfaceProperties;

Face

typedef struct {
    int32 render_flags;             // I'm unsure what this does. Editor flags perhaps?
    int32 vertex_count;             // Number of vertices in the face
    SurfaceProperties surface;      // The texture information of the face
    Vector3 normal;                 // Face plane normal
    float distance;                 // Face plane distance from origin
    int32 aligned_axis;             // 0 = X-axis, 1 = Y-axis, 2 = Z-axis, 3 = face normal is not axis-aligned
    Vertex[vertex_count] vertices;  // The vertices that make up the face
} Face;

Vertex

typedef struct {
    Vector3 coordinates;     // Vertex coordinates
    Vector2 texture_uv;      // The U/V coordinates of the texture on this vertex are stored in the X/Y values of this vector
    int32 selection_state;   // 1 = vertex is selected, 2 = midpoint between this and next vertex is selected
} Vertex;

Mesh

typedef struct {
    int32 width;                    // Mesh width (number of points)
    int32 height;                   // Mesh height (number of points)
    SurfaceProperties surface;      // The texture information of the mesh
    byte[4] unknown;
    MeshPoint[1024] points;         // Yes, every curve stores 1024 points, regardless of width/height
} Mesh;
Mesh points are stored in rows - the first point is (0, 0), the second point is (0, 1), and so on up until (0, 31), before moving onto the next row - (1, 0). Only the points where x < width and y < height are actually parts of the mesh, the others are to be ignored.

Mesh point

typedef struct {
    Vector3 position;
    Vector3 normal;
    Vector2 texture_uv;
    byte is_selected;
} MeshPoint

Sources and further reference

1 Comment

Commented 3 months ago2023-12-30 12:38:07 UTC Comment #105812
With the December 2023 update the JMF version was changed to 122, now including a data for background images in the 2D displayports.
From what I've found so far, the new structure looks like this:
typedef struct {
    char[4] magic;                      // File format magic number, "JHMF" in ASCII encoding
    int32 version;                      // File format version (currrently 122)
    int32 ep_count;                     // Number of recent export paths
    p_char[ep_count] export_paths;      // Recent export paths
    bg_image[3] bg_images;              // Background images
    int32 group_count;                  // Number of groups in the map
    Group[group_count] groups;          // Group objects
    int32 visgroup_count;               // Number of VisGroups
    VisGroup[visgroup_count] visgroups; // VisGroups objects
    Vector cordon_min;                  // Minimum corner of the Cordon box
    Vector cordon_max;                  // Maximum corner of the Cordon box
    int32 camera_count;                 // Number of Camera objects
    Camera[camera_count] cameras;       // Camera objects
    int32 path_count;                   // Number of path objects
    Path[path_count] paths;             // Path objects (created with the Path tool)
    Entity[] entities;                  // All entities including worldspawn, read until the end of the file
} Jmf;
and for the background image struct:
typedef struct {
    p_char path;                        // Background image path
    double scale;                       // Image scale
    int32 luminance;                    // Image brightness (0-255)
    int32 filtering;                    // Image filtering (0=nearest, 1=linear)
    int32 invert;                       // Invert image colours
    int32 offset_x;                     // Image horizontal offset from center
    int32 offset_y;                     // Image vertical offset from center
    byte[4] unknown;
} bg_image

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