Team Ai
Apppublic

OpenMotionLab/MotionGPT

sourceHugging Facemitupdated 1y agoView on Hugging Face
118likes
mesh.py329 linesDownload Raw Back to pyrender
1"""Meshes, conforming to the glTF 2.0 standards as specified in2https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#reference-mesh3 4Author: Matthew Matl5"""6import copy7 8import numpy as np9import trimesh10 11from .primitive import Primitive12from .constants import GLTF13from .material import MetallicRoughnessMaterial14 15 16class Mesh(object):17    """A set of primitives to be rendered.18 19    Parameters20    ----------21    name : str22        The user-defined name of this object.23    primitives : list of :class:`Primitive`24        The primitives associated with this mesh.25    weights : (k,) float26        Array of weights to be applied to the Morph Targets.27    is_visible : bool28        If False, the mesh will not be rendered.29    """30 31    def __init__(self, primitives, name=None, weights=None, is_visible=True):32        self.primitives = primitives33        self.name = name34        self.weights = weights35        self.is_visible = is_visible36 37        self._bounds = None38 39    @property40    def name(self):41        """str : The user-defined name of this object.42        """43        return self._name44 45    @name.setter46    def name(self, value):47        if value is not None:48            value = str(value)49        self._name = value50 51    @property52    def primitives(self):53        """list of :class:`Primitive` : The primitives associated54        with this mesh.55        """56        return self._primitives57 58    @primitives.setter59    def primitives(self, value):60        self._primitives = value61 62    @property63    def weights(self):64        """(k,) float : Weights to be applied to morph targets.65        """66        return self._weights67 68    @weights.setter69    def weights(self, value):70        self._weights = value71 72    @property73    def is_visible(self):74        """bool : Whether the mesh is visible.75        """76        return self._is_visible77 78    @is_visible.setter79    def is_visible(self, value):80        self._is_visible = value81 82    @property83    def bounds(self):84        """(2,3) float : The axis-aligned bounds of the mesh.85        """86        if self._bounds is None:87            bounds = np.array([[np.infty, np.infty, np.infty],88                               [-np.infty, -np.infty, -np.infty]])89            for p in self.primitives:90                bounds[0] = np.minimum(bounds[0], p.bounds[0])91                bounds[1] = np.maximum(bounds[1], p.bounds[1])92            self._bounds = bounds93        return self._bounds94 95    @property96    def centroid(self):97        """(3,) float : The centroid of the mesh's axis-aligned bounding box98        (AABB).99        """100        return np.mean(self.bounds, axis=0)101 102    @property103    def extents(self):104        """(3,) float : The lengths of the axes of the mesh's AABB.105        """106        return np.diff(self.bounds, axis=0).reshape(-1)107 108    @property109    def scale(self):110        """(3,) float : The length of the diagonal of the mesh's AABB.111        """112        return np.linalg.norm(self.extents)113 114    @property115    def is_transparent(self):116        """bool : If True, the mesh is partially-transparent.117        """118        for p in self.primitives:119            if p.is_transparent:120                return True121        return False122 123    @staticmethod124    def from_points(points, colors=None, normals=None,125                    is_visible=True, poses=None):126        """Create a Mesh from a set of points.127 128        Parameters129        ----------130        points : (n,3) float131            The point positions.132        colors : (n,3) or (n,4) float, optional133            RGB or RGBA colors for each point.134        normals : (n,3) float, optionals135            The normal vectors for each point.136        is_visible : bool137            If False, the points will not be rendered.138        poses : (x,4,4)139            Array of 4x4 transformation matrices for instancing this object.140 141        Returns142        -------143        mesh : :class:`Mesh`144            The created mesh.145        """146        primitive = Primitive(147            positions=points,148            normals=normals,149            color_0=colors,150            mode=GLTF.POINTS,151            poses=poses152        )153        mesh = Mesh(primitives=[primitive], is_visible=is_visible)154        return mesh155 156    @staticmethod157    def from_trimesh(mesh, material=None, is_visible=True,158                     poses=None, wireframe=False, smooth=True):159        """Create a Mesh from a :class:`~trimesh.base.Trimesh`.160 161        Parameters162        ----------163        mesh : :class:`~trimesh.base.Trimesh` or list of them164            A triangular mesh or a list of meshes.165        material : :class:`Material`166            The material of the object. Overrides any mesh material.167            If not specified and the mesh has no material, a default material168            will be used.169        is_visible : bool170            If False, the mesh will not be rendered.171        poses : (n,4,4) float172            Array of 4x4 transformation matrices for instancing this object.173        wireframe : bool174            If `True`, the mesh will be rendered as a wireframe object175        smooth : bool176            If `True`, the mesh will be rendered with interpolated vertex177            normals. Otherwise, the mesh edges will stay sharp.178 179        Returns180        -------181        mesh : :class:`Mesh`182            The created mesh.183        """184 185        if isinstance(mesh, (list, tuple, set, np.ndarray)):186            meshes = list(mesh)187        elif isinstance(mesh, trimesh.Trimesh):188            meshes = [mesh]189        else:190            raise TypeError('Expected a Trimesh or a list, got a {}'191                            .format(type(mesh)))192 193        primitives = []194        for m in meshes:195            positions = None196            normals = None197            indices = None198 199            # Compute positions, normals, and indices200            if smooth:201                positions = m.vertices.copy()202                normals = m.vertex_normals.copy()203                indices = m.faces.copy()204            else:205                positions = m.vertices[m.faces].reshape((3 * len(m.faces), 3))206                normals = np.repeat(m.face_normals, 3, axis=0)207 208            # Compute colors, texture coords, and material properties209            color_0, texcoord_0, primitive_material = Mesh._get_trimesh_props(m, smooth=smooth, material=material)210 211            # Override if material is given.212            if material is not None:213                #primitive_material = copy.copy(material)214                primitive_material = copy.deepcopy(material)  # TODO215 216            if primitive_material is None:217                # Replace material with default if needed218                primitive_material = MetallicRoughnessMaterial(219                    alphaMode='BLEND',220                    baseColorFactor=[0.3, 0.3, 0.3, 1.0],221                    metallicFactor=0.2,222                    roughnessFactor=0.8223                )224 225            primitive_material.wireframe = wireframe226 227            # Create the primitive228            primitives.append(Primitive(229                positions=positions,230                normals=normals,231                texcoord_0=texcoord_0,232                color_0=color_0,233                indices=indices,234                material=primitive_material,235                mode=GLTF.TRIANGLES,236                poses=poses237            ))238 239        return Mesh(primitives=primitives, is_visible=is_visible)240 241    @staticmethod242    def _get_trimesh_props(mesh, smooth=False, material=None):243        """Gets the vertex colors, texture coordinates, and material properties244        from a :class:`~trimesh.base.Trimesh`.245        """246        colors = None247        texcoords = None248 249        # If the trimesh visual is undefined, return none for both250        if not mesh.visual.defined:251            return colors, texcoords, material252 253        # Process vertex colors254        if material is None:255            if mesh.visual.kind == 'vertex':256                vc = mesh.visual.vertex_colors.copy()257                if smooth:258                    colors = vc259                else:260                    colors = vc[mesh.faces].reshape(261                        (3 * len(mesh.faces), vc.shape[1])262                    )263                material = MetallicRoughnessMaterial(264                    alphaMode='BLEND',265                    baseColorFactor=[1.0, 1.0, 1.0, 1.0],266                    metallicFactor=0.2,267                    roughnessFactor=0.8268                )269            # Process face colors270            elif mesh.visual.kind == 'face':271                if smooth:272                    raise ValueError('Cannot use face colors with a smooth mesh')273                else:274                    colors = np.repeat(mesh.visual.face_colors, 3, axis=0)275 276                material = MetallicRoughnessMaterial(277                    alphaMode='BLEND',278                    baseColorFactor=[1.0, 1.0, 1.0, 1.0],279                    metallicFactor=0.2,280                    roughnessFactor=0.8281                )282 283        # Process texture colors284        if mesh.visual.kind == 'texture':285            # Configure UV coordinates286            if mesh.visual.uv is not None and len(mesh.visual.uv) != 0:287                uv = mesh.visual.uv.copy()288                if smooth:289                    texcoords = uv290                else:291                    texcoords = uv[mesh.faces].reshape(292                        (3 * len(mesh.faces), uv.shape[1])293                    )294 295            if material is None:296                # Configure mesh material297                mat = mesh.visual.material298 299                if isinstance(mat, trimesh.visual.texture.PBRMaterial):300                    material = MetallicRoughnessMaterial(301                        normalTexture=mat.normalTexture,302                        occlusionTexture=mat.occlusionTexture,303                        emissiveTexture=mat.emissiveTexture,304                        emissiveFactor=mat.emissiveFactor,305                        alphaMode='BLEND',306                        baseColorFactor=mat.baseColorFactor,307                        baseColorTexture=mat.baseColorTexture,308                        metallicFactor=mat.metallicFactor,309                        roughnessFactor=mat.roughnessFactor,310                        metallicRoughnessTexture=mat.metallicRoughnessTexture,311                        doubleSided=mat.doubleSided,312                        alphaCutoff=mat.alphaCutoff313                    )314                elif isinstance(mat, trimesh.visual.texture.SimpleMaterial):315                    glossiness = mat.kwargs.get('Ns', 1.0)316                    if isinstance(glossiness, list):317                        glossiness = float(glossiness[0])318                    roughness = (2 / (glossiness + 2)) ** (1.0 / 4.0)319                    material = MetallicRoughnessMaterial(320                        alphaMode='BLEND',321                        roughnessFactor=roughness,322                        baseColorFactor=mat.diffuse,323                        baseColorTexture=mat.image,324                    )325                elif isinstance(mat, MetallicRoughnessMaterial):326                    material = mat327 328        return colors, texcoords, material329