Skip to content

Commit

Permalink
OBJ scene export and github actions
Browse files Browse the repository at this point in the history
  • Loading branch information
mikedh committed Feb 29, 2020
1 parent dca5750 commit 6107aa0
Show file tree
Hide file tree
Showing 5 changed files with 234 additions and 80 deletions.
71 changes: 71 additions & 0 deletions .github/workflows/pythonpublish.yml
@@ -0,0 +1,71 @@
name: Test And Publish

on:
push:
branches:
- actions
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
python-version: [2.7, 3.5, 3.6, 3.7, 3.8]
os: [ubuntu-latest, windows-latest, macos-latest]
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v1
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install .[easy]
- name: Test with pytest
run: |
pip install pytest
pytest
deploy:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v1
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install setuptools wheel twine
- name: Build and publish
env:
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
run: |
python setup.py sdist bdist_wheel
twine upload dist/*
containers:
needs: test
runs-on: ubuntu-latest
steps:
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1
- name: Build, tag, and push to DockerHub and AWS ECR
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
ECR_REPOSITORY: trimesh
run: |
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:latest .
# push docker image to AWS ECR
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
# TODO : push same docker image to DockerHub public image
# docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG mikedh/trimesh:latest
- name: Logout of registries
if: always()
run: |
docker logout ${{ steps.login-ecr.outputs.registry }}
docker logout
11 changes: 11 additions & 0 deletions tests/test_obj.py
Expand Up @@ -162,6 +162,17 @@ def test_mtl(self):
# make sure the faces are the same size
assert rec.faces.shape == m.faces.shape

def test_scene(self):
s = g.get_mesh('cycloidal.3DXML')

e = g.trimesh.load(
g.io_wrap(s.export(file_type='obj')),
file_type='obj',
split_object=True,
group_materials=False)

assert g.np.isclose(e.area, s.area, rtol=.01)


if __name__ == '__main__':
g.trimesh.util.attach_to_log()
Expand Down
166 changes: 96 additions & 70 deletions trimesh/exchange/obj.py
@@ -1,13 +1,17 @@
import collections
import numpy as np
from collections import deque, defaultdict

try:
# `pip install pillow`
# optional: used for textured meshes
import PIL.Image as Image
except ImportError:
pass
except BaseException as E:
# if someone tries to use Image re-raise
# the import error so they can debug easily
from ..exceptions import ExceptionModule
Image = ExceptionModule(E)

from .. import util

from ..visual.color import to_float
from ..visual.texture import unmerge_faces, TextureVisuals
from ..visual.material import SimpleMaterial
Expand Down Expand Up @@ -526,7 +530,7 @@ def _parse_vertices(text):
per_row = {k: len(v[0].split()) for k, v in data.items()}

# convert data values into numpy arrays
result = collections.defaultdict(lambda: None)
result = defaultdict(lambda: None)
for k, value in data.items():
# use joining and fromstring to get as numpy array
array = np.fromstring(
Expand Down Expand Up @@ -599,7 +603,7 @@ def _group_by_material(face_tuples):
"""

# store the chunks grouped by material
grouped = collections.defaultdict(lambda: ['', '', []])
grouped = defaultdict(lambda: ['', '', []])
# loop through existring
for material, obj, chunk in face_tuples:
grouped[material][0] = material
Expand Down Expand Up @@ -702,9 +706,11 @@ def _preprocess_faces(text, split_object=False):
def export_obj(mesh,
include_normals=True,
include_color=True,
include_texture=False):
include_texture=False,
digits=8):
"""
Export a mesh as a Wavefront OBJ file
Export a mesh as a Wavefront OBJ file.
TODO: scenes with textured meshes
Parameters
-----------
Expand All @@ -719,6 +725,9 @@ def export_obj(mesh,
False by default as it will change the return
values to include files that must be saved in the
same location as the exported mesh.
digits : int
Number of digits to include for floating point
Returns
-----------
Expand All @@ -735,77 +744,94 @@ def export_obj(mesh,
('v', 'vn'): '{}//{}',
('v', 'vt'): '{}/{}',
('v', 'vn', 'vt'): '{}/{}/{}'}
# we are going to reference face_formats with this
face_type = ['v']

# OBJ includes vertex color as RGB elements on the same line
if include_color and mesh.visual.kind in ['vertex', 'face']:
# create a stacked blob with position and color
v_blob = np.column_stack((
mesh.vertices,
to_float(mesh.visual.vertex_colors[:, :3])))

# check the input
if util.is_instance_named(mesh, 'Trimesh'):
meshes = [mesh]
elif util.is_instance_named(mesh, 'Scene'):
meshes = mesh.dump()
else:
# otherwise just export vertices
v_blob = mesh.vertices
raise ValueError('must be Trimesh or Scene!')

# add the first vertex key and convert the array
export = collections.deque(
['v ' + util.array_to_string(
v_blob,
col_delim=' ',
row_delim='\nv ',
digits=8) + '\n'])

# only include vertex normals if they're already stored
if include_normals and 'vertex_normals' in mesh._cache:
# if vertex normals are stored in cache export them
face_type.append('vn')
export.append('vn ' + util.array_to_string(
mesh.vertex_normals,
col_delim=' ',
row_delim='\nvn ',
digits=8) + '\n')

tex_data = None
if include_texture and hasattr(mesh.visual, 'uv'):
# if vertex texture exists and is the right shape
face_type.append('vt')
# add the uv coordinates
export.append('vt ' + util.array_to_string(
mesh.visual.uv,
objects = deque([])

counts = {'v': 0, 'vn': 0, 'vt': 0}

for mesh in meshes:
# we are going to reference face_formats with this
face_type = ['v']
# OBJ includes vertex color as RGB elements on the same line
if include_color and mesh.visual.kind in ['vertex', 'face']:
# create a stacked blob with position and color
v_blob = np.column_stack((
mesh.vertices,
to_float(mesh.visual.vertex_colors[:, :3])))
else:
# otherwise just export vertices
v_blob = mesh.vertices

# add the first vertex key and convert the array
# add the vertices
export = deque(
['v ' + util.array_to_string(
v_blob,
col_delim=' ',
row_delim='\nv ',
digits=digits)])
# only include vertex normals if they're already stored
if include_normals and 'vertex_normals' in mesh._cache.cache:
# if vertex normals are stored in cache export them
face_type.append('vn')
export.append('vn ' + util.array_to_string(
mesh.vertex_normals,
col_delim=' ',
row_delim='\nvn ',
digits=digits))

tex_data = None
if include_texture and hasattr(mesh.visual, 'uv'):
# if vertex texture exists and is the right shape
face_type.append('vt')
# add the uv coordinates
export.append('vt ' + util.array_to_string(
mesh.visual.uv,
col_delim=' ',
row_delim='\nvt ',
digits=digits))
(tex_data,
tex_name,
mtl_name) = mesh.visual.material.to_obj()
# add the reference to the MTL file
objects.appendleft('mtllib {}'.format(mtl_name))
# add the directive to use the exported material
export.appendleft('usemtl {}'.format(tex_name))
# the format for a single vertex reference of a face
face_format = face_formats[tuple(face_type)]
# add the exported faces to the export
export.append('f ' + util.array_to_string(
mesh.faces + 1 + counts['v'],
col_delim=' ',
row_delim='\nvt ',
digits=8) + '\n')
tex_data, tex_name, mtl_name = mesh.visual.material.to_obj()
# add the reference to the MTL file
export.appendleft('mtllib {}'.format(mtl_name))
# add the directive to use the exported material
export.appendleft('usemtl {}'.format(tex_name))

# the format for a single vertex reference of a face
face_format = face_formats[tuple(face_type)]
# add the exported faces to the export
export.append('f ' + util.array_to_string(
mesh.faces + 1,
col_delim=' ',
row_delim='\nf ',
value_format=face_format))

# add object name if found in metadata
if 'name' in mesh.metadata:
export.appendleft('o {}'.format(mesh.metadata['name']))
row_delim='\nf ',
value_format=face_format))
# offset our vertex position
counts['v'] += len(mesh.vertices)

# add object name if found in metadata
if 'name' in mesh.metadata:
export.appendleft(
'\no {}'.format(mesh.metadata['name']))
# add this object
objects.append('\n'.join(export))

# add a created-with header to the top of the file
export.appendleft('# https://github.com/mikedh/trimesh')

objects.appendleft('# https://github.com/mikedh/trimesh')
# combine elements into a single string
export = '\n'.join(export)

text = '\n'.join(objects)
# if we exported texture it changes returned values
if include_texture and tex_data is not None:
return export, tex_data
return text, tex_data

return export
return text


_obj_loaders = {'obj': load_obj}

0 comments on commit 6107aa0

Please sign in to comment.