extends SceneTree ## One photograph of a character holding each weapon, from far enough away to ## see the SILHOUETTE. ## ## godot --path . --windowed --resolution 640x900 \ ## -s res://debug/hold_capture.gd -- [skin] ## ## The rig lab's camera is a close-up on the hands, which is right for tuning a ## grip and useless for the question this feature exists to answer: can you tell ## what someone is carrying from across a map. That is a whole-body question, so ## this frames the whole body, side-on, where the difference between a shouldered ## tube and a low-ready rifle actually lives. ## ## Side-on and not three-quarter, deliberately. A profile is the harshest test of ## a hold — it shows exactly how far the weapon sits from the shoulder and how ## the head is tilted, with no foreshortening to hide behind. const WEAPONS := ["m4", "mp7", "awp", "double_barrel_shotgun", "rocket_launcher", "knife"] var _out := "." var _skin := "taila" func _initialize() -> void: var args := OS.get_cmdline_user_args() if args.size() > 0: _out = String(args[0]) if args.size() > 1: _skin = String(args[1]) _run() func _run() -> void: # `_initialize` runs BEFORE the autoloads' `_ready`, so SkinManager's table is # still empty here and even its "default" fallback is missing — asking it for # a skin at this point fails on the fallback rather than on the skin asked # for, which reads as "no skin 'taila'" and sends you looking in the wrong # place entirely. await process_frame var world := Node3D.new() root.add_child(world) var env := WorldEnvironment.new() env.environment = LevelEnvironment.make_environment("sunset") world.add_child(env) var key := DirectionalLight3D.new() key.rotation_degrees = Vector3(-38, 155, 0) key.light_energy = 2.0 world.add_child(key) var fill := DirectionalLight3D.new() fill.rotation_degrees = Vector3(-20, -40, 0) fill.light_energy = 0.7 fill.light_color = Color(0.6, 0.7, 1.0) world.add_child(fill) var cam := Camera3D.new() world.add_child(cam) # Side-on, at chest height, far enough back that the whole figure and the # whole weapon are in frame. cam.position = Vector3(3.4, 1.05, 0.15) cam.look_at(Vector3(0, 0.95, 0)) cam.fov = 42.0 var mgr = root.get_node_or_null("SkinManager") if mgr == null: print("hold_capture: no SkinManager") quit(1) return var skin = mgr.get_skin(_skin) if skin == null: print("hold_capture: no skin '%s'" % _skin) quit(1) return for weapon in WEAPONS: var model := SkinnedPlayerModel.new() model.skin_id = _skin model.first_person_mode = false world.add_child(model) model.load_model(skin.model_path) for _i in 40: await process_frame model.set_weapon("res://weapons/%s.gd" % weapon) model.update_state("idle", 0.0, false) # The hold blends in at ~8/s and the weapon measures itself on `ready`, # so give it several time constants before believing the pose. for _i in 100: model.update_state("idle", 0.0, false) await process_frame await process_frame var img := root.get_texture().get_image() var path := "%s/hold_%s.png" % [_out, weapon] img.save_png(path) print("hold_capture: saved ", path) model.queue_free() for _i in 4: await process_frame quit(0)