fixed xynova texture generation problem

This commit is contained in:
ykcheungab
2026-09-19 21:02:34 +08:00
parent 55bab861c3
commit 71c2a29ccb
15 changed files with 663118 additions and 63 deletions
+55 -62
View File
@@ -33,6 +33,11 @@ import bpy
import numpy as np
from mathutils import Vector
# Blender -P does not consistently include the script directory on sys.path.
sys.path.insert(0, str(Path(__file__).resolve().parent))
from patch_geometry import clip_rect
from patch_projection import build_patch_geometry
# ----------------------------------------------------------------------
# Common Utilities
# ----------------------------------------------------------------------
@@ -522,6 +527,20 @@ def generate_valid_color(neighbors, r):
color[channel] = sampled
return color
def precompute_cell_colors(nx, ny, r):
"""Assign every seeded grid cell once, independent of geometry/UV visibility."""
colors = np.zeros((ny, nx, 3), dtype=np.float32)
for j in range(ny):
for i in range(nx):
neighbors = []
if j > 0:
neighbors.append(colors[j - 1, i])
if i > 0:
neighbors.append(colors[j, i - 1])
colors[j, i] = generate_valid_color(neighbors, r) if neighbors else np.random.rand(3)
return colors
def checker_patch_color(i, j):
"""Return a black/white checker color for patch index (i, j)."""
if (i + j) % 2 == 0:
@@ -848,7 +867,7 @@ def generate_patch_texture(
vector_width_mm=164.0, vector_height_mm=160.0,
uv_centre=None, cross_length=40,
cross_thickness=3, cross_color=(1,0,0,1), edge_sample_grid=5,
uv_layer=None, image_name="PatchTexture"
uv_layer=None, image_name="PatchTexture", patch_colors=None, geometry=None
):
if not output_path:
raise ValueError("output_path is required (absolute PNG path)")
@@ -856,50 +875,22 @@ def generate_patch_texture(
_warn_if_page_aspect_differs(
texture_width, texture_height, vector_width_mm, vector_height_mm
)
cam_data = cam.data
render = scene.render
res_x = render.resolution_x
res_y = render.resolution_y
if cam_data.sensor_fit == 'HORIZONTAL':
sensor_width = cam_data.sensor_width
sensor_height = sensor_width / (res_x / res_y)
else:
sensor_height = cam_data.sensor_height
sensor_width = sensor_height * (res_x / res_y)
focal = cam_data.lens
frame_width = sensor_width / focal
frame_height = sensor_height / focal
if uv_layer is None:
uv_layer = obj.data.uv_layers.active
if uv_layer is None:
raise ValueError("A UV layer is required")
if patch_colors is None:
patch_colors = precompute_cell_colors(nx, ny, r)
if geometry is None:
geometry = build_patch_geometry(
obj, cam, scene, depsgraph, [uv_layer.name], nx, ny, edge_sample_grid
)[uv_layer.name]
edge_sample_grid = max(2, int(edge_sample_grid))
patch_uv_polygons = []
patch_colors = np.zeros((ny, nx, 3), dtype=np.float32)
skipped_patches = 0
print(f"Raycasting {nx*ny} patches...")
for j in range(ny):
v_start = j / ny
v_end = (j+1) / ny
for i in range(nx):
u_start = i / nx
u_end = (i+1) / nx
uv_poly = sample_patch_uv_polygon(
scene, depsgraph, cam, obj,
u_start, u_end, v_start, v_end,
frame_width, frame_height,
edge_sample_grid=edge_sample_grid, uv_layer=uv_layer
)
if uv_poly is None:
patch_uv_polygons.append(None)
skipped_patches += 1
continue
patch_uv_polygons.append(uv_poly)
neighbors = []
if j > 0: neighbors.append(patch_colors[j-1, i])
if i > 0: neighbors.append(patch_colors[j, i-1])
color = np.random.rand(3) if len(neighbors)==0 else generate_valid_color(neighbors, r)
patch_colors[j, i] = color
print(f"Skipped {skipped_patches} patches with fewer than 3 mesh samples.")
def pixel_polygon(uv_polygon):
# Authored per-loop UVs are authoritative, including wide/mirrored islands.
# Clip to this output tile; do not infer a periodic seam or draw copies.
return [(u * (texture_width - 1), v * (texture_height - 1))
for u, v in clip_rect(uv_polygon, 0.0, 1.0, 0.0, 1.0)]
# Create image – start with black background
img_name = image_name
@@ -913,9 +904,8 @@ def generate_patch_texture(
# Rasterize all mesh UVs in white (visible + non-visible parts). Retain
# the exact same polygons for native SVG/PDF output.
mesh_polygons = mesh_uv_pixel_polygons(
obj, texture_width, texture_height, uv_layer=uv_layer
)
mesh_polygons = [pixel_polygon(poly) for poly in geometry['mesh']]
mesh_polygons = [poly for poly in mesh_polygons if len(poly) >= 3]
for pixel_poly in mesh_polygons:
fill_polygon(
None, pixels, texture_width, texture_height,
@@ -924,16 +914,14 @@ def generate_patch_texture(
# Draw the visible patches and retain each editable vector shape.
patch_shapes = []
idx = 0
for j in range(ny):
for i in range(nx):
uv_poly = patch_uv_polygons[idx]; idx += 1
if uv_poly is None: continue
color = patch_colors[j,i]
rgba = (color[0], color[1], color[2], 1.0)
for pixel_poly in uv_polygon_to_pixel_polygons(uv_poly, texture_width, texture_height):
patch_shapes.append((pixel_poly, rgba))
fill_polygon(image, pixels, texture_width, texture_height, pixel_poly, rgba)
for i, j, uv_poly in geometry['patches']:
color = patch_colors[j, i]
rgba = (color[0], color[1], color[2], 1.0)
pixel_poly = pixel_polygon(uv_poly)
if len(pixel_poly) < 3:
continue
patch_shapes.append((pixel_poly, rgba))
fill_polygon(image, pixels, texture_width, texture_height, pixel_poly, rgba)
cross_shapes = []
if uv_centre is not None:
@@ -1608,13 +1596,17 @@ def main():
if config["seed"] is not None:
np.random.seed(config["seed"])
print(f"Using colour seed {config['seed']}")
# One RNG snapshot is replayed for every target, so the same physical patch
# keeps the same colour in each UV layout (and reruns reproduce when seeded).
rng_state = np.random.get_state()
# Both targets share the very same cell table and visibility-clipped fragments.
patch_colors = precompute_cell_colors(config['nx'], config['ny'], config['color_r'])
start_time = time.time()
try:
geometry = build_patch_geometry(
obj, cam, scene, depsgraph, [name for name, _, _ in targets],
config['nx'], config['ny'], config['edge_sample_grid'])
except Exception as exc:
print(f'ERROR: patch geometry failed: {exc}', file=sys.stderr)
return 1
for uv_map, artifacts, uv_layer in targets:
np.random.set_state(rng_state)
uv_centre = _uv_centre(uv_layer) if config["cross_enabled"] else None
print(f"--- UV map {uv_map!r}")
try:
@@ -1634,6 +1626,7 @@ def main():
edge_sample_grid=config["edge_sample_grid"],
uv_layer=uv_layer,
image_name=f"PatchTexture_{uv_map}",
patch_colors=patch_colors, geometry=geometry[uv_map],
)
except Exception as exc:
print(