Back to Blog
Galen Guan

OpenMontage 3D World Pipeline Source Walkthrough: How Semantic World Specs Drive Deterministic Terrain

Everyone who's tried building a full 3D world with text-to-3D models has hit the same wall: the model hands you a textured GLB, you open it, and the entire scene is one uneditable triangle blob—no regions, no camera paths, no replaceable asset instances. The terrain is a random noise field. You want to fly the camera to the other side of a mountain, but the "other side" doesn't exist.

On August 13, 2026, OpenMontage (the agentic video production system I analyzed previously) merged commit 2702362—a 3,979-line addition across 42 files titled feat: add production 3D world pipeline. This isn't another text-to-3D wrapper. It's a complete, auditable 3D world production pipeline built on a core thesis: don't have AI generate the entire world—have AI plan the world, then materialize that plan into an editable 3D scene using deterministic math.

The Problem This Pipeline Solves

Before commit 2702362, OpenMontage's 2D world was flat—cards, charts, motion graphics. It lacked a real 3D space: not a single product model rotating on a turntable, but a complete 3D world with terrain elevation, region partitioning, environmental scatter, and camera fly-throughs.

Existing text-to-3D models (Atlas Cloud's tripo-h3.1, fal.ai's Hunyuan 3D v3.1) are good at generating individual objects, but the pipeline's skill document is explicit:

Never ask a text-to-3D model to generate a whole cinematic world in one mesh. Generate hero objects, use licensed catalogs for high-volume scatter, and assemble everything in Blender from a semantic specification.

— from .agents/skills/3d-asset-generation/SKILL.md

This judgment is correct. WorldClaw (arXiv:2608.05248v1, 2026) takes the same approach: establish a structured scene specification first, then fill in details layer by layer. The difference is that WorldClaw's public repository contains only the paper, not executable code. OpenMontage turned this architectural idea into five callable tools.

OpenMontage 3D World Pipeline Architecture

world_spec: The Semantic Contract Between Agent Creativity and Render Engine

The pipeline's core is a JSON object called world_spec. It's not a render configuration file—it's a semantic world specification that tells the render engine "what exists in this world" rather than "what to draw each frame."

Separating Explicit Constraints from Inferred Details

The pipeline first requires the agent to separate two types of information:

"explicit_constraints": ["a volcanic rift divides a living valley"],
"inferred_details": ["three semantic regions", "dawn atmosphere"],

explicit_constraints holds only facts the user explicitly stated. inferred_details holds what the agent inferred to make the world executable. This separation is emphasized repeatedly in the skill documentation:

Never smuggle an inferred landmark, biome, or story beat into the explicit list.

This isn't pedantry—it's traceability. When a final render's geography doesn't match the user's intent, you can trace whether it came from the user's original request or the agent's creative license.

The Complete world_spec Structure

From .agents/skills/threejs-world-generation/references/world-spec.md:

{
  "version": "1.0",
  "title": "The Luminous Divide",
  "seed": 2048,
  "world": {
    "size": 120,
    "resolution": 160,
    "elevation_scale": 18,
    "water_level": -1.5
  },
  "atmosphere": {
    "sky_color": "#07111f",
    "fog_density": 0.009,
    "sun_color": "#ffd7a3",
    "sun_intensity": 3.2,
    "sun_position": [45, 70, 20]
  },
  "regions": [
    {
      "id": "ember-rift",
      "center": [-0.35, 0.12],
      "radius": 0.58,
      "landform": "ridge",
      "frequency": 1.4,
      "color": "#5b271f",
      "scatter": {"rock": 90, "crystal": 22, "tree": 0},
      "slope_limit": 1.8
    }
  ],
  "landmarks": [
    {
      "id": "rift-gate",
      "type": "arch",
      "region_id": "ember-rift",
      "position": [-24, 0, 8],
      "scale": 5.5
    }
  ],
  "camera_path": [
    {"time": 0, "position": [72, 42, 72], "target": [0, 3, 0], "fov": 46},
    {"time": 60, "position": [-54, 13, -32], "target": [-12, 4, 5], "fov": 40}
  ]
}

Several things to note:

  1. Region coordinates are normalized to [-1, 1], not world coordinates. The tool converts normalized positions to world space—the agent doesn't need to know the physical dimensions to plan the layout.
  2. Each region has its own landform operator—7 terrain types (plain, peak, ridge, dune, terrace, basin, canyon), each defining a different height-field function.
  3. Landmarks and camera paths use world coordinates, but position[1] is a vertical offset relative to terrain, not an absolute Y coordinate. The runtime samples terrain height at that position and adds the offset.

This specification is the pipeline's "spatial contract." The skill documentation positions it as:

Create a persistent scene graph, not a sequence of unrelated 2D shots. Preserve the user's explicit constraints, infer missing construction details separately, establish the global terrain first, and refine selected regions without disturbing the world-wide spatial contract.

Deterministic Terrain: 7 Landform Operators × Region Weight Field

This is the pipeline's most elegant design. Terrain height isn't read from a hand-painted heightmap—it's computed in real time as a pure mathematical function of the region weight field.

The Region Weight Field

The _region_weights method in threejs_world.py (732 lines) computes, for each point (nx, nz), a weight for each region:

@classmethod
def _region_weights(cls, spec, nx, nz) -> list[float]:
    raw = []
    for region in spec["regions"]:
        dx = nx - region["center"][0]
        dz = nz - region["center"][1]
        radius = max(0.05, region["radius"])
        distance = math.sqrt(dx*dx + dz*dz) / radius
        softness = max(0.02, region["blend_width"])
        value = math.exp(-max(0.0, distance - 0.05)**2 / (softness * 2.8))
        raw.append(max(1e-5, value))
    total = sum(raw) or 1.0
    return [value / total for value in raw]

The weight uses Gaussian distance falloff, then normalizes. The critical point: the same weights drive height, vertex color, scatter density, and camera clearance detection. This means region boundaries blend continuously without visible seams.

Deterministic Terrain Generation

The Height Field Function

_height_at combines region weights with landform operators:

@classmethod
def _height_at(cls, spec, x, z) -> float:
    nx = x / (size / 2.0)
    nz = z / (size / 2.0)
    weights = cls._region_weights(spec, nx, nz)
    seed = spec["seed"] * 0.01337
    elevation = 0.0
    for index, (region, weight) in enumerate(zip(spec["regions"], weights)):
        frequency = region["frequency"]
        noise = (
            math.sin((nx * 3.1 + seed + index) * frequency * math.pi)
            + math.cos((nz * 2.7 - seed * 0.7 + index) * frequency * math.pi)
            + 0.5 * math.sin((nx + nz) * frequency * 7.3 + seed * 3.0 + index)
        ) / 2.5
        landform = cls._landform(region["landform"], dx, dz, distance)
        elevation += weight * (
            region["base_elevation"]
            + region["amplitude"] * (noise * 0.48 + landform * 0.8)
        )
    return elevation * spec["world"]["elevation_scale"]

The noise function uses three superimposed sine/cosine waves—no Perlin/Simplex noise library needed, pure arithmetic, and completely deterministic for a given seed. This is critical: the same world_spec produces vertex-identical terrain on any machine.

The 7 Landform Operators

@staticmethod
def _landform(kind, dx, dz, distance) -> float:
    if kind == "peak":    return max(0.0, 1.0 - distance) ** 2.2
    if kind == "ridge":   return max(0.0, 1.0 - abs(dx * 1.8 + math.sin(dz * 5.0) * 0.16))
    if kind == "dune":    return (math.sin((dx + dz * 0.25) * 18.0) + 1.0) * 0.24
    if kind == "terrace": return math.floor(max(0.0, 1.0 - distance) * 5.0) / 5.0
    if kind == "basin":   return -max(0.0, 1.0 - distance) ** 1.7
    if kind == "canyon":  return -max(0.0, 1.0 - abs(dx + math.sin(dz * 7.0) * 0.1)) ** 2.0
    return 0.0  # plain

Each operator is a standalone mathematical function defining that region's macro terrain character. terrace uses floor for stepped effects, canyon uses negative exponentials for deep valleys, dune uses high-frequency sines for ripple patterns. Simple functions, but visually distinct—at least at the blockout level.

My assessment: this parametric-operator terrain system falls far short of Houdini or World Machine Erosion networks in realism. But its advantage is complete determinism, zero external dependencies, and real-time browser computation. For agentic video production—where you need repeated iteration and auditability—determinism matters more than photorealism.

Region-Aware Asset Scattering

Once terrain is built, the next question: where do trees, rocks, and crystals go?

The pipeline doesn't allow random scatter. The scatterPoints function (running in the browser-side world-runtime.js) applies multi-layer filtering to each candidate point:

function scatterPoints(region, count, salt) {
  const random = mulberry32((WORLD_SPEC.seed ^ hashString(region.id + salt)) >>> 0);
  const points = [];
  for (let index = 0; index < count; index += 1) {
    let accepted = null;
    for (let attempt = 0; attempt < 14; attempt += 1) {
      const angle = random() * Math.PI * 2;
      const radius = Math.sqrt(random()) * region.radius * half;
      const x = region.center[0] * half + Math.cos(angle) * radius;
      const z = region.center[1] * half + Math.sin(angle) * radius;
      // Filter 1: dominant region check
      const dominant = dominantRegion(x, z);
      if (dominant.region.id !== region.id || dominant.weight < 0.34) continue;
      // Filter 2: slope limit
      if (slopeAt(x, z) > region.slope_limit) continue;
      accepted = { x, z, y: heightAt(x, z), rotation: random() * Math.PI * 2,
                   scale: 0.72 + random() * 0.72 };
      break;
    }
    if (accepted) points.push(accepted);
  }
  return points;
}

Two filter layers:

  1. Dominant region check: samples the point's region weights. If the dominant region isn't the current one, or if dominance weight is below 0.34, the point is rejected. This prevents trees from spilling into neighboring regions.
  2. Slope limit: if the point's slope exceeds slope_limit (default 1.8), it's rejected. This prevents assets from clinging to cliff faces.

The PRNG is mulberry32—a deterministic generator seeded as WORLD_SPEC.seed ^ hashString(region.id + salt). Same seed, same spec, identical scatter results point-for-point.

On the Blender side (blender-world-runtime.py), scatter logic goes further with exclusion_zones—asset-free areas around settlements, roads, rivers, and landmarks:

exclusions = list(asset.get("exclusion_zones") or [])
# ...
if any(
    math.hypot(x - zone.get("center", [0, 0])[0],
               y - zone.get("center", [0, 0])[1]) < float(zone.get("radius", 0))
    for zone in exclusions
):
    continue

The Blender runtime also does something Three.js doesn't: asset size normalization. Imported GLBs often have inconsistent units, origins, and orientations. Instead of eyeballing scale adjustments, the pipeline measures the imported model's bounding box, normalizes to target_height, and aligns the bounding-box floor to the sampled terrain height.

Two Render Paths

The pipeline offers two render paths, locked at the proposal stage. Silent switching is a contract violation:

Dimension Three.js / HyperFrames Blender
Purpose Browser-native delivery, fast iteration Reference-grade rendering, final output
Engine Three.js WebGL Blender 4.5 EEVEE Next
Assets CC0 GLTF catalog or procedural primitives Same + PBR material layers
Interaction hf-seek event-driven free viewpoint None, outputs PNG sequence
Output Editable workspace (HTML + JS) .blend file + PNG frames + report.json
Render resume N/A (real-time) resume: true from missing frames

The Three.js runtime (world-runtime.js, 478 lines) is the pipeline's most complex single file. It reconstructs in the browser the exact same terrain functions, scatter logic, and camera interpolation as the Python side—ensuring preview matches final render. The runtime listens for HyperFrames' hf-seek event and renders deterministically at any time point:

function renderAt(timeValue) {
  const time = Math.max(0, Number(timeValue) || 0);
  const state = cameraAt(time);
  camera.position.copy(state.positionV);
  camera.fov = state.fov;
  camera.updateProjectionMatrix();
  camera.lookAt(state.targetV);
  // Water opacity breathing
  if (water) water.material.opacity = 0.73 + Math.sin(time * 0.42) * 0.035;
  // Sun intensity subtle variation
  sun.intensity = WORLD_SPEC.atmosphere.sun_intensity * (0.96 + Math.sin(time * 0.09) * 0.04);
  renderer.render(scene, camera);
}

The Blender side (blender-world-runtime.py, 434 lines) uses Blender's hetero_terrain and fractal noise instead of sine superposition, producing more natural terrain. The camera uses Bezier-interpolated keyframes with animatable focal length (lens). The Blender path also supports "visibility windows"—landmarks can appear/disappear at precise times via hide_render keyframes:

visible_from = placement.get("visible_from_seconds")
if visible_from is not None:
    reveal_frame = max(1, round(float(visible_from) * int(spec.get("fps", 30))))
    instance.hide_render = True
    instance.keyframe_insert("hide_render", frame=max(1, reveal_frame - 1))
    instance.hide_render = False
    instance.keyframe_insert("hide_render", frame=reveal_frame)

The Fidelity Gate: Blockout vs Production as a Hard Error

One of the pipeline's most notable designs is the fidelity gate—not a recommendation, but a hard validation that refuses to build.

The _fidelity_gate method in threejs_world.py checks the following for production tier. Any unmet condition returns an error, not a warning:

Fidelity Gate Comparison

if len(asset_palette) < 8:
    errors.append("Production tier requires at least 8 distinct asset-palette entries.")
if len(terrain_materials) < 3:
    errors.append("Production tier requires at least 3 terrain material layers.")
if any(not item.get("catalog_id") or not item.get("model_id") for item in asset_palette):
    errors.append("Every production asset-palette entry requires catalog_id and model_id.")

Plus CC0 license declaration checks, catalog manifest existence checks, and a minimum of 4 semantic categories. The implication: you cannot run an empty production tier and get a "looks okay" result. The system refuses to build rather than silently degrading to blockout and pretending it passed.

The blockout tier is deliberately permissive—procedural primitives, flat materials, no external dependencies—but gets explicitly labeled as "not presentable as reference-grade output":

if quality_tier == "blockout":
    return [], [
        "Blockout tier may use procedural primitives and flat materials; "
        "do not present it as reference-grade or production-fidelity output."
    ]

This design touches a core question for agentic systems: AI agents naturally gravitate toward the easiest path. Without a hard gate, an agent would likely pass off a blockout as production output. The fidelity gate moves "can you fake it?" from a behavioral constraint to a technical one.

3D Asset Generation: When to Call APIs and When Not To

The pipeline integrates three external 3D asset generation services, with a precise routing table determining when to use each:

Need Tool Model Unit Cost (as of 2026-08-13)
Repeated vegetation, rocks, generic props threejs_asset_catalog CC0 Kenney catalog Free
Unique object described in words atlas_3d tripo-h3.1/text-to-3d $0.33–$0.44/gen
Object matching concept art fal_3d Hunyuan 3D v3.1 image-to-3D $0.225–$0.375/gen
Multiple objects from regional composition fal_3d SAM 3D Objects $0.02/reconstruction

One code comment in Atlas Cloud's tool caught my eye:

"""The tool deliberately exposes mesh generation as its own capability.
Atlas's HTTP endpoint happens to be named ``generateImage`` for historical
reasons; that implementation detail must not make 3D assets look like image
outputs to the OpenMontage registry or pipeline.
"""

Atlas's API endpoint is named generateImage—a legacy name. The pipeline code actively corrects this naming misdirection, declaring the capability as 3d_asset_generation rather than image_generation. Small detail, but it reflects engineering discipline: the registry's semantic classification must not be polluted by upstream API naming habits.

Each 3D asset tool outputs a provenance manifest recording provider, model id, prompt, seeds, source page, cost, and output path. This manifest is part of the audit chain—during asset gate review, reviewers can trace every mesh back to its source, parameters, and cost.

Diagnostic Views: Five Review Viewpoints

The pipeline doesn't just produce a final render. The diagnostic report from _report includes five mandatory review views:

"review_views": ["global", "regional", "walk", "semantic", "wireframe"],
"diagnostic_passes": {
    "cinematic": "lit beauty render for final review",
    "semantic": "stable region-color pass for layout review",
    "wireframe": "explicit terrain and asset geometry pass"
},

The skill documentation explains why a single viewpoint isn't enough:

A wide aerial alone can hide broken contacts; a walk shot alone can hide an empty world.

skills/creative/3d-world-generation.md

  • Global: bird's-eye, checking overall layout and region distribution
  • Regional: medium distance, checking transitions and landmark relationships
  • Walk: ground level, checking asset contact and repetition
  • Semantic: saturated region colors, diagnosing layout issues
  • Wireframe: terrain topology, diagnosing geometry problems

The camera path report also includes minimum_camera_clearance—the closest distance between camera and terrain. If below 2.0 world units, it triggers a warning:

if min_clearance < 2.0:
    warnings.append(
        f"Camera path minimum terrain clearance is {min_clearance:.2f}; "
        "review for clipping."
    )

This multi-view diagnostic system is the most systematic approach I've seen in the landscape of AI video generation tools. Most projects either give you only the final render or a pile of uncontrollable random previews. OpenMontage's approach: each view solves a specific class of problems, and all views derive deterministically from the same world_spec.

The WorldClaw Connection

The skill documentation includes a dedicated reference file (worldclaw-principles.md) explaining the pipeline's relationship to the WorldClaw paper.

WorldClaw (arXiv:2608.05248v1, Guo et al., 2026) proposes ten architectural principles for agentic 3D world generation. OpenMontage adopts these principles but does not reuse WorldClaw's code—because WorldClaw's public repository contains only the paper and assets, not an executable generation stack.

The document includes a mapping table connecting WorldClaw concepts to OpenMontage implementations. The key entries:

WorldClaw Concept OpenMontage Implementation
Structured scene specification world_spec JSON + tool schema
Semantic layout map Continuous normalized region weight field
Region-aware height field Weighted procedural landform operators
Reusable prototypes vs functional landmarks Scatter procedural instances; place landmarks explicitly
Render-guided bounded refinement HyperFrames snapshots + agent issue queue

The document also candidly lists scope differences: no single-view object reconstruction, no segmentation, no generated PBR texture maps, and the current runnable path doesn't depend on Blender or Unreal.

Limitations

After reading 3,979 lines of code, I see several notable boundaries:

  1. Terrain operator expressiveness ceiling. The 7 parametric operators cover basic landform types but cannot generate erosion gullies, river cuts, glacial U-valleys, or other complex terrain. dune's sine ripples show periodicity at scale. For realistic terrain, Blender's hetero_terrain + fractal noise is better, but the Three.js side only has sine superposition.

  2. Asset diversity ceiling. Production tier requires ≥8 asset models and ≥4 semantic categories, but the CC0 catalogs (three Kenney kits) share a unified style—low-poly, cartoon. If you need realistic vegetation or architecture, you'd need to bring your own Quixel Megascans or similar—but the pipeline doesn't currently include that path.

  3. No characters or physics. The pipeline explicitly disclaims articulated characters, physics, navmeshes, or interactive game logic. This is a "fly-over world," not a "walk-into world."

  4. Camera paths are interpolation, not AI. camera_path is explicitly planned by the agent as a list of time-position-target-FOV keys, interpolated at runtime with smoothstep. There's no automated cinematography, no target tracking, no collision avoidance—minimum_camera_clearance reports but doesn't prevent.

These limitations are mostly deliberate design choices, not omissions. The pipeline positions itself as "the 3D world layer in agentic video production," not "a general-purpose 3D world engine."

Conclusion

The 3D world pipeline in commit 2702362 does three things right:

First, it transforms world generation from "AI generates everything at once" to "AI plans, deterministic math materializes." Text-to-3D models generate individual objects. world_spec describes world structure. Mathematical functions turn structure into terrain. The result is editable, auditable, and reproducible.

Second, it moves quality constraints from the behavioral layer to the technical layer with the fidelity gate. An agent can't cut corners and use blockout as production—the system refuses to build. This is far more effective than writing "please ensure quality" in a prompt.

Third, its diagnostic system is multi-viewpoint and structured. Five views each serve distinct purposes. Camera clearance, semantic coverage, and scatter density all produce quantified reports. This isn't a loose "render and glance" workflow.

If you're building agentic video or 3D content production, this architecture is worth studying. Its specific implementations—sine-superposition terrain, mulberry32 scatter, Three.js runtime—may not be the optimal solution for every scenario. But the architectural idea of "semantic specs driving deterministic rendering" is directional. While most 3D tools are still chasing "generate more realistic meshes," OpenMontage is solving a harder problem: how to let AI generate editable 3D worlds with persistent controllability.

References

  1. calesthio — OpenMontage commit 2702362: feat: add production 3D world pipeline (2026-08-13)
  2. Guo et al. — WorldClaw: Agentic 3D Open-World Generation at Scale (arXiv:2608.05248v1, 2026)
  3. Atlas Cloud — tripo-h3.1 text-to-3D model (as of 2026-08-13)
  4. fal.ai — Hunyuan 3D v3.1 Rapid (as of 2026-08-13)
  5. Kenney — Nature Kit / Fantasy Town Kit / Survival Kit (CC0-1.0)
  6. Three.js — r181 ESM modules
  7. Blender Foundation — Blender 4.5 LTS EEVEE Next
  8. Galen Guan — OpenMontage Deep Dive: World's First Open-Source Agentic Video Production System (this site, 2026-06-29)
  9. Galen Guan — 2026 Open-Source AI Video Generation Tools: A Comparative Review (this site, 2026-07-21)