Nodes/SAM3D Cam Shot Toolkit/Cam Shot Toolkit: Assemble Scene
ComfyUI Node

Cam Shot Toolkit: Assemble Scene

Populate a floor with people who don't overlap

By EnviralDesign·Created 5 months ago·Updated 2 days ago· 1
Cam Shot Toolkit: Assemble Scene
  • rigs
  • camera
  • scene
  • scene_json
seed0
count_weights1:0.6,2:0.3,3:0.1
max_bodies8
min_gap_ft1.0
region_x_ft4.0
region_z_ft3.0
allow_same_labeltrue
center_singletrue
mirror_modeoff
yaw_jitter_deg0.0
max_tries20
max_occlusion_frac0.00
placements_json
occlusion_check_scale0.25
required_labels

Most synthetic-data nodes stop at "place things randomly." Assemble Scene is the one that refuses to let your pose references turn into garbage: it rolls a random-but-plausible group of actors onto a floor and only keeps placements that physically clear each other, land fully in frame, and don't hide each other past your tolerance. It's the dice between Load Pose Rigs (what's in your library) and Render Clay Grid (what you actually see).

Think of it as the anti-overlap bouncer for the whole pipeline. When you're generating pose-reference images to train a model, a frame where one person's head is cut off by the frame edge, or two bodies clip through each other, is a poisoned sample. This node exists so that failure mode is rare by construction rather than caught by eyeballing 10,000 renders.

How it works

The scene is built from a seed and a set of rules. First the actor-group count K is drawn from count_weights - a weighted distribution, default 1:0.6,2:0.3,3:0.1 means "60% single actor, 30% pairs, 10% trios," and 1:1 means one actor, always. Then each actor picks a rig from the loaded set and tries a random (x, z) inside ±region_x_ft by ±region_z_ft around the origin.

A placement is only accepted if all of these hold:

  • its footprint circle clears every already-placed actor by min_gap_ft,
  • it projects fully inside the frame - when a camera is connected,
  • no actor's silhouette is occluded past max_occlusion_frac (an id-render check at occlusion_check_scale, default 0.25, so it's cheap).

After max_tries failures an actor is dropped rather than squeezed in and mis-labeled. That's a design choice worth appreciating: the node prefers fewer, clean actors over more, overlapping ones.

The inputs that matter

  • rigs (POSE_RIGS) - from Load Pose Rigs.
  • seed - bind to the sample seed; deterministic scenes.
  • count_weights, max_bodies - how many people, and the hard cap (group rigs count all their actors).
  • region_x_ft / region_z_ft - how big the stage is.
  • min_gap_ft - personal-space enforcement.
  • camera (optional, POSE_CAMERA) - connect it or the in-frame and occlusion checks are simply off. max_occlusion_frac does nothing without a camera; its default of 0 means "no overlap at all."
  • mirror_mode (off/random/on) and yaw_jitter_deg - orientation variety, so the same gesture doesn't face the same way every frame.
  • required_labels - labels that must appear, placed first; a dataset bucket can bind its own gesture here.
  • placements_json - skip the dice entirely and pin exact positions: [{"label": "01_star", "xz_ft": [-2.5, 0], "yaw_deg": 0, "mirror": false}, ...].

Outputs: scene (POSE_SCENE) into Render Clay Grid, plus scene_json with the placed actors, their placements, labels, and placement stats (placed vs requested, rerolls).

Install

ComfyUI Manager (search sam3d-body-comfyUI-camshottoolkit) or:

cd ComfyUI/custom_nodes
git clone https://github.com/EnviralDesign/sam3d-body-comfyUI-camshottoolkit
cd sam3d-body-comfyUI-camshottoolkit
python -m pip install -r requirements.txt

Restart after installing.

Gotchas

The console log is your friend: it prints placed_k/requested_k actors and reroll count. If you're seeing far fewer placed than requested, your region is too small, max_bodies is too tight, or you've set allow_same_label off with too few distinct gestures in the library - the node is dropping actors because it can't fit them, not malfunctioning. If placements_json names a rig or label that isn't in the loaded set, it raises instead of guessing, so keep pin lists in sync with what Load actually returned. And remember the camera rule: wire a POSE_CAMERA before you trust any occlusion number, or the scene is assembled with framing and overlap checks disabled entirely.

CategoryCamShotToolkit/posing

Inputs (17)

NameTypeDefaultDescription
rigsPOSE_RIGSLoaded rig set.
seedINT00–18446744073709550000Scene seed (bind to the sample seed).
count_weightsSTRING1:0.6,2:0.3,3:0.1Distribution of actor groups per scene, 'k:weight,...'. '1:1' = single actor always.
max_bodiesINT81–64Hard cap on people in frame (group rigs count all their actors).
min_gap_ftFLOAT1.00–20Minimum clear floor between footprints, feet.
region_x_ftFLOAT4.00–100Placement half-width about the origin, feet.
region_z_ftFLOAT3.00–100Placement half-depth about the origin, feet.
allow_same_labelBOOLEANtrueAllow two groups doing the same gesture (still one label).
center_singleBOOLEANtrueSingle-actor scenes sit exactly at the origin (camera jitter provides the variety).
mirror_modeCOMBOoffMirror actors about their own vertical axis (guest orientation vs concept orientation).
yaw_jitter_degFLOAT0.00–180Uniform ±yaw per actor, degrees.
max_triesINT201–500Rerolls per actor before it is dropped.
max_occlusion_fracFLOAT0.000–1Reject placements hiding more than this fraction of any actor's silhouette (needs camera). 0 = no overlap at all.
cameraoptPOSE_CAMERAEnables the in-frame and 2D occlusion checks.
placements_jsonoptSTRINGOptional pinned placements: [{"label": "01_star", "xz_ft": [-2.5, 0], "yaw_deg": 0, "mirror": false}, ...]. Skips the dice.
occlusion_check_scaleoptFLOAT0.250.05–1Render scale for the occlusion check.
required_labelsoptSTRINGComma-separated labels that must appear in every scene (placed first). A dataset bucket binds its own gesture here.

Outputs (2)

NameTypeDescription
scenePOSE_SCENE
scene_jsonSTRING