extends SceneTree ## Quick visual check for the billboard crowd: builds the generated fallback stand and ## captures two frames a moment apart (so the flipbook visibly advances) from an angle ## (so we can confirm the sprites turn to face the camera). ## ## godot --script tools/preview_crowd.gd ## Output: tools/preview/crowd_a.png, crowd_b.png const OUT := "res://tools/preview" func _init() -> void: _run.call_deferred() func _run() -> void: DirAccess.make_dir_recursive_absolute(OUT) var we := WorldEnvironment.new() var env := Environment.new() env.background_mode = Environment.BG_COLOR env.background_color = Color(0.10, 0.12, 0.16) we.environment = env root.add_child(we) var crowd := MultiMeshInstance3D.new() crowd.set_script(load("res://crowd_billboards.gd")) crowd.set("source_path", NodePath()) # force the generated fallback stand crowd.set("fallback_count", 90) root.add_child(crowd) var cam := Camera3D.new() cam.fov = 62.0 cam.current = true root.add_child(cam) # Outside the ring edge, elevated, looking across the centre: the NEAR arc has its # back to us (facing inward), the FAR arc faces us — so this shot proves both the # per-segment inward facing and the front/back sheet selection. cam.look_at_from_position(Vector3(0.0, 16.0, 44.0), Vector3(0.0, 3.0, 0.0), Vector3.UP) await create_timer(0.15).timeout _shot("crowd_a.png") await create_timer(0.45).timeout # ~4–5 clap frames later _shot("crowd_b.png") quit(0) func _shot(name: String) -> void: var img := root.get_viewport().get_texture().get_image() if img == null: print("capture failed: ", name) return img.save_png(OUT + "/" + name) print("captured ", name, " ", img.get_width(), "x", img.get_height())