This commit is contained in:
jack
2026-09-01 01:04:57 +01:00
parent d3d1c12b28
commit 65c6aaac5b
11 changed files with 1068 additions and 106 deletions
+134 -2
View File
@@ -5,8 +5,8 @@ Drop a `.dff` and `.txd` into `models/`, open `/samp/skin/<id>`, and it renders.
The RenderWare files are parsed in the browser — no conversion step, no Blender,
no build tooling.
`/samp/skin/<id>` is the only thing the server exposes. The root, `/index.html`
and any other path return 404.
`/samp/skin/<id>` and `/samp/vehicle/<id>` are the only things the server
exposes. The root, `/index.html` and any other path return 404.
## Running
@@ -52,6 +52,137 @@ That yields **265 skins**. Three groups are absent for real reasons:
Extracted models are gitignored; they're Rockstar's assets, not yours to ship.
## Vehicles
`/samp/vehicle/<id>` renders a vehicle, where `<id>` is the SA-MP model id
(400-611). They come out of your own install the same way skins do:
```bash
node tools/extract-vehicles.js --game "D:/SteamLibrary/steamapps/common/Grand Theft Auto San Andreas"
```
That reads `data/vehicles.ide` for the id -> model mapping, pulls each `.dff`
and `.txd` out of `models/gta3.img`, and writes them to
`models/vehicles/<id>.dff` / `<id>.txd`. All 212 come across, along with the 194
upgrade parts from `data/maps/veh_mods/veh_mods.ide` - vehicles are 400-611 and
parts 1000-1193, so one flat folder holds both. It also copies two things the
viewer needs:
- `vehicle.txd`, from `models/generic/`. The grunge, glass, light and tyre
textures every vehicle's materials name live there rather than in the
per-vehicle txd, so without it most of a car renders untextured.
- `vehicles.json`, built from `data/carcols.dat` and `data/carmods.dat`: the 127
entry colour palette, each vehicle's factory colour combinations, how many
paintjobs it has, and which upgrade parts it accepts.
A vehicle clump is not just "every atomic, drawn at its frame". Three rules come
with it, all in [public/js/vehicle.js](public/js/vehicle.js):
- **Damage variants.** `bump_front_ok` and `bump_front_dam` are both in the
clump. The `_dam` ones are hidden, or they z-fight the intact panels.
- **`chassis_vlo`.** The low detail chassis sits inside the real one, and is
hidden for the same reason.
- **Wheels.** A car carries exactly *one* `wheel` atomic, parented to whichever
`wheel_XX_dummy` the modeller happened to pick. The game clones it to every
dummy, so the viewer does too - mirrored on the left, because all the dummies
share one orientation and differ only in position. Six wheelers work out of
the box (`wheel_lm_dummy`), and a vehicle that models a corner itself keeps
its own geometry.
### Colours
SA encodes paint in the material colour: the game looks for a reserved value and
swaps in the carcols entry. Checked against the retail models - every car uses
`60,255,0` for the primary, two thirds also use `255,0,175` for the secondary,
and the `car4` vehicles (camper, cement, squalo) add `0,255,255` for the third.
Those materials keep their colour when textured rather than being forced to
white, so the grunge map multiplies over the body colour the way it does in
game.
Colours are carcols palette indices - the same numbers `ChangeVehicleColor`
takes - and default to the vehicle's first factory combination:
```
/samp/vehicle/596?c1=3&c2=1
```
Repainting does not need a reload, so the console gets `setColors(3, 1)`, plus
`carcols` for the palette and `viewer` for everything else.
### Upgrade parts
Parts are fitted by component id - the same numbers `AddVehicleComponent` takes
- and stack with the colour parameters:
```
/samp/vehicle/401?u=1005,1006,1001,1007,1020,1013,1008,1025&c1=3&c2=1
```
An upgrade dff is a single atomic on a single root frame and carries no
attachment information at all. What holds it on is a dummy frame in the vehicle,
picked by the prefix of the part's model name:
| prefix | frame | |
| --- | --- | --- |
| `spl_` `rf_` `nto_` `lgt_` | `ug_spoiler` `ug_roof` `ug_nitro` `ug_lights` | |
| `bnt_` `bntl_` `bntr_` | `ug_bonnet` `ug_bonnet_left` `ug_bonnet_right` | |
| `wg_l_` `wg_r_` | `ug_wing_left` `ug_wing_right` | |
| `misc_a_` `misc_c_` | `misc_a` `misc_c` | |
| `fbb_` `bbb_` | `ug_frontbullbar` `ug_backbullbar` | slamvan only |
| `fbmp_` `rbmp_` | `bump_front_dummy` `bump_rear_dummy` | replaces the stock bumper |
| `exh_` | `exhaust_ok` | replaces the stock exhaust |
| `wheel_` | every wheel dummy | |
Two of those took measuring rather than guessing:
- **Exhausts hang off `exhaust_ok`, not `exhaust`.** The `exhaust` frame is the
smoke emitter and sits about a metre behind the car. Across the 31 vehicles
that accept `exh_b_l`, mounting on the stock exhaust component puts the pipe
end within a few cm of the rear of the body every time; mounting on `exhaust`
overshoots by a metre every time.
- **A part's own root transform is dropped.** Fitting one re-frames its atomic
onto the vehicle's dummy, which discards the frame it came with. Only the
bumpers have a non-identity root - `fbmp_a_l`'s is a metre off centre and its
geometry cancels that out, exactly like the stock bumper does against
`bump_front_dummy` - so they are the only parts where it shows.
Parts carry their own `_dam` twins, skipped like the vehicle's own. Fitting a
left hand body kit part fits its right hand twin too, which is what carmods'
`link` section is for. Wheels are the one part that needs scaling: a stock wheel
already comes at the vehicle's own size, an upgrade wheel is modelled at unit
diameter and the ide's wheel scale is what sizes it - measured, infernus stock
0.70 against a scale of 0.70, linerun 1.10 against 1.10, every `wheel_*` upgrade
0.99.
`?u=` accepts anything; `mods` in the console lists what the vehicle is actually
offered, id to part name.
### Paintjobs
Thirteen vehicles take one - the six Transfender tuners, the six lowriders and
the camper. Three each, except broadway with two and camper with one:
```
/samp/vehicle/562?pj=0
```
A paintjob is a one texture dictionary stored beside the vehicle in the archive
as `<model>1.txd` upwards, extracted zero based to `<id>_pj<n>.txd` because that
is what `ChangeVehiclePaintjob` counts from. It is not a name for name texture
swap: the vehicle's own txd marks the panels a paintjob covers by giving them a
texture called `remap*`, and fitting one replaces that texture on every material
using it - seven of them on the elegy, across the chassis, doors, bonnet, boot
and both bumpers.
The artwork carries the colour, so those panels stop taking the primary paint
while a paintjob is on and go back to it when it comes off. `setPaintjob(0)` in
the console switches without a reload, and anything out of range takes it back
off the way 3 does in game.
Not yet handled: `stereo` and `hydralics` have component ids but no geometry,
the light corona materials, dirt level, and the `_hi`/`_lo` LOD split some
planes use.
## Naming
Files are named by id: `<id>.dff` and `<id>.txd`, in `models/` locally or under
@@ -88,6 +219,7 @@ failed fetch instead.
## API
- `GET /api/skin/:id` — the URLs for one id, or 404
- `GET /api/vehicle/:id` — the same, plus the shared txd and catalogue URLs
## What's implemented