How a cable is laid out in the file, the ground and physics scene, and what the Primvaria contract writes on every cable.
OpenUSD layout
Z up, metres, kilograms, /World the default prim.
/World
/PhysicsScene gravity (shared, see below)
/Looks, /PhysicsMaterials UsdPreviewSurface materials; friction and restitution
/Cables/<cable> Xform, PhysicsArticulationRootAPI, filaire:* attributes,
variant sets "end_a" and "end_b"
/Segments/seg_000 … rigid bodies (RigidBodyAPI, MassAPI), x along the cable
/Visual ONE mesh: the piece of tube this segment carries
/Collision Capsule along x (round, coiled) or Cube (flat, figure-8), purpose guide
/Joints/joint_000 … PhysicsJoint: translations locked (coil: transX free between limits),
drives rotX (twist), rotY, rotZ (bending) [, transX (coil)]
/End_A, /End_B filled by the selected variant:
/Body rigid body of the connector or stripped end
/Visual ONE mesh, a GeomSubset per material (shell, body, insulators, clear plastic, cores, copper)
/Collision/hull_00 … convex parts (MeshCollisionAPI convexHull, purpose guide)
/MateFrame origin on the mating plane, +Z = insertion direction
/Attach PhysicsFixedJoint to the end segment
/Ground the shared ground: a collision box, its top face at z = 0
/GroundMaterial its physics material
Ground and physics scene (the same in every Primvaria project)
Every file with physics carries the same two prims, so that assets from different generators can share one scene and every engine gets the same floor:
| Prim | What it is |
|---|---|
/World/PhysicsScene |
gravity 9.81 m/s² along −Z |
/World/Ground |
a static box (UsdGeom.Cube, CollisionAPI), its top face at z = 0, 2 cm thick, covering the content with 0.5 m to spare on each side (1 m × 1 m at least), grey and rendered, no PhysX attributes (engines derive their contact offsets) |
/World/GroundMaterial |
its physics material: static friction 0.6, dynamic friction 0.5, restitution 0.1, a wooden or concrete floor |
The code is one file, ground.py, identical in Granulaire, Maillage, Filaire
and Quincaillerie; a test checks every generated file against it. The MuJoCo
checks and the Blender rigid-body checks take the ground's friction from the
file (Blender's cloth, in Maillage, has its own collision settings).
Each rigid body has exactly one visual mesh, as in Quincaillerie: Blender and other DCCs import one object per body, so a rigid body set on it moves the whole part. Each segment spans the chord between its two joints, so neighbouring segments meet exactly at their joint even on a tight curve.
PhysX layer
The files hold plain OpenUSD (UsdGeom, UsdShade, UsdPhysics) and
nothing specific to an engine. What only PhysX reads is written beside each
file as a layer over it, filaire_<preset>_physx.usda and
sockets_physx.usda:
| On | Schema | Values |
|---|---|---|
| every rigid body | PhysxRigidBodyAPI |
32 position and 4 velocity solver iterations (light links on stiff joints need many), depenetration velocity capped at 1 m/s, no CCD |
| every collider but the ground | PhysxCollisionAPI |
contact offset 0.5 mm, rest offset 0 |
The layer has the file as its only sublayer and holds overs only, so
opening it (Isaac Sim: File > Open) gives the file with these settings, and
the file alone is still complete for every other engine. The settings of a
connector are written inside the variant that brings it, so they follow the
end you select. python -m filaire.physx <folder> checks the layers of a
pack; the OpenUSD validators run on them in CI. Not simulated: see
Limits.
Without physics (--no-physics, write_outputs(…, physics=False)):
each cable is one mesh, /World/Cables/<cable>/Visual, each end a Body
with its Visual and MateFrame; no rigid bodies, colliders, joints,
physics materials, physics scene or ground. The filaire:* attributes stay.
OpenUSD compliance.
python -m filaire.validate output/usb_c # files or folders; exit 1 on any issue
This runs every validator of OpenUSD's UsdValidation registry, the checks
usdchecker runs (composition, stage metadata, geometry and subsets,
material bindings, shaders, UsdPhysics bodies, colliders, joints and
articulations), on each file as authored and once per end variant of each
cable, since a variant that is not selected is not composed. From Python:
filaire.validate.validate(file) returns the messages. The tests and CI
require no error and no warning, with and without physics.
Selecting a variant (end_b = "usb_c_inline", "cut"…) swaps the end, its
collision parts and its joint. Angular drives use UsdPhysics units
(stiffness per degree); the same values per radian and the rod's stiffness
are kept as engine-neutral attributes:
| Attribute (on the cable) | Content |
|---|---|
filaire:bendingStiffness |
E·I about the width axis and about the height axis (N·m²) |
filaire:torsionalStiffness |
G·J (N·m²) |
filaire:jointStiffness |
per joint: twist, bend y, bend z (N·m/rad) |
filaire:youngsModulus, filaire:shearModulus, filaire:linearDensity |
material (Pa, kg/m) |
filaire:segmentLength, filaire:segmentCount, filaire:restShape |
discretisation; rest shape is straight |
filaire:dampingTime |
joint damping = stiffness × 0.01 s |
filaire:coil:*, filaire:axialStiffness |
coiled cords: coil diameter, pitch, turns, wire length, coiled length, straight leads (m, end A and B); spring k (N/m) |
filaire:length, filaire:layout, filaire:profile, filaire:jacket, … |
what the cable is |
Attribute (on a MateFrame) |
Content |
|---|---|
filaire:connector, filaire:name, filaire:standard |
family and name (usb_c, "USB-C plug") |
filaire:role, filaire:mount, filaire:contactGender |
plug / receptacle, cable / panel, male / female |
filaire:insertionDepth, filaire:clearance, filaire:keying |
for insertion tasks (m) |
filaire:lockTwist |
bayonets only: the turn that locks the plug once fully in (degrees, about +Z) |
Primvaria contract
python -m filaire.conformance output/usb_c
Filaire is part of Primvaria and follows its
mission and its
OpenUSD contract,
version 0, which the four generators share. Every cable and every socket is an asset any tool can find without knowing Filaire: a model of kind
component with assetInfo (name, identifier, version) and the attributes
primvaria:contract, primvaria:generator, primvaria:version,
primvaria:preset and primvaria:seed; /World is an assembly, /World/Cables and /World/Sockets are groups; a socket is not drawn, so its preset is its connector family and its seed 0. The command above checks
the stage units, the model hierarchy and this identity; the tests and CI
require it on every preset.
What was drawn for each cable is in the file too, its ground truth:
primvaria:variation:aspects lists the aspects that were switched on
(length, layout, ends, color, stiffness, density) and primvaria:variation:<name> holds each drawn value
(length_m, nominal_length_m, layout, end_a, end_b, color, youngs_modulus_pa, linear_density_kg_per_m). A socket draws nothing: its list of aspects is empty. An aspect that is switched off is still recorded, at its
nominal value, and the other aspects keep their draws.
What was measured on each cable once generated is there as well, as
primvaria:measured:<name> (cable_mass_kg, end_a_mass_kg, bending_stiffness_y_nm2, torsional_stiffness_nm2, segments…).
The pack's instances.csv is read back from the OpenUSD files, one row per
asset with the same names (variation:<name>, measured:<name>), and
manifest.json says what the pack is; both have the same shape in the four
generators, and the command above fails when the table and the files disagree.
Each cable also says what it is with OpenUSD's own semantic labels
(UsdSemantics.LabelsAPI), which renderers such as Isaac Sim turn into
segmentation masks: semantics:labels:class holds cable and the preset (usb_c); for a socket, socket and its connector family (rj45), and
semantics:labels:instance the asset's name. The classes are a
column of instances.csv and are listed in manifest.json.