Tattoo test bed: Quaternius v3-v6 bakes + Lena pipeline (bake, de-mirror, re-UV, viewers)
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>
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
# 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}
|
||||
Reference in New Issue
Block a user