| import math |
|
|
| import bpy |
| import numpy as np |
|
|
| from mGPT.utils.joints import (humanml3d_joints, humanml3d_kinematic_tree, |
| mmm_joints, mmm_kinematic_tree, |
| mmm_to_smplh_scaling_factor) |
|
|
| |
| from .materials import colored_material_relection_BSDF as colored_material |
|
|
| sat_factor = 1.1 |
|
|
| JOINTS_MATS = [ |
| |
| |
| |
| |
| |
| |
| |
| colored_material(0.3500, 0.0357, 0.0349, saturation_factor=sat_factor), |
| |
| colored_material(0.6500, 0.175, 0.0043, saturation_factor=sat_factor), |
| colored_material(0.0349, 0.3500, 0.0349, saturation_factor=sat_factor), |
| colored_material(0.018, 0.059, 0.600, saturation_factor=sat_factor), |
| colored_material(0.032, 0.325, 0.421, saturation_factor=sat_factor), |
| colored_material(0.3, 0.3, 0.3, saturation_factor=sat_factor), |
| ] |
|
|
|
|
| class Joints: |
|
|
| def __init__(self, |
| data, |
| *, |
| mode, |
| canonicalize, |
| always_on_floor, |
| jointstype="mmm", |
| **kwargs): |
| data = prepare_joints( |
| data, |
| canonicalize=canonicalize, |
| always_on_floor=always_on_floor, |
| jointstype=jointstype, |
| ) |
|
|
| self.data = data |
| self.mode = mode |
|
|
| self.N = len(data) |
|
|
| self.N = len(data) |
| self.trajectory = data[:, 0, [0, 1]] |
|
|
| if jointstype == "mmm": |
| self.kinematic_tree = mmm_kinematic_tree |
| self.joints = mmm_joints |
| self.joinst.append("") |
| elif jointstype == "humanml3d": |
| self.kinematic_tree = humanml3d_kinematic_tree |
| self.joints = humanml3d_joints |
|
|
| self.mat = JOINTS_MATS |
|
|
| def get_sequence_mat(self, frac): |
| return self.mat |
|
|
| def get_root(self, index): |
| return self.data[index][0] |
|
|
| def get_mean_root(self): |
| return self.data[:, 0].mean(0) |
|
|
| def load_in_blender(self, index, mats): |
| skeleton = self.data[index] |
| head_mat = mats[0] |
| body_mat = mats[-1] |
| for lst, mat in zip(self.kinematic_tree, mats): |
| for j1, j2 in zip(lst[:-1], lst[1:]): |
| |
| if self.joints[j2] in [ |
| "BUN", |
| ]: |
| sphere_between(skeleton[j1], skeleton[j2], head_mat) |
| elif self.joints[j2] in [ |
| "LE", |
| "RE", |
| "LW", |
| "RW", |
| ]: |
| cylinder_sphere_between(skeleton[j1], skeleton[j2], 0.040, |
| mat) |
| elif self.joints[j2] in [ |
| "LMrot", |
| "RMrot", |
| "RK", |
| "LK", |
| ]: |
| cylinder_sphere_between(skeleton[j1], skeleton[j2], 0.040, |
| mat) |
| elif self.joints[j2] in [ |
| "LS", |
| "RS", |
| "LF", |
| "RF", |
| ]: |
| cylinder_between(skeleton[j1], skeleton[j2], 0.040, mat) |
| elif self.joints[j2] in ["RK", "LK"]: |
| print(self.joints[j1], self.joints[j2]) |
| |
| sphere(0.14, skeleton[self.joints.index("BLN")], body_mat) |
| sphere_between( |
| skeleton[self.joints.index("BLN")], |
| skeleton[self.joints.index("root")], |
| body_mat, |
| factor=0.28, |
| ) |
| sphere(0.11, skeleton[self.joints.index("root")], body_mat) |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| return ["Cylinder", "Sphere"] |
|
|
| def __len__(self): |
| return self.N |
|
|
|
|
| def softmax(x, softness=1.0, dim=None): |
| maxi, mini = x.max(dim), x.min(dim) |
| return maxi + np.log(softness + np.exp(mini - maxi)) |
|
|
|
|
| def softmin(x, softness=1.0, dim=0): |
| return -softmax(-x, softness=softness, dim=dim) |
|
|
|
|
| def get_forward_direction(poses, jointstype="mmm"): |
| if jointstype == "mmm" or jointstype == "mmmns": |
| joints = mmm_joints |
| elif jointstype == "humanml3d": |
| joints = humanml3d_joints |
| else: |
| raise TypeError("Only supports mmm, mmmns and humanl3d jointstype") |
| |
| LS, RS = joints.index("LS"), joints.index("RS") |
| |
| LH, RH = mmm_joints.index("LH"), mmm_joints.index("RH") |
|
|
| across = (poses[..., RH, :] - poses[..., LH, :] + poses[..., RS, :] - |
| poses[..., LS, :]) |
| forward = np.stack((-across[..., 2], across[..., 0]), axis=-1) |
| forward = forward / np.linalg.norm(forward, axis=-1) |
| return forward |
|
|
|
|
| def cylinder_between(t1, t2, r, mat): |
| x1, y1, z1 = t1 |
| x2, y2, z2 = t2 |
|
|
| dx = x2 - x1 |
| dy = y2 - y1 |
| dz = z2 - z1 |
| dist = math.sqrt(dx**2 + dy**2 + dz**2) |
|
|
| bpy.ops.mesh.primitive_cylinder_add(radius=r, |
| depth=dist, |
| location=(dx / 2 + x1, dy / 2 + y1, |
| dz / 2 + z1)) |
|
|
| phi = math.atan2(dy, dx) |
| theta = math.acos(dz / dist) |
| bpy.context.object.rotation_euler[1] = theta |
| bpy.context.object.rotation_euler[2] = phi |
| |
| bpy.context.object.active_material = mat |
|
|
| bpy.ops.mesh.primitive_uv_sphere_add(radius=r, location=(x1, y1, z1)) |
| bpy.context.object.active_material = mat |
| bpy.ops.mesh.primitive_uv_sphere_add(radius=r, location=(x2, y2, z2)) |
| bpy.context.object.active_material = mat |
|
|
|
|
| def cylinder_sphere_between(t1, t2, r, mat): |
| x1, y1, z1 = t1 |
| x2, y2, z2 = t2 |
| dx = x2 - x1 |
| dy = y2 - y1 |
| dz = z2 - z1 |
| dist = math.sqrt(dx**2 + dy**2 + dz**2) |
| phi = math.atan2(dy, dx) |
| theta = math.acos(dz / dist) |
| dist = dist - 0.2 * r |
| |
| sphere(r * 0.9, t1, mat) |
| sphere(r * 0.9, t2, mat) |
| |
| bpy.ops.mesh.primitive_cylinder_add( |
| radius=r, |
| depth=dist, |
| location=(dx / 2 + x1, dy / 2 + y1, dz / 2 + z1), |
| enter_editmode=True, |
| ) |
| bpy.ops.mesh.select_mode(type="EDGE") |
| bpy.ops.mesh.select_all(action="DESELECT") |
| bpy.ops.mesh.select_face_by_sides(number=32, extend=False) |
| bpy.ops.mesh.bevel(offset=r, segments=8) |
| bpy.ops.object.editmode_toggle(False) |
| |
| bpy.context.object.rotation_euler[1] = theta |
| bpy.context.object.rotation_euler[2] = phi |
| bpy.context.object.active_material = mat |
|
|
|
|
| def sphere(r, t, mat): |
| bpy.ops.mesh.primitive_uv_sphere_add(segments=50, |
| ring_count=50, |
| radius=r, |
| location=t) |
| |
| |
| bpy.context.object.active_material = mat |
|
|
|
|
| def sphere_between(t1, t2, mat, factor=1): |
| x1, y1, z1 = t1 |
| x2, y2, z2 = t2 |
|
|
| dx = x2 - x1 |
| dy = y2 - y1 |
| dz = z2 - z1 |
| dist = math.sqrt(dx**2 + dy**2 + dz**2) * factor |
|
|
| bpy.ops.mesh.primitive_uv_sphere_add( |
| segments=50, |
| ring_count=50, |
| |
| radius=dist, |
| location=(dx / 2 + x1, dy / 2 + y1, dz / 2 + z1)) |
|
|
| |
| bpy.context.object.active_material = mat |
|
|
|
|
| def matrix_of_angles(cos, sin, inv=False): |
| sin = -sin if inv else sin |
| return np.stack((np.stack( |
| (cos, -sin), axis=-1), np.stack((sin, cos), axis=-1)), |
| axis=-2) |
|
|
|
|
| def get_floor(poses, jointstype="mmm"): |
| if jointstype == "mmm" or jointstype == "mmmns": |
| joints = mmm_joints |
| elif jointstype == "humanml3d": |
| joints = humanml3d_joints |
| else: |
| raise TypeError("Only supports mmm, mmmns and humanl3d jointstype") |
| |
| LM, RM = joints.index("LMrot"), joints.index("RMrot") |
| LF, RF = joints.index("LF"), joints.index("RF") |
| ndim = len(poses.shape) |
|
|
| foot_heights = poses[..., (LM, LF, RM, RF), 1].min(-1) |
| floor_height = softmin(foot_heights, softness=0.5, dim=-1) |
| return floor_height[tuple((ndim - 2) * [None])].T |
|
|
|
|
| def canonicalize_joints(joints, jointstype="mmm"): |
| poses = joints.copy() |
|
|
| translation = joints[..., 0, :].copy() |
|
|
| |
| translation[..., 1] = 0 |
| |
| trajectory = translation[..., [0, 2]] |
|
|
| |
| poses[..., 1] -= get_floor(poses, jointstype) |
|
|
| |
| poses[..., [0, 2]] -= trajectory[..., None, :] |
|
|
| |
| trajectory = trajectory - trajectory[..., 0, :] |
|
|
| |
| forward = get_forward_direction(poses[..., 0, :, :], jointstype) |
|
|
| |
| sin, cos = forward[..., 0], forward[..., 1] |
| rotations_inv = matrix_of_angles(cos, sin, inv=True) |
|
|
| |
| trajectory_rotated = np.einsum("...j,...jk->...k", trajectory, |
| rotations_inv) |
|
|
| |
| poses_rotated = np.einsum("...lj,...jk->...lk", poses[..., [0, 2]], |
| rotations_inv) |
| poses_rotated = np.stack( |
| (poses_rotated[..., 0], poses[..., 1], poses_rotated[..., 1]), axis=-1) |
|
|
| |
| poses_rotated[..., (0, 2)] += trajectory_rotated[..., None, :] |
| return poses_rotated |
|
|
|
|
| def prepare_joints(joints, |
| canonicalize=True, |
| always_on_floor=False, |
| jointstype="mmm"): |
| |
| if canonicalize: |
| data = canonicalize_joints(joints, jointstype) |
| else: |
| data = joints |
|
|
| |
| if jointstype == "humanml3d": |
| data = data * mmm_to_smplh_scaling_factor |
| data[..., 1] = - data[..., 1] |
|
|
| |
| data = data[..., [2, 0, 1]] |
|
|
| if jointstype == "mmm": |
| |
| data[..., [1]] = -data[..., [1]] |
|
|
| |
| data -= data[[0], [0], :] |
|
|
| |
| data[..., 2] -= data[..., 2].min() |
|
|
| |
| if always_on_floor: |
| data[..., 2] -= data[..., 2].min(1)[:, None] |
|
|
| return data |
|
|
|
|
| def NormalInDirection(normal, direction, limit=0.5): |
| return direction.dot(normal) > limit |
|
|
|
|
| def GoingUp(normal, limit=0.5): |
| return NormalInDirection(normal, (0, 0, 1), limit) |
|
|
|
|
| def GoingDown(normal, limit=0.5): |
| return NormalInDirection(normal, (0, 0, -1), limit) |
|
|
|
|
| def GoingSide(normal, limit=0.5): |
| return GoingUp(normal, limit) == False and GoingDown(normal, |
| limit) == False |
|
|