Feat/outline thickness and tp weapon hold #22
@@ -1,6 +1,26 @@
|
|||||||
extends Object
|
extends Object
|
||||||
class_name SkinLegRepair
|
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.
|
## 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:
|
## 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
|
## 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
|
## hips/spine influence, and the excess goes to the leg bone that already
|
||||||
## dominates them, so the thigh follows its own bone.
|
## dominates them, so the thigh follows its own bone.
|
||||||
## 4. Hang DRAPING CLOTH from the hips instead of the legs. The skirt is
|
## 4. Drop any triangle still spanning the two legs below the knee. Those are
|
||||||
## 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
|
## the midline band between the ankles, which has no correct pose either
|
||||||
## way.
|
## 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
|
## 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.
|
## rounds the hip off — but past this the thigh stops following its own bone.
|
||||||
const MAX_TORSO := 0.15
|
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.
|
## Ignore influences below this — they are rounding, not real weighting.
|
||||||
const EPSILON := 0.005
|
const EPSILON := 0.005
|
||||||
|
|
||||||
@@ -139,13 +146,12 @@ static func _repair_mesh(mi: MeshInstance3D, skeleton: Skeleton3D, knee: float,
|
|||||||
limb_radius: float) -> Array:
|
limb_radius: float) -> Array:
|
||||||
var side := _side_map(mi.skin, skeleton)
|
var side := _side_map(mi.skin, skeleton)
|
||||||
var torso_bone := _torso_map(mi.skin, skeleton)
|
var torso_bone := _torso_map(mi.skin, skeleton)
|
||||||
var hips_slot := _hips_bind(mi.skin, skeleton)
|
|
||||||
var surfaces: Array = []
|
var surfaces: Array = []
|
||||||
var snapped := 0
|
var snapped := 0
|
||||||
var removed := 0
|
var removed := 0
|
||||||
for s in range(mi.mesh.get_surface_count()):
|
for s in range(mi.mesh.get_surface_count()):
|
||||||
var arrays: Array = mi.mesh.surface_get_arrays(s)
|
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]
|
snapped += r[0]
|
||||||
removed += r[1]
|
removed += r[1]
|
||||||
surfaces.append({
|
surfaces.append({
|
||||||
@@ -167,18 +173,6 @@ static func _repair_mesh(mi: MeshInstance3D, skeleton: Skeleton3D, knee: float,
|
|||||||
return [snapped, removed]
|
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.
|
## Is each bind a torso bone? Keyed by SKIN BIND index, like _side_map.
|
||||||
static func _torso_map(skin: Skin, skeleton: Skeleton3D) -> Array:
|
static func _torso_map(skin: Skin, skeleton: Skeleton3D) -> Array:
|
||||||
var out: 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,
|
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,
|
chain_l: PackedVector3Array, chain_r: PackedVector3Array,
|
||||||
limb_radius: float) -> Array:
|
limb_radius: float) -> Array:
|
||||||
var verts: PackedVector3Array = arrays[Mesh.ARRAY_VERTEX]
|
var verts: PackedVector3Array = arrays[Mesh.ARRAY_VERTEX]
|
||||||
@@ -316,63 +310,7 @@ static func _repair_surface(arrays: Array, side: PackedInt32Array,
|
|||||||
weights[v * per + k] /= sum
|
weights[v * per + k] /= sum
|
||||||
snapped += 1
|
snapped += 1
|
||||||
|
|
||||||
# Step 3 — draping cloth hangs from the hips. A skirt pulled by both thighs
|
# Step 3 — drop triangles that still span the legs below the knee.
|
||||||
# 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
|
var removed := 0
|
||||||
if not idx.is_empty():
|
if not idx.is_empty():
|
||||||
var keep_idx := PackedInt32Array()
|
var keep_idx := PackedInt32Array()
|
||||||
|
|||||||
Reference in New Issue
Block a user