Team Ai
Apppublic

OpenMotionLab/MotionGPT

sourceHugging Facemitupdated 1y agoView on Hugging Face
118likes
node.py264 linesDownload Raw Back to pyrender
1"""Nodes, conforming to the glTF 2.0 standards as specified in2https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#reference-node3 4Author: Matthew Matl5"""6import numpy as np7 8import trimesh.transformations as transformations9 10from .camera import Camera11from .mesh import Mesh12from .light import Light13 14 15class Node(object):16    """A node in the node hierarchy.17 18    Parameters19    ----------20    name : str, optional21        The user-defined name of this object.22    camera : :class:`Camera`, optional23        The camera in this node.24    children : list of :class:`Node`25        The children of this node.26    skin : int, optional27        The index of the skin referenced by this node.28    matrix : (4,4) float, optional29        A floating-point 4x4 transformation matrix.30    mesh : :class:`Mesh`, optional31        The mesh in this node.32    rotation : (4,) float, optional33        The node's unit quaternion in the order (x, y, z, w), where34        w is the scalar.35    scale : (3,) float, optional36        The node's non-uniform scale, given as the scaling factors along the x,37        y, and z axes.38    translation : (3,) float, optional39        The node's translation along the x, y, and z axes.40    weights : (n,) float41        The weights of the instantiated Morph Target. Number of elements must42        match number of Morph Targets of used mesh.43    light : :class:`Light`, optional44        The light in this node.45    """46 47    def __init__(self,48                 name=None,49                 camera=None,50                 children=None,51                 skin=None,52                 matrix=None,53                 mesh=None,54                 rotation=None,55                 scale=None,56                 translation=None,57                 weights=None,58                 light=None):59        # Set defaults60        if children is None:61            children = []62 63        self._matrix = None64        self._scale = None65        self._rotation = None66        self._translation = None67        if matrix is None:68            if rotation is None:69                rotation = np.array([0.0, 0.0, 0.0, 1.0])70            if translation is None:71                translation = np.zeros(3)72            if scale is None:73                scale = np.ones(3)74            self.rotation = rotation75            self.translation = translation76            self.scale = scale77        else:78            self.matrix = matrix79 80        self.name = name81        self.camera = camera82        self.children = children83        self.skin = skin84        self.mesh = mesh85        self.weights = weights86        self.light = light87 88    @property89    def name(self):90        """str : The user-defined name of this object.91        """92        return self._name93 94    @name.setter95    def name(self, value):96        if value is not None:97            value = str(value)98        self._name = value99 100    @property101    def camera(self):102        """:class:`Camera` : The camera in this node.103        """104        return self._camera105 106    @camera.setter107    def camera(self, value):108        if value is not None and not isinstance(value, Camera):109            raise TypeError('Value must be a camera')110        self._camera = value111 112    @property113    def children(self):114        """list of :class:`Node` : The children of this node.115        """116        return self._children117 118    @children.setter119    def children(self, value):120        self._children = value121 122    @property123    def skin(self):124        """int : The skin index for this node.125        """126        return self._skin127 128    @skin.setter129    def skin(self, value):130        self._skin = value131 132    @property133    def mesh(self):134        """:class:`Mesh` : The mesh in this node.135        """136        return self._mesh137 138    @mesh.setter139    def mesh(self, value):140        if value is not None and not isinstance(value, Mesh):141            raise TypeError('Value must be a mesh')142        self._mesh = value143 144    @property145    def light(self):146        """:class:`Light` : The light in this node.147        """148        return self._light149 150    @light.setter151    def light(self, value):152        if value is not None and not isinstance(value, Light):153            raise TypeError('Value must be a light')154        self._light = value155 156    @property157    def rotation(self):158        """(4,) float : The xyzw quaternion for this node.159        """160        return self._rotation161 162    @rotation.setter163    def rotation(self, value):164        value = np.asanyarray(value)165        if value.shape != (4,):166            raise ValueError('Quaternion must be a (4,) vector')167        if np.abs(np.linalg.norm(value) - 1.0) > 1e-3:168            raise ValueError('Quaternion must have norm == 1.0')169        self._rotation = value170        self._matrix = None171 172    @property173    def translation(self):174        """(3,) float : The translation for this node.175        """176        return self._translation177 178    @translation.setter179    def translation(self, value):180        value = np.asanyarray(value)181        if value.shape != (3,):182            raise ValueError('Translation must be a (3,) vector')183        self._translation = value184        self._matrix = None185 186    @property187    def scale(self):188        """(3,) float : The scale for this node.189        """190        return self._scale191 192    @scale.setter193    def scale(self, value):194        value = np.asanyarray(value)195        if value.shape != (3,):196            raise ValueError('Scale must be a (3,) vector')197        self._scale = value198        self._matrix = None199 200    @property201    def matrix(self):202        """(4,4) float : The homogenous transform matrix for this node.203 204        Note that this matrix's elements are not settable,205        it's just a copy of the internal matrix. You can set the whole206        matrix, but not an individual element.207        """208        if self._matrix is None:209            self._matrix = self._m_from_tqs(210                self.translation, self.rotation, self.scale211            )212        return self._matrix.copy()213 214    @matrix.setter215    def matrix(self, value):216        value = np.asanyarray(value)217        if value.shape != (4,4):218            raise ValueError('Matrix must be a 4x4 numpy ndarray')219        if not np.allclose(value[3,:], np.array([0.0, 0.0, 0.0, 1.0])):220            raise ValueError('Bottom row of matrix must be [0,0,0,1]')221        self.rotation = Node._q_from_m(value)222        self.scale = Node._s_from_m(value)223        self.translation = Node._t_from_m(value)224        self._matrix = value225 226    @staticmethod227    def _t_from_m(m):228        return m[:3,3]229 230    @staticmethod231    def _r_from_m(m):232        U = m[:3,:3]233        norms = np.linalg.norm(U.T, axis=1)234        return U / norms235 236    @staticmethod237    def _q_from_m(m):238        M = np.eye(4)239        M[:3,:3] = Node._r_from_m(m)240        q_wxyz = transformations.quaternion_from_matrix(M)241        return np.roll(q_wxyz, -1)242 243    @staticmethod244    def _s_from_m(m):245        return np.linalg.norm(m[:3,:3].T, axis=1)246 247    @staticmethod248    def _r_from_q(q):249        q_wxyz = np.roll(q, 1)250        return transformations.quaternion_matrix(q_wxyz)[:3,:3]251 252    @staticmethod253    def _m_from_tqs(t, q, s):254        S = np.eye(4)255        S[:3,:3] = np.diag(s)256 257        R = np.eye(4)258        R[:3,:3] = Node._r_from_q(q)259 260        T = np.eye(4)261        T[:3,3] = t262 263        return T.dot(R.dot(S))264