# Robot cell, game-ready, rigged An industrial robot cell after the user's picture (2026-10-22, `reference/ref_user.jpg`; the user: *"create me exact 3d model with rig of this reference picture"*): a dark charcoal steel frame on copper levelling feet, a glazed front with two sliding leaves, a glass side door on copper hinges, a dark back wall with a louvre vent, LED strips in the ceiling, a diamond-plate floor, an orange six-axis robot on a black pedestal with a copper cage tool, a chrome turntable on a black drive, a copper drain tray sunk in the floor, and a control pod with a screen, four buttons and an emergency stop. "Robot cell" is a working name; the user gave none. Not in Unreal yet. Built **headless** by script (`build/`), nothing in anyone's open Blender. Nothing is a boolean: chamfered prisms and blocks, lathes, lofted rounded rectangles, tubes (`build/rc_lib.py`, from the canister station's `cs_lib.py`). ## How it was made from one picture The picture is one perspective shot. The cell is a box, so its camera was **fitted to the six corners the picture shows** (`build/rc_fit.py`, Levenberg-Marquardt over place, yaw / pitch / roll, focal length and the box's width and depth; **3 px rms**): about 21 degrees off the front, at about 1.4 m, 7.5 m away, a 61 mm lens. The model keeps it as `RC_PictureCamera`, so a render from it lands on the picture. Everything else was read through that camera from ruler crops (`reference/z_*.png`, `build/zoom.py`, `build/probe.py`) and fitted by overlay (`build/rc_overlay.py`, `previews/fit/overlay_a.png`). The robot's pose was fitted to the picture's joint centres (`build/rc_fitpose.py`, 6 px rms: A1 -22.4, A2 22.5, A3 3.6, A5 40.6 degrees). **Chosen, not in the picture:** - **Scale.** The picture has none. The box is 2.40 m from the base's underside to the roof on 0.09 m feet, so the pod's buttons stand at 1.16 m and the emergency stop at 0.98 m. That makes the cell 3.05 x 2.88 x 2.49 m and the robot a mid-size one (upper arm 0.82 m, forearm 0.70 m). - **What the picture's camera says about the hidden parts:** the left 0.49 m of the front is a solid column (the pod's mount), 0.50 m deep; the back wall is glass on its left 0.36 m and dark panels from there; the left side is glass. With these, the back-left corner, the panel's edge and the ceiling's back edge land where the picture shows them. - **An AI picture's perspective:** the robot, the turntable and the tray are drawn bigger and more from above than its camera allows. They are placed to fit the cell (the robot 0.12 m right of where the picture would put it, so its turret clears the left glass) and fall within about 20 px of their picture positions. - **The doors' hardware.** The picture draws copper "hinges" beside handles on the front and on the side, which cannot all be hinges. Read as: the front leaves **slide** (the bottom guide and stop under their meeting stiles are in the picture); the copper parts at the front-right post are interlock switches; the side door hangs on the two copper hinges at its back edge and closes against the copper latch keepers on the front post. - **No brand names** (the project's rule): the maker's logo and name on the glass became a ring mark and "CELL 01", the robot maker's name on the forearm "R-60", the screen's product name "CELL 01". The robot is a generic six-axis design in that style, not a copy of a real model. - **The back of the cell** (never in the picture): four panels with seams, so it does not read as one slab. ## Files - `RobotCell.blend` (Blender 5.2): collections `RobotCell` → `RC | static`, `RC | moving`, `RC | robot`, `RC | collision` (UCX hulls, excluded from the view layer); `Studio` (hall floor, lights, `RC_PictureCamera`, `RC_View`; not exported). - `fbx/`: `SM_RobotCell_Body.fbx` (with its 12 `UCX_` hulls), `SM_RobotCell_DoorLeft`, `_DoorRight`, `_DoorSide`, `_TurntablePlate`, `_EStop` (each at its pivot with one hull), `SK_RobotCell_Robot.fbx` (skeleton, skinned mesh and the take `RC_Robot_Rig|WORK_CYCLE`), `RobotCell_Assembled.fbx` (everything in the picture's pose), `verify_report.json`. - `RobotCell_layout.json`: pivots, motions, the robot's bones, axes, limits and poses, sizes, in Unreal units. - `textures/`: `T_RC_Body_Masks.png`, `T_RC_Robot_Masks.png` (4096), `T_RC_Moving_Masks.png` (2048), baked (R occlusion, G edges, B crevices); `T_RC_Decals.png` (the atlas: ring mark, button labels, "EMERGENCY STOP", "R-60") and `T_RC_Screen_UI.png`, drawn by `build/rc_textures.py`. The tiling sets are the Season 2 pack's own files, read in place from `../vessel-interior/textures/ModSci_Engineer/Textures`; the floor's relief is Poly Haven's `metal_plate` (CC0) in `source_polyhaven/` (**relative paths: keep the .blend here**). - `previews/`: Cycles pictures `RobotCell_v01_*` (the picture's view, front-left, back-right, doors open, the robot, the tool over the turntable, the pod) and the sheet; `wire_*` (topology); `compare/lookalike_v01.png` (the picture left, the model right, same camera); `fit/` (working renders and overlays). - `reference/`: the picture, ruler crops, checks of the drawn textures and the mask maps. ## Scale, axes, size 1 unit = 1 m, Z up. The picture's front (the sliding leaves, the pod on the left) faces −Y; the side door is on +X. **Origin: on the ground (the feet's soles) under the middle of the footprint.** In Unreal: **ue_cm = (x, −y, z) × 100** (the project's FBX pair; the data is turned −90° about Z on export). | | | |---|---| | cell | 305 x 288 cm, 249 cm high; floor 31.4 cm up, ceiling 228 cm | | openings | front: two leaves 115 and 129 cm wide, 198 cm high, each opening about 112 cm when slid; side door 129 x 188 cm | | robot | on a pedestal (74 cm square top, at 62 cm); reach from the shoulder about 2.06 m with the tool | | turntable | plate 90 cm across, its top at 72 cm | | tray | 84 x 67 cm, 6 cm deep, rim on the floor | ## Parts and pivots | Object | Pivot | Motion (Blender) | |---|---|---| | `SM_RobotCell_Body` | origin | static: base with the tray's pocket, posts, column, beams, roof, walls, floor, ceiling and lights, pedestal, turntable drive, tray, pod | | `SM_RobotCell_DoorLeft` | the leaf's bottom middle | slides +X 1.10 m (front track, in front of the right leaf) | | `SM_RobotCell_DoorRight` | the leaf's bottom middle | slides −X 1.11 m (back track, behind the left leaf) | | `SM_RobotCell_DoorSide` | on the hinge axis at the leaf's bottom | swings out about Z, 0 to 100 degrees | | `SM_RobotCell_TurntablePlate` | on its axis at its underside | turns about Z, free | | `SM_RobotCell_EStop` | on its axis on the pod's face | presses 7 mm (+Y) | | `RC_Robot_Rig` + `SK_RobotCell_Robot` | the robot's base, on the A1 axis at the pedestal's top | the armature below | ## Rig (Blender) **Sliders** on `RobotCell_Root`: `door_left`, `door_right`, `side_door` (0 shut .. 1 open, eased), `turntable` (degrees), `estop` (0 .. 1). The drivers are simple expressions (the user's Blender runs with Python auto-run off). Each moving part keeps `closed_location`; the leaves also `open_location`. **The robot** is an armature `RC_Robot_Rig`: bones `Base`, `A1` … `A6`, `Tool`, `Tool_Spin`, each mesh part rigid-skinned to its bone; the two hoses are weighted across the joints they cross, so they bend. Pose the bones directly: each turns about **one axis only** (the others are locked), within joint limits (Limit Rotation constraints, also set as IK limits): | Joint | Axis | Limits | | |---|---|---|---| | A1 base | up (bone Y) | ±185 | | | A2 shoulder | robot's lateral (bone X) | −60 / +90 | + leans the upper arm forward | | A3 elbow | lateral (bone X) | −120 / +48 | + tips the forearm down | | A4 forearm roll | along the forearm (bone Y) | ±350 | | | A5 wrist | lateral (bone X) | ±95 | + bends the tool down | | A6 flange | along the flange (bone Y) | ±350 | | | Tool_Spin | along the tool | free | the nozzle | A2, A3 and A5 are narrower than a real robot's: past them a single joint folds the arm into itself from the picture's pose (`build/rc_check.py` sweeps each joint). Bind pose: upper arm vertical, forearm and tool straight forward; **frame 1 is the picture's pose**. **Timeline**, 30 fps, 1–460 (actions `Robot | WORK_CYCLE` and `RobotCell | DEMO`): the picture's pose 1–30; down to the tray and up 60–115; over to the turntable, down and up 160–215 (the tool spins while working); back by 260; the plate turns half a turn 195–260; the left leaf slides open 270–320 and shut 360–410; the side door swings open 280–330 and shut 370–420; the emergency stop is pressed 430–450. The work poses were solved by inverse kinematics in `build/rc_rig.py` (tool tip on the target, the tool straight down over the tray, leaning 22 degrees out over the turntable). There is no IK constraint in the rig: the bones are the controls. ## Checked `build/rc_check.py` (triangle overlap, BVH, world space): - both leaves through their whole slide (57 steps), alone and with the other open; the side door through 0–100 degrees (51 steps); a full turn of the plate (37 steps); the emergency stop's press: **no clash** with the cell or each other; - the robot at every second frame of its work cycle (230 frames) against the cell, the moving parts and itself (rigid parts pairwise; the hoses are soft and left out): **no clash**; - each joint alone swept over its limits from the picture's pose: **no self-clash**; - drivers valid and simple. Fixed on the way: the right leaf ran 14 mm into the deep front-right post; a cable socket on the robot's base dipped 1 cm into the pedestal. Topology: no n-gons and no non-manifold edges on any mesh; triangles only at flat caps of concave outlines, bolt tips and small plates. Open edges are the faces left out where parts sit on each other (beam ends, floor plates on the base, the backs of panels) and the decal quads. The FBX files re-import at scale 1 on their pivots with both UV layers, their materials and hulls; the robot with its 9 bones, skin groups and the work-cycle take (`fbx/verify_report.json`). ## Surfaces Two UV layers on each mesh: `UVMap` (UV0, a unique unwrap into its group's mask map; decals and the screen take their textures' 0..1) and `UV_Tile` (UV1, metres, box-projected, for the tiling sets). Tints from the picture. Low wear. | Material | What it is | Set | |---|---|---| | `M_RC_Frame` | charcoal powder coat: frame, posts, column, beams, roof, side panel | ScratchedMetal | | `M_RC_Panel` | the back wall's panels, the ceiling | ScratchedMetal | | `M_RC_Black` | door frames, track, pedestal, drive, pod, robot base and covers | ScratchedMetal | | `M_RC_Orange` | the robot's paint, with a clear coat | ScratchedMetal | | `M_RC_Copper` | feet, hinges, latches, interlocks, the tray, the tool's cage | BrushedMetal + scratches | | `M_RC_Chrome` | handles, bolts, the tool's nozzle, button bezels | BrushedMetal + scratches | | `M_RC_Steel` | the turntable plate | BrushedMetal + scratches | | `M_RC_Tread` | the diamond-plate floor: the scan's relief and lugs, worn lug tops | Poly Haven `metal_plate` | | `M_RC_Rubber` | hoses, foot soles | — | | `M_RC_Gap` | slots, seams, the vent's back | — | | `M_RC_Glass` | glass, a slight blue tint, smudges in the roughness | Glass_DirtRough | | `M_RC_LED`, `M_RC_Lamp` | ceiling strips and downlight; the pod's lamps (emissive) | — | | `M_RC_Screen` | the pod's screen (emissive UI) | `T_RC_Screen_UI` | | `M_RC_Decal` | ring mark, labels, "R-60" (alpha-masked) | `T_RC_Decals` | | `M_RC_Red`, `M_RC_Yellow` | the emergency stop and its ring | — | Materials used by the robot or the moving parts have copies (`*_Robot`, `*_Moving`) that read those meshes' mask maps. ## Collision Body: twelve `UCX_SM_RobotCell_Body_NN` boxes (base and floor, column, pod, three posts, roof, back wall, left wall, the side's rear panel, pedestal, turntable drive); the front and side openings stay open. Each leaf and the side door: a box (handles left out); the plate a 16-sided disc; the e-stop a small one. The robot needs its physics asset in Unreal. ## Triangles Body 9,368, robot 13,544, left leaf 2,030, right leaf 1,260, side door 1,788, plate 2,544, e-stop 576: **31.1k**. ## Not done / open - Unreal: not imported. The layout JSON has what an import, the door actors and the robot's animation need; the ceiling strips need real lights there. - The back, the left side, the column's depth and the robot's far side are my design (the picture shows none of them). - No game role decided, and no place in the carrier or at Anchorage yet. - The previews' studio is mine: a dark hall like the picture's, lit brighter than it. ## Rebuild `sh build/run_all.sh` (Git Bash) runs the whole chain headless, one Blender at a time, all threads (the user allows full CPU since 2026-10-19); the GPU (HIP) does the bakes and the renders. About 2.5 minutes. `rc_build.py` rewrites `RobotCell.blend` from scratch, so run the rest of the chain after it. `rc_fit.py` (the camera) and `rc_fitpose.py` (the robot's pose) ran once; their results are in `rc_lib.py` and `build/pose_fit.json`. ## Copy: the moon pool's door arm with a welding torch (`RobotCell_DoorArm.blend`) The user, the same day: *"create another copy with robot arm replaced with moon pool door arm"*, then *"make a welding tip instead of plain plate"*. `RobotCell.blend` is untouched; the copy is made from it by `build/rc_doorarm.py`. - **The arm:** version B of the door robot arm (`models/door-robot-arm`, the one installed on the ship's moon pool doors), taken from its file's showroom at rest: turret, lower arm, upper arm, forearm, wrist head, flange, the J3 hydraulic cylinder and piston, two elbow cables, two cylinder hoses, with its own red look (materials and textures read in place from `../door-robot-arm/`; that file is not changed). **Scaled 0.28x** of the showroom's 1.6x size so it fits the cell: reach from the shoulder about 1.9 m, 1.11 m tall at rest. **Left out:** its rail carriage and pinion (no rail here) and its plate (tool B). **Mine:** a black base ring under the turret on the cell's pedestal (6 cm). - **The welding torch** (mine, in place of the plate): mounting plate, insulator, collision sensor, neck clamp, a chrome swan neck bent 45 degrees, a copper gas nozzle, the contact tip and 12 mm of wire; a torch hose from the sensor over the wrist along the forearm. The neck's bend lets the tip point down although this arm's wrist only bends 20 degrees that way. - **Rig:** armature `RC_DoorArm_Rig`, the parts joined into one skinned mesh `SK_RobotCell_DoorArm` (cables and hose weighted between the parts they join). Bones `Base`, `J1` … `J6`, `Tool`, `J3_Cylinder`, `J3_Piston` (+ the helper `J3_Pin`, not deforming). Pose J1–J6; **the cylinder follows J3 by itself** (Damped Track to the pin, Copy Location of the rod eye; constraints, no Python). Limits: the door arm's own, narrowed where `rc_check.py` found the arm meeting itself or the piston leaving its barrel: J1 ±170, J2 −42 / +72, J3 −55 / +16, J4 ±170, J5 −80 / +20, J6 ±55 (+ about the arm's left leans forward / tips down). - **Timeline** (`DoorArm | WELD_CYCLE`): the picture's pose 1–30 (as near the orange robot's as this arm can come without its lower arm touching the left glass: the tip 11 cm and 20 degrees off), over the weld 70, down 85 (the wire's end 4 mm over the turntable plate, the torch leaning 15 degrees), **welds 85–205 while the turntable turns once**, up 225, back by 260; the leaves, the side door and the e-stop as in the main file. - **Checked** (`build/check_report_doorarm.json`): the cell's travels as before; the weld cycle (230 frames) against the cell, the moving parts and itself: no clash; each joint swept over its limits: no self-clash; the piston stays in its barrel (0.27–0.52 m of 0.23–0.56 m allowed). - **Export:** `fbx/SK_RobotCell_DoorArm.fbx` (10 bones, the take `RC_DoorArm_Rig|WELD_CYCLE`), `fbx/RobotCell_DoorArm_Assembled.fbx`, `RobotCell_DoorArm_layout.json`; the cell's own meshes are the same as the main file's FBX. - **Triangles:** the arm with torch 142.8k (the door arm's full LOD0; its LODs in `models/door-robot-arm/fbx` are not used here). - **Pictures:** `previews/RobotCell_doorarm_v02_*` (the picture's view, front-left, doors open, the arm, the weld, the torch close-up) and the sheet; `previews/compare/lookalike_doorarm_v02.png`. The `_v01` set shows the plate (before the torch). - **Open:** the scale (0.28x) and the torch are my choices; no arc light or weld seam; any J1 turn toward the left wall can still swing the arm into the glass (as with the orange robot: the limits are per joint, not a cell zone).