rusting_engine 2.5.7

Vulkan 3D game engine with GPU-accelerated physics for massive physics-heavy scenes
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
# Assets: import and export

A project keeps its files under `assets/`: images, glTF models, sounds and
data assets. Import copies a file into `assets/`, checks that the engine
can load it, and records where it came from. Export builds a release and
packages the game with its assets into one folder that runs without the
engine checkout.

For typed data files (`.rdata`), see "Data assets" in
[Core concepts](concepts.md). For where to find free models and
textures, see [Look and feel](look-and-feel.md).

## Import a file

```bash
rusting asset import my_game ~/Downloads/crate.png --to props --license CC0-1.0 --author "Kenney"
```

```text
Asset: assets/props/crate.png (5cd9ac84-b54f-4ed7-88ad-c8e73cb76421, license CC0-1.0)
```

| Kind | Files |
| --- | --- |
| Image | `.png`, `.jpg`, `.jpeg`, `.bmp`, `.tga` |
| Model | `.gltf` (with its external buffers and images), `.glb` |
| Sound | `.wav`, `.ogg` |
| Font | `.ttf`, `.otf` |

Import loads the file as the game would, then copies it to
`assets/<folder>` (`--to props` and `--to assets/props` mean the same
folder). Any other type fails with `ASSET_UNSUPPORTED`, a file the engine
cannot read fails with `ASSET_INVALID`, and a file that is already there
fails with `ASSET_EXISTS`: replace it with `asset reimport` instead.

Flags:

- `--author`, `--license`, `--url`, `--generator` and `--notes` record
  where the file came from. Use an SPDX name for the license, such as
  `CC0-1.0` or `CC-BY-4.0`. Without a license, import warns
  `ASSET_NO_LICENSE`.
- `--max-size 512` scales an image down so its longest edge is 512 pixels,
  keeping its shape.
- `--linear` marks an image as data, not color: a normal map, a
  metallic-roughness map or an occlusion map. Import also cooks
  `<name>-linear.rtexture` next to it, and the `reference` field points at
  that file, so the texture reads its values without sRGB decoding.
- `--dry-run` runs every check on a staged copy and writes nothing.

Importing a glTF cooks it at once, so the first play does not pay for it.
The report lists every cooked mesh under `meshes`, with its triangle count
and the triangle count of each generated level of detail:

```text
Asset: assets/models/fir.glb (5cd9ac84-..., license CC0-1.0)
Mesh: fir.mesh-0-0.rmesh 48210 triangles, LODs 24104/12051/6024
```

A count that is far too high for the model's size on screen is the cue to
bake its foliage into cards (see "Bake foliage into cards").

### The `.rmeta` file

Import writes `<file>.rmeta` next to the asset:

```json
{
  "format_version": 1,
  "id": "5cd9ac84-b54f-4ed7-88ad-c8e73cb76421",
  "kind": "image",
  "settings": {"max_size": null},
  "dependencies": [],
  "content_hash": "a5c162ecbe3adeea",
  "source": {
    "original": "crate.png",
    "author": "Kenney",
    "license": "CC0-1.0",
    "url": null,
    "generator": null,
    "notes": null
  }
}
```

- `id` is stable. It survives a reimport or a replacement, so tools can
  follow an asset across edits. Scenes still refer to the file by path.
- `dependencies` lists the files a glTF loads, such as its `.bin` buffers
  and textures.
- `cooked` lists the files import cooked beside the asset: a glTF's
  `.rmesh` meshes, their `.lodN.rmesh` levels and `.rlod` groups, and its
  `.rtexture` textures, or a `--linear` image's `-linear.rtexture`. The
  field is absent when nothing was cooked. Reimport recooks the files and
  rewrites the list.
- `content_hash` spots edits made to the file outside import.
- `source` is the license record. Keep the `.rmeta` with the asset in
  version control.

## Use an asset in a scene

Scenes refer to a file by its path relative to the scene file. The
`reference` field of `asset import --json` gives that path. A texture goes
into a material slot:

```json
{
  "operations": [
    {"op": "set", "name": "Cube", "path": "/mesh_renderer/material/Inline/base_color_texture", "value": "../assets/textures/bricks_3.png"}
  ]
}
```

The other slots are `normal_texture`, `metallic_roughness_texture`,
`occlusion_texture` and `emissive_texture`; `uv_scale` repeats the
texture across a face. A plain image (`.png`, `.jpg`) in the normal,
metallic-roughness or occlusion slot loads as linear data, and one in the
base color or emissive slot loads as sRGB. A cooked `.rtexture` keeps the
color space it was cooked with. Run `rusting docs show component/mesh_renderer`
for every field.

A model goes in as one object with a child for each glTF node:

```bash
rusting scene add-model my_game/scenes/main.rscene my_game/assets/models/barrel.glb --name "Barrel 1"
```

`add-model` keeps the model's materials, textures, animations and skin.
It writes the converted meshes (`.rmesh`) and textures (`.rtexture`) next
to the model; keep them with the project. Run it again with another
`--name` for each copy, or `spawn_copy` the object in code.

A material whose textures carry a `KHR_texture_transform` (Blender and
Poly Haven exports set one for tiling bark and ground) has the offset,
rotation and scale folded into the mesh's UVs on import. A mesh has one
set of UVs, so the import stops with an error naming the material when
its textures use different transforms; bake them into `TEXCOORD_0` first.

Sounds are the exception: `rusting.sound_cue.clip` and `play_sound` take a
path relative to `assets/`, such as `sounds/hit.wav`. See
[Audio](audio.md).

### Cap texture size for low-end GPUs

`scene.set_max_texture_size(1024)` caps every texture side at 1024 pixels,
so a Low video preset loads only the 1K level of 2K textures. Larger
textures are halved until they fit and upload again when the cap changes;
0 removes the cap. HDR textures keep their full size. The cap is not saved:
set it again at startup like the other video settings.

## Replace an asset

```bash
rusting asset reimport my_game assets/props/crate.png --from art/crate_v2.png
```

Reimport replaces the file and keeps its ID. The asset is named by its
path or its ID. The new file comes from `--from`, else from the recorded
original if it still exists; otherwise reimport only checks the asset in
place. It takes the same provenance flags and `--max-size` as import, and
each given flag replaces the recorded value. A glTF also cooks its
`.rmesh` and `.rtexture` files again, next to the copy in the project.

If the project copy was edited after the last import, reimport without
`--from` keeps the edited copy instead of copying the recorded original
over it. It warns `ASSET_EDITED_COPY` and records the copy as the new
original, so the next reimport keeps it too.

Reimport reports the scenes that use the asset. A running game or the
editor reloads the file, so the change shows without a restart.

A file put under `assets/` by hand, or by `scene add-model`, has no
`.rmeta`. Reimport it to give it an ID and register it.

## List and check assets

```bash
rusting asset list my_game
```

```text
Asset: assets/models/barrel_4.glb (e01073d3-883d-4f5a-a711-8836b88e0aeb, license CC0-1.0)
Asset: assets/props/crate.png (5cd9ac84-b54f-4ed7-88ad-c8e73cb76421, license none)
warning [ASSET_NO_LICENSE]: no license recorded (.../assets/props/crate.png)
```

With `--json`, each asset also lists its dependencies, settings,
provenance and the scenes that refer to it (`referenced_by`). The command
fails on a missing file or dependency and on invalid or duplicate
metadata. It warns on a file changed since import, a file without
`.rmeta`, and an asset without a license. Run it before you ship.

`rusting validate` checks that every asset a scene refers to exists.

## Generate assets

`asset generate` makes a file from a prompt and imports it. Five
generators are built in, in one shared palette and chunky style. Each
gives the same CC0 file for the same prompt, and the optional number
picks a variation:

| Hook | Prompt | Makes |
| --- | --- | --- |
| `sfx` | `jump`, `coin`, `hit`, `explosion`, `laser`, `powerup` or `blip`, then a seed: `coin 7` | a WAV |
| `texture` | `grid`, `checker`, `bricks`, `planks`, `tiles` or `noise`: `bricks 3` | a 256 px tileable PNG |
| `sprite` | `circle`, `square`, `triangle`, `diamond`, `star` or `heart`: `star 2` | a 128 px outlined PNG on a clear background |
| `mesh` | `crate`, `barrel`, `rock`, `tree` or `gem`: `barrel 4` | a flat-shaded low-poly `.glb` standing on its origin |
| `font` | `pixel` or `bold`, with no number | a TrueType pixel font of printable ASCII, for `rusting.hud` `font` and `set_text_in_font` |

```bash
rusting asset generate my_game texture "bricks 3" --to textures
```

```text
Asset: assets/textures/bricks_3.png (2e0d5d19-64f4-48e8-9a46-3872c71afc82, license CC0-1.0)
```

`--replace <asset>` reimports an existing asset from the output instead,
keeping its ID.

### Your own generator

A project can add hooks that run any tool or model service. List them in
`project.json`:

```json
"generators": {
  "icons": {"command": ["python3", "tools/make_icon.py"], "license": "CC0-1.0", "settings": {"max_size": 256}}
}
```

The engine runs the command with the prompt in `RUSTING_PROMPT` and an
empty folder in `RUSTING_OUTPUT_DIR`. The command writes one file there
and prints, as its last line, JSON naming it:

```json
{"file": "icon.png", "license": "CC0-1.0", "author": "...", "generator": "my-model v2"}
```

Only `file` is required. The file then goes through `asset import`, so
its type, size and metadata are checked like any other file. Output with
no license, from the hook or its config, is refused. A hook named `sfx`,
`texture`, `sprite`, `mesh` or `font` replaces the built-in one.

## Bake foliage into cards

Scanned and modelled trees often model every leaf or needle: a free
fir can have four million triangles. Simplifying such a mesh destroys the
crown. `asset bake-foliage` replaces the foliage with alpha-tested cards
instead:

```bash
rusting asset bake-foliage art/fir_tree_01_2k.gltf --node fir_tree_01_a_LOD0 --cards 1500 --impostor --out assets/trees/fir_a
```

```json
{"source_triangles": 4176819, "foliage_triangles": 4066119, "cards": 1500,
 "card_triangles": 6000, "impostor_triangles": 4, "coverage": 0.52,
 "files": ["assets/trees/fir_a-cards.glb", "assets/trees/fir_a-impostor.glb"]}
```

- Foliage is every triangle whose material is `MASK` or `BLEND`. The
  triangles are grouped into `--cards` clusters, and each cluster's
  texture is drawn onto two crossed quads, so each card costs 4
  triangles. Texels the source texture cuts away are left out.
- The cards share one RGBA atlas (`--atlas`, 2048 px) and a `MASK`,
  double-sided material. `--tint 3e4b2a` scales the atlas's mean colour
  to that sRGB colour, to match an art direction.
- `--impostor` also writes two crossed quads showing the whole tree,
  bark included, for the farthest level of detail.
- `--node` bakes one node and its children at that node's origin. Use it
  for files that place several variants side by side.
- The bark (the opaque triangles) is not in the cards file. Draw it from
  a simplified copy of the model, as a second object at the same place.
- The same model and flags always write the same bytes.

Import the files with `asset import`, then list the cards and the
impostor as the far levels of a `.rlod` (see
[Look and feel](look-and-feel.md)).

## Export the game

```bash
rusting export my_game ~/exports
```

```text
Exported: /home/you/exports/my_game_export
Game exit code: 0, timed out: false
```

Export validates and cooks the main scene, builds a release, and copies
into a new folder:

- the executable;
- the cooked main scene (`build/`);
- `project.json`, `assets/` and the source scenes in `scenes/`;
- the engine license, as `RUSTING_ENGINE_LICENSE.md`;
- a `README.txt` that says where settings and saves live.

It then copies the folder to a fresh temporary folder and runs the game
there for 60 headless ticks. A game that reads a file from outside its
folder fails this check with `EXPORT_VERIFY_FAILED`. An existing export
is never replaced; the next one is named `my_game_export_2`.

`--target x86_64-pc-windows-gnu` builds for another system, with that
Rust target installed. The folder is named after the system, such as
`my_game_windows_export`. A cross-target export cannot run here, so it is
not checked and export warns `EXPORT_NOT_VERIFIED`.

### Icon

Set `icon` in `project.json` to a PNG under `assets/`, so exports carry
it:

```json
"icon": "assets/icon.png"
```

The game window shows it on Windows and X11; Wayland shows the icon of the
desktop entry instead. A file that cannot be read prints a warning and
keeps the system's icon. The icon of the `.exe` file itself is not set
yet.

### Export presets

To keep the settings of each kind of build, name them in `project.json`
under `export_presets`:

```json
"export_presets": {
  "windows": {"target": "x86_64-pc-windows-gnu", "features": ["steam"]},
  "demo": {"features": ["demo"], "exclude": ["assets/source/**", "**/*.psd", "scenes/test_*"]}
}
```

Then export with one of them:

```bash
rusting export my_game ~/exports --preset demo
```

- `target` is the Rust target to build for. Leave it out to build for
  this machine.
- `features` turns on Cargo features of the game's own crate. Declare
  them under `[features]` in its `Cargo.toml`, and use
  `#[cfg(feature = "demo")]` in game code.
- `exclude` leaves files out of `assets/` and `scenes/`. Patterns are
  relative to the project folder: `*` matches within one name, `**` any
  number of folders. The executable, the cooked main scene and
  `project.json` are always copied.
- `sign` is a code-signing command, run on the exported executable before
  the export is checked. Give the program, then its arguments; `{exe}`
  becomes the executable's path, as in `["signtool", "sign", "/a",
  "{exe}"]`. The command must sign the file in place. It runs in the
  project folder.
  When it fails, the export folder is deleted and export fails with
  `EXPORT_SIGN_FAILED`.
- `pack` set to `true` packs the exported `assets/` and `scenes/` folders
  into `assets.rpak` and `scenes.rpak` after excluded files are removed,
  and deletes the loose folders. The game reads a file that is missing on
  disk from the `.rpak` beside its folder, so asset paths stay the same and
  a loose file still wins over the packed one. Files are read straight from
  the pack without unpacking it; streamed audio holds the encoded clip in
  memory. A glTF model imported while the game runs writes its cooked
  meshes beside the source, so leave `pack` off for a game that calls
  `add_gltf`; models placed in the editor are cooked already and load from
  the pack.
  Packing only happens at export.
- `zip` set to `true` zips the finished export folder into
  `<folder>.zip` beside it, after it is signed and checked, ready to
  upload to a store or share. Files sit under the folder's name inside
  the zip and keep their Unix permissions, so the game stays executable
  after unzipping on Linux. The files are stored without compression, and
  a zip holds at most 65,535 files and 4 GiB.

The export's JSON lists the `features`, the `excluded` files, the
`packed` archives and the `zip` path. A native
export is still run headless, so leaving out a file the game loads fails
with `EXPORT_VERIFY_FAILED`. An unknown preset name fails with
`EXPORT_PRESET_UNKNOWN`, which lists the presets the project has.
Packed data is not compressed or encrypted yet.

### Crash reports

When game code or the engine panics, the game writes a crash report
before it exits and prints its path:

```text
crash report written to /home/you/.local/share/my_game/crashes/crash-1791682755782.txt
```

Reports go to `crashes/` in the user data folder, the same folder as
saves. Each one holds the engine version, the system, the panic message
and where it happened, and the stack trace with function names, such as
`my_game::update`. The folder keeps the 20 newest reports. Ask players
to send the newest file with a bug report.

A release build has function names but no file and line numbers in the
trace. To add them, put this in the game's `Cargo.toml`:

```toml
[profile.release]
debug = "line-tables-only"
```

Before you ship, run `rusting asset list` and add the credits that
`CC-BY` assets need to your README.