Documentation

Model standard

What a prepared car has to contain before World Vehicles can drive it, from scale to acceptance.

What a car has to contain before World Vehicles can drive it. This page is for whoever prepares the assets. Once a car exists, Handling tunes it and the API controls it at runtime.

The reference module is the baseline for materials and assembly. It is not a set of coordinates to copy: every car needs its own measurements.

What you are building

The chassis is a networked dynamic RDR object, not a vehicle entity. World Vehicles supplies the physics, controls, seats, panels, lighting, audio and server state. You supply the geometry and the measurements.

Part What you author
Chassis RDR drawable, real body collision, measured physical properties
Wheels One shared wheel drawable, four suspension mounts
Doors, hood, trunk Separately pivoted drawables with their own collision
Bumpers Separate intact parts, optionally dented and crushed variants
Paint Compatible materials, palette textures, an explicit preset table
Lights Off and on lens meshes, measured emitter anchors, forward direction
Seats One to four positions, exits, entry routes
Plates One or two measured mounts; the core supplies backgrounds and glyphs
Audio An existing pack ID, engine and exhaust anchors
Resource Manifest, complete vehicle.json, archetypes

There are four suspension contacts. Wheel props are non-colliding visuals and the probes provide support, so switching on wheel collision never fixes wrong mounts or a missing body bound.

Before you start

Keep your source archive, rig, collision data and project files intact, and never edit your only original. Snapshot the project and the server resources separately: their settings can differ, and silently picking one causes confusing results later.

Write down a worksheet before touching geometry: IDs, scale, dimensions, mass, centre of mass, inertia, wheels, real seats and panels, materials, lamp anchors, plate locations. Keep it outside vehicle.json; inventing runtime fields there does nothing.

Ship only geometry you authored or hold a commercial licence for. Keep licences and credits with your source files, separate from the in-game fiction.

Scale and axes

One body-local frame throughout:

  • +X right, +Y forward, +Z up.
  • Lengths in metres, panel rotations in degrees.
  • Seat 0 is the driver; the rest are passengers.

Steering is the exception: SteeringAngleLimit is radians and SteeringRate radians per second. See Handling.

Bake the scale once

The fleet convention is 1.25× the original model, except Comet Spyder at 1.0. That is a design decision, not a unit conversion.

Scale together: render vertices, wheels, panels and damage variants, bind transforms and pivots, collision and bounds, centre-of-mass position, mounts and radii and suspension lengths, seat and exit routes, spawn clearance, and lamp, audio and plate anchors.

Do not scale UVs, angles, gear ratios, RPM, field of view, grip or audio pitch. Normalise the final basis and remove any negative or non-uniform scale. Never apply runtime enlargement on top of already enlarged assets.

Plate meshes are shared and already sized. Scale the car-side mount positions, not the plate again.

Mass, centre of mass, inertia

The embedded chassis bound and the JSON have to agree. Mass is kilograms. InertiaX/Y/Z are unit-mass inertia in m², not kg·m².

Take the whole source fragment's aggregate tensor divided by its aggregate mass, not the first collision child. Uniform scaling by s scales unit-mass inertia by ; mass is a separate decision. A changed centre of mass or frame needs the tensor transformed properly, not the offsets quietly dropped.

Panels

Evaluate the full parent and bind transform chain first. Children are not always relative to the chassis, and mixed-part triangles need checking before you extract a door.

Names follow wv_example_body, wv_example_wheel, wv_example_door_l, wv_example_door_r, wv_example_hood, wv_example_trunk, wv_example_bumper_f, wv_example_bumper_r. Use only the parts the car really has; there is room for sixteen.

For each hinged panel:

  1. Put the local origin at the real hinge.
  2. Express the render mesh and its collision in that same pivot-local frame.
  3. Put the hinge's body-local location in bodyParts[].offset.
  4. Define the closed rotation and the opening delta openRotation.
  5. Test closed, halfway and fully open.

Keep the IDs stable, because seat routes and damage logic reference them. An opened door has to reveal a real opening, and the chassis must not keep a duplicate solid copy of a panel that moved.

Collision

Author collision around this car's solid areas. A copied box from another model is the most common cause of trouble here.

  • The body blocks pedestrians, NPCs and objects.
  • Real cabin and door openings stay open, with their CAR_VOID semantics.
  • Every moving panel needs its own valid hull. Setting collision:true does not create one.
  • Check the underside, wheel arches and bumpers through full suspension travel. Avoid protrusions that catch the road, without filling the cabin solid.
  • Use validated inclusion and type masks with pinned numeric definitions. Do not guess flags, and do not give metal the semantics of wood.
  • Rebuild bounds after any geometry change, and do not transform child-local collision by its parent twice.

A ray hit is not proof of pedestrian collision. Walk into a fresh spawn from all four sides before entering, test an NPC, then repeat with the panels open. Never switch off body collision to make entry easier; that hides the defect rather than fixing it.

Convert, then read it back

  1. Extract and inspect the model.
  2. Run FleetPrep for flattening, transforms, materials and physical preparation.
  3. Convert the prepared YDR/YTD with the pinned converter.
  4. Build YTYP registration and the module configuration.
  5. Inspect the final RDR candidate independently.
text
FleetPrep extract <dlc.rpf> <output-dir> <source-id>
FleetPrep <source-dir> <intermediate-dir> <source-id> <lore-id> <name> <mass-kg> [scale]
convert_fleet.exe <source.ydr|ytd> <target.ydr|ytd> [chassis_mass]
python tools/inspect_fleet_resource.py <candidate-resource> --report <report.json>

FleetPrep output is still an intermediate, and the converter consumes YDR/YTD rather than arbitrary YFT. Renaming a file or editing a header is not conversion.

Read back the actual RDR render assets, bindings and bounds. Every drawable needs a matching archetype and the right texture dictionary. A colliding drawable's physics dictionary matches its model stem, and a non-colliding one must not advertise physical data it does not have.

Always write into a new candidate directory, never straight into a live resource, and preserve later interior, chrome, plate and paint work when rebuilding.

Materials

Inspect the actual bound textures and find every consumer of a shared atlas before changing it.

Surface Target
Paint Deep retro colours, separate primary and secondary regions
Chrome, rims, bumpers Metallic reflection, not grey plastic gloss
Tyres and seals Dark matte rubber with tread, alpha and normals preserved
Cabin Dark upholstery, readable gauges, separate chrome and glass
Underside Authored matte undercoat with meaningful UVs
Lenses and windows Correct transparency and emission, independent of body paint

A missing interior texture is not fixed by binding an unrelated atlas, and collapsed UVs need repair rather than more contrast. Preserve alpha, mips, normal conventions and tangent orientation, and never globally darken a texture before checking everything that uses it.

Chrome reference

Audited bare metal on the reference module uses generic RDR standard, hash 0xBCF03FC1, not standard_vehicle, which expects vehicle-class instance data.

Binding Content
DiffuseTex Neutral metal reflectance, rim pattern preserved
BumpTex Compatible normal map
SpecularTex Raw RGBA 255,25,255,255: metallic 1, roughness 25/255
SpecularTex2 Raw RGBA 0,0,0,255: detail off
TintPaletteTex White 256×64, independent of body paint

Those packed channels are material data, not sRGB colours, and RGBA semantics are not BGRA byte order. High gloss in a legacy shader does not reproduce the same metallic response.

Paint uses the normal_spec_tnt path with real independent palettes. Convert palette sRGB to linear once, not across all source art.

Removing real-world branding

Inspect pixels and raised mesh lettering: grille, hood, trunk, sides, hubcaps, steering wheel, dashboard, old plates. A fictional file name does not remove visible branding from geometry.

Use narrow reviewed masks, preserve everything outside the approved area, keep instrument markings and unrelated chrome intact, and document what you authored as a replacement.

Wheels and suspension

Index Corner Fields
0 Left front Mount0, Driven0
1 Right front Mount1, Driven1
2 Left rear Mount2, Driven2
3 Right rear Mount3, Driven3

Front wheels steer and at least one wheel must be driven. The wheel model is shared; wheelFlipRight, wheelVisualRotation and wheelVisualOffset change appearance only.

Mounts are suspension top anchors, not wheel centres. The runtime places each wheel below its mount by the current suspension length along local Z. Measure WheelRadius on the final scaled tyre.

Bad rear mounts, or a chassis resting on the road, produce spinning wheels and no movement. Check ride height, travel, probe contact and underside clearance before tuning anything in Handling.

Seats

seats holds one to four body-local positions, driver first. If you supply seatExitOffsets, the count has to match.

Enabling seatTransitions requires a route per seat, a real matching door with an openRotation, and measured approach and threshold coordinates. Timeouts run from 1000 to 20000 ms. A doorless racer must not reference doors it does not have.

seatAnimation picks a compatible RDR dictionary, clip and root offset. Align the character with the real seat and wheel in game. A generic drive clip from another engine is not automatically usable here, and a correct sitting pose does not mean a steering animation was ported.

Test every seat, nearest-door selection, occupied slots, normal exits, moving and airborne exits, rollover and water exit, without globally disabling collision to get through it.

Lights

Glowing lenses and light on the road are two separate deliverables. Front, tail and brake groups each use two measured anchors.

The offModel and onModel lens meshes attach at body origin, not at each light anchor, and stay in body-local coordinates. Emitter anchors position the light sources; applying them to the lens geometry as well misplaces it.

The reference module uses emissive entities, warm front RGB 255,200,16 and red tail and brake lamps. Its beam correction values are specific to that path, not a universal lamp rotation.

Check lenses in daylight and beams at night from front, side and rear. Beams point forward, not down or into the hood. Verify spread and range, independent red brake response, off and on states, and cleanup after damage and repair. A working road beam is not proof that the lens itself lights up.

Paint

The core supports one to seven primary presets, optionally one to seven secondary presets, and every pair exactly once, up to forty-nine combinations. tintIndex values are unique integers 0–63. The rgb value is swatch metadata for the interface, not a mesh recolour.

For two colours:

  1. Assign real exterior regions to separate primary and secondary materials.
  2. Author two palettes where the same row picks the intended pair.
  3. Supply paint.presets, paint.secondary.presets and the full explicit paint.combinations table.
  4. Match each primary tintIndex to its default secondary pair. Secondary presets carry no tintIndex.
  5. Use the same mapping on painted panels and damage variants, leaving chrome, rubber, glass, cabin, lamps and plates independent.

The reference module's primaries are original, cherry, ivory, beige, black, navy and forest; secondary starts with cream instead of original. Its existing 36 pairs stay at indices 0–35, with forest combinations occupying 36–48. Do not renumber existing pairs when extending a palette.

Test every pair, on several cars, through repair, re-streaming and two clients. The server accepting a selection is not proof the material rendered.

Plates

The core supplies backgrounds and glyphs; each car defines one or two mounts with unique front and rear IDs.

json
{
  "plate": {
    "defaultState": "lemoyne",
    "mounts": [
      {"id": "front", "offset": [0, 2.5, 0],  "rotation": [0, 0, 0]},
      {"id": "rear",  "offset": [0, -2.5, 0], "rotation": [0, 0, 180]}
    ]
  }
}

Those coordinates show the shape only; measure your own. The shared plate is 0.43 × 0.215 m, its face points +Y and its top +Z, and viewed from +Y visual right is −X. Check rear glyph readability after rotation, avoid intersecting the bumper, and remove the model's original plate underneath.

The five states and the text rules are in the API reference. Plates hide from bumper stage 1 and return on repair, and they do not follow detached debris. Generated numbers are session-local, so a garage has to save and reapply them.

Audio and damage assets

Set audio.pack, the engine and exhaust anchors, maxDistance, masterGain and cabinGain. The registry and recordings live in the core, so a pack placed only in a car module is never discovered.

Use licensed recordings and test RPM and load transitions inside and outside the car. The current pack has five engine loops and no dedicated recordings for ignition, shutdown, horn, doors, impacts or tyre events: an event name without a recording is not a finished sound.

Visible bumper stages need intact bumper_f and bumper_r parts plus two models each, dented and crushed, sharing the original pivots. Thresholds run 1000 > dent > crushed > detach >= 0. These are authored stages, not live mesh deformation, and stage collision keeps the original hull. Do not combine them with the experimental whole-body replacement path.

Packaging

One car per resource.

text
wv_example/
  fxmanifest.lua
  vehicle.json
  stream/
    wv_example_body.ydr
    wv_example_wheel.ydr
    wv_example.ytd
    wv_example_archetypes.ytyp
lua
fx_version 'cerulean'
game 'rdr3'
rdr3_warning 'I acknowledge that this is a prerelease build of RedM, and I am aware my resources *will* become incompatible once RedM ships.'
author 'Appi'
version '1.0.0'
files {
    'vehicle.json',
    'stream/wv_example_archetypes.ytyp'
}
data_file 'DLC_ITYP_REQUEST' 'stream/wv_example_archetypes.ytyp'

Register each YTYP once and include vehicle.json in files so clients can read it.

One unique profile per module, up to 32 configured profiles overall. IDs are 1 to 64 ASCII letters, digits, underscores or hyphens, with no case-only duplicates. A module caps at 262144 characters and 64 levels of JSON nesting. Duplicate keys, trailing JSON, non-finite values and unsupported fields are rejected.

Installing is covered in Getting started: start the module and the core finds it by name and reads its vehicle.json, and remember that spawning selects the profile ID, not the folder or the body model.

Acceptance

Offline checks first:

  • Baseline hashes preserved, only approved files changed.
  • Final RDR readback passes, not just the intermediate export.
  • Model, archetype and texture references resolve, with no duplicate registrations.
  • Scale, axes, pivots, bounds and mass, centre of mass and inertia match the profile.
  • Panel collision and wheel placement checked through full travel.
  • Materials, mips, alpha, UVs, tangents and both palettes inspected.
  • Real-world branding gone from pixels and from geometry.
  • Every paint pair and every plate, seat, lamp and damage reference validates.

Then in the game, which is the part that actually decides:

  • A fresh spawn assembles fully and the chassis is not in the road.
  • Fifteen seconds idle with no bounce or drift that prevents entry.
  • Pedestrian and NPC collision from all sides, before entry and after exit.
  • Panels block correctly closed and open, without sealing real openings.
  • Every seat, plus moving, airborne, rollover and water exits.
  • Flat-road acceleration, gears, braking, steering and measured speed.
  • Slopes, rear-wheel support, bumps, flight and landing.
  • Night beams, lit lenses, separate red brake lights, damage and repair.
  • Every colour pair, both plate mounts, several cars at once.
  • Engine audio inside and outside, with missing event recordings listed.
  • Bumper stages and repair never remove the chassis.
  • Two clients: turning, a late join, re-streaming, ownership migration.
  • Deleting or stopping removes every wheel, panel, plate, lamp and sound.
  • A missing or stopped module does not break the remaining cars.

Collision preloading is not continuous collision detection and proves nothing about high-speed terrain safety. Improved visual wheel steering does not prove remote drift is solved. Contact particles are not persistent tyre tracks.

Keep automated results, direct observations and owner feedback separate, and never present old evidence as if it tested a new candidate. For each release retain the worksheet, the material map, hashes, the validation report, logs, known limitations and rollback instructions.