Feat/outline thickness and tp weapon hold #22
@@ -32,13 +32,27 @@ class_name SkinLegRepair
|
||||
## leg's own bone chain, NOT by height or by surface name: a thigh vertex
|
||||
## hugs its bone, while a skirt vertex hangs well clear of both and is
|
||||
## left blended, which is what lets a skirt drape across both legs.
|
||||
## 3. Drop any triangle still spanning the two legs below the knee. Those are
|
||||
## 3. Cap how much the TORSO owns a leg vertex. The top of the thigh is
|
||||
## weighted between the hips and the thigh; at a wide stride, linear-blend
|
||||
## skinning averages the near-static hips against a thigh swung 60 degrees
|
||||
## out, and the top of the leg flattens into a wedge. That is the hip
|
||||
## collapse visible from the side while running and jumping, and it is the
|
||||
## worst number the measurement reports (cross-section 0.85 at the hip
|
||||
## 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
|
||||
## the midline band between the ankles, which has no correct pose either
|
||||
## way.
|
||||
##
|
||||
## Measure with debug/limb_deform_check.gd.
|
||||
|
||||
const LEG_BONE_HINTS := ["thigh", "shin", "foot", "toe"]
|
||||
## Bones that belong to the torso, not the leg.
|
||||
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
|
||||
## Ignore influences below this — they are rounding, not real weighting.
|
||||
const EPSILON := 0.005
|
||||
|
||||
@@ -111,12 +125,13 @@ static func _repair_mesh(mi: MeshInstance3D, skeleton: Skeleton3D, knee: float,
|
||||
chain_l: PackedVector3Array, chain_r: PackedVector3Array,
|
||||
limb_radius: float) -> Array:
|
||||
var side := _side_map(mi.skin, skeleton)
|
||||
var torso_bone := _torso_map(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, 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({
|
||||
@@ -138,6 +153,23 @@ static func _repair_mesh(mi: MeshInstance3D, skeleton: Skeleton3D, knee: float,
|
||||
return [snapped, removed]
|
||||
|
||||
|
||||
## 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 = []
|
||||
out.resize(skin.get_bind_count())
|
||||
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 ""
|
||||
out[b] = false
|
||||
for hint in TORSO_BONE_HINTS:
|
||||
if n.findn(hint) != -1:
|
||||
out[b] = true
|
||||
break
|
||||
return out
|
||||
|
||||
|
||||
## -1 left leg, +1 right leg, 0 anything else — keyed by SKIN BIND index, which
|
||||
## is what ARRAY_BONES stores (not the skeleton's bone index).
|
||||
static func _side_map(skin: Skin, skeleton: Skeleton3D) -> PackedInt32Array:
|
||||
@@ -164,7 +196,8 @@ static func _side_map(skin: Skin, skeleton: Skeleton3D) -> PackedInt32Array:
|
||||
return out
|
||||
|
||||
|
||||
static func _repair_surface(arrays: Array, side: PackedInt32Array, knee: float,
|
||||
static func _repair_surface(arrays: Array, side: PackedInt32Array,
|
||||
torso_bone: Array, knee: float,
|
||||
chain_l: PackedVector3Array, chain_r: PackedVector3Array,
|
||||
limb_radius: float) -> Array:
|
||||
var verts: PackedVector3Array = arrays[Mesh.ARRAY_VERTEX]
|
||||
@@ -220,7 +253,44 @@ static func _repair_surface(arrays: Array, side: PackedInt32Array, knee: float,
|
||||
weights[v * per + k] /= total
|
||||
snapped += 1
|
||||
|
||||
# Step 2 — drop triangles that still span the legs below the knee.
|
||||
# Step 2 — stop the torso holding the top of the leg back. Only vertices that
|
||||
# actually belong to a limb are touched, so the skirt keeps swinging from
|
||||
# the hips as it should.
|
||||
for v in verts.size():
|
||||
if vside[v] == 0:
|
||||
continue
|
||||
var chain: PackedVector3Array = chain_l if vside[v] == -1 else chain_r
|
||||
if _dist_to_chain(verts[v], chain) > limb_radius:
|
||||
continue
|
||||
var torso := 0.0
|
||||
var dom_k := -1
|
||||
var dom_w := 0.0
|
||||
for k in per:
|
||||
var b: int = bones[v * per + k]
|
||||
var w: float = weights[v * per + k]
|
||||
if torso_bone[b]:
|
||||
torso += w
|
||||
elif side[b] != 0 and w > dom_w:
|
||||
dom_w = w
|
||||
dom_k = k
|
||||
if torso <= MAX_TORSO or dom_k < 0:
|
||||
continue
|
||||
# Scale the torso influence down to the cap and hand the rest to the
|
||||
# leg bone this vertex already follows.
|
||||
var keep_scale: float = MAX_TORSO / torso
|
||||
for k in per:
|
||||
if torso_bone[bones[v * per + k]]:
|
||||
weights[v * per + k] *= keep_scale
|
||||
weights[v * per + dom_k] += torso - MAX_TORSO
|
||||
var sum := 0.0
|
||||
for k in per:
|
||||
sum += weights[v * per + k]
|
||||
if sum > 0.0:
|
||||
for k in per:
|
||||
weights[v * per + k] /= sum
|
||||
snapped += 1
|
||||
|
||||
# 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