diff --git a/docs/research/builds/aetheria-bloom-habitat.md b/docs/research/builds/aetheria-bloom-habitat.md index befb4bf..0e17949 100644 --- a/docs/research/builds/aetheria-bloom-habitat.md +++ b/docs/research/builds/aetheria-bloom-habitat.md @@ -47,6 +47,10 @@ The build follows the current Bloom model: luxury spoke districts, urban mixed belts, suburban/industrial areas, industrial/agricultural bands, beach rim, sea band, roads, rivers, cloud patches, and service routes +- fractal favela wall detail: batched core shacks, child shacks, patched roofs, + stilts, diagonal braces, and catwalks climbing the hubward wall +- elaborated spoke plazas and town fields: plaza rings, kiosks, feeder walks, + nested town houses, and lane networks - one small Service Ring Kappa marker among many maintenance systems, not the compositional focus @@ -72,7 +76,7 @@ Result: ```text AETHERIA_BLOOM_VERIFY ok AETHERIA_BLOOM_VERIFY group VG Bloom Light Spine 21 31 -AETHERIA_BLOOM_VERIFY objects 735 +AETHERIA_BLOOM_VERIFY objects 1102 ``` ## Procedural Translation Notes @@ -85,8 +89,8 @@ This is intentionally hybrid: geometry-node group used by a Nodes modifier in the scene. - bpy builds the full shell surfaces, despun core, hub cap, docking port, spun sheath, spiral spoke loops, endcaps, frame-transfer arteries, wrapped region - patches, city blocks, roads, rivers, clouds, utility lines, service markers, - and cameras. + patches, city blocks, batched favela/town/plaza detail, roads, rivers, clouds, + utility lines, service markers, and cameras. The IRCSS pass paid off in three ways: @@ -104,6 +108,10 @@ The IRCSS pass paid off in three ways: - **Golden-angle filling:** spoke placement uses the sunflower trick, stepping by the golden angle along the axis so each new spoke avoids the previous radial lanes while still reading as one organic exchange network. +- **Batched micro-detail:** rickety visual detail should not mean one Blender + object per plank. The favela/town pass batches many boxes into shared meshes + and many brace/catwalk paths into shared curve objects, preserving detail + without making scene generation stall. - **Python as orchestration:** the cylinder layers, region maps, city blocks, social-gradient bands, roads, rivers, forests, clouds, and spokes come from small tables and named helpers rather than copied object placement. @@ -111,7 +119,8 @@ The IRCSS pass paid off in three ways: ## Current Limits - The build is a spatial systems study, not final art. -- The civic surface is a symbolic map-layer world, not a lived city. +- The civic surface is still a symbolic map-layer world, but the hubward favela + now has enough nested structure to read as precarious built habitat. - The current whole-Bloom view is less skeletal, but still a study. It needs denser horizon city detail and better art-directed lighting before it earns the word cinematic without fraud. diff --git a/examples/geometry_script/aetheria_bloom_habitat.py b/examples/geometry_script/aetheria_bloom_habitat.py index 11639b9..6120ece 100644 --- a/examples/geometry_script/aetheria_bloom_habitat.py +++ b/examples/geometry_script/aetheria_bloom_habitat.py @@ -177,6 +177,208 @@ def add_surface_block_field(name, x_values, angles, material, size, radial_lift= ) +def append_box_parts(verts, faces, loc, axes, size): + from mathutils import Vector + + origin = Vector(loc) + ax = [Vector(axis).normalized() for axis in axes] + sx, sy, sz = (value * 0.5 for value in size) + base = len(verts) + for dx, dy, dz in [ + (-sx, -sy, -sz), + (sx, -sy, -sz), + (sx, sy, -sz), + (-sx, sy, -sz), + (-sx, -sy, sz), + (sx, -sy, sz), + (sx, sy, sz), + (-sx, sy, sz), + ]: + v = origin + ax[0] * dx + ax[1] * dy + ax[2] * dz + verts.append((v.x, v.y, v.z)) + faces.extend( + [ + (base + 0, base + 1, base + 2, base + 3), + (base + 4, base + 7, base + 6, base + 5), + (base + 0, base + 4, base + 5, base + 1), + (base + 1, base + 5, base + 6, base + 2), + (base + 2, base + 6, base + 7, base + 3), + (base + 3, base + 7, base + 4, base + 0), + ] + ) + + +def add_mesh_parts(name, verts, faces, material): + import bpy + + mesh = bpy.data.meshes.new(name + "Mesh") + mesh.from_pydata(verts, [], faces) + mesh.update() + obj = bpy.data.objects.new(name, mesh) + bpy.context.collection.objects.link(obj) + obj.data.materials.append(material) + return obj + + +def add_multi_polyline_curve(name, lines, bevel, material, resolution=2): + import bpy + + curve = bpy.data.curves.new(name + "Curve", "CURVE") + curve.dimensions = "3D" + curve.resolution_u = resolution + curve.bevel_depth = bevel + curve.bevel_resolution = 2 + for line in lines: + spline = curve.splines.new("POLY") + spline.points.add(len(line) - 1) + for point, co in zip(spline.points, line): + point.co = (co[0], co[1], co[2], 1.0) + obj = bpy.data.objects.new(name, curve) + bpy.context.collection.objects.link(obj) + obj.data.materials.append(material) + return obj + + +def add_surface_stilt(name, x, angle, inner_lift, outer_lift, material, radius=0.009): + start = surface_point(x, angle, INNER_RADIUS, lift=inner_lift) + end = surface_point(x, angle, INNER_RADIUS, lift=outer_lift) + return add_cylinder_between(name, start, end, radius, material, vertices=6) + + +def add_surface_brace(name, x0, angle0, x1, angle1, lift0, lift1, material, bevel=0.006): + return add_curve_polyline( + name, + [ + surface_point(x0, angle0, INNER_RADIUS, lift=lift0), + surface_point((x0 + x1) * 0.5, (angle0 + angle1) * 0.5, INNER_RADIUS, lift=(lift0 + lift1) * 0.5 - 0.03), + surface_point(x1, angle1, INNER_RADIUS, lift=lift1), + ], + bevel, + material, + resolution=3, + ) + + +def add_favela_fractal_cluster(prefix, x_values, angles, mats): + shack_verts, shack_faces = [], [] + roof_verts, roof_faces = [], [] + brace_lines = [] + for ix, x in enumerate(x_values): + for ia, angle in enumerate(angles): + lean = ((ix * 5 + ia * 3) % 5 - 2) * 0.018 + root = f"{prefix}_{ix:02d}_{ia:02d}" + axes = ((1, 0, 0), tangent(angle + lean), radial(angle + lean)) + append_box_parts(shack_verts, shack_faces, surface_point(x, angle + lean, INNER_RADIUS, lift=-0.38), axes, (0.2, 0.12, 0.24)) + for level in range(2): + lift = -0.55 - level * 0.14 + width = 0.13 - level * 0.015 + for wing in (-1, 1): + dx = wing * (0.09 + 0.035 * level) + da = wing * (0.018 + 0.012 * level) + lean + local_axes = ((1, 0, 0), tangent(angle + da), radial(angle + da)) + append_box_parts( + shack_verts, + shack_faces, + surface_point(x + dx, angle + da, INNER_RADIUS, lift=lift), + local_axes, + (width, 0.075, 0.11), + ) + roof_axes = ((1, 0, 0), tangent(angle + da + 0.006 * wing), radial(angle + da + 0.006 * wing)) + append_box_parts( + roof_verts, + roof_faces, + surface_point(x + dx + 0.012 * wing, angle + da + 0.006 * wing, INNER_RADIUS, lift=lift - 0.07), + roof_axes, + (width * 1.22, 0.095, 0.018), + ) + brace_lines.append( + [ + surface_point(x + dx - width * 0.25, angle + da - 0.006, INNER_RADIUS, lift=lift + 0.05), + surface_point(x + dx - width * 0.25, angle + da - 0.006, INNER_RADIUS, lift=-0.25), + ] + ) + brace_lines.append( + [ + surface_point(x + dx - width * 0.42, angle + da - 0.012, INNER_RADIUS, lift=lift + 0.02), + surface_point(x + dx, angle + da, INNER_RADIUS, lift=lift - 0.04), + surface_point(x + dx + width * 0.42, angle + da + 0.012, INNER_RADIUS, lift=lift - 0.09), + ] + ) + for step in range(2): + brace_lines.append( + [ + surface_point(x - 0.18, angle + lean + step * 0.028, INNER_RADIUS, lift=-0.62 - step * 0.08), + surface_point(x, angle + lean + 0.035 + step * 0.025, INNER_RADIUS, lift=-0.62 - step * 0.08), + surface_point(x + 0.2, angle + lean + step * 0.02, INNER_RADIUS, lift=-0.62 - step * 0.08), + ] + ) + add_mesh_parts(prefix + "_shack_mesh", shack_verts, shack_faces, mats["favela"]) + add_mesh_parts(prefix + "_roof_mesh", roof_verts, roof_faces, mats["shack_roof"]) + add_multi_polyline_curve(prefix + "_brace_and_catwalks", brace_lines, 0.005, mats["brace"], resolution=2) + + +def add_town_detail_cluster(prefix, x_values, angles, material, road_material): + verts, faces = [], [] + lanes = [] + for ix, x in enumerate(x_values): + for ia, angle in enumerate(angles): + root = f"{prefix}_{ix:02d}_{ia:02d}" + for lane in (-1, 0, 1): + local_angle = angle + lane * 0.024 + lanes.append( + [ + surface_point(x - 0.32, local_angle - 0.014, INNER_RADIUS, lift=-0.37), + surface_point(x, local_angle + 0.008, INNER_RADIUS, lift=-0.37), + surface_point(x + 0.32, local_angle - 0.006, INNER_RADIUS, lift=-0.37), + ] + ) + for b in range(3): + dx = -0.22 + b * 0.22 + ((ix + ia + b) % 3 - 1) * 0.018 + da = lane * 0.026 + ((ix * 7 + ia * 5 + b) % 5 - 2) * 0.004 + height = 0.12 + 0.035 * ((ix + ia + b) % 4) + append_box_parts( + verts, + faces, + surface_point(x + dx, angle + da, INNER_RADIUS, lift=-0.3 - height * 0.5), + ((1, 0, 0), tangent(angle + da), radial(angle + da)), + (0.12, 0.07, height), + ) + add_mesh_parts(prefix + "_nested_house_mesh", verts, faces, material) + add_multi_polyline_curve(prefix + "_lane_network", lanes, 0.006, road_material, resolution=2) + + +def add_spoke_plaza_detail(prefix, x, angle, mats, seed): + add_surface_curve( + f"{prefix}_plaza_ring", + [(x + 0.34 * pymath.cos(t), angle + 0.065 * pymath.sin(t)) for t in [TAU * i / 28 for i in range(29)]], + INNER_RADIUS, + 0.01, + mats["road"], + lift=-0.42, + ) + for i in range(10): + t = TAU * i / 10 + seed * 0.13 + dx = 0.28 * pymath.cos(t) + da = 0.052 * pymath.sin(t) + add_surface_box( + f"{prefix}_kiosk_{i:02d}", + x + dx, + angle + da, + mats["luxury"], + size=(0.09, 0.055, 0.1 + 0.025 * (i % 3)), + radial_lift=-0.37, + ) + for i, spoke_da in enumerate((-0.11, -0.055, 0.055, 0.11)): + add_surface_curve( + f"{prefix}_feeder_walk_{i:02d}", + [(x - 0.45, angle + spoke_da), (x, angle + spoke_da * 0.3), (x + 0.45, angle - spoke_da * 0.6)], + INNER_RADIUS, + 0.008, + mats["pressure_line"], + lift=-0.43, + ) + + def add_rotating_spoke(name, x, angle, inner_radius, outer_radius, material, radius=0.045, vertices=10): r = radial(angle) return add_cylinder_between( @@ -374,6 +576,8 @@ def build_scene(): "farm": mat("TCS Root farms", (0.15, 0.55, 0.24), alpha=0.92), "district": mat("surface districts", (0.55, 0.58, 0.62), alpha=0.95), "favela": mat("hubcap terrace slums", (0.74, 0.48, 0.26), alpha=0.98), + "shack_roof": mat("patched favela sheet metal", (0.46, 0.39, 0.32), alpha=0.98), + "brace": mat("rickety brace dark steel", (0.12, 0.10, 0.085), alpha=0.98), "luxury": mat("luxury spoke districts", (0.82, 0.78, 0.62), alpha=0.98), "industrial": mat("industrial works", (0.32, 0.34, 0.36), alpha=0.98), "suburban": mat("suburban mixed belt", (0.5, 0.64, 0.5), alpha=0.96), @@ -483,12 +687,33 @@ def build_scene(): dense_angles = [pymath.radians(a) for a in range(-165, 180, 15)] add_surface_block_field("layered_hubcap_slum_stack", [-8.65, -8.25, -7.85, -7.45], dense_angles, mats["favela"], size=(0.16, 0.08, 0.22), radial_lift=-0.35, jitter=0.035) add_surface_block_field("hyperurban_microtower", [-7.0, -6.55, -6.1, -5.7], dense_angles[::2], mats["city"], size=(0.2, 0.1, 0.36), radial_lift=-0.42, jitter=0.03) + add_favela_fractal_cluster( + "fractal_wall_favela", + [-8.78, -8.42, -8.06, -7.7, -7.34, -6.98, -6.62], + [pymath.radians(a) for a in range(-165, 181, 30)], + mats, + ) + add_town_detail_cluster( + "hyperurban_nested_town", + [-7.1, -6.65, -6.2, -5.75], + [pymath.radians(a) for a in range(-150, 181, 45)], + mats["city"], + mats["road"], + ) for x in [-5.0, -4.2, -3.4]: - for a in prestige_angles[:8]: + for ia, a in enumerate(prestige_angles[:8]): add_surface_box(f"luxury_spoke_plaza_{x:.1f}_{a:.2f}", x, a, mats["luxury"], size=(0.55, 0.34, 0.14), radial_lift=-0.31) + add_spoke_plaza_detail(f"luxury_spoke_plaza_detail_{x:.1f}_{ia:02d}", x, a, mats, ia) for da in (-0.16, 0.16): add_surface_box(f"luxury_spoke_tower_{x:.1f}_{a:.2f}_{da:.1f}", x + da, a + da, mats["city"], size=(0.18, 0.1, 0.42), radial_lift=-0.44) add_surface_block_field("mixed_suburban_industrial_block", [-2.2, -1.4, -0.6, 0.2], [pymath.radians(a) for a in range(-150, 181, 30)], mats["suburban"], size=(0.42, 0.18, 0.12), radial_lift=-0.27, jitter=0.04) + add_town_detail_cluster( + "mixed_belt_nested_town", + [-2.35, -1.65, -0.95, -0.25, 0.45], + [pymath.radians(a) for a in range(-150, 181, 60)], + mats["suburban"], + mats["road"], + ) add_surface_block_field("factory_yard_block", [1.2, 2.0, 2.8, 3.6], [pymath.radians(a) for a in range(-150, 181, 45)], mats["industrial"], size=(0.62, 0.22, 0.16), radial_lift=-0.29, jitter=0.035) add_surface_block_field("farm_service_shed", [4.2, 5.0, 5.6], [pymath.radians(a) for a in range(-165, 181, 45)], mats["farm"], size=(0.52, 0.16, 0.08), radial_lift=-0.24, jitter=0.04) for i, deg in enumerate(range(-170, 181, 20)): diff --git a/experiments/generated/aetheria_bloom/aetheria_bloom_habitat.blend b/experiments/generated/aetheria_bloom/aetheria_bloom_habitat.blend index 013ffe8..5e1ef2e 100644 Binary files a/experiments/generated/aetheria_bloom/aetheria_bloom_habitat.blend and b/experiments/generated/aetheria_bloom/aetheria_bloom_habitat.blend differ diff --git a/experiments/generated/aetheria_bloom/aetheria_bloom_habitat.png b/experiments/generated/aetheria_bloom/aetheria_bloom_habitat.png index 3143084..0dbfb0c 100644 Binary files a/experiments/generated/aetheria_bloom/aetheria_bloom_habitat.png and b/experiments/generated/aetheria_bloom/aetheria_bloom_habitat.png differ diff --git a/experiments/generated/aetheria_bloom/aetheria_bloom_interior_world.png b/experiments/generated/aetheria_bloom/aetheria_bloom_interior_world.png index ae373e5..e14302d 100644 Binary files a/experiments/generated/aetheria_bloom/aetheria_bloom_interior_world.png and b/experiments/generated/aetheria_bloom/aetheria_bloom_interior_world.png differ diff --git a/notes/fresh-workspace-handoff.md b/notes/fresh-workspace-handoff.md index 8cb7962..db608fb 100644 --- a/notes/fresh-workspace-handoff.md +++ b/notes/fresh-workspace-handoff.md @@ -60,11 +60,11 @@ generates an Aetheria Bloom whole-habitat study using Bloom lore plus Eric Bruneton's Rama making-of as a cylindrical-world reference. It now emphasizes whole-cylinder mapped spaces and frame interfaces: full shell layers, despun hub cap and docking core, spun spire sheath, shared x/angle/radius coordinate -helpers, golden-angle spiral spoke and transfer-loop distribution, layered -endcaps, hub-cap terrace slums, favela/hyperurban belts, luxury spoke districts, -mixed urban/suburban/industrial belts, industrial/agricultural areas, beach and -sea bands, roads, rivers, airflow cloud patches, and surface service seams. -Kappa is only a small service marker. Build notes live at +helpers, golden-angle spoke/transfer loops, layered endcaps, mapped civic +bands, roads, rivers, cloud patches, and service seams. The hubward favela wall +uses batched fractal shack/roof/brace/catwalk detail; plazas and towns have +nested buildings, kiosks, feeder walks, and lane networks. Kappa is only a +small service marker. Build notes live at `docs/research/builds/aetheria-bloom-habitat.md`; verification lives at `tools/verify_aetheria_bloom_habitat.py`. diff --git a/state/evidence.jsonl b/state/evidence.jsonl index ecd1ca0..ee83d06 100644 --- a/state/evidence.jsonl +++ b/state/evidence.jsonl @@ -37,3 +37,4 @@ {"ts": "2026-05-04T23:10:37+00:00", "type": "implementation", "status": "accepted", "note": "Built a from-scratch Aetheria Bloom habitat study after reading Bloom lore and Bruneton Rama making-of references. Refocused from Kappa detail to whole-cylinder spaces: layered shell, axial hub/light spine, spokes, central water band, plains/farms, forests, city clusters, wrapped roads/rivers, cloud patches, utility routes, and one small service-ring marker. Blender verification passes with VG Bloom Light Spine at 21 nodes/31 links and 98 geometry/text objects."} {"ts": "2026-05-04T23:38:17+00:00", "type": "implementation", "status": "accepted", "note": "Reworked the Aetheria Bloom build from a cutout/map-layer view into a full 360-degree whole-habitat composition: full shell layers, despun center-axis core and docking hub cap, spun spire sheath, multi-station major spokes, thin utility spokes, frame-transfer arteries, hub-cap terrace slums, favela/hyperurban belts, luxury spoke districts, mixed urban/suburban/industrial belts, industrial/agricultural areas, beach and sea bands, roads/rivers, and airflow cloud patches. Verification now passes with 481 geometry/text objects and VG Bloom Light Spine still 21 nodes/31 links."} {"ts": "2026-05-04T23:59:24+00:00", "type": "implementation", "status": "accepted", "note": "Aetheria Bloom whole-habitat pass now uses shared cylindrical coordinate helpers, golden-angle spiral spoke/transfer loops, and explicit layered endcaps; Blender verifier passed with 735 geometry/text objects and light-spine Geometry Script group at 21 nodes/31 links."} +{"ts": "2026-05-05T00:21:08+00:00", "type": "implementation", "status": "accepted", "note": "Bloom detail pass added batched fractal favela wall detail, elaborated spoke plazas, and nested town fields; initial per-object version timed out, batching preserved visual detail and verifier passed with 1102 geometry/text objects."} diff --git a/state/map.yaml b/state/map.yaml index 2bd7807..f5f7866 100644 --- a/state/map.yaml +++ b/state/map.yaml @@ -96,8 +96,8 @@ data_flow: - If state grows faster than understanding, cut it back. current_status: - summary: VibeGeometry has an initial persistence scaffold plus verified Geometry Script translations for local math/utility samples, a first official Blender demo tranche, IRCSS French Houses city-scale graph doctrine, and a from-scratch Aetheria Bloom whole-habitat build. The IRCSS pass verified MakeSpiral and PointyGothicCone, then mapped the 108-group town kit through a dependency map, all-node atlas, and node mechanism study. The Bloom pass used Aetheria lore plus Bruneton's Rama making-of to generate a full cylindrical world study with shell anatomy, despun hub cap and docking core, spun spire sheath, shared x/angle/radius coordinate helpers, golden-angle spiral spoke and transfer-loop distribution, layered endcaps, social-gradient inner geography from terrace slums to luxury spokes to industrial/agricultural/beach zones, roads/rivers, cloud-flow patches, surface service seams, and service markers. The remaining large-system blockers are repeat-zone feedback, repeat Top outputs, foreach zones, and gizmo nodes. - next_action: Continue the Aetheria Bloom build by moving the shared cylindrical coordinate helpers plus spiral spoke/transfer-artery network into reusable Geometry Script groups, then improve atmospheric/light readability from render evidence. + summary: VibeGeometry has an initial persistence scaffold plus verified Geometry Script translations for local math/utility samples, a first official Blender demo tranche, IRCSS French Houses city-scale graph doctrine, and a from-scratch Aetheria Bloom whole-habitat build. The IRCSS pass verified MakeSpiral and PointyGothicCone, then mapped the 108-group town kit through a dependency map, all-node atlas, and node mechanism study. The Bloom pass used Aetheria lore plus Bruneton's Rama making-of to generate a full cylindrical world study with shell anatomy, despun hub cap and docking core, spun spire sheath, shared x/angle/radius coordinate helpers, golden-angle spiral spoke and transfer-loop distribution, layered endcaps, social-gradient inner geography, batched fractal favela wall detail, elaborated spoke plazas/town fields, roads/rivers, cloud-flow patches, surface service seams, and service markers. The remaining large-system blockers are repeat-zone feedback, repeat Top outputs, foreach zones, and gizmo nodes. + next_action: Continue the Aetheria Bloom build by moving the shared cylindrical coordinate helpers plus batched favela/town/plaza and spiral spoke/transfer-artery networks into reusable Geometry Script groups, then improve atmospheric/light readability from render evidence. remotes: vibegeometry: https://github.com/GameCult/VibeGeometry geometry_script_fork: https://github.com/GameCult/geometry-script diff --git a/tools/verify_aetheria_bloom_habitat.py b/tools/verify_aetheria_bloom_habitat.py index fbfe580..2a8ac1d 100644 --- a/tools/verify_aetheria_bloom_habitat.py +++ b/tools/verify_aetheria_bloom_habitat.py @@ -54,7 +54,16 @@ REQUIRED_OBJECTS = { "wrapped_river_0", "airflow_cloud_patch_0", "layered_hubcap_slum_stack_00_00", + "fractal_wall_favela_shack_mesh", + "fractal_wall_favela_roof_mesh", + "fractal_wall_favela_brace_and_catwalks", + "hyperurban_nested_town_nested_house_mesh", + "hyperurban_nested_town_lane_network", "luxury_spoke_plaza_-5.0_-3.14", + "luxury_spoke_plaza_detail_-5.0_00_plaza_ring", + "luxury_spoke_plaza_detail_-5.0_00_kiosk_00", + "luxury_spoke_plaza_detail_-5.0_00_feeder_walk_00", + "mixed_belt_nested_town_nested_house_mesh", "factory_yard_block_00_00", "axial_surface_service_lane_00", "circumferential_market_seam_00", @@ -86,7 +95,7 @@ def verify_scene(): assert_true(path.stat().st_size > 10_000, f"Generated artifact is suspiciously small: {path}") mesh_objects = [obj for obj in bpy.data.objects if obj.type in {"MESH", "CURVE", "FONT"}] - assert_true(len(mesh_objects) >= 500, f"Expected a populated whole-Bloom study scene, found {len(mesh_objects)} geometry/text objects") + assert_true(len(mesh_objects) >= 760, f"Expected a populated whole-Bloom study scene, found {len(mesh_objects)} geometry/text objects") print("AETHERIA_BLOOM_VERIFY ok") print("AETHERIA_BLOOM_VERIFY group", group.name, len(group.nodes), len(group.links))