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,86 @@
|
||||
# Measures left/right UV overlap for arms and legs (mirror-mapping check).
|
||||
extends SceneTree
|
||||
|
||||
const GLB_PATH := "C:/Users/CAN/tinqs-ltd/tattoo-test/lena_demirrored.glb"
|
||||
const GRID := 256
|
||||
|
||||
var skel: Skeleton3D
|
||||
var uvs: PackedVector2Array
|
||||
var vbones: PackedInt32Array
|
||||
var vweights
|
||||
var bind_bone: PackedInt32Array
|
||||
var nverts: int
|
||||
var influences: int
|
||||
|
||||
func _init() -> void:
|
||||
var doc := GLTFDocument.new()
|
||||
var state := GLTFState.new()
|
||||
if doc.append_from_file(GLB_PATH, state) != OK:
|
||||
quit(1)
|
||||
return
|
||||
var scene := doc.generate_scene(state)
|
||||
var body: Node = null
|
||||
var stack: Array = [scene]
|
||||
while not stack.is_empty():
|
||||
var n: Node = stack.pop_back()
|
||||
if n is Skeleton3D and skel == null:
|
||||
skel = n
|
||||
if (n is MeshInstance3D or n.get_class() == "ImporterMeshInstance3D") \
|
||||
and String(n.name).to_lower().contains("lena"):
|
||||
body = n
|
||||
for c in n.get_children():
|
||||
stack.push_back(c)
|
||||
var mesh = body.mesh
|
||||
if mesh is ImporterMesh:
|
||||
mesh = mesh.get_mesh()
|
||||
var arrays: Array = mesh.surface_get_arrays(0)
|
||||
uvs = arrays[Mesh.ARRAY_TEX_UV]
|
||||
vbones = arrays[Mesh.ARRAY_BONES]
|
||||
vweights = arrays[Mesh.ARRAY_WEIGHTS]
|
||||
nverts = (arrays[Mesh.ARRAY_VERTEX] as PackedVector3Array).size()
|
||||
influences = int(float(vbones.size()) / float(nverts))
|
||||
|
||||
var skin: Skin = body.skin
|
||||
bind_bone = PackedInt32Array()
|
||||
for i in skin.get_bind_count():
|
||||
var b := skin.get_bind_bone(i)
|
||||
if b < 0:
|
||||
b = skel.find_bone(skin.get_bind_name(i))
|
||||
bind_bone.append(b)
|
||||
|
||||
_overlap("ARM", ["upperarm_r", "lowerarm_r", "hand_r"], ["upperarm_l", "lowerarm_l", "hand_l"])
|
||||
_overlap("LEG", ["thigh_r", "calf_r", "foot_r"], ["thigh_l", "calf_l", "foot_l"])
|
||||
quit()
|
||||
|
||||
func _mask(bones: Array) -> PackedByteArray:
|
||||
var ids := {}
|
||||
for bn in bones:
|
||||
ids[skel.find_bone(bn)] = true
|
||||
var binds := {}
|
||||
for i in bind_bone.size():
|
||||
if ids.has(bind_bone[i]):
|
||||
binds[i] = true
|
||||
var mask := PackedByteArray()
|
||||
mask.resize(GRID * GRID)
|
||||
for v in nverts:
|
||||
var w := 0.0
|
||||
for k in influences:
|
||||
if binds.has(vbones[v * influences + k]):
|
||||
w += vweights[v * influences + k]
|
||||
if w > 0.3:
|
||||
var gx := clampi(int(uvs[v].x * GRID), 0, GRID - 1)
|
||||
var gy := clampi(int(uvs[v].y * GRID), 0, GRID - 1)
|
||||
mask[gy * GRID + gx] = 1
|
||||
return mask
|
||||
|
||||
func _overlap(tag: String, bones_r: Array, bones_l: Array) -> void:
|
||||
var r := _mask(bones_r)
|
||||
var l := _mask(bones_l)
|
||||
var cr := 0
|
||||
var ov := 0
|
||||
for i in GRID * GRID:
|
||||
if r[i] == 1:
|
||||
cr += 1
|
||||
if l[i] == 1:
|
||||
ov += 1
|
||||
print("%s UV overlap: %d/%d cells (%.0f%%)" % [tag, ov, cr, 100.0 * ov / maxf(cr, 1)])
|
||||
Reference in New Issue
Block a user