Python Tools - Session 2A
3D Mosaic Project — Part 2
January 2020
Session Navigation
Overview
Building on the mosaic foundation from Session 1B, we now add color. This session introduces UV space, the colorAtPoint() command for sampling texture colors, and how to map UV coordinates to tile grid positions. By the end, each tile in the mosaic is colored to match the source image.
Key Concepts
- UV space — a 2D coordinate system on textures where U runs horizontally (0 to 1) and V runs vertically (0 to 1)
- colorAtPoint() — a PyMEL command that samples the RGB color from a file texture at a given UV coordinate
- Coordinate mapping — converting tile grid indices to normalized UV values by dividing by the total count
- Per-vertex color — applying sampled RGB values directly to mesh vertices with
polyColorPerVertex - Nested loop tracking — maintaining both position (
x_pos,z_pos) and UV (u_coord,v_coord) counters simultaneously
UV Coordinate Mapping
As we iterate through the tile grid, we need to calculate where each tile falls in UV space. The key insight is that UV coordinates are normalized (0 to 1), so we increment by 1.0 / count each step:
- U coordinate increases left-to-right:
u_coord += 1.0 / width_count - V coordinate decreases top-to-bottom:
v_coord -= 1.0 / height_count(V=1 is the top of the image)
Code
The complete mosaic script with color sampling — each tile is created, positioned, and colored to match the source image:
import pymel.core as pm
pm.newFile(force=True)
tile_size = 10
image_path = r"C:\path\to\your\image.jpg"
file_node = pm.createNode('file', name='input_image')
file_node.fileTextureName.set(image_path)
width_count = int(file_node.outSizeX.get() / tile_size)
height_count = int(file_node.outSizeY.get() / tile_size)
x_pos = 0.0
u_coord = 0.0
for x in xrange(0, width_count):
x_pos += tile_size
u_coord += 1.0/width_count
z_pos = 0.0
v_coord = 1.0
for z in xrange(0, height_count):
z_pos += tile_size
v_coord -= 1.0/height_count
rgb = pm.colorAtPoint(file_node, u=u_coord, v=v_coord, output='RGB')
box = pm.polyCube(w=tile_size, h=tile_size, d=tile_size)[0]
pm.move(box, [x_pos, 0.0, z_pos])
pm.polyColorPerVertex(box, rgb=rgb, colorDisplayOption=True)