Files
Papay-Shooter/characters/anime_mecha_kit.gd
T
2026-08-09 01:31:44 -04:00

1018 lines
37 KiB
GDScript

extends Node3D
class_name AnimeMechaKit
## High-detail, game-native anime mecha frame layered over an authored pilot.
##
## Each equipment group is built from many small mechanical forms, then merged
## into one ArrayMesh surface per material. The result keeps panel seams,
## vents, pistons, cabling, fasteners and nested thruster hardware without
## turning every visual detail into a draw call. Bone-rest correction makes the
## same frame fit Mixamo, Auto-Rig Pro and Rigify-style skeleton axes.
const ARMOR := 0
const ARMOR_ALT := 1
const FRAME := 2
const ACCENT := 3
const GLOW := 4
const METAL := 5
const THEMES := {
"sakura": {
"armor": Color("e8edf4"),
"armor_alt": Color("737d9e"),
"frame": Color("303852"),
"accent": Color("d95291"),
"glow": Color("ff9fd0"),
"metal": Color("171b2e"),
},
"akiba": {
"armor": Color("242a3a"),
"armor_alt": Color("dfe7ef"),
"frame": Color("111622"),
"accent": Color("20c9df"),
"glow": Color("ff45ba"),
"metal": Color("080b12"),
},
}
class Assembly:
var parent: Node3D
var surfaces: Dictionary = {}
var materials: Dictionary = {}
var part_count := 0
func _init(target: Node3D) -> void:
parent = target
func add(mesh: Mesh, transform: Transform3D, slot: int,
material: Material) -> void:
var surface: SurfaceTool = surfaces.get(slot)
if surface == null:
surface = SurfaceTool.new()
surfaces[slot] = surface
materials[slot] = material
surface.append_from(mesh, 0, transform)
part_count += 1
func commit(node_name: String) -> MeshInstance3D:
var combined := ArrayMesh.new()
var slots: Array = surfaces.keys()
slots.sort()
for slot in slots:
var surface: SurfaceTool = surfaces[slot]
surface.set_material(materials[slot])
surface.commit(combined)
var instance := MeshInstance3D.new()
instance.name = node_name
instance.mesh = combined
parent.add_child(instance)
return instance
var _skeleton: Skeleton3D
var _roles: Dictionary = {}
var _theme: Dictionary = {}
var _materials: Dictionary = {}
var _nozzles: Array[Node3D] = []
var _attachments: Array[BoneAttachment3D] = []
var _detail_parts := 0
var _helmet_progress := 0.0
var _helmet_target := 0.0
var _helmet_fx_left := 0.0
var _visor_left: MeshInstance3D
var _visor_right: MeshInstance3D
var _jaw_left: MeshInstance3D
var _jaw_right: MeshInstance3D
var _helmet_scan: MeshInstance3D
var _helmet_sweep: MeshInstance3D
var _helmet_light: OmniLight3D
var _helmet_fx_material: StandardMaterial3D
func setup(skeleton: Skeleton3D, roles: Dictionary, theme_name: String) -> void:
_skeleton = skeleton
_roles = roles
_theme = THEMES.get(theme_name, THEMES["sakura"])
_build_materials()
if theme_name == "sakura":
# The hero pilot is one continuous robotic second skin. Its thin shells
# follow the authored anatomy instead of hanging equipment boxes from it.
_build_second_skin_torso()
_build_second_skin_waist()
_build_second_skin_limbs()
_build_second_skin_nozzles()
_build_deployable_helmet()
else:
# Preserve the alternate skin while the hero design is being focused.
_build_torso()
_build_waist()
_build_limbs()
_build_nozzles()
set_process(true)
func _process(delta: float) -> void:
if not is_instance_valid(_visor_left):
return
var blend := 1.0 - exp(-12.0 * delta)
_helmet_progress = lerpf(_helmet_progress, _helmet_target, blend)
if absf(_helmet_progress - _helmet_target) < 0.001:
_helmet_progress = _helmet_target
_update_helmet_parts(_helmet_progress)
if _helmet_fx_left <= 0.0:
_helmet_scan.visible = false
_helmet_sweep.visible = false
_helmet_light.light_energy = 0.0
return
_helmet_fx_left = maxf(0.0, _helmet_fx_left - delta)
var phase := 1.0 - _helmet_fx_left / 0.38
var flash := sin(phase * PI)
_helmet_scan.visible = true
_helmet_scan.scale = Vector3.ONE * lerpf(0.55, 1.65, phase)
_helmet_scan.rotation.z = phase * PI * 0.65
_helmet_sweep.visible = true
_helmet_sweep.position.x = lerpf(-0.105, 0.105, phase)
_helmet_light.light_energy = 2.8 * flash
var alpha := flash * 0.82
_helmet_fx_material.albedo_color.a = alpha
func set_helmet_closed(closed: bool, immediate: bool = false) -> void:
var wanted := 1.0 if closed else 0.0
if is_equal_approx(wanted, _helmet_target) and not immediate:
return
_helmet_target = wanted
if immediate:
_helmet_progress = wanted
_update_helmet_parts(wanted)
return
_helmet_fx_left = 0.38
func toggle_helmet() -> void:
set_helmet_closed(_helmet_target < 0.5)
func helmet_closed() -> bool:
return _helmet_target > 0.5
func helmet_progress_debug() -> float:
return _helmet_progress
func _build_second_skin_torso() -> void:
var chest := _attachment(_chest_bone(), "SecondSkinTorso")
if chest == null:
return
var a := Assembly.new(chest)
# A continuous dermal chassis sits only millimetres outside the authored
# bodysuit. Broad ellipsoids preserve the waist/chest curves; all brighter
# geometry below is inset-looking surfacing rather than a floating breastplate.
_ellipsoid(a, Vector3(0.335, 0.350, 0.176),
Vector3(0.0, -0.030, 0.0), FRAME)
_ellipsoid(a, Vector3(0.300, 0.265, 0.220),
Vector3(0.0, -0.235, -0.018), FRAME)
_capsule_plate(a, Vector3(0.046, 0.225, 0.010),
Vector3(0.0, -0.018, -0.095), METAL)
_capsule_plate(a, Vector3(0.012, 0.178, 0.005),
Vector3(0.0, -0.010, -0.103), GLOW)
for side in [-1.0, 1.0]:
# Three flexible pectoral laminates follow muscle flow into the ribs.
for panel in 3:
var y := 0.072 - panel * 0.067
var width := 0.128 - panel * 0.018
_capsule_plate(a, Vector3(width, 0.043, 0.009),
Vector3((0.070 + width * 0.14) * side, y, -0.092),
ARMOR if panel == 0 else ARMOR_ALT,
Vector3(0.0, 0.0, (-0.16 + panel * 0.035) * side))
_capsule_plate(a, Vector3(width * 0.62, 0.005, 0.004),
Vector3((0.078 + width * 0.10) * side, y - 0.024, -0.100),
ACCENT, Vector3(0.0, 0.0, -0.13 * side))
# Collar laminates grow out of the chest shell and terminate at the arm.
_capsule_plate(a, Vector3(0.142, 0.036, 0.012),
Vector3(0.078 * side, 0.142, -0.062), ARMOR,
Vector3(0.0, 0.0, -0.20 * side))
_capsule_plate(a, Vector3(0.090, 0.006, 0.004),
Vector3(0.084 * side, 0.151, -0.073), GLOW,
Vector3(0.0, 0.0, -0.20 * side))
# The rear reads as an artificial spinal system, not a backpack.
_capsule_plate(a, Vector3(0.052, 0.270, 0.014),
Vector3(0.0, -0.018, 0.096), METAL)
for vertebra in 7:
var y := 0.108 - vertebra * 0.037
_ellipsoid(a, Vector3(0.054, 0.022, 0.015),
Vector3(0.0, y, 0.105), ARMOR_ALT)
_sphere(a, 0.0045, Vector3(0.0, y, 0.115), GLOW, 8)
for side in [-1.0, 1.0]:
for rail in 4:
_capsule_plate(a, Vector3(0.012, 0.040, 0.008),
Vector3((0.070 + rail * 0.022) * side,
0.075 - rail * 0.052, 0.091), ACCENT,
Vector3(0.0, 0.0, 0.16 * side))
_detail_parts += a.part_count
a.commit("SecondSkinTorsoCombined")
func _build_second_skin_waist() -> void:
var hips := _attachment(_role_bone("hips"), "SecondSkinWaist")
if hips == null:
return
var a := Assembly.new(hips)
_ellipsoid(a, Vector3(0.310, 0.320, 0.205),
Vector3(0.0, 0.115, 0.0), FRAME)
_capsule_plate(a, Vector3(0.095, 0.060, 0.018),
Vector3(0.0, 0.012, -0.067), ARMOR_ALT)
_capsule_plate(a, Vector3(0.038, 0.038, 0.008),
Vector3(0.0, 0.014, -0.080), GLOW)
for side in [-1.0, 1.0]:
_ellipsoid(a, Vector3(0.112, 0.145, 0.094),
Vector3(0.120 * side, -0.060, -0.002), FRAME)
_capsule_plate(a, Vector3(0.060, 0.105, 0.008),
Vector3(0.145 * side, -0.052, -0.044), ARMOR_ALT,
Vector3(0.0, 0.0, 0.10 * side))
_capsule_plate(a, Vector3(0.010, 0.090, 0.005),
Vector3(0.158 * side, -0.050, -0.053), ACCENT,
Vector3(0.0, 0.0, 0.10 * side))
for seam in 3:
_sphere(a, 0.006, Vector3((0.094 + seam * 0.024) * side,
-0.002, 0.060), GLOW, 8)
_detail_parts += a.part_count
a.commit("SecondSkinWaistCombined")
func _build_second_skin_limbs() -> void:
for side_name in ["L", "R"]:
var side := -1.0 if side_name == "L" else 1.0
_build_second_skin_shoulder(side_name, side)
_build_second_skin_segment("upper_arm.%s" % side_name,
"UpperArm%s" % side_name, 0.165, 0.130, 0.245, 0.145, side)
_build_second_skin_segment("forearm.%s" % side_name,
"Forearm%s" % side_name, 0.158, 0.112, 0.265, 0.138, side)
_build_second_skin_hand(side_name, side)
_build_second_skin_segment("thigh.%s" % side_name,
"Thigh%s" % side_name, 0.195, 0.148, 0.315, 0.178, side, true)
_build_second_skin_segment("shin.%s" % side_name,
"Shin%s" % side_name, 0.158, 0.112, 0.350, 0.142, side, true)
_build_second_skin_boot(side_name, side)
func _build_second_skin_shoulder(side_name: String, side: float) -> void:
var mount := _attachment(_role_bone("upper_arm.%s" % side_name),
"SecondSkinShoulder%s" % side_name)
if mount == null:
return
var a := Assembly.new(mount)
# A soft deltoid shell, continuous with the upper-arm membrane. The only
# hard edge is the clavicle seam; there is no independent shoulder pauldron.
_ellipsoid(a, Vector3(0.172, 0.112, 0.148),
Vector3(0.0, -0.010, 0.0), FRAME)
_capsule_plate(a, Vector3(0.128, 0.040, 0.009),
Vector3(0.0, 0.032, -0.071), ARMOR_ALT)
_capsule_plate(a, Vector3(0.090, 0.006, 0.004),
Vector3(0.0, 0.042, -0.078), GLOW)
for seam in [-1.0, 1.0]:
_capsule_plate(a, Vector3(0.008, 0.062, 0.004),
Vector3(0.044 * seam, -0.005, -0.073), ACCENT,
Vector3(0.0, 0.0, 0.15 * seam * side))
_detail_parts += a.part_count
a.commit("SecondSkinShoulderCombined")
func _build_second_skin_segment(role: String, node_name: String,
top_width: float, bottom_width: float, length: float, depth: float,
side: float, leg: bool = false) -> void:
var mount := _attachment(_role_bone(role), "SecondSkin%s" % node_name)
if mount == null:
return
var a := Assembly.new(mount)
var center := -length * (0.25 if node_name.begins_with("Thigh") \
else (0.47 if leg else 0.47))
# One smooth dermal sleeve. A rounded anatomical volume avoids the flat ends
# and ruler-straight sides that make a tapered cylinder read as a strapped-on
# greave; the authored animation still supplies the underlying muscle motion.
_ellipsoid(a, Vector3((top_width + bottom_width) * 0.54, length, depth),
Vector3(0.0, center, 0.0), FRAME)
# Flush pearlescent laminates occupy only the front/outer quadrant and share
# the underlying limb curvature instead of forming a box around it.
_capsule_plate(a, Vector3(top_width * 0.30, length * 0.48, 0.007),
Vector3(0.0, center, -depth * 0.515), ARMOR)
_capsule_plate(a, Vector3(top_width * 0.15, length * 0.34, 0.005),
Vector3(top_width * 0.20 * side, center - length * 0.02, -depth * 0.53),
ARMOR_ALT)
_capsule_plate(a, Vector3(0.007, length * 0.70, 0.004),
Vector3(top_width * 0.13 * side, center, -depth * 0.56), GLOW)
# Two transverse flex seams articulate the synthetic muscle without adding
# rings around the silhouette.
for seam_at in [0.34, 0.68]:
var seam_y: float = -length * float(seam_at)
_capsule_plate(a, Vector3(top_width * 0.40, 0.005, 0.0035),
Vector3(-top_width * 0.05 * side, seam_y, -depth * 0.545), METAL,
Vector3(0.0, 0.0, 0.08 * side))
_sphere(a, 0.0045 if leg else 0.0038,
Vector3(top_width * 0.37 * side, seam_y, -depth * 0.40), ACCENT, 8)
# A calf/thigh rear tendon gives the long leg a technological line without
# broadening it into greaves.
if leg:
_capsule_plate(a, Vector3(0.010, length * 0.60, 0.006),
Vector3(-top_width * 0.28 * side, center, depth * 0.49), ACCENT)
_detail_parts += a.part_count
a.commit("SecondSkin%sCombined" % node_name)
func _build_second_skin_hand(side_name: String, side: float) -> void:
var mount := _attachment(_role_bone("hand.%s" % side_name),
"SecondSkinHand%s" % side_name)
if mount == null:
return
var a := Assembly.new(mount)
# A flexible palm/knuckle membrane leaves finger articulation readable while
# visually continuing the forearm shell through the wrist.
_ellipsoid(a, Vector3(0.092, 0.118, 0.050),
Vector3(0.0, -0.043, 0.0), FRAME)
_capsule_plate(a, Vector3(0.050, 0.070, 0.005),
Vector3(0.010 * side, -0.048, -0.028), ARMOR_ALT,
Vector3(0.0, 0.0, 0.10 * side))
_capsule_plate(a, Vector3(0.005, 0.068, 0.003),
Vector3(-0.020 * side, -0.048, -0.032), GLOW)
_detail_parts += a.part_count
a.commit("SecondSkinHandCombined%s" % side_name)
func _build_second_skin_boot(side_name: String, side: float) -> void:
var mount := _attachment(_role_bone("foot.%s" % side_name),
"SecondSkinBoot%s" % side_name)
if mount == null:
return
var a := Assembly.new(mount)
_ellipsoid(a, Vector3(0.138, 0.086, 0.214),
Vector3(0.0, -0.012, -0.050), FRAME)
_ellipsoid(a, Vector3(0.116, 0.032, 0.166),
Vector3(0.0, 0.024, -0.069), ARMOR)
_capsule_plate(a, Vector3(0.010, 0.008, 0.112),
Vector3(0.026 * side, 0.034, -0.070), GLOW)
for tread in 4:
_capsule_plate(a, Vector3(0.074, 0.007, 0.016),
Vector3(0.0, -0.047, -0.116 + tread * 0.039), METAL)
_detail_parts += a.part_count
a.commit("SecondSkinBootCombined")
func _build_second_skin_nozzles() -> void:
for i in 4:
var nozzle := Node3D.new()
nozzle.name = "DorsalVectorJet%s" % ("L" if i == 0 else "R") \
if i < 2 else "HeelVectorJet%s" % ("L" if i == 2 else "R")
nozzle.set_meta("mecha_thruster", true)
add_child(nozzle)
_nozzles.append(nozzle)
var a := Assembly.new(nozzle)
var k := 1.0 if i < 2 else 0.66
# A compact vector nozzle is the one unapologetically external tool. Its
# tapered root appears to grow through a reinforced pore in the second skin;
# no flower ring or floating satellite housing competes with the body.
_ellipsoid(a, Vector3(0.082, 0.064, 0.108) * k,
Vector3(0.0, 0.0, 0.050 * k), FRAME)
_cylinder(a, 0.042 * k, 0.032 * k, 0.072 * k,
Vector3(0.0, 0.0, 0.025 * k), Vector3(PI * 0.5, 0.0, 0.0),
METAL, 16)
_torus(a, 0.022 * k, 0.043 * k, Vector3.ZERO,
Vector3(PI * 0.5, 0.0, 0.0), ACCENT, 16, 6)
_cylinder(a, 0.022 * k, 0.016 * k, 0.040 * k,
Vector3(0.0, 0.0, 0.018 * k), Vector3(PI * 0.5, 0.0, 0.0),
METAL, 16)
_cylinder(a, 0.015 * k, 0.015 * k, 0.006 * k,
Vector3.ZERO, Vector3(PI * 0.5, 0.0, 0.0), GLOW, 16)
for shroud in 3:
var angle := shroud * TAU / 3.0 + PI * 0.5
_capsule_plate(a, Vector3(0.010, 0.034, 0.006) * k,
Vector3(cos(angle) * 0.039 * k, sin(angle) * 0.039 * k,
0.052 * k), ARMOR, Vector3(0.0, 0.0, angle))
_sphere(a, 0.004 * k,
Vector3(cos(angle) * 0.032 * k, sin(angle) * 0.032 * k,
0.074 * k), GLOW, 8)
_detail_parts += a.part_count
a.commit("VectorJetCombined")
func _build_deployable_helmet() -> void:
var head := _attachment(_role_bone("head"), "DeployableHelmet")
if head == null:
return
var shell := Assembly.new(head)
# Rear crown, temple rails, and cheek hinges stay visible when open; they
# make the visor feel stored inside a robotic cranial frame.
# Open-sided crown rails leave hair and face readable. A full ellipsoid here
# would become a solid mask under cel rendering even while the visor is open.
for side in [-1.0, 1.0]:
_capsule_plate(shell, Vector3(0.034, 0.135, 0.020),
Vector3(0.074 * side, 0.062, 0.080), FRAME,
Vector3(0.10, 0.0, 0.18 * side))
_capsule_plate(shell, Vector3(0.022, 0.105, 0.012),
Vector3(0.058 * side, 0.100, 0.094), ARMOR,
Vector3(0.14, 0.0, 0.28 * side))
_capsule_plate(shell, Vector3(0.060, 0.082, 0.015),
Vector3(0.0, 0.104, -0.070), ARMOR_ALT)
_capsule_plate(shell, Vector3(0.013, 0.066, 0.006),
Vector3(0.0, 0.108, -0.081), GLOW)
for side in [-1.0, 1.0]:
_capsule_plate(shell, Vector3(0.027, 0.110, 0.018),
Vector3(0.091 * side, 0.018, -0.006), ARMOR_ALT,
Vector3(0.0, 0.0, 0.12 * side))
_torus(shell, 0.013, 0.030, Vector3(0.095 * side, 0.002, -0.050),
Vector3(PI * 0.5, 0.0, 0.0), ACCENT, 12, 5)
for port in 3:
_sphere(shell, 0.005, Vector3(0.096 * side,
0.067 - port * 0.028, -0.054), GLOW, 8)
_detail_parts += shell.part_count
shell.commit("HelmetCranialFrameCombined")
var visor_mat := _visor_material(_theme["glow"])
_visor_left = _helmet_ellipsoid(head, "VisorLeft", visor_mat)
_visor_right = _helmet_ellipsoid(head, "VisorRight", visor_mat)
_jaw_left = _helmet_ellipsoid(head, "JawSealLeft", _materials[FRAME])
_jaw_right = _helmet_ellipsoid(head, "JawSealRight", _materials[FRAME])
_helmet_fx_material = _visor_material(_theme["glow"])
_helmet_fx_material.albedo_color = Color(_theme["glow"], 0.78)
_helmet_fx_material.emission_energy_multiplier = 4.0
_helmet_scan = MeshInstance3D.new()
_helmet_scan.name = "HelmetDeployRing"
var ring := TorusMesh.new()
ring.inner_radius = 0.072
ring.outer_radius = 0.078
ring.rings = 24
ring.ring_segments = 5
_helmet_scan.mesh = ring
_helmet_scan.material_override = _helmet_fx_material
_helmet_scan.rotation.x = PI * 0.5
_helmet_scan.position = Vector3(0.0, 0.120, 0.235)
_helmet_scan.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
head.add_child(_helmet_scan)
_helmet_sweep = MeshInstance3D.new()
_helmet_sweep.name = "HelmetVisorSweep"
var sweep_mesh := CapsuleMesh.new()
sweep_mesh.radius = 0.004
sweep_mesh.height = 0.080
sweep_mesh.radial_segments = 8
_helmet_sweep.mesh = sweep_mesh
_helmet_sweep.material_override = _helmet_fx_material
_helmet_sweep.position = Vector3(0.0, 0.120, 0.242)
_helmet_sweep.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
head.add_child(_helmet_sweep)
_helmet_light = OmniLight3D.new()
_helmet_light.name = "HelmetDeployFlash"
_helmet_light.light_color = _theme["glow"]
_helmet_light.omni_range = 1.25
_helmet_light.shadow_enabled = false
_helmet_light.position = Vector3(0.0, 0.120, 0.275)
head.add_child(_helmet_light)
_detail_parts += 8
_update_helmet_parts(0.0)
func _helmet_ellipsoid(parent: Node3D, node_name: String,
material: Material) -> MeshInstance3D:
var node := MeshInstance3D.new()
node.name = node_name
var mesh := SphereMesh.new()
mesh.radius = 0.5
mesh.height = 1.0
mesh.radial_segments = 16
mesh.rings = 8
node.mesh = mesh
node.material_override = material
node.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
parent.add_child(node)
return node
func _update_helmet_parts(amount: float) -> void:
var t := smoothstep(0.0, 1.0, amount)
# Two translucent robotic lenses iris out from the temples and lock on the
# centerline. Opening reverses the same physical path into the crown rails.
_visor_left.position = Vector3(lerpf(-0.082, -0.026, t),
lerpf(0.105, 0.142, t), lerpf(-0.040, 0.205, t))
_visor_right.position = Vector3(lerpf(0.082, 0.026, t),
lerpf(0.105, 0.142, t), lerpf(-0.040, 0.205, t))
_visor_left.scale = Vector3(0.060, lerpf(0.009, 0.022, t), 0.014)
_visor_right.scale = Vector3(0.060, lerpf(0.009, 0.022, t), 0.014)
_visor_left.rotation.z = lerpf(-0.72, -0.24, t)
_visor_right.rotation.z = lerpf(0.72, 0.24, t)
_jaw_left.position = Vector3(lerpf(-0.110, -0.075, t),
lerpf(-0.010, 0.018, t), lerpf(-0.025, 0.205, t))
_jaw_right.position = Vector3(lerpf(0.110, 0.075, t),
lerpf(-0.010, 0.018, t), lerpf(-0.025, 0.205, t))
_jaw_left.scale = Vector3(0.038, lerpf(0.030, 0.082, t), 0.018)
_jaw_right.scale = Vector3(0.038, lerpf(0.030, 0.082, t), 0.018)
_jaw_left.rotation.z = lerpf(-0.65, -0.28, t)
_jaw_right.rotation.z = lerpf(0.65, 0.28, t)
func _visor_material(color: Color) -> StandardMaterial3D:
var material := StandardMaterial3D.new()
material.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
material.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
material.blend_mode = BaseMaterial3D.BLEND_MODE_MIX
material.cull_mode = BaseMaterial3D.CULL_DISABLED
material.albedo_color = Color(_theme["frame"], 0.84)
material.emission_enabled = true
material.emission = _theme["accent"]
material.emission_energy_multiplier = 1.15
return material
func _build_materials() -> void:
_materials = {
ARMOR: _armor_material(_theme["armor"]),
ARMOR_ALT: _armor_material(_theme["armor_alt"]),
FRAME: _armor_material(_theme["frame"]),
ACCENT: _armor_material(_theme["accent"]),
GLOW: _glow_material(_theme["glow"]),
METAL: _armor_material(_theme["metal"]),
}
func _build_torso() -> void:
var chest := _attachment(_chest_bone(), "MechaTorso")
if chest == null:
return
var a := Assembly.new(chest)
# Body-hugging backpack frame and central reactor spine. Rounded capsules
# overlap like vertebral armor instead of reading as a refrigerator slab.
_capsule_plate(a, Vector3(0.235, 0.255, 0.052),
Vector3(0.0, 0.0, 0.112), FRAME)
_capsule_plate(a, Vector3(0.195, 0.215, 0.027),
Vector3(0.0, 0.0, 0.157), ARMOR_ALT)
_capsule_plate(a, Vector3(0.075, 0.175, 0.025),
Vector3(0.0, 0.012, 0.180), ARMOR)
_capsule_plate(a, Vector3(0.026, 0.135, 0.010),
Vector3(0.0, 0.015, 0.200), ACCENT)
_cylinder(a, 0.044, 0.050, 0.026, Vector3(0.0, 0.025, 0.213),
Vector3(PI * 0.5, 0.0, 0.0), FRAME, 16)
_torus(a, 0.023, 0.046, Vector3(0.0, 0.025, 0.229),
Vector3(PI * 0.5, 0.0, 0.0), ACCENT)
_cylinder(a, 0.021, 0.021, 0.007, Vector3(0.0, 0.025, 0.236),
Vector3(PI * 0.5, 0.0, 0.0), GLOW, 16)
# Radiator vanes make the backpack read at distance and break up the slab.
for vane in 7:
var y := -0.090 + vane * 0.030
_capsule_plate(a, Vector3(0.165, 0.009, 0.016),
Vector3(0.0, y, 0.205), METAL)
for side in [-1.0, 1.0]:
# Shoulder-side power modules, coolant bottles and exposed conduits.
_tapered_shell(a, 0.070, 0.050, 0.170, 0.056,
Vector3(0.145 * side, -0.005, 0.126), FRAME,
Vector3(0.0, 0.0, -0.08 * side))
_capsule_plate(a, Vector3(0.058, 0.145, 0.020),
Vector3(0.147 * side, 0.0, 0.169), ARMOR,
Vector3(0.0, 0.0, -0.08 * side))
_capsule_plate(a, Vector3(0.025, 0.095, 0.009),
Vector3(0.149 * side, 0.005, 0.186), ACCENT)
_cylinder(a, 0.021, 0.021, 0.135,
Vector3(0.116 * side, -0.020, 0.190),
Vector3(0.0, 0.0, 0.0), METAL, 10)
_cylinder(a, 0.014, 0.014, 0.112,
Vector3(0.178 * side, -0.025, 0.178),
Vector3(0.0, 0.0, 0.0), ACCENT, 10)
for bolt_y in [-0.072, 0.072]:
_cylinder(a, 0.010, 0.010, 0.008,
Vector3(0.178 * side, bolt_y, 0.194),
Vector3(PI * 0.5, 0.0, 0.0), METAL, 8)
# Front chest armor: floating plate, inset seam and collar latch.
_capsule_plate(a, Vector3(0.105, 0.125, 0.025),
Vector3(0.060 * side, -0.020, -0.076), ARMOR,
Vector3(0.0, 0.0, 0.10 * side))
_capsule_plate(a, Vector3(0.072, 0.080, 0.010),
Vector3(0.056 * side, -0.018, -0.094), ARMOR_ALT,
Vector3(0.0, 0.0, 0.10 * side))
_capsule_plate(a, Vector3(0.056, 0.010, 0.006),
Vector3(0.052 * side, 0.004, -0.103), ACCENT,
Vector3(0.0, 0.0, 0.10 * side))
_capsule_plate(a, Vector3(0.015, 0.045, 0.006),
Vector3(0.015 * side, 0.045, -0.104), GLOW,
Vector3(0.0, 0.0, 0.10 * side))
# Articulated-looking dorsal fin: spar, armor leaves and luminous edge.
_capsule_plate(a, Vector3(0.018, 0.195, 0.016),
Vector3(0.195 * side, 0.015, 0.137), METAL,
Vector3(0.0, 0.0, -0.18 * side))
for segment in 3:
var sy := -0.058 + segment * 0.058
var reach := 0.064 - segment * 0.008
_capsule_plate(a, Vector3(reach, 0.040, 0.014),
Vector3((0.211 + reach * 0.28) * side, sy, 0.137),
ARMOR if segment % 2 == 0 else ARMOR_ALT,
Vector3(0.0, 0.0, -0.22 * side))
_capsule_plate(a, Vector3(0.008, 0.172, 0.007),
Vector3(0.230 * side, 0.014, 0.148), ACCENT,
Vector3(0.0, 0.0, -0.18 * side))
_detail_parts += a.part_count
a.commit("TorsoFrameCombined")
func _build_waist() -> void:
var hips := _attachment(_role_bone("hips"), "MechaWaist")
if hips == null:
return
var a := Assembly.new(hips)
_capsule_plate(a, Vector3(0.245, 0.060, 0.072),
Vector3(0.0, 0.015, 0.0), FRAME)
_capsule_plate(a, Vector3(0.082, 0.072, 0.022),
Vector3(0.0, 0.015, -0.052), ARMOR)
_capsule_plate(a, Vector3(0.042, 0.035, 0.008),
Vector3(0.0, 0.018, -0.068), ACCENT)
_sphere(a, 0.010, Vector3(0.0, 0.018, -0.075), GLOW, 8)
for side in [-1.0, 1.0]:
_capsule_plate(a, Vector3(0.066, 0.165, 0.025),
Vector3(0.150 * side, -0.078, 0.012), ARMOR,
Vector3(0.04, 0.0, 0.15 * side))
_capsule_plate(a, Vector3(0.044, 0.118, 0.011),
Vector3(0.153 * side, -0.074, -0.004), ARMOR_ALT,
Vector3(0.04, 0.0, 0.15 * side))
_capsule_plate(a, Vector3(0.010, 0.096, 0.006),
Vector3(0.174 * side, -0.072, -0.012), ACCENT,
Vector3(0.04, 0.0, 0.15 * side))
_cylinder(a, 0.014, 0.014, 0.090,
Vector3(0.116 * side, -0.072, 0.028),
Vector3(0.0, 0.0, 0.12 * side), METAL, 8)
for seam in 3:
_capsule_plate(a, Vector3(0.050, 0.006, 0.005),
Vector3(0.153 * side, -0.118 + seam * 0.048, -0.020), METAL,
Vector3(0.04, 0.0, 0.15 * side))
_detail_parts += a.part_count
a.commit("WaistFrameCombined")
func _build_limbs() -> void:
for side_name in ["L", "R"]:
var side := -1.0 if side_name == "L" else 1.0
_build_shoulder(side_name, side)
_build_forearm(side_name, side)
_build_thigh(side_name, side)
_build_shin(side_name, side)
_build_boot(side_name, side)
func _build_shoulder(side_name: String, side: float) -> void:
var mount := _attachment(_role_bone("upper_arm.%s" % side_name),
"MechaShoulder%s" % side_name)
if mount == null:
return
var a := Assembly.new(mount)
_ellipsoid(a, Vector3(0.145, 0.080, 0.125),
Vector3(0.0, 0.025, 0.0), FRAME)
_ellipsoid(a, Vector3(0.165, 0.052, 0.100),
Vector3(0.0, 0.062, 0.0), ARMOR)
_capsule_plate(a, Vector3(0.120, 0.017, 0.060),
Vector3(0.0, 0.094, 0.0), ARMOR_ALT)
_capsule_plate(a, Vector3(0.080, 0.008, 0.010),
Vector3(0.0, 0.108, -0.025), ACCENT)
_cylinder(a, 0.027, 0.027, 0.180, Vector3.ZERO,
Vector3(0.0, 0.0, PI * 0.5), METAL, 12)
_torus(a, 0.018, 0.036, Vector3(0.090 * side, 0.0, 0.0),
Vector3(0.0, 0.0, PI * 0.5), ACCENT)
for vent in 4:
_capsule_plate(a, Vector3(0.010, 0.006, 0.038),
Vector3(-0.038 + vent * 0.025, 0.118, 0.018), METAL)
for bolt_x in [-0.060, 0.060]:
for bolt_z in [-0.040, 0.040]:
_sphere(a, 0.008, Vector3(bolt_x, 0.137, bolt_z), ACCENT, 8)
_detail_parts += a.part_count
a.commit("ShoulderFrameCombined")
func _build_forearm(side_name: String, side: float) -> void:
var mount := _attachment(_role_bone("forearm.%s" % side_name),
"MechaForearm%s" % side_name)
if mount == null:
return
var a := Assembly.new(mount)
_tapered_shell(a, 0.100, 0.074, 0.215, 0.080,
Vector3(0.0, -0.105, 0.0), FRAME)
_capsule_plate(a, Vector3(0.088, 0.145, 0.025),
Vector3(0.0, -0.105, -0.060), ARMOR)
_capsule_plate(a, Vector3(0.055, 0.100, 0.011),
Vector3(0.0, -0.095, -0.078), ARMOR_ALT)
_capsule_plate(a, Vector3(0.011, 0.105, 0.006),
Vector3(0.026 * side, -0.095, -0.087), ACCENT)
_capsule_plate(a, Vector3(0.014, 0.038, 0.007),
Vector3(-0.019 * side, -0.125, -0.089), GLOW)
for rib in 4:
_torus(a, 0.045, 0.060, Vector3(0.0, -0.035 - rib * 0.052, 0.0),
Vector3.ZERO, METAL, 10, 5)
for bolt_side in [-1.0, 1.0]:
for bolt_y in [-0.055, -0.155]:
_sphere(a, 0.006, Vector3(0.049 * bolt_side, bolt_y, -0.060), ACCENT, 8)
_detail_parts += a.part_count
a.commit("ForearmFrameCombined")
func _build_thigh(side_name: String, side: float) -> void:
var mount := _attachment(_role_bone("thigh.%s" % side_name),
"MechaThigh%s" % side_name)
if mount == null:
return
var a := Assembly.new(mount)
_tapered_shell(a, 0.120, 0.088, 0.235, 0.082,
Vector3(0.0, -0.115, 0.0), FRAME)
_capsule_plate(a, Vector3(0.086, 0.175, 0.025),
Vector3(0.0, -0.105, -0.060), ARMOR)
_capsule_plate(a, Vector3(0.018, 0.140, 0.007),
Vector3(0.029 * side, -0.105, -0.078), ACCENT)
_capsule_plate(a, Vector3(0.036, 0.090, 0.018),
Vector3(0.070 * side, -0.115, 0.0), ARMOR_ALT)
_cylinder(a, 0.018, 0.018, 0.165, Vector3(-0.075 * side, -0.110, 0.025),
Vector3.ZERO, METAL, 8)
for seam in 3:
_capsule_plate(a, Vector3(0.060, 0.006, 0.006),
Vector3(0.0, -0.055 - seam * 0.062, -0.079), METAL)
for bolt_y in [-0.055, -0.175]:
_sphere(a, 0.008, Vector3(0.070 * side, bolt_y, -0.090), ACCENT, 8)
_detail_parts += a.part_count
a.commit("ThighFrameCombined")
func _build_shin(side_name: String, side: float) -> void:
var mount := _attachment(_role_bone("shin.%s" % side_name),
"MechaShin%s" % side_name)
if mount == null:
return
var a := Assembly.new(mount)
_tapered_shell(a, 0.118, 0.082, 0.305, 0.086,
Vector3(0.0, -0.155, 0.012), FRAME)
_capsule_plate(a, Vector3(0.096, 0.235, 0.026),
Vector3(0.0, -0.155, -0.062), ARMOR)
_capsule_plate(a, Vector3(0.056, 0.180, 0.011),
Vector3(0.0, -0.150, -0.081), ARMOR_ALT)
_capsule_plate(a, Vector3(0.013, 0.205, 0.006),
Vector3(0.029 * side, -0.150, -0.090), ACCENT)
_capsule_plate(a, Vector3(0.013, 0.062, 0.007),
Vector3(-0.022 * side, -0.195, -0.092), GLOW)
# Calf actuator, protective rail and vent bank.
_cylinder(a, 0.022, 0.022, 0.230, Vector3(-0.080 * side, -0.150, 0.035),
Vector3.ZERO, METAL, 10)
_cylinder(a, 0.011, 0.011, 0.175, Vector3(-0.080 * side, -0.150, 0.067),
Vector3.ZERO, ACCENT, 8)
for vent in 5:
_capsule_plate(a, Vector3(0.038, 0.007, 0.010),
Vector3(0.060 * side, -0.255 + vent * 0.042, 0.030), METAL,
Vector3(0.0, 0.0, 0.10 * side))
for bolt_y in [-0.055, -0.255]:
for bolt_x in [-0.045, 0.045]:
_sphere(a, 0.006, Vector3(bolt_x, bolt_y, -0.080), ACCENT, 8)
_detail_parts += a.part_count
a.commit("ShinFrameCombined")
func _build_boot(side_name: String, side: float) -> void:
var mount := _attachment(_role_bone("foot.%s" % side_name),
"MechaBoot%s" % side_name)
if mount == null:
return
var a := Assembly.new(mount)
_ellipsoid(a, Vector3(0.120, 0.072, 0.205),
Vector3(0.0, -0.020, -0.058), FRAME)
_ellipsoid(a, Vector3(0.108, 0.038, 0.165),
Vector3(0.0, 0.020, -0.068), ARMOR)
_capsule_plate(a, Vector3(0.074, 0.015, 0.080),
Vector3(0.0, 0.043, -0.092), ARMOR_ALT)
_capsule_plate(a, Vector3(0.012, 0.008, 0.105),
Vector3(0.031 * side, 0.052, -0.068), ACCENT)
for tread in 4:
_capsule_plate(a, Vector3(0.088, 0.010, 0.019),
Vector3(0.0, -0.061, -0.130 + tread * 0.047), METAL)
for bolt_x in [-0.045, 0.045]:
_sphere(a, 0.007, Vector3(bolt_x, 0.060, -0.155), ACCENT, 8)
_detail_parts += a.part_count
a.commit("BootFrameCombined")
func _build_nozzles() -> void:
for i in 4:
var nozzle := Node3D.new()
nozzle.name = "ShoulderThruster%s" % ("L" if i == 0 else "R") \
if i < 2 else "HeelThruster%s" % ("L" if i == 2 else "R")
nozzle.set_meta("mecha_thruster", true)
add_child(nozzle)
_nozzles.append(nozzle)
var a := Assembly.new(nozzle)
var k := 1.0 if i < 2 else 0.72
# Gimbal neck and nested convergent nozzle.
_cylinder(a, 0.052 * k, 0.052 * k, 0.060 * k,
Vector3(0.0, 0.0, 0.090 * k), Vector3(PI * 0.5, 0.0, 0.0),
METAL, 16)
_torus(a, 0.038 * k, 0.066 * k, Vector3(0.0, 0.0, 0.075 * k),
Vector3(PI * 0.5, 0.0, 0.0), FRAME, 14, 6)
_cylinder(a, 0.070 * k, 0.052 * k, 0.115 * k,
Vector3(0.0, 0.0, 0.015 * k), Vector3(PI * 0.5, 0.0, 0.0),
FRAME, 16)
_cylinder(a, 0.051 * k, 0.035 * k, 0.092 * k,
Vector3(0.0, 0.0, -0.010 * k), Vector3(PI * 0.5, 0.0, 0.0),
METAL, 16)
_torus(a, 0.035 * k, 0.072 * k, Vector3(0.0, 0.0, -0.052 * k),
Vector3(PI * 0.5, 0.0, 0.0), ACCENT, 16, 6)
_cylinder(a, 0.032 * k, 0.032 * k, 0.008 * k,
Vector3(0.0, 0.0, -0.060 * k), Vector3(PI * 0.5, 0.0, 0.0),
GLOW, 16)
# Four armored petals, actuator rods and fasteners around each mouth.
for petal in 4:
var angle := petal * TAU / 4.0
var x := cos(angle) * 0.082 * k
var y := sin(angle) * 0.082 * k
_capsule_plate(a, Vector3(0.028, 0.065, 0.016) * k,
Vector3(x, y, -0.005 * k),
ARMOR if petal % 2 == 0 else ARMOR_ALT,
Vector3(0.0, 0.0, angle))
_cylinder(a, 0.007 * k, 0.007 * k, 0.080 * k,
Vector3(x * 0.72, y * 0.72, 0.035 * k),
Vector3(PI * 0.5, 0.0, angle), METAL, 8)
_sphere(a, 0.009 * k, Vector3(x, y, -0.026 * k), ACCENT, 8)
# Cooling slots around the outer housing.
for slot in 8:
var angle := slot * TAU / 8.0
_capsule_plate(a, Vector3(0.008, 0.024, 0.012) * k,
Vector3(cos(angle) * 0.105 * k, sin(angle) * 0.105 * k,
0.040 * k), METAL, Vector3(0.0, 0.0, angle))
_detail_parts += a.part_count
a.commit("ThrusterHardwareCombined")
func update_nozzles(world_positions: Array[Vector3], body_basis: Basis) -> void:
if world_positions.size() != _nozzles.size():
return
for i in _nozzles.size():
_nozzles[i].global_transform = Transform3D(body_basis.orthonormalized(),
world_positions[i])
func nozzle_world_positions() -> Array[Vector3]:
var result: Array[Vector3] = []
for nozzle in _nozzles:
result.append(nozzle.global_position)
return result
func nozzle_count() -> int:
return _nozzles.size()
func detail_part_count_debug() -> int:
return _detail_parts
func _attachment(bone: int, node_name: String) -> BoneAttachment3D:
if bone < 0 or not is_instance_valid(_skeleton):
return null
var attachment := BoneAttachment3D.new()
attachment.name = node_name
attachment.bone_idx = bone
# Cancel each rig's authored rest-bone roll. Equipment is modeled in a
# consistent character-local frame, then follows only the animated delta.
var rest := _skeleton.get_bone_global_rest(bone)
attachment.transform = Transform3D(rest.basis.inverse(), Vector3.ZERO)
_skeleton.add_child(attachment)
_attachments.append(attachment)
return attachment
func _role_bone(role: String) -> int:
if not is_instance_valid(_skeleton):
return -1
var name := String(_roles.get(role, ""))
return RigRoles.find_imported_bone(_skeleton, name)
func _chest_bone() -> int:
var spine: Array = _roles.get("spine", [])
var neck_name := String(_roles.get("neck", ""))
var neck_index := spine.find(neck_name)
if neck_index > 0:
return _skeleton.find_bone(String(spine[neck_index - 1]))
for i in range(spine.size() - 1, -1, -1):
var name := String(spine[i])
if name.findn("chest") >= 0 or name.findn("spine_03") >= 0:
return _skeleton.find_bone(name)
return _role_bone("neck")
func _box(a: Assembly, size: Vector3, pos: Vector3, slot: int,
rotation: Vector3 = Vector3.ZERO) -> void:
var mesh := BoxMesh.new()
mesh.size = size
a.add(mesh, Transform3D(Basis.from_euler(rotation), pos), slot,
_materials[slot])
## A flattened capsule used as a close-fitting armor plate. Its rounded ends
## and elliptical cross-section keep the silhouette continuous with the pilot
## beneath it while still accepting the same panel/vent overlays as a box.
func _capsule_plate(a: Assembly, size: Vector3, pos: Vector3, slot: int,
rotation: Vector3 = Vector3.ZERO) -> void:
var mesh := CapsuleMesh.new()
var vertical := size.y >= size.x
var diameter := maxf(minf(size.x, size.y), 0.002)
mesh.radius = diameter * 0.5
mesh.height = maxf(maxf(size.x, size.y), diameter)
mesh.radial_segments = 10
mesh.rings = 2
var flatten := maxf(size.z / diameter, 0.04)
var axis := Basis.IDENTITY if vertical \
else Basis.from_euler(Vector3(0.0, 0.0, PI * 0.5))
var basis := Basis.from_euler(rotation) * axis \
* Basis.from_scale(Vector3(1.0, 1.0, flatten))
a.add(mesh, Transform3D(basis, pos), slot, _materials[slot])
## Faceted tapered shell around an arm or leg. Ten sides preserve the graphic
## mecha language, but the changing radius follows natural limb taper instead
## of placing a rectangular crate around every joint.
func _tapered_shell(a: Assembly, top_width: float, bottom_width: float,
height: float, depth: float, pos: Vector3, slot: int,
rotation: Vector3 = Vector3.ZERO) -> void:
var mesh := CylinderMesh.new()
mesh.top_radius = top_width * 0.5
mesh.bottom_radius = bottom_width * 0.5
mesh.height = height
mesh.radial_segments = 10
var average_width := maxf((top_width + bottom_width) * 0.5, 0.002)
var basis := Basis.from_euler(rotation) \
* Basis.from_scale(Vector3(1.0, 1.0, depth / average_width))
a.add(mesh, Transform3D(basis, pos), slot, _materials[slot])
## Smooth joint/boot housing. The mesh is deliberately low-ring rather than a
## perfect sphere so it remains anime-mechanical instead of looking organic.
func _ellipsoid(a: Assembly, size: Vector3, pos: Vector3, slot: int,
rotation: Vector3 = Vector3.ZERO) -> void:
var mesh := SphereMesh.new()
mesh.radius = 0.5
mesh.height = 1.0
mesh.radial_segments = 12
mesh.rings = 6
var basis := Basis.from_euler(rotation) * Basis.from_scale(size)
a.add(mesh, Transform3D(basis, pos), slot, _materials[slot])
func _cylinder(a: Assembly, top_radius: float, bottom_radius: float,
height: float, pos: Vector3, rotation: Vector3, slot: int,
segments: int = 12) -> void:
var mesh := CylinderMesh.new()
mesh.top_radius = top_radius
mesh.bottom_radius = bottom_radius
mesh.height = height
mesh.radial_segments = segments
a.add(mesh, Transform3D(Basis.from_euler(rotation), pos), slot,
_materials[slot])
func _torus(a: Assembly, inner_radius: float, outer_radius: float,
pos: Vector3, rotation: Vector3, slot: int, rings: int = 12,
segments: int = 6) -> void:
var mesh := TorusMesh.new()
mesh.inner_radius = inner_radius
mesh.outer_radius = outer_radius
mesh.rings = rings
mesh.ring_segments = segments
a.add(mesh, Transform3D(Basis.from_euler(rotation), pos), slot,
_materials[slot])
func _sphere(a: Assembly, radius: float, pos: Vector3, slot: int,
segments: int = 10) -> void:
var mesh := SphereMesh.new()
mesh.radius = radius
mesh.height = radius * 2.0
mesh.radial_segments = segments
mesh.rings = max(4, segments / 2)
a.add(mesh, Transform3D(Basis.IDENTITY, pos), slot, _materials[slot])
func _glow_material(color: Color) -> StandardMaterial3D:
var material := StandardMaterial3D.new()
material.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
material.albedo_color = color
material.emission_enabled = true
material.emission = color
material.emission_energy_multiplier = 3.0
return material
func _armor_material(color: Color) -> Material:
if DisplayServer.get_name() == "headless":
return LevelMaterials.unlit(color)
return LevelMaterials.cel(color, LevelMaterials.RAMP_3,
Color(0.72, 0.76, 0.90))