Skip to content

Latest commit

 

History

History
538 lines (348 loc) · 16.7 KB

rmesh_format_scp-cb.md

File metadata and controls

538 lines (348 loc) · 16.7 KB

SCP – Containment Breach RMesh file format

This document explains how RMesh files from SCP – Containment Breach are stored internally. The files in question are from SCP – Containment Breach v1.3.11.

Note: Non-representable characters in hex-to-text examples have been replaced with periods. The parts of hex code which are of main focus are enclosed in >[ ]<.

Before we start, you should understand the term 'B3D string' and how it works, since it's used quite a lot here. A B3D string (or Blitz3D string) is a data type used by Blitz3D, the engine used by SCP – CB.

B3D string structure:

Example B3D string:

Hex: 08 00 00 00 52 6F 6F 6D 4D 65 73 68
Text: ....RoomMesh

>[08 00 00 00]< 52 6F 6F 6D 4D 65 73 68

4-byte 'int' value representing the string's length in bytes. In this case, 8.

08 00 00 00 >[52 6F 6F 6D 4D 65 73 68]<

Variable-byte 'string' value, which is the string itself. In this case, 'RoomMesh'.

Now, we can finally look onto how the RMesh files are stored.

File structure

At the file's start, a B3D string header is written. This is always either 'RoomMesh' or 'RoomMesh.HasTriggerBox' if the map contains triggers boxes.

Hex: >[08 00 00 00 52 6F 6F 6D 4D 65 73 68]< 0D 00 00 00
Text: ....RoomMesh....

Without trigger boxes.
Hex: >[16 00 00 00 52 6F 6F 6D 4D 65 73 68 2E 48 61 73 54 72 69 67 67 65 72 42 6F 78]< 28 00
Text: ....RoomMesh.HasTriggerBox(.

With trigger boxes.

After the header, the map's texture count is stored. This is the number of unique textures the map contains.

Hex: 4D 65 73 68 >[0D 00 00 00]< 02 10 00 00
Text: Mesh........

4-byte 'int' value representing the unique texture count. In this case, 13.

Next, for each unique texture, the data for all faces with that texture is stored. SCP – CB handles opaque and transparent textures differently. First, we need to understand what different 'texture flags' represent. These are taken from SCP – CB's code comments.

Texture flags:

1 – The texture is opaque (non-transparent).
2 – The texture is a lightmap texture.
3 – The texture has transparency.

How opaque textures are handled:

For every opaque texture, a lightmap is generated. First, a 1-byte 'Byte' value gets stored, which is the lightmap's texture flag.

Hex: 0D 00 00 00 >[02]< 10 00 00 00 72 6F 6F 6D 34
Text: .........room4

In this case, the flag is 2, meaning the texture is a lightmap.

Then, the lightmap texture's path relative to the .rmesh file's directory is stored as a B3D string.

Hex: 02 >[10 00 00 00 72 6F 6F 6D 34 70 69 74 5F 6C 6D 31 2E 70 6E 67]< 01 11
Text: .....room4pit_lm1.png..

After that, the same is done for the actual texture. A 1-byte 'Byte' value is stored, which is the actual texture's texture flag.

Hex: 6C 6D 31 2E 70 6E 67 >[01]< 11 00 00 00 63 6F 6E 63 72
Text: lm1.png.....concr

Then, the texture path is stored as a B3D string.

Hex: 01 >[11 00 00 00 63 6F 6E 63 72 65 74 65 66 6C 6F 6F 72 2E 6A 70 67]< F0 00
Text: .....concretefloor.jpgð.

Next, the amount of vertices that are associated with the texture is stored.

Hex: 6F 72 2E 6A 70 67 >[F0 00 00 00]< 00 00 80 44
Text: or.jpgð.....€D

4-byte 'int' value representing the vertex count. In this case, 240.

For each vertice, some data gets stored. This data always takes up 31 bytes.

4-byte 'float32' value, representing the vertex' X position (in SCP – CB).
4-byte 'float32' value, representing the vertex' Y position (in SCP – CB).
4-byte 'float32' value, representing the vertex' Z position (in SCP – CB).

4-byte 'float32' value, representing the U value of the texture's UV values.
4-byte 'float32' value, representing the V value of the texture's UV values.

4-byte 'float32' value, representing the U value of the lightmap texture's UV values.
4-byte 'float32' value, representing the V value of the lightmap texture's UV values.

Three 1-byte values. These are the vertex' vertex colors.

How transparent textures are handled:

For transparent textures, a lightmap is not generated. First, a 1-byte 'Byte' value gets stored, which is the lightmap's texture flag. This flag is always 1.

Hex: 62 01 00 00 >[01]< 00 00 00 00 03 09 00 00 00 67 6C 61
Text: b.............gla

If the lightmap isn't generated, an empty B3D string is written taking up 4 bytes.

Hex: 62 01 00 00 01 >[00 00 00 00]< 03 09 00 00 00 67 6C 61
Text: b.............gla

After that, for the actual texture. A 1-byte 'Byte' value is stored, which is the actual texture's texture flag. This flag is always 3.

Hex: 00 00 00 00 >[03]< 09 00 00 00 67 6C 61 73 73 2E 70 6E 67 60 00
Text: .........glass.png`.

Then, the texture's path relative to the .rmesh file's directory is stored as a B3D string.

Hex: 00 00 00 00 03 >[09 00 00 00 67 6C 61 73 73 2E 70 6E 67]< 60 00
Text: .........glass.png`.

Next, the amount of all the vertices that are associated with the texture are stored.

Hex: 67 6C 61 73 73 2E 70 6E 67 >[60 00 00 00]< FE 7F 29 45
Text: glass.png`...þ.)E

4-byte 'int' value representing the vertex count. In this case, 96.

For each vertice, some data gets stored. This data always takes up 31 bytes.

4-byte 'float32' value, representing the vertex' X position (in SCP – CB).
4-byte 'float32' value, representing the vertex' Y position (in SCP – CB).
4-byte 'float32' value, representing the vertex' Z position (in SCP – CB).

4-byte 'float32' value, representing the U value of the texture's UV values.
4-byte 'float32' value, representing the V value of the texture's UV values.

4-byte 'float32' value, representing the U value of the lightmap texture's UV values.
4-byte 'float32' value, representing the V value of the lightmap texture's UV values.

Three 1-byte values. These are the vertex' vertex colors.

After the data for each vertex is stored, the amount of all the triangles that are associated with the texture is stored.

Hex: 3D FF FF FF >[18 00 00 00]< 00 00 00 00 01
Text: =ÿÿÿ.........

3D FF FF FF >[18 00 00 00]< 00 00 00 00 01

4-byte 'int' value representing the triangle count. In this case, 24.

After the vertex data, the triangle indices are stored. Each indice is a 4-byte 'int' value. The amount of indices is the triangle count * 3. So, in this example, there would be 30 indices written, so that means the indice data would take up 288 bytes (24 * 3 * 4).

Hex: 18 00 00 00 00 00 00 00 01 00 00 00 02 00 00 00 03 00 00 00 00
Text: ....................

18 00 00 00 00 00 00 00 >[01 00 00 00]< 02 00 00 00 03 00 00 00 00

One of the indices stored. In this case, 1, and after it, 2, 3 and so on.

Next, the number of invisible collisions (invisible surfaces with collision) is stored.

Hex: 2E 00 00 00 01 00 00 00 12 00 00 00 00 80 83 44
Text: .............€ƒD

>[2E 00 00 00]< 01 00 00 00 12 00 00 00 00 80 83 44

Leftover indice data from textures.

2E 00 00 00 >[01 00 00 00]< 12 00 00 00 00 80 83 44

Number of invisible collision surfaces. In this case, 1.

For each invisible collision surface, the total vertex count is stored.

Hex: 2E 00 00 00 01 00 00 00 12 00 00 00 00 80 83 44
Text: .............€ƒD

2E 00 00 00 01 00 00 00 >[12 00 00 00]< 00 80 83 44

This invisible collision surface's vertex count. In this case, 18.

For each vertice, some data gets stored. This data always takes up 12 bytes.

4-byte 'float32' type, representing the vertex' X position (in SCP – CB).
4-byte 'float32' type, representing the vertex' Y position (in SCP – CB).
4-byte 'float32' type, representing the vertex' Z position (in SCP – CB).

Then, the triangle count is stored.

Hex: 00 00 68 44 08 00 00 00 00 00 00 00 01 00 00 00
Text: ..hD............

>[00 00 68 44]< 08 00 00 00 00 00 00 00 01 00 00 00

Excerpt from the end of the invisible collision vertex data.

00 00 68 44 >[08 00 00 00]< 00 00 00 00 01 00 00 00

4-byte 'int' value representing the triangle count. In this case, 8.

After that, the triangle indices are stored as 4-byte 'int' values.

Hex: 08 00 00 00 00 00 00 00 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00
Text: ........................

08 00 00 00 >[00 00 00 00 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00]<

Some of the beginning indices. In this case, 0, 1, 2, 3, 4 and so on.

Next, if the map contains trigger boxes (header is RoomMesh.HasTriggerBox), the data for them is stored.

First, the amount of trigger boxes is stored.

Hex: 0E 00 00 00 10 00 00 00 03 00 00 00 01 00 00 00
Text: ................

>[0E 00 00 00 10 00 00 00]< 03 00 00 00 01 00 00 00

Leftover indice data from invisible collisions.

0E 00 00 00 10 00 00 00 >[03 00 00 00]< 01 00 00 00

4-byte 'int' value representing the amount of trigger boxes. In this case, 3.

Then, for each trigger box, some data gets stored.

First, the surface amount for the trigger box is stored.

Hex: 03 00 00 00 01 00 00 00 18 00 00 00 00 00 2A 45 00
Text: ..............*E.

03 00 00 00 >[01 00 00 00]< 18 00 00 00 00 00 2A 45 00

4-byte 'int' value representing the surface count for the trigger box. In this case, 1.

For each surface, some data is stored.

First, the surface's vertex count is stored.

Hex: 03 00 00 00 01 00 00 00 18 00 00 00 00 00 2A 45 00
Text: ..............*E.

03 00 00 00 01 00 00 00 >[18 00 00 00]< 00 00 2A 45 00

4-byte 'int' value representing the vertex count for the surface. In this case, 24.

Then, for each vertex, some data is stored.

4-byte 'float32' value representing the vertex' X position (in SCP – CB).
4-byte 'float32' value representing the vertex' Y position (in SCP – CB).
4-byte 'float32' value representing the vertex' Z position (in SCP – CB).

Next, the surface triangle count is stored.

Hex: 00 00 BC 43 00 00 E0 C3 0C 00 00 00 00 00 00 00 01 00 00 00
Text: ..¼C..àÃ............

?[00 00 BC 43 00 00 E0 C3]< 0C 00 00 00 00 00 00 00 01 00 00 00

Leftover trigger box vertex data.

00 00 BC 43 00 00 E0 C3 >[0C 00 00 00]< 00 00 00 00 01 00 00 00

4-byte 'int' value representing the surface triangle count. In this case, 12.

Last, the trigger box' name is stored as a B3D string.

Hex: 14 00 00 00 16 00 00 00 0E 00 00 00 31 37 33 73 63 65 6E 65 5F 74 69 6D 65 72 01 00 00 00
Text: ............173scene_timer....

>[14 00 00 00 16 00 00 00]< 0E 00 00 00 31 37 33 73 63 65 6E 65 5F 74 69 6D 65 72 01 00 00 00

Some leftover trigger box triangle indice data.

14 00 00 00 16 00 00 00 >[0E 00 00 00 31 37 33 73 63 65 6E 65 5F 74 69 6D 65 72]< 01 00 00 00

The name of the trigger box as a B3D string.

After all that comes the entity data. First, the collective amount of all entites present in the map is stored.

Hex: 63 65 6E 65 5F 65 6E 64 41 00 00 00 05 00 00 00
Text: cene_endA.......

>[63 65 6E 65 5F 65 6E 64]< 41 00 00 00 05 00 00 00

Leftover trigger box data.

63 65 6E 65 5F 65 6E 64 >[41 00 00 00]< 05 00 00 00

4-byte 'int' value representing the entity count. In this case, 65.

Then, for each entity type, some data for it is stored.

Screen entity (classname screen)

B3D string "screen".

4-byte 'float32' value representing the screen's X position (in SCP – CB).
4-byte 'float32' value representing the screen's Y position (in SCP – CB).
4-byte 'float32' value representing the screen's Z position (in SCP – CB).

B3D string representing the file path of the image the screen uses.

Example screen entity:

Hex: 06 00 00 00 73 63 72 65 65 6E 00 00 00 00 00 00 60 43 00 00 60 C3 0A 00 00 00 73 63 72 65 65 6E 2F 30 30 38

Text: ....screen......`C..`Ã....screen/008

X: 0, Y: 224, Z: -224, Image path: screen/008

Waypoint entity (classname waypoint)

B3D string "waypoint".

4-byte 'float32' value representing the waypoint's X position (in SCP – CB).
4-byte 'float32' value representing the waypoint's Y position (in SCP – CB).
4-byte 'float32' value representing the waypoint's Z position (in SCP – CB).

Example waypoint entity:

Hex: 08 00 00 00 77 61 79 70 6F 69 6E 74 00 00 90 43 00 00 20 43 00 00 28 44

Text: ....waypoint...C.. C..(D

X: 288 Y: 160 Z: 672

Light entity (classname light)

B3D string "light".

4-byte 'float32' value representing the light's X position (in SCP – CB).
4-byte 'float32' value representing the light's Y position (in SCP – CB).
4-byte 'float32' value representing the light's Z position (in SCP – CB).

4-byte 'float32' value representing the light's range.
B3D string representing the light color.
4-byte 'float32' value representing the light's intensity.

Example light entity:

Hex: 05 00 00 00 6C 69 67 68 74 00 00 40 44 00 00 40 43 00 00 A4 44 00 00 16 44 0B 00 00 00 31 32 38 20 32 35 35 20 32 35 35 00 00 00 40

Text: ....light..@D..@C..¤D...D....128 255 255...@

X: 768, Y: 192, Z: 1312, Range: 600, Color: 128 255 255, Intensity: 2

Spotlight entity (classname spotlight)

B3D string "spotlight".

4-byte 'float32' value representing the light's X position (in SCP – CB).
4-byte 'float32' value representing the light's Y position (in SCP – CB).
4-byte 'float32' value representing the light's Z position (in SCP – CB).

4-byte 'float32' value representing the light's range.
B3D string representing the light color.
4-byte 'float32' value representing the light's intensity.
B3D string representing the spotlight's angles.

4-byte 'int' value representing the spotlight's inner cone angle.
4-byte 'int' value representing the spotlight's outer cone angle.

Example spotlight entity:

Hex: 09 00 00 00 73 70 6F 74 6C 69 67 68 74 00 00 C2 C3 00 00 BC 43 00 00 20 C2 00 00 48 44 0B 00 00 00 32 35 35 20 32 35 35 20 32 35 35 9A 99 99 3F 06 00 00 00 39 30 20 30 20 30 23 00 00 00 2D 00 00 00

Text: ....spotlight..ÂÃ..¼C.. Â..HD....255 255 255š™™?....90 0 0#...-...

X: -388, Y: 376, Z: -40, Range: 800, Color: 255 255 255, Intensity: 1.20000004768372, Angles: 90 0 0, Inner cone angle: 35, Outer cone angle: 45

Sound emitter entity (classname soundemitter)

B3D string "soundemitter".

4-byte 'float32' value representing the sound emitter's X position (in SCP – CB).
4-byte 'float32' value representing the sound emitter's Y position (in SCP – CB).
4-byte 'float32' value representing the sound emitter's Z position (in SCP – CB).

4-byte 'int' value representing the sound emitter's sound index.
4-byte 'float32' value representing the sound emitter's range.

Note: SCP – CB uses 'sound indexes' to play back sounds from sound emitters.

Example sound emitter entity:

Hex: 0C 00 00 00 73 6F 75 6E 64 65 6D 69 74 74 65 72 00 00 60 44 00 00 00 43 FD FF 1F 43 01 00 00 00 00 00 FA 43

Text: ....soundemitter..`D...Cýÿ.C......úC

X: 896, Y: 128, Z: 159.999954223633, Sound index: 1, Range: 500

Player start entity (classname playerstart)

B3D string "playerstart"

4-byte 'float32' value representing the player start's X position (in SCP – CB).
4-byte 'float32' value representing the player start's Y position (in SCP – CB).
4-byte 'float32' value representing the player start's Z position (in SCP – CB).

B3D string representing the player start's angles.

Example player start entity:

Note: (I think) no player start entity is used in any of SCP – CB's .rmesh files, as I couldn't find any.

Hex: 0B 00 00 00 70 6C 61 79 65 72 73 74 61 72 74 00 00 E0 42 00 00 AA 43 00 40 B5 44 06 00 00 00 30 20 34 35 20 30

Text: ....playerstart..àB..ªC.@µD....0 45 0

X: 112, Y: 340, Z: 1450, Angles: 0 45 0

Model entity (classname model)

B3D string "model".
B3D string representing the model name stripped of the file path.

4-byte 'float32' value representing the model's X position (in SCP – CB).
4-byte 'float32' value representing the model's Y position (in SCP – CB).
4-byte 'float32' value representing the model's Z position (in SCP – CB).

4-byte 'float32' value representing the model's Pitch (X) rotation (in SCP – CB).
4-byte 'float32' value representing the model's Yaw (Y) rotation (in SCP – CB).
4-byte 'float32' value representing the model's Roll (Z) rotation (in SCP – CB).

4-byte 'float32' value representing the model's X scale (in SCP – CB).
4-byte 'float32' value representing the model's Y scale (in SCP – CB).
4-byte 'float32' value representing the model's Z scale (in SCP – CB).

Example model entity:

Hex: 05 00 00 00 6D 6F 64 65 6C 0F 00 00 00 63 6F 6E 74 64 6F 6F 72 66 72 61 6D 65 2E 78 00 00 6C 44 00 00 A0 C4 06 00 00 38 00 00 00 80 FD FF B3 C2 00 00 00 00 FF FF 0B 42 00 00 50 42 FF FF 47 42

Text: ....model....contdoorframe.x..1D.. Ä...8...€ýÿ³Â....ÿÿ.B..PBÿÿGB

Model name: contdoorframe.x, Position X: 944, Position Y: -1280, Position Z: 3.05175999528728E-5, Rotation Pitch (X): -0, Rotation Yaw (Y): -89.9999771118164, Rotation Roll (Z): 0, Scale X: 34.9999961853027, Scale Y: 52, Scale Z: 49.9999961853027