# ArmorPaint Clone Brush Architecture: How 3D Sampling Replaces UV Space Mapping

> Explore the ArmorPaint clone brush architecture. Discover how 3D sampling replaces UV space mapping using ray-casts into scene space for accurate texture reading.

- Repository: [Armory 3D/armorpaint](https://github.com/armory3d/armorpaint)
- Tags: architecture
- Published: 2026-09-14

---

**The ArmorPaint clone brush utilizes a dual-layer architecture where [`util_clone.c`](https://github.com/armory3d/armorpaint/blob/main/util_clone.c) provides deep-copy utilities for brush data structures, while [`makeclone.c`](https://github.com/armory3d/armorpaint/blob/main/makeclone.c) implements the sampling logic that performs ray-casts into 3D scene space to read texture values from world coordinates rather than relying on UV parameterization.**

The clone brush in the **armory3d/armorpaint** repository eliminates dependency on UV mapping quality by sampling colors directly from 3D intersections. This clone brush architecture separates data integrity operations from spatial sampling algorithms, enabling artists to paint seamlessly across seams and distorted topology.

## Data Deep-Copy Layer (util_clone.c)

The first architectural pillar resides in [`paint/sources/util/util_clone.c`](https://github.com/armory3d/armorpaint/blob/main/paint/sources/util/util_clone.c), which provides **deep-copy** utilities ensuring the clone operation works on isolated data copies. This prevents accidental modification of original source materials during the painting process.

Key functions in this layer include:

- `util_clone_f32_array()` – Duplicates float arrays containing brush parameters
- `util_clone_u8_array()` – Clones byte arrays for texture data buffers
- `util_clone_canvas_sockets()`, `util_clone_canvas_buttons()`, `util_clone_canvas_node()` – Replicate UI-node canvas structures used by the brush interface
- `util_clone_obj()` – Clones full object hierarchies including transform data
- `util_clone_swatch_color()` – Duplicates color-swatch structures for palette management

These utilities are consumed by the clone brush and many other ArmorPaint systems requiring non-destructive data duplication.

## Brush Implementation Layer (makeclone.c)

The second pillar in [`paint/sources/util/makeclone.c`](https://github.com/armory3d/armorpaint/blob/main/paint/sources/util/makeclone.c) contains the actual brush factory and sampling implementation. This file registers the brush-type handlers and defines the critical departure from traditional UV-based painting.

The primary entry point is `make_clone_brush()`, a factory function that:
- Instantiates the brush object
- Stores references to the source object and source texture
- Wires up callback pointers for the sampling routine

The core sampling logic lives in `clone_brush_sample()`, which executes the 3D space querying that distinguishes this tool from standard paint brushes.

## 3D Sampling Pipeline vs UV Space Sampling

Traditional paint brushes follow a **UV-space** workflow: cursor position converts to screen-space, projects onto the mesh, retrieves UV coordinates, and samples the texture at those 2D parameters. This fails when UV seams are present or when stretching distorts the texture mapping.

The clone brush instead implements a **world-space** pipeline:

1. **Ray-cast** from the camera through the cursor position using `iron_raycast()` (defined in [`base/sources/iron_raycast.c`](https://github.com/armory3d/armorpaint/blob/main/base/sources/iron_raycast.c))
2. Retrieve the `RayHit` structure containing **world coordinates**, surface normal, and hit mesh pointer
3. Transform the world-space hit point into the **source object's local space** using the source object's inverse transformation matrix
4. Project the local point onto the source mesh's UVs to fetch the source texel
5. If UVs are missing, fall back to **projective-space sampling** using the world position and brush projection matrix
6. Return the sampled color to the paint engine for application to the target texture

This pipeline allows the brush to work correctly on any topology regardless of UV seam placement or distortion.

## Source Code Implementation Details

The sampling routine relies on intersection testing provided by the Iron rendering engine. When `clone_brush_sample()` executes, it calls `iron_raycast()` to obtain the `RayHit` data. The hit validation checks ensure the brush only samples when geometry is present beneath the cursor.

The coordinate transformation follows this pattern:

```c
/* Simplified sampling logic from makeclone.c */
color_t clone_brush_sample(brush_t *brush, ray_t cursor_ray) {
    /* Step 1: Ray-cast into 3D scene */
    RayHit hit = iron_raycast(cursor_ray);
    if (!hit.valid) return COLOR_NULL;
    
    /* Step 2: Transform world hit to source object local space */
    vec3 local_pos = mat4_mul_vec3(
        brush->source_obj->inverse_matrix, 
        hit.position
    );
    
    /* Step 3: Sample source texture via UV lookup or projective fallback */
    color_t sampled;
    if (brush->source_mesh->has_uvs) {
        vec2 uv = mesh_get_uv_at_point(brush->source_mesh, local_pos);
        sampled = texture_sample(brush->source_tex, uv);
    } else {
        sampled = projective_sample(brush, local_pos);
    }
    
    return sampled;
}

```

The [`render_path_paint.c`](https://github.com/armory3d/armorpaint/blob/main/render_path_paint.c) file in `paint/sources/render/` consumes this returned color and writes it to the destination texture at the brush's current target UV coordinates, completing the transfer from source to destination.

## Practical Usage Example

Implementing the clone brush in a custom tool requires initializing the brush with source references, then executing the sampling loop:

```c
/* Initialize the clone brush with source object and texture */
brush_t *clone = make_clone_brush(
    source_obj,      /* Object to sample from */
    source_texture,  /* Texture to read */
    brush_params     /* Size, strength, falloff settings */
);

/* Main painting loop */
while (painting_active) {
    ray_t cursor_ray = get_cursor_ray(camera, mouse_pos);
    
    /* Sample from 3D space */
    color_t paint_color = clone_brush_sample(clone, cursor_ray);
    
    if (paint_color.valid) {
        /* Apply to destination texture at current UV */
        paint_apply(
            paint_color,
            current_uv,
            brush_params.strength
        );
    }
}

```

This implementation demonstrates how the architecture abstracts the complexity of 3D intersection testing while providing a simple color-retrieval interface.

## Summary

- The **clone brush architecture** separates concerns between data duplication ([`util_clone.c`](https://github.com/armory3d/armorpaint/blob/main/util_clone.c)) and spatial sampling ([`makeclone.c`](https://github.com/armory3d/armorpaint/blob/main/makeclone.c))
- **3D sampling** replaces UV mapping by ray-casting into scene space via `iron_raycast()` and transforming world coordinates back to local object space
- The system gracefully handles missing UVs through **projective-space sampling** fallbacks
- Source files [`util_clone.c`](https://github.com/armory3d/armorpaint/blob/main/util_clone.c), [`makeclone.c`](https://github.com/armory3d/armorpaint/blob/main/makeclone.c), and [`iron_raycast.c`](https://github.com/armory3d/armorpaint/blob/main/iron_raycast.c) collectively enable topology-independent texture cloning

## Frequently Asked Questions

### What is the difference between util_clone.c and makeclone.c in ArmorPaint?

[`util_clone.c`](https://github.com/armory3d/armorpaint/blob/main/util_clone.c) provides generic deep-copy utilities used across the entire application to duplicate data structures like arrays, objects, and canvas nodes without referencing the original memory. [`makeclone.c`](https://github.com/armory3d/armorpaint/blob/main/makeclone.c) specifically implements the clone brush tool, containing the brush factory function and the 3D sampling algorithm that distinguishes this brush from others.

### How does the clone brush handle meshes without UV coordinates?

When the hit mesh lacks UVs, the `clone_brush_sample()` function falls back to **projective-space sampling**. It uses the world-space hit position transformed into the brush's projection matrix to generate sampling coordinates, effectively treating the texture as a decal projected onto the geometry rather than a parameterized surface mapping.

### Why does the clone brush use ray-casting instead of direct UV sampling?

Ray-casting allows the brush to sample from the **actual geometric intersection** in world space rather than relying on the UV parameterization of the target mesh. This design eliminates artifacts from UV seams, stretching, or overlapping islands, enabling artists to clone textures across arbitrary topology where UV continuity would otherwise be required.

### Which function handles the world-to-local coordinate transformation?

The sampling routine inside `clone_brush_sample()` performs this transformation by multiplying the hit world position (`hit.position` from the `RayHit` structure) by the source object's inverse transformation matrix. This converts the global ray-cast intersection into the source object's local coordinate system before texture lookup occurs.