Feat/outline thickness and tp weapon hold #22
@@ -41,7 +41,17 @@ 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. Drop any triangle still spanning the two legs below the knee. Those are
|
||||
## 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
|
||||
## the midline band between the ankles, which has no correct pose either
|
||||
## way.
|
||||
##
|
||||
@@ -53,6 +63,9 @@ 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
|
||||
|
||||
@@ -126,12 +139,13 @@ 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, knee, chain_l, chain_r, limb_radius)
|
||||
var r := _repair_surface(arrays, side, torso_bone, hips_slot, knee, chain_l, chain_r, limb_radius)
|
||||
snapped += r[0]
|
||||
removed += r[1]
|
||||
surfaces.append({
|
||||
@@ -153,6 +167,18 @@ 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 = []
|
||||
@@ -197,7 +223,7 @@ static func _side_map(skin: Skin, skeleton: Skeleton3D) -> PackedInt32Array:
|
||||
|
||||
|
||||
static func _repair_surface(arrays: Array, side: PackedInt32Array,
|
||||
torso_bone: Array, knee: float,
|
||||
torso_bone: Array, hips_slot: int, knee: float,
|
||||
chain_l: PackedVector3Array, chain_r: PackedVector3Array,
|
||||
limb_radius: float) -> Array:
|
||||
var verts: PackedVector3Array = arrays[Mesh.ARRAY_VERTEX]
|
||||
@@ -290,7 +316,63 @@ static func _repair_surface(arrays: Array, side: PackedInt32Array,
|
||||
weights[v * per + k] /= sum
|
||||
snapped += 1
|
||||
|
||||
# Step 3 — drop triangles that still span the legs below the knee.
|
||||
# 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.
|
||||
var removed := 0
|
||||
if not idx.is_empty():
|
||||
var keep_idx := PackedInt32Array()
|
||||
|
||||
Reference in New Issue
Block a user