Files
modbison/scripts/tools/avatar_preview.gd
daddy 3fb45f0ef2
checkpoint / checkpoint (push) Successful in 1m47s
Avatar: tune Roblox proportions + walk cycle from real rendered screenshots
Now that cloudbison can render (Radeon GPU via Xvfb, or llvmpipe software),
added scripts/tools/screenshot_rig.gd + scenes/dev/ so a scene can be booted,
run a few frames, and its rendered frame saved to PNG -- caught three things
the headless smoke never could:

- limbs floated detached from the torso: widened the torso and overlapped the
  arm/leg boxes into it, and pulled limb depth in behind the torso front so
  they stop z-fighting.
- proportions were scrawny: reworked to Roblox R6 ratios (2-stud torso, 2-stud
  arms/legs, 1-stud head) with the head clearly pegged on top.
- the walk swing was a 40-deg lunge: STRIDE 0.7->0.42 rad, separate 0.32 arm
  swing, KNEE 1.0->0.5. Reads as a walk now, not a crouch.

test_level lighting was near-black on the shaded side (ambient was disabled) --
colour ambient at 0.8 + a stronger key light, so the avatar reads in-world.

avatar_preview.tscn (AGP_PREVIEW_WALK=1) renders the assembled avatar walking
in place for quick visual checks.

Verified: avatar smoke 14/14 source + exported pack; fresh-clone boot PASS;
eyeballed in rendered screenshots (game scene + walk sequence).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015jtxAiu5dAW4WxCNdGkrkJ
2026-09-06 14:06:46 +00:00

70 lines
2.3 KiB
GDScript

extends Node3D
## Dev-only. Builds the current avatar via AvatarSystem, stands it on a small
## stage with even lighting, and (optionally) drives the procedural walk cycle
## so screenshots can verify both the silhouette and that the limbs move.
##
## Env:
## AGP_PREVIEW_WALK "1" -> feed the limb animator a walking speed
## AGP_PREVIEW_SPIN degrees/sec turntable (default 0)
## Used together with screenshot_rig.gd (AGP_SHOT_SCENE=res://scenes/dev/avatar_preview.tscn,
## AGP_SHOT_SEQ="6:10" to catch the cycle at several phases).
const LimbAnimator := preload("res://scripts/avatar/limb_animator.gd")
var _avatar: Node3D = null
var _anim: Node = null
var _spin_deg := 0.0
func _ready() -> void:
_spin_deg = float(OS.get_environment("AGP_PREVIEW_SPIN"))
var env := WorldEnvironment.new()
var e := Environment.new()
e.background_mode = Environment.BG_COLOR
e.background_color = Color(0.16, 0.18, 0.22)
e.ambient_light_source = Environment.AMBIENT_SOURCE_COLOR
e.ambient_light_color = Color(0.6, 0.64, 0.7)
e.ambient_light_energy = 0.6
env.environment = e
add_child(env)
var key := DirectionalLight3D.new()
key.rotation = Vector3(-0.9, -0.6, 0.0)
key.light_energy = 1.1
add_child(key)
var ground := MeshInstance3D.new()
var pm := PlaneMesh.new()
pm.size = Vector2(6, 6)
ground.mesh = pm
var gmat := StandardMaterial3D.new()
gmat.albedo_color = Color(0.3, 0.32, 0.36)
ground.material_override = gmat
add_child(ground)
var cam := Camera3D.new()
cam.position = Vector3(2.1, 1.15, 2.6)
cam.look_at_from_position(cam.position, Vector3(0.0, 1.0, 0.0), Vector3.UP)
cam.current = true
add_child(cam)
var avatars := get_node_or_null("/root/AvatarSystem")
if avatars != null and avatars.has_method("build_current_avatar"):
_avatar = avatars.build_current_avatar()
if _avatar != null:
add_child(_avatar)
var skel := _avatar.find_child("Skeleton3D", true, false)
if skel is Skeleton3D and OS.get_environment("AGP_PREVIEW_WALK") == "1":
_anim = LimbAnimator.new()
add_child(_anim)
_anim.call("setup", skel)
_anim.set("speed", 4.2)
else:
push_warning("avatar_preview: AvatarSystem.build_current_avatar() returned null")
func _process(delta: float) -> void:
if _avatar != null and _spin_deg != 0.0:
_avatar.rotate_y(deg_to_rad(_spin_deg) * delta)