cs686's picture
Deploy ARDY ZeroGPU Blender motion API
c1e2af3 verified
Raw
History Blame Contribute Delete
10.2 kB
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
import re
from typing import Optional
import numpy as np
class BvhNode:
"""Abstraction for bvh node."""
def __init__(self, value=[], parent=None):
self.value = value
self.children = []
self.parent = parent
if self.parent:
self.parent.add_child(self)
def add_child(self, item):
item.parent = self
self.children.append(item)
def filter(self, key):
for child in self.children:
if child.value[0] == key:
yield child
def __iter__(self):
for child in self.children:
yield child
def __getitem__(self, key):
for child in self.children:
for index, item in enumerate(child.value):
if item == key:
if index + 1 >= len(child.value):
return None
else:
return child.value[index + 1 :]
raise IndexError("key {} not found".format(key))
def __repr__(self):
return str(" ".join(self.value))
@property
def name(self):
return self.value[1]
class Bvh:
"""Abstraction for bvh."""
def __init__(self, data: str, backend: Optional[str] = "graph"):
"""
Args:
data (str): bvh file content
"""
self.data = data
self.root = BvhNode()
self.frames = []
self.backend = backend
self.tokenize()
if self.backend == "np":
# cache important info for quick access later
self.build_data_array()
elif self.backend == "graph":
pass
else:
raise ValueError(f"Unknown backend for BVH loading: {backend}")
def build_data_array(self):
joints = self.get_joints()
self.joint2idx = dict()
self.joint2channels = dict()
cur_idx = 0
for joint in joints:
self.joint2idx[joint.value[1]] = cur_idx
cur_idx += int(joint["CHANNELS"][0])
self.joint2channels[joint.value[1]] = joint["CHANNELS"][1:]
self.np_data_array = np.array(self.frames, dtype=np.float32)
def tokenize(self):
first_round = []
accumulator = ""
for char in self.data:
if char not in ("\n", "\r"):
accumulator += char
elif accumulator:
first_round.append(re.split("\\s+", accumulator.strip()))
accumulator = ""
node_stack = [self.root]
frame_time_found = False
node = None
for item in first_round:
if frame_time_found:
self.frames.append(item)
continue
key = item[0]
if key == "{":
node_stack.append(node)
elif key == "}":
node_stack.pop()
else:
node = BvhNode(item)
# print("new node: ", node, "\nparent: ", node_stack[-1])
node_stack[-1].add_child(node)
if item[0] == "Frame" and item[1] == "Time:":
frame_time_found = True
def search(self, *items):
found_nodes = []
def check_children(node):
if len(node.value) >= len(items):
failed = False
for index, item in enumerate(items):
if node.value[index] != item:
failed = True
break
if not failed:
found_nodes.append(node)
for child in node:
check_children(child)
check_children(self.root)
return found_nodes
def get_joints(self):
joints = []
def iterate_joints(joint):
joints.append(joint)
for child in joint.filter("JOINT"):
iterate_joints(child)
iterate_joints(next(self.root.filter("ROOT")))
return joints
def get_joints_names(self):
joints = []
def iterate_joints(joint):
joints.append(joint.value[1])
for child in joint.filter("JOINT"):
iterate_joints(child)
iterate_joints(next(self.root.filter("ROOT")))
return joints
def joint_direct_children(self, name):
joint = self.get_joint(name)
return [child for child in joint.filter("JOINT")]
def get_joint_index(self, name):
return self.get_joints().index(self.get_joint(name))
def get_joint(self, name):
found = self.search("ROOT", name)
if not found:
found = self.search("JOINT", name)
if found:
return found[0]
raise LookupError("joint not found")
def joint_offset(self, name, idx=[0, 1, 2]):
joint = self.get_joint(name)
offset = joint["OFFSET"]
if len(offset) < max(idx):
return None
return (float(offset[idx[0]]), float(offset[idx[1]]), float(offset[idx[2]]))
def joint_offset_rot(self, name):
return self.joint_offset(name, idx=[3, 4, 5])
def joint_channels(self, name):
if self.backend == "np":
return self.joint2channels[name]
else:
joint = self.get_joint(name)
return joint["CHANNELS"][1:]
def get_joint_channels_index(self, joint_name):
if self.backend == "np":
return self.joint2idx[joint_name]
else:
index = 0
for joint in self.get_joints():
if joint.value[1] == joint_name:
return index
index += int(joint["CHANNELS"][0])
raise LookupError("joint not found")
def get_joint_channel_index(self, joint, channel):
channels = self.joint_channels(joint)
if channel in channels:
channel_index = channels.index(channel)
else:
raise ValueError(f"Channel {channel} not found in {channels}")
return channel_index
def frame_joint_channel(self, frame_index, joint, channel, value=None):
"""Get single frame data for on specific joint and one specific channel (e.g. Xrotation)."""
joint_index = self.get_joint_channels_index(joint)
channel_index = self.get_joint_channel_index(joint, channel)
if channel_index == -1 and value is not None:
return value
if self.backend == "np":
return self.np_data_array[frame_index, joint_index + channel_index]
else:
return float(self.frames[frame_index][joint_index + channel_index])
def frame_joint_channels(self, frame_index, joint, channels, value=None):
"""Get single frame data for on specific joint from multiple specific channels (e.g.
Xrotation, Yrotation, Zrotation)."""
values = []
joint_index = self.get_joint_channels_index(joint)
if self.backend == "np":
channel_idx = [self.get_joint_channel_index(joint, channel) for channel in channels]
channel_idx = np.array(channel_idx) + joint_index
values = self.np_data_array[frame_index, channel_idx]
else:
for channel in channels:
channel_index = self.get_joint_channel_index(joint, channel)
if channel_index == -1 and value is not None:
values.append(value)
else:
values.append(float(self.frames[frame_index][joint_index + channel_index]))
return values
def frames_joint_channels(self, joint, channels, value=None):
"""Get all frame data for one joint from multiple channels (e.g. Xrotation, Yrotation,
Zrotation)."""
joint_index = self.get_joint_channels_index(joint)
if self.backend == "np":
channel_idx = [self.get_joint_channel_index(joint, channel) for channel in channels]
channel_idx = np.array(channel_idx) + joint_index
all_frames = self.np_data_array[:, channel_idx]
else:
all_frames = []
for frame in self.frames:
values = []
for channel in channels:
channel_index = self.get_joint_channel_index(joint, channel)
if channel_index == -1 and value is not None:
values.append(value)
else:
values.append(float(frame[joint_index + channel_index]))
all_frames.append(values)
return all_frames
def frames_joints_channels(self, joint_names, channels):
"""Get all frames for all specified joints with one specified set of channels."""
if self.backend != "np":
raise NotImplementedError("Only np backend is supported for this function")
joint_indices = [(joint_name, self.joint2idx[joint_name]) for joint_name in joint_names]
data_indices = []
for joint_name, joint_idx in joint_indices:
channel_indices = [self.get_joint_channel_index(joint_name, channel) for channel in channels]
data_indices.extend([joint_idx + channel_idx for channel_idx in channel_indices])
all_frames = self.np_data_array[:, data_indices]
all_frames = all_frames.reshape(-1, len(joint_names), len(channels))
return all_frames
def joint_parent(self, name):
joint = self.get_joint(name)
if joint.parent == self.root:
return None
return joint.parent
def joint_parent_index(self, name):
joint = self.get_joint(name)
if joint.parent == self.root:
return -1
return self.get_joints().index(joint.parent)
@property
def nframes(self):
try:
return int(next(self.root.filter("Frames:")).value[1])
except StopIteration:
raise LookupError("number of frames not found")
@property
def frame_time(self):
try:
return float(next(self.root.filter("Frame")).value[2])
except StopIteration:
raise LookupError("frame time not found")