Feat/outline thickness and tp weapon hold #22
@@ -1,6 +1,26 @@
|
||||
extends Object
|
||||
class_name SkinLegRepair
|
||||
|
||||
## Keeps the HUMANOID rigid while letting the CLOTHES flow.
|
||||
##
|
||||
## That split is the whole design rule here, and it decides what every pass below
|
||||
## is allowed to touch:
|
||||
##
|
||||
## * LIMB vertices — anything hugging a leg's own bone chain: skin, stockings,
|
||||
## boots — are cleaned up hard. They belong to one leg, they follow it, and
|
||||
## they hold their shape. Deforming here is a bug.
|
||||
## * DRAPE vertices — the skirt and anything else hanging clear of both leg
|
||||
## chains — are LEFT ALONE, so they keep swinging with the body.
|
||||
##
|
||||
## DO NOT stiffen the skirt onto the hips. It was tried (to stop it flattening at
|
||||
## a wide stride) and reverted: it makes the clothes read as a rigid shell, which
|
||||
## is worse than the flattening it fixed. Cloth is supposed to move.
|
||||
##
|
||||
## Current split, from debug/limb_deform_check.gd: the body holds 0.98 of its
|
||||
## cross-section at the knee, while the skirt is free at 0.88 — rigid limbs,
|
||||
## moving cloth.
|
||||
##
|
||||
##
|
||||
## Stops below-the-knee geometry being dragged by BOTH legs at once.
|
||||
##
|
||||
## Taila's boots are skinned with weights that bleed across the centre line:
|
||||
@@ -41,17 +61,7 @@ class_name SkinLegRepair
|
||||
## against 0.97-0.99 at the knee). Leg vertices keep at most MAX_TORSO of
|
||||
## hips/spine influence, and the excess goes to the leg bone that already
|
||||
## dominates them, so the thigh follows its own bone.
|
||||
## 4. Hang DRAPING CLOTH from the hips instead of the legs. The skirt is
|
||||
## weighted to both thighs so it can follow them, but at a running or
|
||||
## jumping stride the two thighs pull it in opposite directions and it
|
||||
## collapses flat against the body — its flared volume vanishes and the
|
||||
## whole lower body reads as squashed from the side. Cloth that is clear of
|
||||
## both leg bone chains keeps at most MAX_DRAPE_LEG of leg influence, and
|
||||
## the rest goes to the hips, so the skirt holds its shape and swings as a
|
||||
## unit. This is the "model the skirt differently from the legs" case, and
|
||||
## it is separate from the limb work above: LIMB vertices are snapped to
|
||||
## one leg, DRAPE vertices are taken off the legs almost entirely.
|
||||
## 5. Drop any triangle still spanning the two legs below the knee. Those are
|
||||
## 4. Drop any triangle still spanning the two legs below the knee. Those are
|
||||
## the midline band between the ankles, which has no correct pose either
|
||||
## way.
|
||||
##
|
||||
@@ -63,9 +73,6 @@ const TORSO_BONE_HINTS := ["hips", "spine", "pelvis"]
|
||||
## The most torso influence a leg vertex may keep. Some is wanted — it is what
|
||||
## rounds the hip off — but past this the thigh stops following its own bone.
|
||||
const MAX_TORSO := 0.15
|
||||
## The most LEG influence a draping cloth vertex may keep. A little lets the
|
||||
## skirt react to the legs; more than this and a split stride tears it flat.
|
||||
const MAX_DRAPE_LEG := 0.20
|
||||
## Ignore influences below this — they are rounding, not real weighting.
|
||||
const EPSILON := 0.005
|
||||
|
||||
@@ -139,13 +146,12 @@ static func _repair_mesh(mi: MeshInstance3D, skeleton: Skeleton3D, knee: float,
|
||||
limb_radius: float) -> Array:
|
||||
var side := _side_map(mi.skin, skeleton)
|
||||
var torso_bone := _torso_map(mi.skin, skeleton)
|
||||
var hips_slot := _hips_bind(mi.skin, skeleton)
|
||||
var surfaces: Array = []
|
||||
var snapped := 0
|
||||
var removed := 0
|
||||
for s in range(mi.mesh.get_surface_count()):
|
||||
var arrays: Array = mi.mesh.surface_get_arrays(s)
|
||||
var r := _repair_surface(arrays, side, torso_bone, hips_slot, knee, chain_l, chain_r, limb_radius)
|
||||
var r := _repair_surface(arrays, side, torso_bone, knee, chain_l, chain_r, limb_radius)
|
||||
snapped += r[0]
|
||||
removed += r[1]
|
||||
surfaces.append({
|
||||
@@ -167,18 +173,6 @@ static func _repair_mesh(mi: MeshInstance3D, skeleton: Skeleton3D, knee: float,
|
||||
return [snapped, removed]
|
||||
|
||||
|
||||
## SKIN BIND index of the hips, which is what draping cloth should hang from.
|
||||
static func _hips_bind(skin: Skin, skeleton: Skeleton3D) -> int:
|
||||
for b in skin.get_bind_count():
|
||||
var n := skin.get_bind_name(b)
|
||||
if n == "":
|
||||
var bone := skin.get_bind_bone(b)
|
||||
n = skeleton.get_bone_name(bone) if bone >= 0 else ""
|
||||
if n.findn("hips") != -1 or n.findn("pelvis") != -1:
|
||||
return b
|
||||
return -1
|
||||
|
||||
|
||||
## Is each bind a torso bone? Keyed by SKIN BIND index, like _side_map.
|
||||
static func _torso_map(skin: Skin, skeleton: Skeleton3D) -> Array:
|
||||
var out: Array = []
|
||||
@@ -223,7 +217,7 @@ static func _side_map(skin: Skin, skeleton: Skeleton3D) -> PackedInt32Array:
|
||||
|
||||
|
||||
static func _repair_surface(arrays: Array, side: PackedInt32Array,
|
||||
torso_bone: Array, hips_slot: int, knee: float,
|
||||
torso_bone: Array, knee: float,
|
||||
chain_l: PackedVector3Array, chain_r: PackedVector3Array,
|
||||
limb_radius: float) -> Array:
|
||||
var verts: PackedVector3Array = arrays[Mesh.ARRAY_VERTEX]
|
||||
@@ -316,63 +310,7 @@ static func _repair_surface(arrays: Array, side: PackedInt32Array,
|
||||
weights[v * per + k] /= sum
|
||||
snapped += 1
|
||||
|
||||
# Step 3 — draping cloth hangs from the hips. A skirt pulled by both thighs
|
||||
# at a split stride collapses flat; taking it off the legs keeps its volume.
|
||||
if hips_slot >= 0:
|
||||
for v in verts.size():
|
||||
var leg_total := 0.0
|
||||
for k in per:
|
||||
if side[bones[v * per + k]] != 0:
|
||||
leg_total += weights[v * per + k]
|
||||
if leg_total <= MAX_DRAPE_LEG:
|
||||
continue
|
||||
# ABOVE THE KNEE ONLY. Distance from the bone chain alone is not a
|
||||
# safe test down here: chunky footwear sits further from the ankle
|
||||
# than the limb radius, so a boot reads as "drape" and taking its
|
||||
# leg weight away makes it trail off the hips. A skirt hem is above
|
||||
# the knee; boots and stockings are not.
|
||||
if verts[v].y <= knee:
|
||||
continue
|
||||
# Limb vertices were already handled above; this is only for cloth
|
||||
# that hangs clear of BOTH leg bone chains.
|
||||
if minf(_dist_to_chain(verts[v], chain_l),
|
||||
_dist_to_chain(verts[v], chain_r)) <= limb_radius:
|
||||
continue
|
||||
var k_hips := -1
|
||||
for k in per:
|
||||
if bones[v * per + k] == hips_slot:
|
||||
k_hips = k
|
||||
break
|
||||
if k_hips < 0:
|
||||
# No hips influence yet: take over the weakest leg slot.
|
||||
var weakest := -1
|
||||
var weakest_w := 2.0
|
||||
for k in per:
|
||||
if side[bones[v * per + k]] != 0 and weights[v * per + k] < weakest_w:
|
||||
weakest_w = weights[v * per + k]
|
||||
weakest = k
|
||||
if weakest < 0:
|
||||
continue
|
||||
bones[v * per + weakest] = hips_slot
|
||||
weights[v * per + weakest] = 0.0
|
||||
k_hips = weakest
|
||||
leg_total -= weakest_w
|
||||
if leg_total <= MAX_DRAPE_LEG:
|
||||
continue
|
||||
var keep: float = MAX_DRAPE_LEG / leg_total
|
||||
for k in per:
|
||||
if side[bones[v * per + k]] != 0:
|
||||
weights[v * per + k] *= keep
|
||||
weights[v * per + k_hips] += leg_total - MAX_DRAPE_LEG
|
||||
var total2 := 0.0
|
||||
for k in per:
|
||||
total2 += weights[v * per + k]
|
||||
if total2 > 0.0:
|
||||
for k in per:
|
||||
weights[v * per + k] /= total2
|
||||
snapped += 1
|
||||
|
||||
# Step 4 — drop triangles that still span the legs below the knee.
|
||||
# Step 3 — drop triangles that still span the legs below the knee.
|
||||
var removed := 0
|
||||
if not idx.is_empty():
|
||||
var keep_idx := PackedInt32Array()
|
||||
|
||||
Reference in New Issue
Block a user