217 lines
6.1 KiB
Python
217 lines
6.1 KiB
Python
import os
|
|
import json
|
|
import numpy as np
|
|
|
|
from ..constants import log
|
|
from .. import util
|
|
from .. import resolvers
|
|
|
|
from .urdf import export_urdf # NOQA
|
|
from .gltf import export_glb, export_gltf
|
|
from .obj import export_obj
|
|
from .off import _off_exporters
|
|
from .stl import export_stl, export_stl_ascii
|
|
from .ply import _ply_exporters
|
|
from .dae import _collada_exporters
|
|
from .xyz import _xyz_exporters
|
|
|
|
|
|
def export_mesh(mesh, file_obj, file_type=None, resolver=None, **kwargs):
|
|
"""
|
|
Export a Trimesh object to a file- like object, or to a filename
|
|
|
|
Parameters
|
|
-----------
|
|
file_obj : str, file-like
|
|
Where should mesh be exported to
|
|
file_type : str or None
|
|
Represents file type (eg: 'stl')
|
|
resolver : None or trimesh.resolvers.Resolver
|
|
Resolver to write referenced assets to
|
|
|
|
Returns
|
|
----------
|
|
exported : bytes or str
|
|
Result of exporter
|
|
"""
|
|
# if we opened a file object in this function
|
|
# we will want to close it when we're done
|
|
was_opened = False
|
|
|
|
if util.is_pathlib(file_obj):
|
|
# handle `pathlib` objects by converting to string
|
|
file_obj = str(file_obj.absolute())
|
|
|
|
if util.is_string(file_obj):
|
|
if file_type is None:
|
|
# get file type from file name
|
|
file_type = (str(file_obj).split('.')[-1]).lower()
|
|
if file_type in _mesh_exporters:
|
|
was_opened = True
|
|
# get full path of file before opening
|
|
file_path = os.path.abspath(os.path.expanduser(file_obj))
|
|
file_obj = open(file_path, 'wb')
|
|
if resolver is None:
|
|
# create a resolver which can write files to the path
|
|
resolver = resolvers.FilePathResolver(file_path)
|
|
|
|
# make sure file type is lower case
|
|
file_type = str(file_type).lower()
|
|
|
|
if not (file_type in _mesh_exporters):
|
|
raise ValueError('%s exporter not available!', file_type)
|
|
|
|
if isinstance(mesh, (list, tuple, set, np.ndarray)):
|
|
faces = 0
|
|
for m in mesh:
|
|
faces += len(m.faces)
|
|
log.debug('Exporting %d meshes with a total of %d faces as %s',
|
|
len(mesh), faces, file_type.upper())
|
|
elif hasattr(mesh, 'faces'):
|
|
# if the mesh has faces log the number
|
|
log.debug('Exporting %d faces as %s', len(mesh.faces),
|
|
file_type.upper())
|
|
|
|
# OBJ files save assets everywhere
|
|
if file_type == 'obj':
|
|
kwargs['resolver'] = resolver
|
|
export = _mesh_exporters[file_type](mesh, **kwargs)
|
|
|
|
if hasattr(file_obj, 'write'):
|
|
result = util.write_encoded(file_obj, export)
|
|
else:
|
|
result = export
|
|
|
|
if was_opened:
|
|
file_obj.close()
|
|
|
|
return result
|
|
|
|
|
|
def export_dict64(mesh):
|
|
"""
|
|
Export a mesh as a dictionary, with data encoded
|
|
to base64.
|
|
"""
|
|
return export_dict(mesh, encoding='base64')
|
|
|
|
|
|
def export_dict(mesh, encoding=None):
|
|
"""
|
|
Export a mesh to a dict
|
|
|
|
Parameters
|
|
------------
|
|
mesh : trimesh.Trimesh
|
|
Mesh to be exported
|
|
encoding : str or None
|
|
Such as 'base64'
|
|
|
|
Returns
|
|
-------------
|
|
export : dict
|
|
Data stored in dict
|
|
"""
|
|
|
|
def encode(item, dtype=None):
|
|
if encoding is None:
|
|
return item.tolist()
|
|
else:
|
|
if dtype is None:
|
|
dtype = item.dtype
|
|
return util.array_to_encoded(item, dtype=dtype, encoding=encoding)
|
|
|
|
# metadata keys we explicitly want to preserve
|
|
# sometimes there are giant datastructures we don't
|
|
# care about in metadata which causes exports to be
|
|
# extremely slow, so skip all but known good keys
|
|
meta_keys = ['units', 'file_name', 'file_path']
|
|
metadata = {k: v for k, v in mesh.metadata.items() if k in meta_keys}
|
|
|
|
export = {
|
|
'metadata': metadata,
|
|
'faces': encode(mesh.faces),
|
|
'face_normals': encode(mesh.face_normals),
|
|
'vertices': encode(mesh.vertices)
|
|
}
|
|
if mesh.visual.kind == 'face':
|
|
export['face_colors'] = encode(mesh.visual.face_colors)
|
|
elif mesh.visual.kind == 'vertex':
|
|
export['vertex_colors'] = encode(mesh.visual.vertex_colors)
|
|
|
|
return export
|
|
|
|
|
|
def scene_to_dict(scene, use_base64=False):
|
|
"""
|
|
Export a Scene object as a dict.
|
|
|
|
Parameters
|
|
-------------
|
|
scene : trimesh.Scene
|
|
Scene object to be exported
|
|
|
|
Returns
|
|
-------------
|
|
as_dict : dict
|
|
Scene as a dict
|
|
"""
|
|
|
|
# save some basic data about the scene
|
|
export = {'graph': scene.graph.to_edgelist(),
|
|
'geometry': {},
|
|
'scene_cache': {'bounds': scene.bounds.tolist(),
|
|
'extents': scene.extents.tolist(),
|
|
'centroid': scene.centroid.tolist(),
|
|
'scale': scene.scale}}
|
|
|
|
# encode arrays with base64 or not
|
|
if use_base64:
|
|
file_type = 'dict64'
|
|
else:
|
|
file_type = 'dict'
|
|
|
|
# if the mesh has an export method use it
|
|
# otherwise put the mesh itself into the export object
|
|
for geometry_name, geometry in scene.geometry.items():
|
|
if hasattr(geometry, 'export'):
|
|
# export the data
|
|
exported = {'data': geometry.export(file_type=file_type),
|
|
'file_type': file_type}
|
|
export['geometry'][geometry_name] = exported
|
|
else:
|
|
# case where mesh object doesn't have exporter
|
|
# might be that someone replaced the mesh with a URL
|
|
export['geometry'][geometry_name] = geometry
|
|
return export
|
|
|
|
|
|
def export_json(mesh):
|
|
blob = export_dict(mesh, encoding='base64')
|
|
export = json.dumps(blob)
|
|
return export
|
|
|
|
|
|
def export_msgpack(mesh):
|
|
import msgpack
|
|
blob = export_dict(mesh, encoding='binary')
|
|
export = msgpack.dumps(blob)
|
|
return export
|
|
|
|
|
|
_mesh_exporters = {
|
|
'stl': export_stl,
|
|
'dict': export_dict,
|
|
'json': export_json,
|
|
'glb': export_glb,
|
|
'obj': export_obj,
|
|
'gltf': export_gltf,
|
|
'dict64': export_dict64,
|
|
'msgpack': export_msgpack,
|
|
'stl_ascii': export_stl_ascii
|
|
}
|
|
_mesh_exporters.update(_ply_exporters)
|
|
_mesh_exporters.update(_off_exporters)
|
|
_mesh_exporters.update(_collada_exporters)
|
|
_mesh_exporters.update(_xyz_exporters)
|