Spaces:
Runtime error
Runtime error
| """Nodes, conforming to the glTF 2.0 standards as specified in | |
| https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#reference-node | |
| Author: Matthew Matl | |
| """ | |
| import numpy as np | |
| import trimesh.transformations as transformations | |
| from .camera import Camera | |
| from .mesh import Mesh | |
| from .light import Light | |
| class Node(object): | |
| """A node in the node hierarchy. | |
| Parameters | |
| ---------- | |
| name : str, optional | |
| The user-defined name of this object. | |
| camera : :class:`Camera`, optional | |
| The camera in this node. | |
| children : list of :class:`Node` | |
| The children of this node. | |
| skin : int, optional | |
| The index of the skin referenced by this node. | |
| matrix : (4,4) float, optional | |
| A floating-point 4x4 transformation matrix. | |
| mesh : :class:`Mesh`, optional | |
| The mesh in this node. | |
| rotation : (4,) float, optional | |
| The node's unit quaternion in the order (x, y, z, w), where | |
| w is the scalar. | |
| scale : (3,) float, optional | |
| The node's non-uniform scale, given as the scaling factors along the x, | |
| y, and z axes. | |
| translation : (3,) float, optional | |
| The node's translation along the x, y, and z axes. | |
| weights : (n,) float | |
| The weights of the instantiated Morph Target. Number of elements must | |
| match number of Morph Targets of used mesh. | |
| light : :class:`Light`, optional | |
| The light in this node. | |
| """ | |
| def __init__(self, | |
| name=None, | |
| camera=None, | |
| children=None, | |
| skin=None, | |
| matrix=None, | |
| mesh=None, | |
| rotation=None, | |
| scale=None, | |
| translation=None, | |
| weights=None, | |
| light=None): | |
| # Set defaults | |
| if children is None: | |
| children = [] | |
| self._matrix = None | |
| self._scale = None | |
| self._rotation = None | |
| self._translation = None | |
| if matrix is None: | |
| if rotation is None: | |
| rotation = np.array([0.0, 0.0, 0.0, 1.0]) | |
| if translation is None: | |
| translation = np.zeros(3) | |
| if scale is None: | |
| scale = np.ones(3) | |
| self.rotation = rotation | |
| self.translation = translation | |
| self.scale = scale | |
| else: | |
| self.matrix = matrix | |
| self.name = name | |
| self.camera = camera | |
| self.children = children | |
| self.skin = skin | |
| self.mesh = mesh | |
| self.weights = weights | |
| self.light = light | |
| def name(self): | |
| """str : The user-defined name of this object. | |
| """ | |
| return self._name | |
| def name(self, value): | |
| if value is not None: | |
| value = str(value) | |
| self._name = value | |
| def camera(self): | |
| """:class:`Camera` : The camera in this node. | |
| """ | |
| return self._camera | |
| def camera(self, value): | |
| if value is not None and not isinstance(value, Camera): | |
| raise TypeError('Value must be a camera') | |
| self._camera = value | |
| def children(self): | |
| """list of :class:`Node` : The children of this node. | |
| """ | |
| return self._children | |
| def children(self, value): | |
| self._children = value | |
| def skin(self): | |
| """int : The skin index for this node. | |
| """ | |
| return self._skin | |
| def skin(self, value): | |
| self._skin = value | |
| def mesh(self): | |
| """:class:`Mesh` : The mesh in this node. | |
| """ | |
| return self._mesh | |
| def mesh(self, value): | |
| if value is not None and not isinstance(value, Mesh): | |
| raise TypeError('Value must be a mesh') | |
| self._mesh = value | |
| def light(self): | |
| """:class:`Light` : The light in this node. | |
| """ | |
| return self._light | |
| def light(self, value): | |
| if value is not None and not isinstance(value, Light): | |
| raise TypeError('Value must be a light') | |
| self._light = value | |
| def rotation(self): | |
| """(4,) float : The xyzw quaternion for this node. | |
| """ | |
| return self._rotation | |
| def rotation(self, value): | |
| value = np.asanyarray(value) | |
| if value.shape != (4,): | |
| raise ValueError('Quaternion must be a (4,) vector') | |
| if np.abs(np.linalg.norm(value) - 1.0) > 1e-3: | |
| raise ValueError('Quaternion must have norm == 1.0') | |
| self._rotation = value | |
| self._matrix = None | |
| def translation(self): | |
| """(3,) float : The translation for this node. | |
| """ | |
| return self._translation | |
| def translation(self, value): | |
| value = np.asanyarray(value) | |
| if value.shape != (3,): | |
| raise ValueError('Translation must be a (3,) vector') | |
| self._translation = value | |
| self._matrix = None | |
| def scale(self): | |
| """(3,) float : The scale for this node. | |
| """ | |
| return self._scale | |
| def scale(self, value): | |
| value = np.asanyarray(value) | |
| if value.shape != (3,): | |
| raise ValueError('Scale must be a (3,) vector') | |
| self._scale = value | |
| self._matrix = None | |
| def matrix(self): | |
| """(4,4) float : The homogenous transform matrix for this node. | |
| Note that this matrix's elements are not settable, | |
| it's just a copy of the internal matrix. You can set the whole | |
| matrix, but not an individual element. | |
| """ | |
| if self._matrix is None: | |
| self._matrix = self._m_from_tqs( | |
| self.translation, self.rotation, self.scale | |
| ) | |
| return self._matrix.copy() | |
| def matrix(self, value): | |
| value = np.asanyarray(value) | |
| if value.shape != (4,4): | |
| raise ValueError('Matrix must be a 4x4 numpy ndarray') | |
| if not np.allclose(value[3,:], np.array([0.0, 0.0, 0.0, 1.0])): | |
| raise ValueError('Bottom row of matrix must be [0,0,0,1]') | |
| self.rotation = Node._q_from_m(value) | |
| self.scale = Node._s_from_m(value) | |
| self.translation = Node._t_from_m(value) | |
| self._matrix = value | |
| def _t_from_m(m): | |
| return m[:3,3] | |
| def _r_from_m(m): | |
| U = m[:3,:3] | |
| norms = np.linalg.norm(U.T, axis=1) | |
| return U / norms | |
| def _q_from_m(m): | |
| M = np.eye(4) | |
| M[:3,:3] = Node._r_from_m(m) | |
| q_wxyz = transformations.quaternion_from_matrix(M) | |
| return np.roll(q_wxyz, -1) | |
| def _s_from_m(m): | |
| return np.linalg.norm(m[:3,:3].T, axis=1) | |
| def _r_from_q(q): | |
| q_wxyz = np.roll(q, 1) | |
| return transformations.quaternion_matrix(q_wxyz)[:3,:3] | |
| def _m_from_tqs(t, q, s): | |
| S = np.eye(4) | |
| S[:3,:3] = np.diag(s) | |
| R = np.eye(4) | |
| R[:3,:3] = Node._r_from_q(q) | |
| T = np.eye(4) | |
| T[:3,3] = t | |
| return T.dot(R.dot(S)) | |