|
1 | | -# Models in Blender |
| 1 | +# The models in Blender |
2 | 2 |
|
3 | | -`browse --kinds models` turns every `.lex` on the disc into a glTF 2.0 |
4 | | -binary (`.glb`) under `browse/models/`, mirroring the `dump/` tree: |
| 3 | +Run `browse` with the **Models** box ticked (or `--kinds models` on the |
| 4 | +command line) and every `.lex` model on the disc becomes a `.glb` file |
| 5 | +under `browse/models/`, in the same folders the game uses: |
5 | 6 |
|
6 | 7 | ``` |
7 | | -browse/models/chain0/char/pc/kosmos.glb body + face merged (4,568 tris) |
8 | | -browse/models/chain0/char/pc/kosmos_face.glb the face on its own (1,278 tris) |
9 | | -browse/models/chain0/char/pc/kosmos_h.glb alternate outfit |
10 | | -browse/models/chain0/enemy/… obj/… map/… weapon/… robo/… |
| 8 | +browse/models/chain0/char/pc/kosmos.glb KOS-MOS, face included |
| 9 | +browse/models/chain0/char/pc/kosmos_face.glb just her face |
| 10 | +browse/models/chain0/char/pc/kosmos_h.glb her alternate outfit |
| 11 | +browse/models/chain0/char/npc/andrew.glb Cherenkov |
| 12 | +browse/models/chain0/enemy/… robo/… weapon/… obj/… map/… |
11 | 13 | ``` |
12 | 14 |
|
13 | | -Each file carries the decoded texture atlas inside it, so a `.glb` is |
14 | | -self-contained: no texture paths to fix up. |
15 | | - |
16 | | -## Import |
17 | | - |
18 | | -Blender 2.8 or later, no add-on needed: |
19 | | - |
20 | | -1. **File ▸ Import ▸ glTF 2.0 (.glb/.gltf)** and pick the file. |
21 | | -2. Leave the importer defaults. "Shading: Normals" uses the game's own |
22 | | - vertex normals; "Pack images" keeps the atlas inside the `.blend`. |
23 | | -3. Switch the viewport to **Material Preview** (the sphere icon, or `Z` |
24 | | - ▸ Material Preview) to see the textures. |
25 | | - |
26 | | -What you get: |
27 | | - |
28 | | -* **One object per game mesh**, named `NN/group/bone` |
29 | | - (`01/kosmos_mune/kosmos_body`): the mesh index, the artist's group |
30 | | - name and the bone it is parented to. Face parts merged in from a |
31 | | - companion file are prefixed with that file's name |
32 | | - (`37/kosmos_face/kosmos_face/kosmos_face`). |
33 | | -* **Materials** named `matN_u<min>-<max>_v<min>-<max>`, the region of the |
34 | | - atlas the material draws from (in atlas pixels, 1024×256 for |
35 | | - character sheets). `_rgba` marks a true-colour material (hair). All |
36 | | - share one image texture; alpha is a cutout (`MASK`) except hair, which |
37 | | - blends. |
38 | | -* **Vertex colours** as the `Col` colour attribute (the game's |
39 | | - per-vertex lighting tint). The importer wires them into the material; |
40 | | - if a model looks too dark or too tinted, disconnect the Color |
41 | | - Attribute node from Base Color. |
42 | | -* **Vertex groups** named `j<id>` on every skinned object, with an |
43 | | - **Armature** called `skeleton` whose bones all sit at the origin. The |
44 | | - weights are the game's; the bone *positions* are not decoded yet (they |
45 | | - live in `.jnt`, see below), so the armature is a flat list of named |
46 | | - joints. You can weight-paint, retarget or build a rig on top of the |
47 | | - groups; you cannot pose the import as-is. |
48 | | - |
49 | | -Scale and axes: the game is Y-up in metres (KOS-MOS stands 1.73 units |
50 | | -tall), the same as glTF, so the importer's default "Y up → Z up" |
51 | | -conversion puts her upright on Blender's floor. No scaling needed. |
| 15 | +Each file carries its texture inside it, so there are no paths to fix |
| 16 | +up. Drop one into Blender and it just works. |
| 17 | + |
| 18 | +## Importing |
| 19 | + |
| 20 | +1. **File ▸ Import ▸ glTF 2.0** and pick the `.glb`. |
| 21 | +2. Leave the importer settings alone. |
| 22 | +3. Press `Z` and choose **Material Preview** so you see the textures. |
| 23 | + |
| 24 | +Blender 2.8 or newer, no add-ons needed. |
| 25 | + |
| 26 | +## What you will see |
| 27 | + |
| 28 | +**Objects.** One per game mesh, named `number/group/bone`, for example |
| 29 | +`01/kosmos_mune/kosmos_body`. The group is the artist's name for the |
| 30 | +part; the bone is what it hangs off. Parts that came from a separate |
| 31 | +face file are prefixed with that file's name. |
| 32 | + |
| 33 | +**Materials.** Named after the region of the texture they use, like |
| 34 | +`mat3_u256-320_v128-256`. They all share one image. Hair materials end |
| 35 | +in `_rgba` and blend; everything else uses a cut-out alpha. |
| 36 | + |
| 37 | +**Vertex colours** in the `Col` attribute. That is the game's baked-in |
| 38 | +shading tint. If a model looks too dark, unplug the Color Attribute node |
| 39 | +from Base Color in the material. |
| 40 | + |
| 41 | +**Vertex groups** named `j<number>` on every skinned part, with an |
| 42 | +armature called `skeleton`. The weights are the game's own. The bone |
| 43 | +positions are not decoded yet, so the armature is a flat list of named |
| 44 | +joints at the origin: you can weight-paint or build a rig on top of it, |
| 45 | +but you cannot pose the model as imported. |
| 46 | + |
| 47 | +**Scale.** The game works in metres, Y up, same as glTF. KOS-MOS stands |
| 48 | +1.73 m tall and lands upright on Blender's floor with no scaling. |
52 | 49 |
|
53 | 50 | ## Which files to open |
54 | 51 |
|
55 | | -| Directory under `browse/models/chain0/` | What it is | |
| 52 | +| Folder under `browse/models/chain0/` | What is in it | |
56 | 53 | |---|---| |
57 | | -| `char/pc/` | playable characters: `kosmos`, `shion`, `jr`, `momo`, `chaos`, `ziggy`, variants `*1`, `*2`, `*_h` (alternate outfits), `*_face` (face parts, already merged into the body file) | |
58 | | -| `char/npc/` | every NPC | |
| 54 | +| `char/pc/` | the party: `kosmos`, `shion`, `jr`, `momo`, `chaos`, `ziggy`; `*1`, `*2`, `*_h` are alternate outfits; `*_face` are the face parts (already merged into the body files) | |
| 55 | +| `char/npc/` | every NPC; Cherenkov is `andrew`, Albedo is `albelt` | |
59 | 56 | | `enemy/`, `enemy/boss/` | enemies and bosses | |
60 | | -| `robo/` | A.G.W.S. mechs | |
61 | | -| `weapon/pc/` | weapons (KOS-MOS's blades are `kos_hw*`) | |
62 | | -| `obj/` | field props | |
63 | | -| `map/` | field and battle maps (`MC_BTL*` are battle arenas; large) | |
64 | | - |
65 | | -A body `.glb` already includes its companion files (anything in the same |
66 | | -folder whose name starts with the body's stem and has no atlas of its |
67 | | -own: `kosmos_face.lex`, `kosmos_h_face.lex` …). The companions are also |
68 | | -written on their own, so `kosmos_face.glb` exists if you only want the |
69 | | -head. |
70 | | - |
71 | | -## What is and isn't decoded |
72 | | - |
73 | | -Done: geometry, normals, UVs (scaled by the model's declared texture |
74 | | -size and wrapped into each material's atlas region the way the GS |
75 | | -region-repeat did), vertex colours, three skin-weight |
76 | | -encodings, in-stream material changes, true-colour hair sheets, the |
77 | | -atlas itself. Verified against Lakuwu's xenotool: identical triangle and |
78 | | -weight-group counts on the retail disc. |
79 | | - |
80 | | -Not yet: |
81 | | - |
82 | | -* **Skeleton pose and hierarchy** (`.jnt`). Joints exist as named vertex |
83 | | - groups, bones have no positions. Solving this is the next piece of |
84 | | - work; the `.jnt` holds 0x40-byte blocks with a parent index each. |
85 | | -* **Animation** — not located on the disc yet (the cutscene motion packs |
86 | | - in chain 1 are FL00 containers; battle/field motion is an open thread). |
87 | | -* Two map meshes use vertex formats `0x30`/`0xA0` and are skipped; the |
88 | | - `.glb`'s `extras.warnings` lists them. |
89 | | -* **Eyelashes.** Every character has a `*_matu` lash mesh (UV type |
90 | | - `0x7F`, a 32×32 sheet). Its vertex colours are flat white, so the dark |
91 | | - lash look is entirely the texture — and that sheet is 4-bit (PSMT4), |
92 | | - which the texture decoder does not read yet (the same open thread as |
93 | | - KOS-MOS's visor HUD). The lashes export with the right UVs onto the |
94 | | - atlas but show whatever the PNG has in that spot; the material is |
95 | | - named `…_lash-4bpp-undecoded` so it is easy to hide or recolour. |
96 | | -* Blend shapes (weight format 1024 — the face files carry some) are |
97 | | - ignored. |
98 | | - |
99 | | -## Re-exporting a single model from Python |
| 57 | +| `robo/` | the A.G.W.S. mechs | |
| 58 | +| `weapon/pc/` | weapons | |
| 59 | +| `obj/` | props | |
| 60 | +| `map/` | field and battle maps (`MC_BTL*` are the battle arenas; large) | |
| 61 | + |
| 62 | +A body file already includes its companions: anything in the same |
| 63 | +folder whose name starts with the body's name and has no texture of its |
| 64 | +own (`kosmos_face.lex`, `kosmos_h_face.lex`). The companions are also |
| 65 | +written on their own, so `kosmos_face.glb` is there if you only want |
| 66 | +the head. |
| 67 | + |
| 68 | +## Known gaps |
| 69 | + |
| 70 | +* **No skeleton pose yet.** The joints come through as named vertex |
| 71 | + groups; their positions and hierarchy live in the `.jnt` files, which |
| 72 | + are the next thing to decode. |
| 73 | +* **No eyelashes.** Every character has a lash mesh whose texture is a |
| 74 | + tiny 4-bit sheet the texture decoder cannot read yet. The lashes are |
| 75 | + in the file with correct UVs; their material is named |
| 76 | + `…_lash-4bpp-undecoded` so it is easy to hide. |
| 77 | +* **No animation.** Not located on the disc yet. |
| 78 | +* Two map meshes use vertex formats the parser skips, and blend shapes |
| 79 | + are ignored. Anything skipped is listed in the `.glb`'s `extras`. |
| 80 | + |
| 81 | +## From Python |
100 | 82 |
|
101 | 83 | ```python |
102 | 84 | import arx, lex |
103 | 85 | from browse import decode_xtx, png_bytes |
104 | 86 |
|
105 | | -raw = open("out/dump/chain0/char/pc/kosmos.lex", "rb").read() |
106 | | -model = lex.parse_lex(arx.decompress(raw)) # dump files are ARX-packed |
107 | | -face = lex.parse_lex(arx.decompress(open(".../kosmos_face.lex", "rb").read())) |
108 | | -lex.merge_companion(model, face, "kosmos_face/") |
| 87 | +body_lex = arx.decompress(open("out/dump/chain0/char/pc/kosmos.lex", "rb").read()) |
| 88 | +face_lex = arx.decompress(open("out/dump/chain0/char/pc/kosmos_face.lex", "rb").read()) |
| 89 | +body = lex.parse_lex(body_lex) |
| 90 | +lex.merge_companion(body, lex.parse_lex(face_lex), "kosmos_face/") |
109 | 91 |
|
110 | | -xtx = arx.decompress(open(".../kosmos.xtx", "rb").read()) |
111 | | -w, h, rgba, _ = decode_xtx(xtx, [lex_bytes_for_kosmos, lex_bytes_for_face]) |
112 | | -open("kosmos.glb", "wb").write(lex.to_glb(model, png_bytes(w, h, rgba), (w, h))) |
| 92 | +xtx = arx.decompress(open("out/dump/chain0/char/pc/kosmos.xtx", "rb").read()) |
| 93 | +w, h, rgba, _ = decode_xtx(xtx, [body_lex, face_lex]) |
| 94 | +open("kosmos.glb", "wb").write(lex.to_glb(body, png_bytes(w, h, rgba), (w, h))) |
113 | 95 | ``` |
114 | 96 |
|
115 | | -The format itself is written up in [FORMATS.md](FORMATS.md) § LEX models. |
| 97 | +The file format itself is written up in [FORMATS.md](FORMATS.md) under |
| 98 | +"LEX models". |
0 commit comments