Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Blueprint Zone Editor

Status: Production Ready
Tests: 125+
Crates: astraweave-terrain, astraweave-blend, aw_editor

The Blueprint Zone system provides polygon-based spatial control for vegetation generation and heightmap injection. It bridges .blend scene imports with AstraWeave’s terrain scatter pipeline, enabling both exact 1:1 reproduction (Replica mode) and procedural variation (Inspired mode) of imported environments.

Overview

Pipeline

.blend Scene → Decomposition → BiomePack → BlueprintZone → ZoneScatterGenerator
                    ↓                           ↓                    ↓
              heightmap_raster           ZoneRegistry          VegetationInstance
              (terrain mesh →            (polygon CRUD,        + HeightmapPatch
               heightmap + fixed          spatial queries,          ↓
               placements)                JSON persistence)   apply_heightmap_patches()
                                                                    ↓
                                                              TerrainChunk updates

Key Features

FeatureDescription
Polygon ZonesArbitrary polygons define generation boundaries
Replica Mode1:1 fixed placement from .blend scene data
Inspired ModeProcedural scatter using extracted ScatterConfig
Biome PresetsPure biome-driven scatter without .blend data
Heightmap InjectionRasterized terrain meshes applied as height patches
Boundary BlendingSmoothstep falloff prevents hard cutoffs at zone edges
Adaptive ScalingAuto-adjusts density/scale when zone ≠ source area
Undo/RedoFull command stack in blueprint panel
3D OverlayWireframe zone projection in viewport

Quick Start

#![allow(unused)]
fn main() {
use astraweave_terrain::blueprint_zone::*;
use astraweave_terrain::zone_scatter::*;
use std::path::Path;

// 1. Define a zone
let zone = BlueprintZone {
    id: ZoneId(1),
    name: "Pine Forest".into(),
    vertices: vec![
        [0.0, 0.0], [200.0, 0.0],
        [200.0, 150.0], [0.0, 150.0],
    ],
    source: ZoneSource::BlendScene {
        pack_path: "assets/pine_forest.biomepack".into(),
        placement_mode: PlacementMode::Replica,
    },
    priority: 0,
    enabled: true,
};

// 2. Register it
let mut registry = ZoneRegistry::new();
registry.add_zone(zone.clone());

// 3. Generate scatter
let gen = ZoneScatterGenerator::new(256.0, 128);
let result = gen.generate_zone_scatter(&zone, &biome_pack)?;

println!("{} placements", result.placement_count());
println!("{} height modifications", result.modified_height_count());

// 4. Apply heightmap patches to terrain
let mut chunks = HashMap::new();
// ... populate with TerrainChunks ...
apply_heightmap_patches(&mut chunks, &[result]);

// 5. Persist zones
registry.save(Path::new("assets/zones.json"))?;
}

Zone Data Model

BlueprintZone

A BlueprintZone represents a polygon region with a vegetation/terrain source:

#![allow(unused)]
fn main() {
pub struct BlueprintZone {
    pub id: ZoneId,                    // Unique identifier
    pub name: String,                  // Display name
    pub vertices: Vec<[f32; 2]>,       // Polygon vertices (XZ plane)
    pub source: ZoneSource,            // What to generate
    pub priority: u32,                 // Overlap resolution (higher wins)
    pub enabled: bool,                 // Toggle generation
}
}

ZoneSource

#![allow(unused)]
fn main() {
pub enum ZoneSource {
    BiomePreset(BiomeType),            // Built-in biome scatter
    BlendScene {
        pack_path: String,             // Path to .biomepack
        placement_mode: PlacementMode, // Replica or Inspired
    },
}
}

PlacementMode

ModeDescription
ReplicaExact positions from .blend scene, scaled by AdaptiveScaleParams
InspiredProcedural scatter using extracted density/distribution rules

ZoneRegistry

#![allow(unused)]
fn main() {
let mut registry = ZoneRegistry::new();

// CRUD operations
registry.add_zone(zone);
registry.remove_zone(zone_id);
let zone = registry.get_zone(zone_id);
let zone_mut = registry.get_zone_mut(zone_id);

// Spatial queries
let zones = registry.zones_containing_point(x, z);
let overlaps = registry.zones_overlapping_rect(min_x, min_z, max_x, max_z);

// Persistence
registry.save(Path::new("zones.json"))?;
let loaded = ZoneRegistry::load(Path::new("zones.json"))?;
}

Zone-Scoped Generation

ZoneScatterGenerator

#![allow(unused)]
fn main() {
use astraweave_terrain::zone_scatter::*;

// Create generator with chunk size and heightmap resolution
let gen = ZoneScatterGenerator::new(256.0, 128);

// Generate for a single zone
let result: ZoneGenerationResult = gen.generate_zone_scatter(&zone, &biome_pack)?;
}

ZoneGenerationResult

#![allow(unused)]
fn main() {
pub struct ZoneGenerationResult {
    pub placements: Vec<VegetationInstance>,     // What to place
    pub heightmap_patches: Vec<HeightmapPatch>,  // Height modifications
}

// VegetationInstance fields:
// - position: Vec3
// - rotation: f32
// - scale: f32
// - vegetation_type: String
// - model_path: String
// - terrain_normal: Vec3
}

Multi-Zone Generation

#![allow(unused)]
fn main() {
// Generate scatter for all zones with overlap priority resolution
let results = generate_multi_zone_scatter(&zones, &gen, &biome_packs)?;

// Apply all heightmap patches at once
apply_heightmap_patches(&mut chunk_map, &results);
}

Adaptive Scaling

When a zone’s area differs from the source scene’s footprint, scaling parameters adjust automatically:

#![allow(unused)]
fn main() {
let params = AdaptiveScaleParams::compute(reference_area, zone_area);
}
ParameterFormulaEffect
density_multiplier$\sqrt{\frac{\text{zone}}{\text{ref}}}$Adjusts placement density
scale_multiplier$\left(\frac{\text{zone}}{\text{ref}}\right)^{0.25}$Adjusts object scale
position_scale$\sqrt{\frac{\text{zone}}{\text{ref}}}$Adjusts position spread

Example: A zone 4× larger than the source scene would have:

  • density_multiplier = 2.0 (double the objects)
  • scale_multiplier ≈ 1.41 (slightly larger objects)
  • position_scale = 2.0 (spread positions wider)

Boundary Blending

Smoothstep Falloff

Zone edges use smoothstep interpolation to prevent hard vegetation/height cutoffs:

#![allow(unused)]
fn main() {
// BlendMask provides per-point blending weights
let mask = BlendMask::new(resolution, world_bounds);
let weight = mask.sample(x, z); // 0.0 at edge → 1.0 at center
}

Manual Painting

The editor provides BrushMode::ZoneBlend for manually painting blend weights at zone boundaries, giving artists fine control over transition regions.


Heightmap Rasterization

Terrain meshes extracted from .blend files are rasterized into heightmaps:

#![allow(unused)]
fn main() {
use astraweave_blend::heightmap_raster::*;

let heightmap = rasterize_terrain_meshes(&terrain_meshes, 128)?;

// Query rasterized data
let height = heightmap.sample_bilinear(0.5, 0.5); // Normalized UV coords
let area = heightmap.footprint_area();              // m² world-space

// Fixed placements (exact object positions from scene)
let placements: Vec<FixedPlacement> = extract_fixed_placements(&scene_objects);
}

The rasterizer uses ray-triangle intersection with:

  • Seam averaging for multi-tile terrain boundaries
  • Hole filling via neighbor interpolation for missing samples

Editor Integration

Blueprint Panel

The BlueprintPanel (panel type: Blueprint) provides:

  • 2D Canvas: Pan/zoom view for polygon zone drawing
  • Tools: Select, DrawPolygon, MoveVertex, DeleteZone
  • Zone Inspector: Name, source selection (biome preset or blend scene), placement mode toggle
  • Undo/Redo: Full BlueprintCommand stack with Ctrl+Z/Ctrl+Shift+Z
  • Persistence: Save/Load zones as .zones.json files

Viewport Overlay

BlueprintOverlay projects zone polygons as colored wireframe outlines in the 3D viewport. Zone boundaries are rendered alongside component gizmos and brush cursors in the physics renderer debug line pass.

Asset Browser

The BlendAssetScanner adds .blend file discovery to the asset browser with:

  • Automatic directory scanning for .blend files
  • Decomposition status detection (checking for manifest.json)
  • Quick actions: Import Blend Scene, Use as Zone Source

System Wiring

The editor’s update loop dispatches BlueprintAction events:

ActionHandlerEffect
GenerateZonehandle_generate_zone()Runs ZoneScatterGenerator for one zone
GenerateAllIterates all zonesGenerates scatter for every enabled zone
ClearGenerationClears resultsRemoves generated placements
SaveZoneshandle_save_zones()Saves ZoneRegistry to JSON
LoadZoneshandle_load_zones()Loads registry + syncs panel state

After each action, sync_zone_overlay() pushes updated zone data to the viewport.


Test Coverage

SuiteCountScope
blueprint_zone unit24Zone model, registry, polygon math, persistence
zone_scatter unit16Generation modes, blending, heightmap patches
zone_scatter_e2e integration11Full pipeline, multi-zone, chunk spanning
heightmap_raster unit11Rasterization, terrain bounds, seam stitching
heightmap_raster_e2e integration10Multi-tile, bilinear sampling, edge cases
biome_pack unit9BiomePack extension fields, detection
blueprint_panel unit17Canvas, tools, undo/redo, actions
blueprint_overlay unit7Wireframe generation, color mapping
blend_scanner unit8Directory scanning, status detection
Total113+

See Also