ce4c4e7d13
Session 2026-07-23/24 additions on top of the existing Quaternius bed: - bake_lena.gd: 6-piece bake on QuatSkin Lena (chiefs-mark/iron-spine arms, voyagers/storm-bearer legs, star-eye chest, hearth-warmth back); global-axis wrap frame + 3cm junction blend kill the elbow split line - demirror_lena.gd: tri-level split of mirror-shared limb UVs (arms 100%->0%, legs 99%->5% crotch-only residual) - reuv_lena.gd: Quaternius-style semantic re-UV (16 islands, cylinder limbs + cylinder head/torso with planar caps) + texture transfer; fixes armpit/chin/ cheek planar smears; single 4096 atlas, per-side tattoos native - main_lena / anim_lena viewers (18 UAL clips, U light toggle) + probes + shots Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
66 lines
2.4 KiB
GDScript
66 lines
2.4 KiB
GDScript
# Builds 1024² gen canvases from the per-part UV masks: the island silhouette
|
|
# (padded bbox, aspect-true, centered) as parchment white on black. The exact
|
|
# same transform is recomputed in composite.gd to map results back into UV space.
|
|
# Run headless:
|
|
# tinqs.console.exe --headless --path tattoo-test -s res://prep.gd
|
|
extends SceneTree
|
|
|
|
const MASK_DIR := "C:/Users/CAN/tinqs-ltd/tattoo-test/masks/"
|
|
const OUT_DIR := "C:/Users/CAN/tinqs-ltd/tattoo-test/gen-v4/"
|
|
const PARTS := ["arm_r", "arm_l", "leg_r", "leg_l", "chest", "back"]
|
|
const CANVAS := 1024
|
|
const MARGIN := 48 # silhouette keeps this distance from canvas edges
|
|
const PAD := 24 # px padding around the mask bbox in UV space
|
|
const PARCHMENT := Color(0.97, 0.94, 0.88)
|
|
|
|
func _init() -> void:
|
|
DirAccess.make_dir_recursive_absolute(OUT_DIR)
|
|
for part: String in PARTS:
|
|
var mask := Image.load_from_file(MASK_DIR + "mask_" + part + ".png")
|
|
var t := transform_for(mask)
|
|
var rect: Rect2i = t["rect"]
|
|
var s: float = t["scale"]
|
|
var off: Vector2 = t["offset"]
|
|
var canvas := Image.create(CANVAS, CANVAS, false, Image.FORMAT_RGBA8)
|
|
canvas.fill(Color(0, 0, 0))
|
|
for cy in CANVAS:
|
|
for cx in CANVAS:
|
|
var mx := int((cx - off.x) / s) + rect.position.x
|
|
var my := int((cy - off.y) / s) + rect.position.y
|
|
if mx < rect.position.x or my < rect.position.y \
|
|
or mx >= rect.end.x or my >= rect.end.y:
|
|
continue
|
|
if mask.get_pixel(mx, my).r > 0.5:
|
|
canvas.set_pixel(cx, cy, PARCHMENT)
|
|
var path := OUT_DIR + "canvas_" + part + ".png"
|
|
canvas.save_png(path)
|
|
print("CANVAS %s rect=%s scale=%.4f offset=%.1f,%.1f saved=%s" %
|
|
[part, rect, s, off.x, off.y, path])
|
|
quit()
|
|
|
|
# shared with composite.gd — keep in sync
|
|
static func transform_for(mask: Image) -> Dictionary:
|
|
var size := mask.get_width()
|
|
var minx := size
|
|
var maxx := -1
|
|
var miny := size
|
|
var maxy := -1
|
|
for y in size:
|
|
for x in size:
|
|
if mask.get_pixel(x, y).r > 0.5:
|
|
minx = mini(minx, x)
|
|
maxx = maxi(maxx, x)
|
|
miny = mini(miny, y)
|
|
maxy = maxi(maxy, y)
|
|
var rect := Rect2i(
|
|
maxi(minx - PAD, 0), maxi(miny - PAD, 0),
|
|
mini(maxx + PAD + 1, size) - maxi(minx - PAD, 0),
|
|
mini(maxy + PAD + 1, size) - maxi(miny - PAD, 0))
|
|
var s := minf(
|
|
float(CANVAS - 2 * MARGIN) / rect.size.x,
|
|
float(CANVAS - 2 * MARGIN) / rect.size.y)
|
|
var off := Vector2(
|
|
(CANVAS - rect.size.x * s) * 0.5,
|
|
(CANVAS - rect.size.y * s) * 0.5)
|
|
return {"rect": rect, "scale": s, "offset": off}
|