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
|
## 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. 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
|
## the midline band between the ankles, which has no correct pose either
|
||||||
## way.
|
## 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
|
## 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
|
||||||
|
|
||||||
@@ -126,12 +139,13 @@ 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, 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]
|
snapped += r[0]
|
||||||
removed += r[1]
|
removed += r[1]
|
||||||
surfaces.append({
|
surfaces.append({
|
||||||
@@ -153,6 +167,18 @@ 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 = []
|
||||||
@@ -197,7 +223,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, knee: float,
|
torso_bone: Array, hips_slot: int, 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]
|
||||||
@@ -290,7 +316,63 @@ static func _repair_surface(arrays: Array, side: PackedInt32Array,
|
|||||||
weights[v * per + k] /= sum
|
weights[v * per + k] /= sum
|
||||||
snapped += 1
|
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
|
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