Avatar customization: skin tone, head swap, and hair now actually apply
checkpoint / checkpoint (push) Successful in 1m45s
checkpoint / checkpoint (push) Successful in 1m45s
Three dead controls on the customization screen: - Skin tone slider did nothing: neither the visualizer nor AvatarSystem ever read loadout.skin_tone. Added AvatarLoader.apply_skin_tone() -- it retints the base body's skin surfaces (matched by the placeholder skin albedo, i.e. head + arms) -- and both build paths call it. Slider now maps across a deep-to-pale skin range. - "Cyber Head" did nothing: its catalog entry was type "body_part", which load_attachment() rejects, and the placeholder body is one mesh so there was nothing to hide either. Retyped it as an attachment -> it mounts on the Head bone as a cyber visor. - Mohawk / ponytail sat inside the head: the Head and HeadTop bones were well below the visual head (bone at 1.58, head crown at ~2.03). Moved Head to the head's centre and HeadTop just above the crown (mesh is rigidly bound, so it doesn't move), and retuned the hair/visor/glasses meshes and offsets to match. Verified: avatar smoke 14/14 (source + pack), fresh-clone, 2p multiplayer, and drove the real customization screen in rendered screenshots (equip head+mohawk+mid skin, then ponytail+glasses+pale skin). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015jtxAiu5dAW4WxCNdGkrkJ
This commit is contained in:
@@ -23,6 +23,11 @@ const AvatarCatalog := preload("res://scripts/avatar/avatar_catalog.gd")
|
||||
## Maximum number of attachments allowed in the accessory slot.
|
||||
const MAX_ACCESSORY_COUNT := 8
|
||||
|
||||
## The skin albedo baked into the placeholder base body by
|
||||
## gen_placeholder_avatars.gd. apply_skin_tone() finds the mesh surfaces
|
||||
## painted this colour (head + arms) and retints them.
|
||||
const PLACEHOLDER_SKIN_ALBEDO := Color(0.96, 0.80, 0.19)
|
||||
|
||||
## Skeleton bone names that form the canonical rig (§3.1).
|
||||
const CANONICAL_BONES := [
|
||||
"Root",
|
||||
@@ -239,6 +244,33 @@ func set_base_mesh_visible(slot_id: String, visible: bool) -> void:
|
||||
mesh.visible = visible
|
||||
|
||||
|
||||
## Retint the base body's skin surfaces (the ones the generator painted
|
||||
## PLACEHOLDER_SKIN_ALBEDO -- head + arms) to [param color]. A no-op if the
|
||||
## colour is unset (transparent). Real segmented bodies will do this per mesh.
|
||||
func apply_skin_tone(color: Color) -> void:
|
||||
if color.a <= 0.0:
|
||||
return
|
||||
if avatar_node == null or not is_instance_valid(avatar_node):
|
||||
return
|
||||
var skel := avatar_node.get_node_or_null("Skeleton3D") as Skeleton3D
|
||||
if skel == null:
|
||||
return
|
||||
for child in skel.get_children():
|
||||
if not (child is MeshInstance3D):
|
||||
continue
|
||||
var mi := child as MeshInstance3D
|
||||
if mi.mesh == null:
|
||||
continue
|
||||
for s in mi.mesh.get_surface_count():
|
||||
var mat := mi.get_active_material(s)
|
||||
if mat is BaseMaterial3D and \
|
||||
(mat as BaseMaterial3D).albedo_color.is_equal_approx(PLACEHOLDER_SKIN_ALBEDO):
|
||||
var tinted := StandardMaterial3D.new()
|
||||
tinted.albedo_color = color
|
||||
tinted.roughness = 0.9
|
||||
mi.set_surface_override_material(s, tinted)
|
||||
|
||||
|
||||
## Get the current loadout as a dictionary (for serialization).
|
||||
func get_current_loadout() -> Dictionary:
|
||||
var loadout: Dictionary = {
|
||||
|
||||
Reference in New Issue
Block a user