diff --git a/docs/architecture/visual-blockout-architecture.md b/docs/architecture/visual-blockout-architecture.md index f77632d..886a22d 100644 --- a/docs/architecture/visual-blockout-architecture.md +++ b/docs/architecture/visual-blockout-architecture.md @@ -37,11 +37,6 @@ The blockout plan stores: - `guide_renders`: output guide images per camera/visual scene - `prompt_use_contract`: what the image model should preserve or reinterpret -Guide renders may carry `visible_actor_ids` when a visual scene needs the same -space with a different occupant set. An empty establishing shot should not -inherit every proxy actor just because the shared Blender scene contains them. -The render is the shot contract, not the whole stage dumped into one frame. - ## Blockout, Not Mask Do not treat flat colors as a machine-readable segmentation contract. A general diff --git a/examples/visual-blockouts/pallas-species-strikes.service-ring-kappa.v0.blockout.json b/examples/visual-blockouts/pallas-species-strikes.service-ring-kappa.v0.blockout.json index 8f1eb7b..c69a8ba 100644 --- a/examples/visual-blockouts/pallas-species-strikes.service-ring-kappa.v0.blockout.json +++ b/examples/visual-blockouts/pallas-species-strikes.service-ring-kappa.v0.blockout.json @@ -87,18 +87,6 @@ "intended_read": "Cephalopod body and open dry-operation work-support frame.", "use_in_prompt": "Preserve nonhuman horizontal body volume and open work-support object class." }, - { - "material_id": "callback_ivory", - "display_name": "Aftermath callback objects", - "base_color": [ - 0.88, - 0.82, - 0.68, - 1 - ], - "intended_read": "Small handled objects: mug, parts tray, tagged limiter interface, repaired contact band, and spare oxygenation tube.", - "use_in_prompt": "Use only as visible object callback staging, not as social-state training data." - }, { "material_id": "worker_gray", "display_name": "Engineered seal technicians", @@ -226,21 +214,6 @@ }, "material_id": "teth_blue_gray", "notes": "Open work-support frame with contact-band arcs, oxygenation tubes, and radial tool rails." - }, - { - "module_id": "object_callback_props", - "object_class": "small_prop_cluster", - "script_ref": "scripts/blender/build_visual_blockout.py", - "geometry_script_tree": "object_callback_props", - "parameters": { - "position": [ - 1.05, - 5.85, - 0.72 - ] - }, - "material_id": "callback_ivory", - "notes": "Small aftermath prop cluster: mug, repaired contact band, oxygenation tube, limiter interface, and parts tray." } ], "proxy_actors": [ @@ -365,8 +338,8 @@ ], "position": [ 0, - -8.5, - 2.9 + -7, + 2.4 ], "rotation": [ 1.22, @@ -375,10 +348,10 @@ ], "target": [ 0, - 5.1, + 4.2, 1.7 ], - "lens_mm": 20, + "lens_mm": 24, "shot_role": "wide establishing/service lane orientation", "framing_notes": "Shows shell curve, manifold wall, anchor rail, work-support bay, catwalk, and crawl throat in one readable room." }, @@ -467,9 +440,9 @@ "pallas_object_callback" ], "position": [ - 2.35, - 7.25, - 1.45 + 0.1, + 6.8, + 1.25 ], "rotation": [ 1.38, @@ -477,11 +450,11 @@ -2.65 ], "target": [ - 1.05, - 5.85, - 0.88 + 2.4, + 5.4, + 0.9 ], - "lens_mm": 45, + "lens_mm": 50, "shot_role": "quiet object callback close shot", "framing_notes": "Frames coffee mug, repaired contact band, oxygenation tube, and background manifold touch ritual." } @@ -496,7 +469,6 @@ "clay", "depth_optional" ], - "visible_actor_ids": [], "output_refs": [ "experiments/blender/pallas-species-strikes/renders/pallas_yard_wake.blockout.png" ], @@ -553,7 +525,6 @@ "clay", "depth_optional" ], - "visible_actor_ids": [], "output_refs": [ "experiments/blender/pallas-species-strikes/renders/pallas_object_callback.blockout.png" ], diff --git a/schemas/visual-blockout-plan.schema.json b/schemas/visual-blockout-plan.schema.json index 2ee0c8b..02adb91 100644 --- a/schemas/visual-blockout-plan.schema.json +++ b/schemas/visual-blockout-plan.schema.json @@ -125,10 +125,6 @@ "visual_scene_id": { "type": "string", "minLength": 1 }, "render_modes": { "$ref": "#/$defs/stringArray" }, "output_refs": { "$ref": "#/$defs/stringArray" }, - "visible_actor_ids": { - "type": "array", - "items": { "type": "string", "minLength": 1 } - }, "imagegen_reference_use": { "type": "string", "minLength": 1 } } } diff --git a/scripts/blender/build_visual_blockout.py b/scripts/blender/build_visual_blockout.py index ed52b80..de74d8f 100644 --- a/scripts/blender/build_visual_blockout.py +++ b/scripts/blender/build_visual_blockout.py @@ -85,11 +85,6 @@ def add_cube(name: str, location: tuple[float, float, float], scale: tuple[float return obj -def mark_actor_object(obj: bpy.types.Object, actor_id: str) -> bpy.types.Object: - obj["ghostlight_actor_id"] = actor_id - return obj - - def add_cylinder(name: str, location: tuple[float, float, float], radius: float, depth: float, material: bpy.types.Material, vertices: int = 48, rotation: tuple[float, float, float] = (0, 0, 0)) -> bpy.types.Object: bpy.ops.mesh.primitive_cylinder_add(vertices=vertices, radius=radius, depth=depth, location=location, rotation=rotation) obj = bpy.context.object @@ -218,36 +213,6 @@ def build_cephalopod_work_support_station(module: dict[str, Any], materials: dic add_curve_pipe("teth_oxygenation_loop", [(x - 0.8, y - 0.75, z + 0.25), (x, y, z + 1.0), (x + 0.8, y - 0.75, z + 0.25)], material, bevel_depth=0.028) -def build_object_callback_props(module: dict[str, Any], materials: dict[str, bpy.types.Material]) -> None: - p = module["parameters"] - material = materials[module["material_id"]] - teth_material = materials.get("teth_blue_gray", material) - x, y, z = p.get("position", [1.05, 5.85, 0.72]) - add_cube("callback_low_parts_tray", (x, y, z), (1.2, 0.55, 0.08), material) - add_cylinder("callback_thermal_mug", (x - 0.35, y - 0.12, z + 0.18), 0.11, 0.28, material, vertices=24) - add_curve_pipe( - "callback_repaired_contact_band", - [ - (x - 0.05, y - 0.20, z + 0.14), - (x + 0.18, y - 0.10, z + 0.24), - (x + 0.42, y - 0.20, z + 0.14), - ], - teth_material, - bevel_depth=0.018, - ) - add_curve_pipe( - "callback_spare_oxygenation_tube", - [ - (x - 0.48, y + 0.10, z + 0.13), - (x - 0.08, y + 0.22, z + 0.20), - (x + 0.48, y + 0.08, z + 0.13), - ], - teth_material, - bevel_depth=0.026, - ) - add_cube("callback_tagged_limit_interface", (x + 0.22, y + 0.14, z + 0.17), (0.28, 0.16, 0.05), material) - - MODULE_BUILDERS = { "service_ring_shell": build_service_ring_shell, "crawl_throat": build_crawl_throat, @@ -255,28 +220,27 @@ MODULE_BUILDERS = { "anchor_rail": build_anchor_rail, "supervisor_catwalk": build_supervisor_catwalk, "cephalopod_work_support_station": build_cephalopod_work_support_station, - "object_callback_props": build_object_callback_props, } def add_humanoid_proxy(actor: dict[str, Any], material: bpy.types.Material) -> None: x, y, z = actor["default_position"] sx, sy, sz = actor["scale"] - obj = mark_actor_object(add_uv_sphere(actor["actor_id"], (x, y, z + sz / 2), (sx, sy, sz / 2), material), actor["actor_id"]) + obj = add_uv_sphere(actor["actor_id"], (x, y, z + sz / 2), (sx, sy, sz / 2), material) obj.rotation_euler = tuple(actor["default_rotation"]) if "slate" in actor["proxy_shape"]: - mark_actor_object(add_cube(f"{actor['actor_id']}_slate", (x + 0.25, y, z + 1.0), (0.35, 0.04, 0.25), material), actor["actor_id"]) + add_cube(f"{actor['actor_id']}_slate", (x + 0.25, y, z + 1.0), (0.35, 0.04, 0.25), material) if "hook" in actor["proxy_shape"]: - mark_actor_object(add_curve_pipe(f"{actor['actor_id']}_hook", [(x, y, z + 0.8), (x + 0.45, y, z + 0.65)], material, bevel_depth=0.035), actor["actor_id"]) + add_curve_pipe(f"{actor['actor_id']}_hook", [(x, y, z + 0.8), (x + 0.45, y, z + 0.65)], material, bevel_depth=0.035) def add_cephalopod_proxy(actor: dict[str, Any], material: bpy.types.Material) -> None: x, y, z = actor["default_position"] sx, sy, sz = actor["scale"] - mark_actor_object(add_uv_sphere(actor["actor_id"], (x, y, z + 0.25), (sx * 0.45, sy * 0.45, sz * 0.45), material), actor["actor_id"]) + add_uv_sphere(actor["actor_id"], (x, y, z + 0.25), (sx * 0.45, sy * 0.45, sz * 0.45), material) for index in range(8): angle = index * math.tau / 8 - mark_actor_object(add_curve_pipe( + add_curve_pipe( f"{actor['actor_id']}_tentacle_{index:02d}", [ (x, y, z + 0.22), @@ -285,7 +249,7 @@ def add_cephalopod_proxy(actor: dict[str, Any], material: bpy.types.Material) -> ], material, bevel_depth=0.035, - ), actor["actor_id"]) + ) def build_proxy_actors(plan: dict[str, Any], materials: dict[str, bpy.types.Material]) -> None: @@ -365,23 +329,11 @@ def save_blend(plan: dict[str, Any], output_blend: Path | None) -> None: def render_guides(plan: dict[str, Any], output_dir: Path | None) -> None: render_root = output_dir or ROOT cameras = {obj.name: obj for obj in bpy.data.objects if obj.type == "CAMERA"} - actor_objects = [obj for obj in bpy.data.objects if "ghostlight_actor_id" in obj] for render in plan["guide_renders"]: camera = cameras.get(render["camera_id"]) if not camera: print(f"warning: missing camera {render['camera_id']} for {render['render_id']}") continue - visible_actor_ids = render.get("visible_actor_ids") - if visible_actor_ids is not None: - visible = set(visible_actor_ids) - for obj in actor_objects: - hide = obj["ghostlight_actor_id"] not in visible - obj.hide_viewport = hide - obj.hide_render = hide - else: - for obj in actor_objects: - obj.hide_viewport = False - obj.hide_render = False output_ref = resolve_project_path(render["output_refs"][0]) if output_dir: output_ref = output_dir / Path(render["output_refs"][0]).name diff --git a/state/evidence.jsonl b/state/evidence.jsonl index ecb38b3..5bbaebf 100644 --- a/state/evidence.jsonl +++ b/state/evidence.jsonl @@ -65,4 +65,3 @@ {"ts": "2026-05-03T12:34:07+00:00", "type": "visual-doctrine", "status": "ok", "note": "Revised Pallas visual prompts to use cephalopod-specific tentacle/mantle/support-harness anatomy instead of human-limb shorthand, replaced supervisor-glass staging with open catwalk and transparent safety-rail geometry, and added generalized reviewer checks for nonhuman anatomy humanization. BFL FLUX.2 Max spot-check with simplified prompt and prompt upsampling disabled still dropped Teth and missed the Dreams-style target, so BFL remains unsuitable for complex ensemble IF frames without further proof."} {"ts": "2026-05-03T12:55:07+00:00", "type": "visual-doctrine", "status": "ok", "note": "Finished Pallas visual prompt cleanup after imagegen spot check: Teth now uses affirmative cephalopod work-support rig language, padded contact bands, support loops, humidity rings, oxygenation, and tool access instead of containment-shaped harness wording. Added generalized reviewer guardrail for assistive/workplace equipment object-class drift. Did not add scene-geometry continuity guardrails; multi-angle continuity should be handled by future Blender/blockout guide assets, not prompt policing."} {"ts": "2026-05-03T13:33:16+00:00", "type": "visual-blockout", "status": "ok", "note": "Added first visual blockout plan seam for illustrated IF: Pallas Service Ring Kappa now has a validated ghostlight.visual_blockout_plan.v0 artifact, Blender wrapper for the Steam install, fully scripted low-poly blockout generator, and ignored guide-render outputs. The blockout is a spatial/camera reference, not a segmentation mask; Geometry Script/Geometry Nodes can replace individual procedural modules via geometry_script_tree handles."} -{"ts": "2026-05-03T13:57:41+00:00", "type": "visual-blockout", "status": "ok", "note": "Corrected first Pallas blockout guide render problems: empty establishing renders now use per-render visible_actor_ids, the object callback shot has explicit prop geometry, and cameras target the intended shot subjects rather than inherited shared-scene clutter."} diff --git a/tools/validate_visual_blockouts.py b/tools/validate_visual_blockouts.py index e835ac7..2b92671 100644 --- a/tools/validate_visual_blockouts.py +++ b/tools/validate_visual_blockouts.py @@ -152,8 +152,6 @@ def validate_blockout_plan(document: dict[str, Any], source: Path) -> None: require_nonempty_string(render[key], f"{path}.{key}") require_string_list(render["render_modes"], f"{path}.render_modes") require_string_list(render["output_refs"], f"{path}.output_refs") - if "visible_actor_ids" in render: - require_string_list(render["visible_actor_ids"], f"{path}.visible_actor_ids") contract = document["prompt_use_contract"] require_keys(contract, ["reference_image_role", "must_preserve", "may_reinterpret", "must_not_assume"], f"{source}.prompt_use_contract")