File size: 3,363 Bytes
7e120dd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
from pathlib import Path

import bpy
import numpy as np
import math
from mathutils import Matrix, Vector

# Hardcode your NPZ path here. Use raw string or forward slashes to avoid backslash escapes.
NPZ_PATH = Path(r"D:\Users\Adam\Downloads\VRM_JG6Z7WA_clip_0_global_smplx.npz")
CAMERA_NAME = "GenmoCamera"
SPHERE_NAME = "TestSphere"
FPS = 30
ROTATE_LOCAL_Z_DEG = 180.0
ROTATE_LOCAL_Y_DEG = 180.0


def _ensure_camera(name: str) -> bpy.types.Object:
    cam_obj = bpy.data.objects.get(name)
    if cam_obj is None:
        cam_data = bpy.data.cameras.new(name)
        cam_obj = bpy.data.objects.new(name, cam_data)
        bpy.context.collection.objects.link(cam_obj)
    return cam_obj


def _apply_fov(cam_obj: bpy.types.Object, npz_data: np.lib.npyio.NpzFile) -> None:
    cam_data = cam_obj.data
    fov_y_deg = float(npz_data["fov_y_deg"]) if "fov_y_deg" in npz_data else None
    fov_x_deg = float(npz_data["fov_x_deg"]) if "fov_x_deg" in npz_data else None

    if (fov_x_deg is None or fov_y_deg is None) and "K_fullimg" in npz_data:
        K_fullimg = npz_data["K_fullimg"]
        if K_fullimg.ndim == 3:
            K0 = K_fullimg[0]
        else:
            K0 = K_fullimg
        fx = float(K0[0, 0])
        fy = float(K0[1, 1])
        cx = float(K0[0, 2])
        cy = float(K0[1, 2])
        width = 2.0 * cx
        height = 2.0 * cy
        if fov_x_deg is None and fx > 0 and width > 0:
            fov_x_deg = math.degrees(2.0 * math.atan(width / (2.0 * fx)))
        if fov_y_deg is None and fy > 0 and height > 0:
            fov_y_deg = math.degrees(2.0 * math.atan(height / (2.0 * fy)))

    if fov_y_deg is not None:
        cam_data.angle_y = math.radians(float(fov_y_deg))
    elif fov_x_deg is not None:
        cam_data.angle = math.radians(float(fov_x_deg))


def _ensure_sphere(name: str) -> bpy.types.Object:
    obj = bpy.data.objects.get(name)
    if obj is None:
        bpy.ops.mesh.primitive_uv_sphere_add(radius=0.2, location=(0.0, 0.0, 0.0))
        obj = bpy.context.active_object
        obj.name = name
    return obj


def main():
    npz_path = NPZ_PATH
    if not npz_path.is_absolute():
        npz_path = Path(bpy.path.abspath("//")) / npz_path
    data = np.load(str(npz_path))
    if "camera_transform" not in data:
        raise ValueError("NPZ is missing 'camera_transform'")
    camera_transform = data["camera_transform"]  # (F, 4, 4)

    scene = bpy.context.scene
    scene.render.fps = FPS
    scene.frame_start = 1
    scene.frame_end = int(camera_transform.shape[0])

    cam_obj = _ensure_camera(CAMERA_NAME)
    scene.camera = cam_obj
    _ensure_sphere(SPHERE_NAME)
    _apply_fov(cam_obj, data)

    for i, T in enumerate(camera_transform):
        frame = i + 1
        mat = Matrix(T.tolist())
        rot_z = Matrix.Rotation(math.radians(ROTATE_LOCAL_Z_DEG), 3, Vector((0.0, 0.0, 1.0)))
        rot_y = Matrix.Rotation(math.radians(ROTATE_LOCAL_Y_DEG), 3, Vector((0.0, 1.0, 0.0)))
        rot_local = rot_y @ rot_z  # first local Z, then local Y
        new_rot = mat.to_3x3() @ rot_local
        new_mat = new_rot.to_4x4()
        new_mat.translation = mat.to_translation()
        cam_obj.matrix_world = new_mat
        cam_obj.keyframe_insert(data_path="location", frame=frame)
        cam_obj.keyframe_insert(data_path="rotation_euler", frame=frame)


if __name__ == "__main__":
    main()