feat: 运动相机支持 + move_camera.txt 关键帧驱动
input.txt 新增: move_camera: 0 # 0=固定视角, 1=按 move_camera.txt 运动 move_camera.txt 格式(4列:帧号 距离 俯仰角 方位角): 0 40.0 0 0 100 80.0 -30 180 200 40.0 0 360 display.txt header 传递 camera_keyframes JSON 数组, draw.py 按帧时间线性插值驱动相机运动(循环播放)。
This commit is contained in:
+27
-1
@@ -32,6 +32,31 @@ def _fmt_alpha(v):
|
||||
return str(float(v))
|
||||
|
||||
|
||||
def _load_camera_kf(config, runtime_base):
|
||||
"""加载 move_camera.txt → JSON 字符串,供 display.txt header 使用。"""
|
||||
if not int(config.get("move_camera", 0)):
|
||||
return ""
|
||||
cam_file = str(config.get("move_camera_file",
|
||||
os.path.join("input", "move_camera.txt")))
|
||||
cam_path = cam_file
|
||||
if not os.path.isabs(cam_file):
|
||||
cam_path = os.path.join(runtime_base, cam_file)
|
||||
if not os.path.exists(cam_path):
|
||||
return ""
|
||||
import json
|
||||
kfs = []
|
||||
with open(cam_path, "r", encoding="utf-8") as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if not line or line.startswith("#"):
|
||||
continue
|
||||
parts = line.split()
|
||||
if len(parts) >= 4:
|
||||
kfs.append([int(parts[0]), float(parts[1]),
|
||||
float(parts[2]), float(parts[3])])
|
||||
return json.dumps(kfs) if kfs else ""
|
||||
|
||||
|
||||
def read_optional_index(data, key, default_value):
|
||||
"""Read an optional integer index from structured txt metadata."""
|
||||
if key not in data:
|
||||
@@ -257,7 +282,8 @@ def run_case(config_path, runtime_base, input_dir="input", output_dir="output",
|
||||
"atom_radii": _fmt_alpha(data.get("atom_radii", [])),
|
||||
"camera_distance": str(config.get("camera_distance", 40.0)),
|
||||
"camera_elevation": str(config.get("camera_elevation", 0)),
|
||||
"camera_azimuth": str(config.get("camera_azimuth", 0))}
|
||||
"camera_azimuth": str(config.get("camera_azimuth", 0)),
|
||||
"camera_keyframes": _load_camera_kf(config, str(runtime_base))}
|
||||
|
||||
n_frames = len(indices)
|
||||
compute.save_display_txt(
|
||||
|
||||
Reference in New Issue
Block a user