Recommiting glbs for LFS

This commit is contained in:
hz
2026-05-12 15:34:12 -05:00
parent a9e546a121
commit fecbefc15c
141 changed files with 7099 additions and 2601 deletions
+110
View File
@@ -0,0 +1,110 @@
extends Node3D
@export var model_path: NodePath = NodePath("AshlingSwarmerModel")
@export var default_animation: StringName = &"Idle"
@export var autoplay: bool = true
var _animation_player: AnimationPlayer
var _model_node: Node3D
var _current_animation: StringName = &""
func _ready() -> void:
call_deferred("_setup_animation")
func _setup_animation() -> void:
_model_node = get_node_or_null(model_path) as Node3D
_animation_player = _find_animation_player(_model_node)
if _animation_player == null:
if autoplay:
push_warning("AshlingSwarmer: no AnimationPlayer found under %s." % model_path)
return
if autoplay:
play_animation(default_animation)
func play_animation(animation_name: StringName = &"") -> void:
if _animation_player == null:
_animation_player = _find_animation_player(get_node_or_null(model_path))
if _animation_player == null:
return
var requested_animation := animation_name if animation_name != StringName() else default_animation
var resolved_animation := _resolve_animation_name(requested_animation)
if resolved_animation == StringName():
push_warning("AshlingSwarmer: animation '%s' not found. Available animations: %s" % [requested_animation, _animation_player.get_animation_list()])
return
var animation := _animation_player.get_animation(resolved_animation)
if animation != null:
animation.loop_mode = Animation.LOOP_LINEAR
_animation_player.play(resolved_animation)
_current_animation = _classify_animation(resolved_animation)
func play_idle() -> void:
play_animation(&"Idle")
func play_run() -> void:
play_animation(&"Run")
func play_attack() -> void:
play_animation(&"Attack_Leap")
func play_hit() -> void:
play_animation(&"Hit")
func play_death() -> void:
play_animation(&"Death_Explode")
func _resolve_animation_name(animation_name: StringName) -> StringName:
if _animation_player.has_animation(animation_name):
return animation_name
var desired := String(animation_name).to_lower()
for candidate in _animation_player.get_animation_list():
var candidate_text := String(candidate)
if candidate_text.to_lower() == desired:
return StringName(candidate)
for candidate in _animation_player.get_animation_list():
var candidate_text := String(candidate)
if candidate_text.to_lower().contains(desired):
return StringName(candidate)
for candidate in _animation_player.get_animation_list():
var candidate_text := String(candidate)
if candidate_text.to_lower().contains("idle"):
return StringName(candidate)
var animations := _animation_player.get_animation_list()
if animations.size() > 0:
return StringName(animations[0])
return StringName()
func _classify_animation(animation_name: StringName) -> StringName:
var text := String(animation_name).to_lower()
if text.contains("run"):
return &"Run"
if text.contains("idle"):
return &"Idle"
if text.contains("attack"):
return &"Attack_Leap"
if text.contains("hit"):
return &"Hit"
if text.contains("death"):
return &"Death_Explode"
return animation_name
func _find_animation_player(root: Node) -> AnimationPlayer:
if root == null:
return null
if root is AnimationPlayer:
return root
for child in root.get_children():
var found := _find_animation_player(child)
if found != null:
return found
return null
@@ -0,0 +1 @@
uid://dptxcarifu7e1
@@ -0,0 +1,36 @@
[gd_scene load_steps=5 format=3]
[ext_resource type="Script" path="res://scenes/Characters/ashling_swarmer.gd" id="1_script"]
[ext_resource type="PackedScene" path="res://assets/models/AshlingSwarmer.glb" id="2_model"]
[ext_resource type="Script" path="res://scenes/Interaction/dialog_trigger_area.gd" id="3_dialog"]
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_body"]
radius = 0.45
height = 1.6
[sub_resource type="SphereShape3D" id="SphereShape3D_interact"]
radius = 1.8
[node name="AshlingSwarmer" type="Node3D"]
script = ExtResource("1_script")
autoplay = false
[node name="Body" type="StaticBody3D" parent="."]
[node name="CollisionShape3D" type="CollisionShape3D" parent="Body"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.8, 0)
shape = SubResource("CapsuleShape3D_body")
[node name="AshlingSwarmerModel" parent="." instance=ExtResource("2_model")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0)
[node name="InteractArea" type="Area3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.8, 0)
script = ExtResource("3_dialog")
collision_layer = 2
collision_mask = 1
prompt_text = "Press E to inspect"
dialog_text = "Ashling Swarmer: A small ember-born creature watches you."
[node name="CollisionShape3D" type="CollisionShape3D" parent="InteractArea"]
shape = SubResource("SphereShape3D_interact")
+103 -103
View File
@@ -19,15 +19,15 @@ extends Node3D
@export var vertical_unlock_height: float = 0.6
@export var vertical_lock_smooth_speed: float = 6.0
@export var vertical_lock_hold_time: float = 0.3
@export var max_look_angle_deg: float = 90.0
@export var eye_return_speed: float = 0.2
@export var max_offset: float = 0.08
@export var side_eye_boost: float = 1.4
@export var head_path: NodePath = NodePath("Body/HeadPivot")
@export var head_turn_speed: float = 16.0
@export var head_max_yaw_deg: float = 55.0
@export var head_max_pitch_deg: float = 22.0
@export var max_look_angle_deg: float = 90.0
@export var eye_return_speed: float = 0.2
@export var max_offset: float = 0.08
@export var side_eye_boost: float = 1.4
@export var head_path: NodePath = NodePath("Body/HeadPivot")
@export var head_turn_speed: float = 16.0
@export var head_max_yaw_deg: float = 55.0
@export var head_max_pitch_deg: float = 22.0
var _left_pupil: Node3D
var _right_pupil: Node3D
var _body: RigidBody3D
@@ -35,9 +35,9 @@ var _left_base: Vector3
var _right_base: Vector3
var _camera: Camera3D
var _look_origin: Node3D
var _head: Node3D
var _head_base_rot: Vector3
var _vertical_lock_factor: float = 1.0
var _head: Node3D
var _head_base_rot: Vector3
var _vertical_lock_factor: float = 1.0
var _vertical_hold_timer: float = 0.0
var _look_reference: Node3D
var _look_target: Node3D
@@ -50,9 +50,9 @@ func _ready() -> void:
_right_pupil = get_node_or_null(right_pupil_path) as Node3D
if _left_pupil:
_left_base = _left_pupil.position
if _right_pupil:
_right_base = _right_pupil.position
_camera = _resolve_camera()
if _right_pupil:
_right_base = _right_pupil.position
_camera = _resolve_camera()
_look_origin = get_node_or_null(look_origin_path) as Node3D
_look_reference = get_node_or_null(look_reference_path) as Node3D
_look_target = get_node_or_null(look_target_path) as Node3D
@@ -76,8 +76,8 @@ func _physics_process(delta: float) -> void:
func _process(_delta: float) -> void:
_sync_body_to_root()
_update_pupils()
func _update_pupils() -> void:
if _look_target == null and look_target_path != NodePath(""):
_look_target = get_node_or_null(look_target_path) as Node3D
@@ -93,56 +93,56 @@ func _update_pupils() -> void:
if origin == null:
origin = self
var target := _look_target.global_position if _look_target != null else _camera.global_position
var dir_world := target - origin.global_position
if dir_world.length_squared() <= 0.0001:
return
dir_world = dir_world.normalized()
var reference := _look_reference if _look_reference != null else origin
var forward := -reference.global_basis.z
var min_dot := cos(deg_to_rad(max_look_angle_deg))
var can_look := dir_world.dot(forward) >= min_dot
if not can_look:
var delta := get_process_delta_time()
if _left_pupil:
var target_left := Vector3(_left_base.x, _left_base.y, _left_base.z)
var pos_left := _left_pupil.position
pos_left.x = move_toward(pos_left.x, target_left.x, eye_return_speed * delta)
pos_left.y = target_left.y
pos_left.z = move_toward(pos_left.z, target_left.z, eye_return_speed * delta)
_left_pupil.position = pos_left
if _right_pupil:
var target_right := Vector3(_right_base.x, _right_base.y, _right_base.z)
var pos_right := _right_pupil.position
pos_right.x = move_toward(pos_right.x, target_right.x, eye_return_speed * delta)
pos_right.y = target_right.y
pos_right.z = move_toward(pos_right.z, target_right.z, eye_return_speed * delta)
_right_pupil.position = pos_right
if _head:
_head.rotation.x = _head_base_rot.x
_head.rotation.y = lerp_angle(_head.rotation.y, _head_base_rot.y, head_turn_speed * delta)
return
if lock_vertical:
var origin_y := origin.global_position.y
var target_offset := target.y - origin_y
var is_above := target_offset > vertical_unlock_height
if is_above:
_vertical_hold_timer = vertical_lock_hold_time
else:
_vertical_hold_timer = max(0.0, _vertical_hold_timer - get_process_delta_time())
if is_above:
_vertical_lock_factor = 1.0
else:
var unlock := 1.0 if _vertical_hold_timer > 0.0 else 0.0
_vertical_lock_factor = move_toward(_vertical_lock_factor, unlock, vertical_lock_smooth_speed * get_process_delta_time())
target.y = lerp(origin_y, target.y, _vertical_lock_factor)
dir_world = target - origin.global_position
if dir_world.length_squared() <= 0.0001:
return
dir_world = dir_world.normalized()
if _left_pupil:
_update_eye(_left_pupil, _left_base, dir_world)
if _right_pupil:
_update_eye(_right_pupil, _right_base, dir_world)
var dir_world := target - origin.global_position
if dir_world.length_squared() <= 0.0001:
return
dir_world = dir_world.normalized()
var reference := _look_reference if _look_reference != null else origin
var forward := -reference.global_basis.z
var min_dot := cos(deg_to_rad(max_look_angle_deg))
var can_look := dir_world.dot(forward) >= min_dot
if not can_look:
var delta := get_process_delta_time()
if _left_pupil:
var target_left := Vector3(_left_base.x, _left_base.y, _left_base.z)
var pos_left := _left_pupil.position
pos_left.x = move_toward(pos_left.x, target_left.x, eye_return_speed * delta)
pos_left.y = target_left.y
pos_left.z = move_toward(pos_left.z, target_left.z, eye_return_speed * delta)
_left_pupil.position = pos_left
if _right_pupil:
var target_right := Vector3(_right_base.x, _right_base.y, _right_base.z)
var pos_right := _right_pupil.position
pos_right.x = move_toward(pos_right.x, target_right.x, eye_return_speed * delta)
pos_right.y = target_right.y
pos_right.z = move_toward(pos_right.z, target_right.z, eye_return_speed * delta)
_right_pupil.position = pos_right
if _head:
_head.rotation.x = _head_base_rot.x
_head.rotation.y = lerp_angle(_head.rotation.y, _head_base_rot.y, head_turn_speed * delta)
return
if lock_vertical:
var origin_y := origin.global_position.y
var target_offset := target.y - origin_y
var is_above := target_offset > vertical_unlock_height
if is_above:
_vertical_hold_timer = vertical_lock_hold_time
else:
_vertical_hold_timer = max(0.0, _vertical_hold_timer - get_process_delta_time())
if is_above:
_vertical_lock_factor = 1.0
else:
var unlock := 1.0 if _vertical_hold_timer > 0.0 else 0.0
_vertical_lock_factor = move_toward(_vertical_lock_factor, unlock, vertical_lock_smooth_speed * get_process_delta_time())
target.y = lerp(origin_y, target.y, _vertical_lock_factor)
dir_world = target - origin.global_position
if dir_world.length_squared() <= 0.0001:
return
dir_world = dir_world.normalized()
if _left_pupil:
_update_eye(_left_pupil, _left_base, dir_world)
if _right_pupil:
_update_eye(_right_pupil, _right_base, dir_world)
if _head:
_update_head(dir_world)
@@ -247,39 +247,39 @@ func _resolve_active_camera() -> Camera3D:
func _resolve_camera() -> Camera3D:
if camera_path != NodePath(""):
var from_path := get_node_or_null(camera_path) as Camera3D
if from_path:
return from_path
var viewport_cam := get_viewport().get_camera_3d()
if viewport_cam:
return viewport_cam
var by_name := get_tree().get_root().find_child("Camera3D", true, false) as Camera3D
return by_name
func _update_eye(eye: Node3D, base_pos: Vector3, dir_world: Vector3) -> void:
var parent := eye.get_parent() as Node3D
if parent == null:
return
var dir_local := parent.global_basis.inverse() * dir_world
var flat := Vector2(dir_local.x, dir_local.y)
flat.x *= side_eye_boost
if flat.length() > 1.0:
flat = flat.normalized()
var offset := Vector3(flat.x, flat.y, 0.0) * max_offset
eye.position = base_pos + offset
func _update_head(dir_world: Vector3) -> void:
var parent := _head.get_parent() as Node3D
if parent == null:
return
var dir_local := parent.global_basis.inverse() * dir_world
var yaw := atan2(-dir_local.x, -dir_local.z)
var pitch := atan2(dir_local.y, -dir_local.z)
yaw = clamp(yaw, deg_to_rad(-head_max_yaw_deg), deg_to_rad(head_max_yaw_deg))
pitch = clamp(pitch, deg_to_rad(-head_max_pitch_deg), deg_to_rad(head_max_pitch_deg))
var target := Vector3(_head_base_rot.x + pitch, _head_base_rot.y + yaw, _head_base_rot.z)
_head.rotation.x = lerp_angle(_head.rotation.x, target.x, head_turn_speed * get_process_delta_time())
_head.rotation.y = lerp_angle(_head.rotation.y, target.y, head_turn_speed * get_process_delta_time())
if camera_path != NodePath(""):
var from_path := get_node_or_null(camera_path) as Camera3D
if from_path:
return from_path
var viewport_cam := get_viewport().get_camera_3d()
if viewport_cam:
return viewport_cam
var by_name := get_tree().get_root().find_child("Camera3D", true, false) as Camera3D
return by_name
func _update_eye(eye: Node3D, base_pos: Vector3, dir_world: Vector3) -> void:
var parent := eye.get_parent() as Node3D
if parent == null:
return
var dir_local := parent.global_basis.inverse() * dir_world
var flat := Vector2(dir_local.x, dir_local.y)
flat.x *= side_eye_boost
if flat.length() > 1.0:
flat = flat.normalized()
var offset := Vector3(flat.x, flat.y, 0.0) * max_offset
eye.position = base_pos + offset
func _update_head(dir_world: Vector3) -> void:
var parent := _head.get_parent() as Node3D
if parent == null:
return
var dir_local := parent.global_basis.inverse() * dir_world
var yaw := atan2(-dir_local.x, -dir_local.z)
var pitch := atan2(dir_local.y, -dir_local.z)
yaw = clamp(yaw, deg_to_rad(-head_max_yaw_deg), deg_to_rad(head_max_yaw_deg))
pitch = clamp(pitch, deg_to_rad(-head_max_pitch_deg), deg_to_rad(head_max_pitch_deg))
var target := Vector3(_head_base_rot.x + pitch, _head_base_rot.y + yaw, _head_base_rot.z)
_head.rotation.x = lerp_angle(_head.rotation.x, target.x, head_turn_speed * get_process_delta_time())
_head.rotation.y = lerp_angle(_head.rotation.y, target.y, head_turn_speed * get_process_delta_time())
+101 -101
View File
@@ -2,119 +2,119 @@
[ext_resource type="Script" path="res://scenes/Characters/repo_bot.gd" id="1_repo_bot"]
[ext_resource type="Script" path="res://scenes/Interaction/dialog_trigger_area.gd" id="2_dialog"]
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_body"]
albedo_color = Color(0.78, 0.8, 0.82, 1)
metallic = 0.2
roughness = 0.35
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_accent"]
albedo_color = Color(0.25, 0.82, 0.55, 1)
emission_enabled = true
emission = Color(0.25, 0.82, 0.55, 1)
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_eye_white"]
albedo_color = Color(0.95, 0.95, 0.95, 1)
roughness = 0.2
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_pupil"]
albedo_color = Color(0.02, 0.02, 0.02, 1)
roughness = 0.8
[sub_resource type="CapsuleMesh" id="CapsuleMesh_body"]
radius = 0.25
height = 0.6
material = SubResource("StandardMaterial3D_body")
[sub_resource type="SphereMesh" id="SphereMesh_head"]
radius = 0.22
height = 0.44
material = SubResource("StandardMaterial3D_body")
[sub_resource type="SphereMesh" id="SphereMesh_eye_white"]
radius = 0.075
height = 0.15
material = SubResource("StandardMaterial3D_eye_white")
[sub_resource type="SphereMesh" id="SphereMesh_pupil"]
radius = 0.028
height = 0.056
material = SubResource("StandardMaterial3D_pupil")
[sub_resource type="CylinderMesh" id="CylinderMesh_limb"]
top_radius = 0.06
bottom_radius = 0.06
height = 0.35
material = SubResource("StandardMaterial3D_body")
[sub_resource type="BoxMesh" id="BoxMesh_pack"]
size = Vector3(0.26, 0.3, 0.12)
material = SubResource("StandardMaterial3D_accent")
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_body"]
albedo_color = Color(0.78, 0.8, 0.82, 1)
metallic = 0.2
roughness = 0.35
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_accent"]
albedo_color = Color(0.25, 0.82, 0.55, 1)
emission_enabled = true
emission = Color(0.25, 0.82, 0.55, 1)
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_eye_white"]
albedo_color = Color(0.95, 0.95, 0.95, 1)
roughness = 0.2
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_pupil"]
albedo_color = Color(0.02, 0.02, 0.02, 1)
roughness = 0.8
[sub_resource type="CapsuleMesh" id="CapsuleMesh_body"]
radius = 0.25
height = 0.6
material = SubResource("StandardMaterial3D_body")
[sub_resource type="SphereMesh" id="SphereMesh_head"]
radius = 0.22
height = 0.44
material = SubResource("StandardMaterial3D_body")
[sub_resource type="SphereMesh" id="SphereMesh_eye_white"]
radius = 0.075
height = 0.15
material = SubResource("StandardMaterial3D_eye_white")
[sub_resource type="SphereMesh" id="SphereMesh_pupil"]
radius = 0.028
height = 0.056
material = SubResource("StandardMaterial3D_pupil")
[sub_resource type="CylinderMesh" id="CylinderMesh_limb"]
top_radius = 0.06
bottom_radius = 0.06
height = 0.35
material = SubResource("StandardMaterial3D_body")
[sub_resource type="BoxMesh" id="BoxMesh_pack"]
size = Vector3(0.26, 0.3, 0.12)
material = SubResource("StandardMaterial3D_accent")
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_body"]
radius = 0.3
height = 1.1
[sub_resource type="SphereShape3D" id="SphereShape3D_interact"]
radius = 1.7
[node name="RepoBot" type="Node3D"]
script = ExtResource("1_repo_bot")
[node name="RepoBot" type="Node3D"]
script = ExtResource("1_repo_bot")
[node name="Body" type="RigidBody3D" parent="."]
mass = 1.5
axis_lock_angular_x = true
axis_lock_angular_z = true
angular_damp = 8.0
linear_damp = 0.5
[node name="CollisionShape3D" type="CollisionShape3D" parent="Body"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.55, 0)
shape = SubResource("CapsuleShape3D_body")
[node name="Torso" type="MeshInstance3D" parent="Body"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0)
mesh = SubResource("CapsuleMesh_body")
[node name="HeadPivot" type="Node3D" parent="Body"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.95, 0)
[node name="Head" type="MeshInstance3D" parent="Body/HeadPivot"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0)
mesh = SubResource("SphereMesh_head")
[node name="EyeLeft" type="MeshInstance3D" parent="Body/HeadPivot"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.09, 0, -0.205)
mesh = SubResource("SphereMesh_eye_white")
[node name="Pupil" type="MeshInstance3D" parent="Body/HeadPivot/EyeLeft"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -0.06)
mesh = SubResource("SphereMesh_pupil")
[node name="EyeRight" type="MeshInstance3D" parent="Body/HeadPivot"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.09, 0, -0.205)
mesh = SubResource("SphereMesh_eye_white")
[node name="Pupil" type="MeshInstance3D" parent="Body/HeadPivot/EyeRight"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -0.06)
mesh = SubResource("SphereMesh_pupil")
[node name="ArmLeft" type="MeshInstance3D" parent="Body"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.32, 0.55, 0)
mesh = SubResource("CylinderMesh_limb")
[node name="ArmRight" type="MeshInstance3D" parent="Body"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.32, 0.55, 0)
mesh = SubResource("CylinderMesh_limb")
[node name="LegLeft" type="MeshInstance3D" parent="Body"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.12, 0.15, 0)
mesh = SubResource("CylinderMesh_limb")
[node name="LegRight" type="MeshInstance3D" parent="Body"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.12, 0.15, 0)
mesh = SubResource("CylinderMesh_limb")
[node name="CollisionShape3D" type="CollisionShape3D" parent="Body"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.55, 0)
shape = SubResource("CapsuleShape3D_body")
[node name="Torso" type="MeshInstance3D" parent="Body"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0)
mesh = SubResource("CapsuleMesh_body")
[node name="HeadPivot" type="Node3D" parent="Body"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.95, 0)
[node name="Head" type="MeshInstance3D" parent="Body/HeadPivot"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0)
mesh = SubResource("SphereMesh_head")
[node name="EyeLeft" type="MeshInstance3D" parent="Body/HeadPivot"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.09, 0, -0.205)
mesh = SubResource("SphereMesh_eye_white")
[node name="Pupil" type="MeshInstance3D" parent="Body/HeadPivot/EyeLeft"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -0.06)
mesh = SubResource("SphereMesh_pupil")
[node name="EyeRight" type="MeshInstance3D" parent="Body/HeadPivot"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.09, 0, -0.205)
mesh = SubResource("SphereMesh_eye_white")
[node name="Pupil" type="MeshInstance3D" parent="Body/HeadPivot/EyeRight"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -0.06)
mesh = SubResource("SphereMesh_pupil")
[node name="ArmLeft" type="MeshInstance3D" parent="Body"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.32, 0.55, 0)
mesh = SubResource("CylinderMesh_limb")
[node name="ArmRight" type="MeshInstance3D" parent="Body"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.32, 0.55, 0)
mesh = SubResource("CylinderMesh_limb")
[node name="LegLeft" type="MeshInstance3D" parent="Body"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.12, 0.15, 0)
mesh = SubResource("CylinderMesh_limb")
[node name="LegRight" type="MeshInstance3D" parent="Body"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.12, 0.15, 0)
mesh = SubResource("CylinderMesh_limb")
[node name="Backpack" type="MeshInstance3D" parent="Body"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.6, 0.22)
mesh = SubResource("BoxMesh_pack")