Blender Integration Module

Low-level Blender mesh operations for terrain visualization.

This module contains Blender-specific code for creating and configuring terrain meshes, applying vertex colors, and managing mesh data. Uses Blender’s high-performance bulk operations (foreach_set, foreach_get) for ~100x faster vertex manipulation.

Vertex Colors

src.terrain.blender_integration.apply_vertex_colors(mesh_obj, vertex_colors, y_valid=None, x_valid=None, n_surface_vertices=None, logger=None)[source]

Apply colors to an existing Blender mesh.

Accepts colors in either vertex-space (n_vertices, 3/4) or grid-space (height, width, 3/4). When grid-space colors are provided with y_valid/x_valid indices, colors are extracted for each vertex using those coordinates.

Uses Blender’s foreach_set for ~100x faster bulk operations.

Parameters:
  • mesh_obj (bpy.types.Object) – The Blender mesh object to apply colors to

  • vertex_colors (np.ndarray) – Colors in one of two formats: - Vertex-space: shape (n_vertices, 3) or (n_vertices, 4) - Grid-space: shape (height, width, 3) or (height, width, 4)

  • y_valid (np.ndarray, optional) – Y indices for grid-space colors

  • x_valid (np.ndarray, optional) – X indices for grid-space colors

  • n_surface_vertices (int, optional) – Number of surface vertices. If provided, boundary vertices (index >= n_surface_vertices) will be skipped to preserve their existing colors (e.g., two-tier edge colors). Default: None (apply to all)

  • logger (logging.Logger, optional) – Logger for progress messages

Apply colors to mesh vertices with automatic grid-to-vertex mapping. Used in Combined Render: Full-Featured Example.

Supports two color formats:

  1. Vertex-space colors: shape (n_vertices, 3) or (n_vertices, 4) - One color per vertex - Direct application (fastest)

  2. Grid-space colors: shape (height, width, 3) or (height, width, 4) - Colors in DEM grid coordinates - Requires y_valid/x_valid indices to extract per-vertex colors - Handles downsampled meshes automatically

Preserves boundary colors:

Use n_surface_vertices parameter to preserve edge colors when applying terrain colors (used with two-tier edges).

Example:

from src.terrain.blender_integration import apply_vertex_colors

# Vertex-space colors (simple)
apply_vertex_colors(mesh_obj, vertex_colors)

# Grid-space colors (with mapping)
apply_vertex_colors(
    mesh_obj,
    grid_colors,
    y_valid=terrain.y_valid,
    x_valid=terrain.x_valid
)

# Preserve edge colors (two-tier mode)
apply_vertex_colors(
    mesh_obj,
    terrain_colors,
    y_valid=terrain.y_valid,
    x_valid=terrain.x_valid,
    n_surface_vertices=len(terrain.y_valid)
)
src.terrain.blender_integration.apply_ring_colors(mesh_obj, ring_mask, y_valid, x_valid, ring_color=(0.15, 0.15, 0.15), logger=None)[source]

Apply a solid color to vertices within a ring mask.

Modifies the existing TerrainColors vertex color layer, setting RGB values for vertices that fall within the ring mask to the specified color. This creates a dark outline around areas of interest (e.g., park zones).

Uses Blender’s foreach_get/foreach_set for efficient bulk operations.

Parameters:
  • mesh_obj (bpy.types.Object) – The Blender mesh object

  • ring_mask (np.ndarray) – 2D boolean array (height, width) where True = apply ring color

  • y_valid (np.ndarray) – Y indices mapping vertices to grid positions

  • x_valid (np.ndarray) – X indices mapping vertices to grid positions

  • ring_color (tuple) – RGB color (0-1 range) to apply to ring vertices. Default: dark gray.

  • logger (logging.Logger, optional) – Logger for progress messages

Apply solid color to ring/boundary vertices.

Used to color two-tier edge extrusions with base materials.

Example:

apply_ring_colors(
    mesh_obj,
    ring_mask,
    y_valid, x_valid,
    ring_color=(0.5, 0.48, 0.45)  # Clay
)
src.terrain.blender_integration.apply_road_mask(mesh_obj, road_mask, y_valid, x_valid, logger=None)[source]

Apply a road mask as a separate vertex color layer for material detection.

Creates a “RoadMask” vertex color layer where road vertices have R=1.0 and non-road vertices have R=0.0. This allows the material shader to detect roads without changing the terrain colors.

Uses Blender’s foreach_set for ~100x faster bulk operations.

Parameters:
  • mesh_obj (bpy.types.Object) – The Blender mesh object

  • road_mask (np.ndarray) – 2D boolean or float array (height, width) where >0.5 = road

  • y_valid (np.ndarray) – Y indices mapping vertices to grid positions

  • x_valid (np.ndarray) – X indices mapping vertices to grid positions

  • logger (logging.Logger, optional) – Logger for progress messages

Apply road mask to color road vertices.

Marks road vertices with a distinctive color for shader-based material detection.

Example:

apply_road_mask(
    mesh_obj,
    road_mask,
    y_valid, x_valid
)

Vertex Positions

src.terrain.blender_integration.apply_vertex_positions(mesh_obj, new_positions, logger=None)[source]

Apply new 3D positions to mesh vertices.

Useful for applying smoothed vertex coordinates to an existing mesh, e.g., after road smoothing or terrain filtering.

Parameters:
  • mesh_obj – Blender mesh object to modify

  • new_positions (ndarray) – Array of shape (n_vertices, 3) with new [x, y, z] positions

  • logger – Optional logger for progress messages

Raises:

ValueError – If new_positions shape doesn’t match mesh vertex count

Return type:

None

Example

>>> # Smooth road vertices and apply to mesh
>>> from src.terrain.roads import smooth_road_vertices
>>>
>>> vertices = np.array([v.co[:] for v in mesh.data.vertices])
>>> smoothed = smooth_road_vertices(vertices, road_mask, y_valid, x_valid)
>>> apply_vertex_positions(mesh, smoothed)

Update mesh vertex positions in place.

Used for modifying terrain geometry after mesh creation (smoothing, offsetting, etc.).

Example:

# Offset vertices by height
new_positions = old_positions.copy()
new_positions[:, 2] += height_offset

apply_vertex_positions(mesh_obj, new_positions)

Mesh Creation

src.terrain.blender_integration.create_blender_mesh(vertices, faces, colors=None, y_valid=None, x_valid=None, boundary_colors=None, name='TerrainMesh', logger=None)[source]

Create a Blender mesh object from vertices and faces.

Creates a new Blender mesh datablock, populates it with geometry data, optionally applies vertex colors, and creates a material with colormap shader.

Parameters:
  • vertices (np.ndarray) – Array of (n, 3) vertex positions

  • faces (list) – List of tuples defining face connectivity

  • colors (np.ndarray, optional) – Array of RGB/RGBA colors (height, width, channels) for surface vertices

  • y_valid (np.ndarray, optional) – Array of y indices for vertex colors

  • x_valid (np.ndarray, optional) – Array of x indices for vertex colors

  • boundary_colors (np.ndarray, optional) – Array of RGB colors (n_boundary, 3) for boundary vertices in two-tier mode

  • name (str) – Name for the mesh and object (default: “TerrainMesh”)

  • logger (logging.Logger, optional) – Logger for progress messages

Returns:

The created terrain mesh object

Return type:

bpy.types.Object

Raises:

RuntimeError – If Blender is not available or mesh creation fails

Create Blender mesh object from vertices and faces.

Low-level mesh creation. Most users should use create_mesh() instead.

Example:

mesh_obj = create_blender_mesh(
    vertices,
    faces,
    name="Terrain"
)

Performance Notes

This module uses Blender’s bulk operations for maximum performance:

  • foreach_set / foreach_get: ~100x faster than Python loops

  • Flat numpy arrays: Direct memory access without Python overhead

  • Vectorized operations: Process all vertices simultaneously

Typical performance:

  • Applying colors to 1M vertices: ~0.1 seconds (vs 10+ seconds with loops)

  • Creating mesh with 1M faces: ~0.5 seconds