# Door robot arm (The Descent) A white industrial robot arm on a rail, modelled from the user's seven reference pictures (`reference/`), rigged and fitted to the moon pool's two doors. The user's words (2026-10-16): > 3d model this in blender. Understand the picture references and rig it accordingly and mechanically. according to the > open and close animation fit it to the scene. for the moon pool doors "Moonpool_Door_Fore", "Moonpool_Door_Aft" are the > door. also consider the rail accordingly. do 2 version with and without the grab limbs shown in the red lined picture > so there will be 2 versions side by side so I can change when I want. dont miss details > this will be standalone project dont add on the ship yet Built as a standalone project first (the doors, rails, deck, pocket walls and lamps are a reference copy, `source/ship_context_2026-10-16.blend`). **Since 2026-10-18 version B is installed in the ship** (section "In the ship" below). The user, with a screenshot of version B: *"ok now install this version of the arm to the ship, you can change the rail system with this try to fit it accordingly how I described you before"*. ## Open it and switch versions Open `DoorRobotArm.blend` (Blender 5.2). It opens in the scene **`DoorArm | Showroom`**: both versions standing side by side on a floor rail, at rest (the pose of the first reference picture): version A (with the grab limbs) at x -1.9, version B (the plate from the red sketch) at x +1.9. The scene **`DoorArm | Ship fit`** holds the arms in the pocket under the deck, one per door, following the doors' own animation (frames 1-120, 30 fps). Play the timeline there. **Switching:** in the Ship fit scene, select the empty **`DoorArm | CONTROLS`** (collection `DoorArm | controls`, a wire cube over the moon pool). In Object Properties > Custom Properties: - **`version`**: 0 = A, the gripper with the grab limbs; 1 = B, the neck and plate drawn in red on ref6. Both doors' arms and fixtures switch together. The other version is hidden in the viewport and in renders. It is not evaluated while hidden, so its objects keep old positions until you switch back. That is normal. - **`grip`**: version A's fingers, 0 open .. 1 closed on the knob on the door (default 1). Collections: `DoorArm | version A`, `DoorArm | version B` (arms, cables, fixtures), `DoorArm | rail kit` (both rail kits and energy chains), `DoorArm | solver (hidden)` (the solver empties), `00 CONTEXT | from the ship` (reference only), `90 STUDIO | lights (not part of the asset)` (lights so the dark pocket shows in Rendered view), `DoorArm | collision` (UCX hulls, in the showroom, eye off). ## Scale, axes, pivots - Metres, Z up, the ship's own coordinates. The arm is large: 2.8 m tall upright, 3.75 m reach. - Arm frame A (the rest pose): origin at the turntable face centre, +X forward to the tool, +Y the arm's left, +Z up. In the ship the arm hangs upside down. Its root empty `DoorArm__` sits at (0, -5.795, -4.182), turned 180 deg about X, under the rail. - Every moving part is its own object. Its origin is on its joint. At rest its rotation is 0 and its scale 1. Joints (A frame, metres): | joint | what | pivot | axis | |---|---|---|---| | J1 | turret yaw | (0, 0, 0) | Z | | J2 | shoulder | (-0.341, 0, 0.517) | Y | | J3 | elbow, driven by the hydraulic cylinder | (-1.210, 0, 1.826) | Y | | J4 | forearm roll | (-0.077, 0, 2.0614) | X | | J5 | wrist pitch | wrist centre (0.957, 0, 2.0614) | Y | | J6 | flange roll | wrist centre | X (tool axis) | The J3 cylinder turns on its trunnion at the knee (-0.8646, 0, 0.847). Its piston's rod eye sits on the knob pin (-1.6764, 0, 1.4564). The carriage pinion turns as the carriage runs on the rack. Every pivot is in `DoorRobotArm_layout.json`. ## The rig: how the arm follows the door Nothing is keyed. Every moving part is driven from the door's location (the door keeps its own animation) by 604 drivers. Each is a simple expression, so the user's Blender runs them with Python auto-run off. - A chain of solver empties computes a closed-form inverse kinematics (DESIGN.md section 5): 1. **IK1** puts the carriage on the rail and the wrist centre in the arm's frame. 2. **IK2** turns the turret. 3. **IK3 and IK4** solve the shoulder and the elbow (elbow up, as in ref1). 4. **IK5 and IK6** solve the spherical wrist (J4, J5, J6), the cylinder's angle and its length. - The end of the arm holds its door rigidly the whole time, position and angle: - Version A's four fingers grip a knob fixture bolted to the door. - Version B's plate is bolted to a pad that follows the door's curve. - Version B's wrist centre sits 0.326 m off the door face (A: 0.63 m), so B has its own pose. - The door fixtures follow their doors (location = door + offset; the doors never turn). - The two energy chains (44 links each) are driven from the carriage's position. ## The rail The door slides 6.14 m into its pocket. The usable rail is 6.49 m long (from the pocket's end wall to the rail's inner end; past it is the moon pool's opening). The user's rail is a plain box fixed under the deck, so the arm hangs from a carriage that wraps the rail's visible part from below. The asset adds a rail kit (the user's rail itself is unchanged): - **Running gear:** two steel running flanges on the rail's sides, four load wheels, four counter-rollers, four side guide rollers. - **Drive:** a rack under the rail and a pinion on the carriage. - **End stops:** rubber buffers at both ends of the travel. - **Energy chain:** in a tray beside the rail. The carriage runs 5.10 m at 0.831 of the door's speed (c = A + B * door_x), so it reaches each end exactly when the door does. The arm's joints take up the rest. ## Checks (all pass, on the final file of 2026-10-18 16:41) Run by script; the reports are in `build/`. - **`rig_report.json`:** frames 1-120 step 1, both doors, both versions. - The end against the door: position error 0.056 mm (A), 0.055 mm (B); angle 0.014 deg. - 0 crossings between any two arm parts. 0 against the deck, rails, end walls, hull lining, basin channel, aperture rim, lamps, both doors, the rail kit, chains and fixtures. 0 for the cables and hoses. - Joints inside their limits. Piston always in its bore. - Carriage inside its travel. Strike pads 3.5 mm short of the buffers. The arm at least 0.24 m (A) / 0.32 m (B) from the end wall. - Version A's four hooks close onto the door's knob: 0.45-0.54 mm off its surface at every frame, never through it. Version B's plate sits on its pad. - Version switch correct (A, B and back to A). 604 drivers, all simple and valid. - **`geo_report.json`:** 0 n-gons, 0 open edges (the floating markings apart), 0 non-manifold edges, 0 crossings at rest. - **`geo_sweep.json`:** each joint through its range on the door path plus 3 deg, others at rest; J1 the whole circle; the grip closed on the knob. 0 hits. - **`fbx_report.json`:** every FBX re-imported headless and compared: - bones, meshes, triangles, materials and UCX hulls; - bounds within 0.1 mm; - no scale on any node, and the default pose equals the bind pose; - animations run frames 1-120. ## Surfaces and textures Materials (few): - **Arm:** `M_DoorArm_PaintWhite` (satin white powder coat), `_Orange`, `_Gunmetal`, `_BlackMetal` (the carriage's stacked plates), `_Steel`, `_Rubber`, `_Glass`, `_Lamp` (a faint indicator). - **Rail kits, fixtures and chain:** `M_DoorArmKit_*`. - **Cables and hoses:** `M_DoorArm_Cable` in Blender. In the FBX the cables use the `M_DoorArm_Rubber` section and the lamp the `M_DoorArm_Glass` section, so Unreal gets 7 material sections (paint white, orange, gunmetal, steel, rubber, glass and, on the carriage, black metal). Wear is low: the ship is clean. What each material is made of is in `build/surf_report.json` and in the layout json. | texture | from | licence | |---|---|---| | `source_polyhaven/textures/blue_metal_plate/*` (2K: colour, normal, roughness, AO): the relief, roughness and scuffs under the paint and the dark metals (only its light and dark, not its blue) | polyhaven.com/a/blue_metal_plate, fetched with curl | CC0 | | `source_polyhaven/hdris/studio_small_09_2k.hdr`: the showroom's light in the renders only | polyhaven.com/a/studio_small_09 | CC0 | | `models/vessel-interior/textures/ModSci_Engineer/Textures/Base/*`: StainedSteel (bright steel), ScratchedMetal roughness (rubber), Glass_DirtRough (smudges), T_Scratch_Norm (fine scratches) | the user's own Modular SciFi pack (already in the Unreal project), read in place | the pack's licence | | `textures/T_DoorArm_Masks.png` (R occlusion, G edges, B crevices) and `textures/T_DoorArm_BevelNormal.png` (OpenGL; flip green for Unreal), 4096 | baked from the model (Cycles) | own work | UV layers on every arm mesh: - **UV0 `UVMap`:** one atlas shared by all 20 arm meshes of both versions, 352 px per metre. - **UV1 `UV_Tile`:** metres, for the tiling sets. The ship context's door materials had missing images already in the ship. In this file they have plain stand-in colours (reference only). ## FBX for Unreal (`fbx/`) Convention as the project's other models: forward -Y, up Z, the data turned -90 deg about Z. Import with **Convert Scene** and **Force Front X Axis**, so Unreal = (x, -y, z) x 100 of the Blender data. The skeletal files are in centimetres with no scale on any node. | file | what | |---|---| | `SK_DoorRobotArm_A.fbx`, `_A_LOD1.fbx`, `_A_LOD2.fbx` | version A: skeleton (root + 19 bones, one per moving part, at its pivot) and one skinned mesh. Every part rigid on its bone; the elbow cables and cylinder hoses blended between their two parts. 191,771 / 96,802 / 39,788 triangles | | `SK_DoorRobotArm_B.fbx`, `_B_LOD1.fbx`, `_B_LOD2.fbx` | version B (root + 11 bones): 166,343 / 84,088 / 34,714 triangles | | `A_DoorRobotArm___OpenClose.fbx` | the door animation for each version and door, frames 1-120, baked from the rig. Place the skeletal mesh at the root's position: Blender (0, -5.795, -4.182) turned 180 deg about X = Unreal (0, 579.5, -418.2) cm with a roll of 180 deg (not tried in Unreal yet); the carriage bone carries the travel along the rail | | `SM_DoorRobotArm_Links.fbx` | every part as a static mesh at its pivot with its `UCX_` hulls (41), for a Blueprint build | | `SM_DoorArm_RailKit_.fbx` | the rail kits, ship coordinates | | `SM_DoorArm_Fixture_.fbx` | the door fixtures (origin on the door's attach point; attach to the door actor), with UCX | | `SK_DoorArm_Chain_.fbx` + `A_DoorArm_Chain__OpenClose.fbx` | the energy chains, one bone per link, ship coordinates | **LODs:** LOD0 keeps every detail (the user asked for every detail). LOD1 (about 50 %) and LOD2 (about 20 %) drop the markings and the small parts first (screws, clips: below 3 cm and 6.5 cm across), then reduce. Import them in Unreal as LOD 1 and 2 of the skeletal mesh (LOD Settings > Import LOD). `previews/lods_A.png`, `previews/lods_B.png` show the three side by side. **Collision:** the skeletal meshes get a Physics Asset in Unreal. The static parts carry `UCX_` hulls (every hull is checked convex when it is made: `build/ucx_report.json`); the rail kits carry box hulls for their flanges, tray, rack and end buffers. **Materials:** they exist in Blender only. Rebuild them in Unreal from the table in `build/surf_report.json` and the textures above. `DoorRobotArm_layout.json` has every number a consumer needs: bones, parents and pivots, joint axes and limits with the ranges the doors use, the roots, the carriage's travel and schedule, the doors' attach points and face normals, the inverse-kinematics constants, the version switch and the materials. ## Pictures - `previews/cycles/`, Cycles: - the showroom at the angles of ref1, ref2 and ref3 (both versions, A alone, B alone); - close-ups like ref4, ref5, ref6 (A and B) and ref7; - the ship fit at frames 1, 12, 30, 48 and 90 for both versions and both doors; - `cycles_ship__switch.png` sheets (A row over B row); - `cycles_compare_sheet.png`: each reference beside its Cycles view. - `previews/geo/`: Workbench views beside each reference. `geo_compare_sheet.png` puts every reference next to its view. - `previews/rig/`: quick renders of the rig in the pocket. `rig_switch_.png` shows the switch. ## Rebuild Everything is made by script, headless, one stage per call: "E:\Steam\steamapps\common\Blender\blender.exe" -b --factory-startup -t 30 --python build/dra_build.py -- --stage Keep `-t 30` (30 of the PC's 32 threads) and start Blender at below-normal priority. A run on all threads held the PC at 100 % CPU (the user, 2026-10-18: *"stop using 100% CPU use 99%"*). Run one stage at a time, never two Blenders at once. 1. **`build`**: geometry + rig + surfaces. It backs the file up into `versions/` first. 2. **`bake`**: the atlas maps. 3. **`ucx`** 4. **`export`**, then **`verify_fbx`** 5. **`render`** 6. Checks, any time: `rig_check`, `geo_audit`, `geo_sweep`. `STATUS.md` holds the history and the numbers. ## In the ship (2026-10-18): version B `models/M_Ship_v2_interior_season2.blend` holds version B on both doors, installed by `build/dra_ship_install.py` (`apply()`, `remove()`, `check()`; the record is the text block `ROBOT DOOR ARMS | what was added`), run headless on a work copy by `build/run_ship_install.py` and swapped in only after the check passed. The ship file before it: `models/M_Ship_v2_interior_season2_before-robot-arm-install_2026-10-18.blend`. - **Scale-up, reinstalled 2026-10-18 22:24** (`run_ship_install.py --stage reinstall`: `remove()` then `apply()`): the arm 1.6x, the plate 1.2 m on a 1.3 m pad at each door's centre; the arm's own rail beam on the new rail line y -3.80 (from the end wall to x -3.12 AFT / 9.22 FORE, top on the deck's underside), the carriage c -3.871 -> -5.50 (AFT). Check, every frame 1-120 against 122 objects and the instances: 0 clashes, plate to pad 0.0025 mm / 0 deg; the rail system's 4696 vertices all under the deck, 20 mm from its edge. Before: `..._before-robot-arm-scale-up_2026-10-18.blend`. - **Collections** under `02 MECHANISM | Starboard pocket doors`: `... | robot door arms (version B)` with `... | arms` (both arms and their door pads), `... | solver (hidden)` (the solver and cable-hook empties, eye off) and `... | rail system` (the two rail beams, the rail kits, the chain trays and chain links). - **The drivers read the ship's own doors** (the append's copies of the reference doors were remapped and deleted). The version switch's hide drivers were dropped: only B is in the ship, so it is always shown. 346 drivers, all simple. - **The rail system** ("you can change the rail system with this"): the user's plain rail boxes `Pocket AFT/FORE | upper rail` are hidden, not deleted; `DoorArm_AFT/FORE | rail beam` take their place, the same section in the kit's dark steel (`M_DoorArmKit_Gunmetal`), with the kit's flanges, rack, buffers and chain tray on it. The four placeholder `rail carriage` cubes stay hidden. - **The earlier flush-hinge door arms** (`02 MECHANISM | Starboard pocket doors | door arms`, another session's) ride the same rails, so their collection is excluded from both scenes' view layers (tick it in the Outliner to see them); nothing of them is deleted. - **Checked in the ship** (`build/ship_install_report.json`), frames 1-120 every frame: each plate holds its door pad within 0.02 mm and 0.0 deg, each pad its door within 0.001 mm; no part of the arms, rail system or chains crosses any of the 122 ship objects in the pockets' box (hull, lining, deck, basin, rim, lamps, end walls, both doors, the kit pieces above, the lower deck). The only contacts are the design's own: each beam set into the deck and into its pocket's end wall, as the user's rail was. Pictures (Workbench): `previews/ship_install/`. - Images: the arm's own textures are relative to the ship file (`//door-robot-arm/...`); the ModSci sets use the ship's own image datablocks. - To take it out again: open the ship in Blender, put `models/door-robot-arm/build` on `sys.path`, `import dra_ship_install; dra_ship_install.remove()` (or open the backup). ## Not done - **Version A is not in the ship** (the user asked for "this version", B). Both stay switchable in this file. - **Nothing tried in Unreal yet:** the import, the root placement, the materials, the Physics Asset. - **Still differs from the references:** the turret and the upper arm are layered and rounded, but simpler than the sculpted reference model (the deeper sculpt of both, CRITIQUE_2 item 9, is left for later). - **The wrist's margin at its deepest bend** (frames 9-15 and 100-112, version A) is 4 deg with J6 on the door path. Other animations should keep J6 near 45 deg when J5 bends past -72 deg: the finger motors pass 10 mm over the wrist slab's top there.