├── .github
└── workflows
│ ├── publish.yml
│ └── static_code_analysis.yml
├── .gitignore
├── LICENSE
├── MANIFEST.in
├── README.md
├── docs
├── human.md
├── human_teaser.gif
└── output.gif
├── examples
├── 01_objects.py
├── 02_robots.py
├── 03_animation.py
├── 04_image_and_video.py
├── 05_teaser.py
├── 05_teaser_with_human.py
├── 06_human.py
├── spade.obj
└── texture.png
├── pyproject.toml
├── setup.py
├── src
└── robomeshcat
│ ├── __init__.py
│ ├── human.py
│ ├── object.py
│ ├── robot.py
│ └── scene.py
└── tests
├── test_robot_q.py
└── test_urdf.urdf
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
1 | name: Publish to PyPI.org
2 | on:
3 | release:
4 | types: [ published ]
5 | jobs:
6 | pypi:
7 | runs-on: ubuntu-latest
8 | steps:
9 | - name: Checkout
10 | uses: actions/checkout@v3
11 | with:
12 | fetch-depth: 0
13 | - run: python3 -m pip install --upgrade build && python3 -m build
14 | - name: Publish package
15 | uses: pypa/gh-action-pypi-publish@release/v1
16 | with:
17 | password: ${{ secrets.PYPI_API_TOKEN }}
18 |
--------------------------------------------------------------------------------
/.github/workflows/static_code_analysis.yml:
--------------------------------------------------------------------------------
1 | name: static_code_analysis
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | pull_request:
8 |
9 | jobs:
10 | flake8-lint:
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: actions/checkout@v3
14 | - uses: actions/setup-python@v4
15 | with:
16 | python-version: "3.8"
17 | - uses: py-actions/flake8@v2
18 | with:
19 | args: "--per-file-ignores=__init__.py:F401"
20 | max-line-length: "120"
21 | path: "src/"
22 |
23 | black-lint:
24 | runs-on: ubuntu-latest
25 | steps:
26 | - uses: actions/checkout@v3
27 | - uses: psf/black@stable
28 | with:
29 | options: "--check --diff --verbose --skip-string-normalization --line-length=120"
30 | src: "src/"
31 |
32 | pytype-lint:
33 | runs-on: ubuntu-latest
34 | steps:
35 | - uses: actions/checkout@v3
36 | - uses: actions/setup-python@v4
37 | with:
38 | python-version: "3.10"
39 | - run: pip install pytype>=2023.2.14
40 | - run: pytype -d import-error src/
41 |
42 | pytest:
43 | runs-on: ubuntu-latest
44 | steps:
45 | - uses: actions/checkout@v3
46 | - uses: actions/setup-python@v4
47 | with:
48 | python-version: "3.10"
49 | - run: pip install pytest
50 | - run: pip install -e .
51 | - run: pytest tests
52 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | pip-wheel-metadata/
24 | share/python-wheels/
25 | *.egg-info/
26 | .installed.cfg
27 | *.egg
28 | MANIFEST
29 |
30 | # PyInstaller
31 | # Usually these files are written by a python script from a template
32 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
33 | *.manifest
34 | *.spec
35 |
36 | # Installer logs
37 | pip-log.txt
38 | pip-delete-this-directory.txt
39 |
40 | # Unit test / coverage reports
41 | htmlcov/
42 | .tox/
43 | .nox/
44 | .coverage
45 | .coverage.*
46 | .cache
47 | nosetests.xml
48 | coverage.xml
49 | *.cover
50 | *.py,cover
51 | .hypothesis/
52 | .pytest_cache/
53 |
54 | # Translations
55 | *.mo
56 | *.pot
57 |
58 | # Django stuff:
59 | *.log
60 | local_settings.py
61 | db.sqlite3
62 | db.sqlite3-journal
63 |
64 | # Flask stuff:
65 | instance/
66 | .webassets-cache
67 |
68 | # Scrapy stuff:
69 | .scrapy
70 |
71 | # Sphinx documentation
72 | docs/_build/
73 |
74 | # PyBuilder
75 | target/
76 |
77 | # Jupyter Notebook
78 | .ipynb_checkpoints
79 |
80 | # IPython
81 | profile_default/
82 | ipython_config.py
83 |
84 | # pyenv
85 | .python-version
86 |
87 | # pipenv
88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
91 | # install all needed dependencies.
92 | #Pipfile.lock
93 |
94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
95 | __pypackages__/
96 |
97 | # Celery stuff
98 | celerybeat-schedule
99 | celerybeat.pid
100 |
101 | # SageMath parsed files
102 | *.sage.py
103 |
104 | # Environments
105 | .env
106 | .venv
107 | env/
108 | venv/
109 | ENV/
110 | env.bak/
111 | venv.bak/
112 |
113 | # Spyder project settings
114 | .spyderproject
115 | .spyproject
116 |
117 | # Rope project settings
118 | .ropeproject
119 |
120 | # mkdocs documentation
121 | /site
122 |
123 | # mypy
124 | .mypy_cache/
125 | .dmypy.json
126 | dmypy.json
127 |
128 | # Pyre type checker
129 | .pyre/
130 | .idea
131 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 2-Clause License
2 |
3 | Copyright (c) 2022, Vladimír Petrík
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include setup.py
2 | include MANIFEST.in
3 | include LICENSE
4 | include README.md
5 |
6 | graft tests
7 | graft examples
8 | graft docs
9 | graft src
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # RoboMeshCat
2 |
3 | [](https://anaconda.org/conda-forge/robomeshcat)
4 | [](https://badge.fury.io/py/robomeshcat)
5 | 
6 | [](https://opensource.org/licenses/BSD-2-Clause)
7 |
8 | Set of utilities for visualizing robots in web-based visualizer [MeshCat](https://github.com/rdeits/meshcat-python).
9 | The whole library is object and robot centric allowing you to modify properties of instances rather than manipulating
10 | the visualization tree of the MeshCat itself.
11 | The library allows you to easily generate videos like this (source code is [here](examples/05_teaser.py)):
12 |
13 | 
14 |
15 | or like this (by installing a few more [dependencies](docs/human.md); [source code](examples/05_teaser_with_human.py)):
16 |
17 | 
18 |
19 | # Installation
20 |
21 | ## From
22 |
23 | ```bash
24 | conda install -c conda-forge robomeshcat
25 | ```
26 |
27 | ## From PyPI
28 |
29 | ```bash
30 | pip install robomeshcat
31 | ```
32 |
33 | # About
34 |
35 | This library allows you to visualize and animate objects and robots in web browser.
36 | Compared to [MeshCat](https://github.com/rdeits/meshcat-python) library, on which we build, our library is
37 | object-oriented allowing you to modify the properties of individual objects, for example:
38 |
39 | ```python
40 | o = Object.create_sphere(radius=0.2)
41 | o.pos[2] = 2. # modify position
42 | o.opacity = 0.3 # modify transparency
43 | o.color[0] = 1. # modify red channel of the color
44 | o.hide() # hide the object, i.e. set o.visible = False
45 | ```
46 |
47 | In addition to objects, it allows you to easily create and manipulate robots (loaded from `URDF` file):
48 |
49 | ```python
50 | r = Robot(urdf_path='robot.urdf')
51 | r[0] = np.pi # set the value of the first joint
52 | r['joint5'] = 0 # set the value of the joint named 'joint5'
53 | r.pos = [0, 0, 0] # set the base pose of the robot
54 | r.color, r.opacity, r.visibility, r.rot = ... # change the color, opacity, visibility, or rotation
55 | ```
56 |
57 | All changes will be displayed immediately in the browser, that we call 'online' rendering.
58 | By default, you can rotate the camera view with your mouse.
59 | However, our library allows you to control the camera from the code as well through the `Scene` object, that is the main
60 | point for visualization:
61 |
62 | ```python
63 | scene = Scene()
64 | scene.add_object(o) # add object 'o' into the scene, that will display it
65 | scene.add_robot(r) # add robot 'r' into the scene, that will display it
66 | scene.camera_pos = [1, 1, 1] # set camera position
67 | scene.camera_pos[2] = 2 # change height of the camera
68 | scene.camera_zoom = 2 # zoom in
69 | scene.reset_camera() # reset camera such that you can control it with your mouse again
70 | ```
71 |
72 | For complete examples of object and robot interface see our two examples: [01_object.py](examples/01_objects.py)
73 | and [02_robots.py](examples/02_robots.py).
74 |
75 | It is also possible to visualize human model after installing a few more dependencies,
76 | see [installation](docs/human.md) and example [06_human.py](examples/06_human.py).
77 |
78 | ## Animation
79 |
80 | This library allows you to animate all the properties of the objects and robots (e.g. position, robot configuration,
81 | color,opacity) inside the browser (from which you can replay, slow-down, etc.). Simply use:
82 |
83 | ```python
84 | scene = Scene()
85 | scene.add_object(o)
86 |
87 | with scene.animation(fps=25):
88 | o.pos[2] = 2.
89 | scene.render() # create a first frame of the animation, with object position z-axis set to 2.
90 | o.pos[2] = 0.
91 | scene.render() # create a second frame of the animation with object on the ground
92 | ```
93 |
94 | You can also store the animation into the video, using the same principle:
95 |
96 | ```python
97 | with scene.video_recording(filename='video.mp4', fps=25):
98 | scene.render()
99 | ```
100 |
101 | See our examples on [Animation](examples/03_animation.py) and [Image and video](examples/04_image_and_video.py).
102 |
103 |
--------------------------------------------------------------------------------
/docs/human.md:
--------------------------------------------------------------------------------
1 | # Visualizing human model in meshcat via SMPL-X
2 |
3 | ## Installation
4 |
5 | The SMPL-X library and model data are not included in a standard RoboMeshCat packages.
6 | It must be installed as described in [SMPL-X package](https://github.com/vchoutas/smplx), i.e. by running
7 |
8 | ```bash
9 | pip install smplx[all]
10 | ```
11 |
12 | and then downloading the data from [here](https://smpl-x.is.tue.mpg.de/), we use SMPL-X v1_1 data from the webpage.
13 | The path to the model must be specified while creating the instance of the Human.
14 | In the examples, the following folder structure is assumed:
15 |
16 | ```bash
17 | examples/
18 | models/ # this is a folder downloaded from the webpage above
19 | smplx/
20 | SMPLX_FEMALE.npz
21 | SMPLX_FEMALE.pkl
22 | SMPLX_MALE.npz
23 | ...
24 | 06_human.py # this is an example script
25 | ```
26 |
27 | ## Usage
28 |
29 | The human model functionality is available through the class `Human`.
30 | You can change human pose (i.e. position and orientation), color, visibility, etc. in the same way as for regular
31 | RoboMeshCat objects, and you can also change the body pose (i.e. configuration), shape and expression through the
32 | instance of the class.
33 | For complete example have a look at [06_human.py](../examples/06_human.py).
34 |
35 | ### Online usage
36 |
37 | ```python
38 | human = Human(pose=human_default_pose, color=[1., 0., 0.], model_path=smplx_models_path)
39 | scene.add_object(human) # add human to the scene, it will be visualized immediately
40 |
41 | human.update_vertices(vertices=human.get_vertices(expression=torch.randn([1, 10])))
42 |
43 | human.smplx_model.body_pose.data += 0.1
44 | human.update_vertices()
45 | ```
46 |
47 | ### Animation
48 |
49 | Function `update_vertices` cannot be used in animation as it is modifying geometry of the object internally.
50 | Instead, you need to use 'morphologies', that need to be specified before adding human to the scene:
51 |
52 | ```python
53 | human = Human(pose=human_default_pose, color=[1., 0., 0.], model_path=smplx_models_path)
54 | human.smplx_model.betas.data += 1
55 | human.add_morph(human.get_vertices()) # the first morph changes the shape
56 |
57 | human.smplx_model.body_pose.data += 0.1
58 | human.add_morph(human.get_vertices()) # the second morp changes the body pose
59 |
60 | scene.add_object(human) # add human to the scene, no morphology can be added/modified after this step
61 |
62 | "Let's animate"
63 | with scene.animation(fps=1):
64 | human.display_morph(None) # this will display the human shape that is not affected by morphologies
65 | scene.render()
66 |
67 | human.display_morph(0)
68 | scene.render()
69 |
70 | human.display_morph(1)
71 | scene.render()
72 | ```
73 |
74 | ### Coloring of human
75 |
76 | You have two options to color the human mesh: (i) uniform color and (ii) per vertex color.
77 | Uniform color is default, it can be set with `color` argument of human and changed/animated by `.color` property.
78 |
79 | Per vertex color cannot be animated as it requires to change the geometry internally.
--------------------------------------------------------------------------------
/docs/human_teaser.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/petrikvladimir/RoboMeshCat/4c803c4c457d4e5db51ac8b11b04eadfec19239d/docs/human_teaser.gif
--------------------------------------------------------------------------------
/docs/output.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/petrikvladimir/RoboMeshCat/4c803c4c457d4e5db51ac8b11b04eadfec19239d/docs/output.gif
--------------------------------------------------------------------------------
/examples/01_objects.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # Copyright (c) CTU -- All Rights Reserved
3 | # Created on: 2022-10-11
4 | # Author: Vladimir Petrik
5 | #
6 |
7 | import numpy as np
8 | from pathlib import Path
9 | from pinocchio.utils import rotate
10 |
11 | from robomeshcat import Object, Scene
12 |
13 | " This example shows how to update properties of the objects online - i.e., changes are apparent in the visualizer "
14 | "immediately as they are set."
15 |
16 | "All elements are stored in Scene that is responsible for rendering them in the MeshCat. "
17 | scene = Scene()
18 |
19 | "You can add objects into the scene and refer to them through the object instance or by the name"
20 | obj1 = Object.create_sphere(radius=0.1, name='red_sphere', opacity=0.5, color=[1., 0., 0.])
21 | scene.add_object(obj1)
22 |
23 |
24 | "Let's set object position in z-axis, both options do the same"
25 | input('Press enter to continue: to move sphere up')
26 | obj1.pos[2] = 1.
27 | scene['red_sphere'].pos[2] = 1.
28 |
29 |
30 | "Other objects could be added to the scene too"
31 | input('Press enter to continue: to add more objects')
32 | scene.add_object(Object.create_cuboid(lengths=0.1, name='box', color=[1, 0, 0]))
33 | scene.add_object(Object.create_cylinder(length=0.6, radius=0.1, name='cylinder'))
34 | scene.add_object(Object.create_mesh(path_to_mesh=Path(__file__).parent.joinpath('spade.obj'), scale=1e-3, name='spade'))
35 |
36 | scene['box'].pos[1] = 0.25 # other options: scene['box'].pos = [1, 1, 1], scene['box'].pose = ...
37 | scene['cylinder'].pos[0] = -0.5
38 | scene['cylinder'].rot = rotate('x', np.deg2rad(90))
39 | scene['spade'].pos[1] = 0.75
40 |
41 |
42 | "You can also adjust color, opacity, or visibility of the object."
43 | input('Press enter to continue: to change the color and opacity')
44 | scene['box'].color = [0, 1, 0] # other options: scene['box'].color[1] = 1.
45 | scene['box'].opacity = 0.5
46 |
47 | input('Press enter to continue: to hide box and finis')
48 | scene['box'].hide() # other options: scene['box'].visible = False
49 |
--------------------------------------------------------------------------------
/examples/02_robots.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # Copyright (c) CTU -- All Rights Reserved
3 | # Created on: 2022-10-11
4 | # Author: Vladimir Petrik
5 | #
6 |
7 | import time
8 | from pathlib import Path
9 | import numpy as np
10 | from robomeshcat import Robot, Scene
11 |
12 | "This example shows how to add and control robots online in the visualizer."
13 | "Robots can be loaded from URDF; we will add multiple robots to show the visual/collision models of the robot."
14 |
15 | "Example robot data package for the robot meshes and URDF, please install it with"
16 | "conda install -c conda-forge example-robot-data"
17 |
18 | "All elements are stored in Scene that is responsible for rendering them in the MeshCat"
19 | scene = Scene()
20 |
21 | from example_robot_data.robots_loader import PandaLoader as RobLoader
22 |
23 | "Create the first robot and add it to the scene"
24 | rob = Robot(urdf_path=RobLoader().df_path, mesh_folder_path=Path(RobLoader().model_path).parent.parent, name='visual')
25 | scene.add_robot(rob)
26 |
27 | "Modify the configuration of the robot"
28 | input('Press enter to change configuration of the robot')
29 | rob[3] = np.deg2rad(-90) # access by joint id, you can also use scene['visual'][3] = ...
30 | rob['panda_joint5'] = np.deg2rad(90) # access by joint name, you can also use scene['visual']['panda_joint5'] = ...
31 |
32 | input('Press enter to change the pose of the robot')
33 | rob.pos[1] = -1
34 |
35 | input('Press enter to change the color of the robot')
36 | rob.color[0] = 1.
37 |
38 | input('Press enter to add a new robot that shows collision model of the robot')
39 |
40 | col = Robot(urdf_path=RobLoader().df_path, mesh_folder_path=Path(RobLoader().model_path).parent.parent,
41 | show_collision_models=True)
42 | scene.add_robot(col)
43 | col[:] = rob[:] # use the same pose for the collision model
44 |
45 | input('Press enter to hide visual model and exit')
46 | rob.hide()
47 |
48 | time.sleep(1.) # note that some delay is needed to propagate all the changes
49 |
--------------------------------------------------------------------------------
/examples/03_animation.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | #
3 | # Copyright (c) CTU -- All Rights Reserved
4 | # Created on: 2022-10-14
5 | # Author: Vladimir Petrik
6 | #
7 |
8 | import time
9 | import numpy as np
10 | from pathlib import Path
11 | from example_robot_data.robots_loader import PandaLoader
12 | from robomeshcat import Scene, Robot
13 |
14 | "Meshcat can also create animations, that you can rewind forward/backward in the browser. This example shows how to "
15 | "create such an animation, by changing the configuration of the robot."
16 |
17 | scene = Scene()
18 | robot = Robot(urdf_path=PandaLoader().df_path, mesh_folder_path=Path(PandaLoader().model_path).parent.parent)
19 | scene.add_robot(robot)
20 |
21 | with scene.animation(fps=1): # start the animation with the current scene
22 | scene.render()
23 |
24 | robot[3] = np.deg2rad(-90) # modify the scene
25 | scene.render() # store the changes in the frame and start new frame modifications
26 |
27 | robot.opacity = 0.2
28 | scene.render()
29 |
30 | robot.opacity = 1.
31 | scene.render()
32 |
33 | robot.color = [0, 0, 0.7]
34 | scene.render()
35 |
36 | # then let's rotate camera around the robot for 10s;
37 | # note that setting camera will forbid user to rotate the scene
38 | for tt in np.linspace(0, 2 * np.pi, 10):
39 | scene.camera_pos[0] = np.sin(tt)
40 | scene.camera_pos[1] = np.cos(tt)
41 | scene.camera_pos[2] = 1
42 | scene.render()
43 |
44 | scene.reset_camera() # resetting the camera will allow user interaction again
45 |
46 | time.sleep(5) # we need some time to send the animation to the browser
47 |
--------------------------------------------------------------------------------
/examples/04_image_and_video.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | #
3 | # Copyright (c) CTU -- All Rights Reserved
4 | # Created on: 2022-11-10
5 | # Author: Vladimir Petrik
6 | #
7 |
8 | from pathlib import Path
9 | import matplotlib.pyplot as plt
10 | import numpy as np
11 |
12 | from robomeshcat import Object, Scene
13 |
14 | example_folder = Path(__file__).parent
15 |
16 | "All elements are stored in Scene that is responsible for rendering them in the MeshCat"
17 | scene = Scene()
18 |
19 | "You can objects into the scene and refer to them through the object instance or by the name"
20 | obj1 = Object.create_sphere(radius=0.1, name='red_sphere', opacity=0.5, color=[1., 0., 0.])
21 | scene.add_object(obj1)
22 |
23 | scene.render()
24 | input('Rotate the scene as you wish and press enter to capture the image.')
25 |
26 | "Render the image via matplotlib"
27 | img = scene.render_image()
28 |
29 | fig, ax = plt.subplots(1, 1, squeeze=True) # type: plt.Figure, plt.Axes
30 | ax.imshow(img)
31 | plt.show()
32 |
33 | "Store the video"
34 | with scene.video_recording(filename='/tmp/video.mp4', fps=30):
35 | for t in np.linspace(1, 0, 30):
36 | obj1.pos[2] = t
37 | scene.render()
38 |
39 | t = np.linspace(0, 2 * np.pi, 60)
40 | for tt in t:
41 | scene.camera_pos[0] = np.sin(tt)
42 | scene.camera_pos[1] = np.cos(tt)
43 | scene.camera_pos[2] = 1
44 | scene.render()
45 |
46 | scene.reset_camera()
47 | scene.render()
48 |
--------------------------------------------------------------------------------
/examples/05_teaser.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | #
3 | # Copyright (c) CTU -- All Rights Reserved
4 | # Created on: 2022-11-10
5 | # Author: Vladimir Petrik
6 | #
7 |
8 | import numpy as np
9 | from pathlib import Path
10 | from example_robot_data.robots_loader import TalosFullLoader as TalosLoader
11 | from example_robot_data.robots_loader import PandaLoader
12 | from robomeshcat import Scene, Robot
13 |
14 | scene = Scene()
15 |
16 | panda = Robot(urdf_path=PandaLoader().df_path, mesh_folder_path=Path(PandaLoader().model_path).parent.parent)
17 | scene.add_robot(panda)
18 | panda.pos = [1, 0, 0]
19 | panda[3] = -np.pi/2
20 | panda[5] = np.pi/2
21 |
22 | talos = Robot(urdf_path=TalosLoader().df_path, mesh_folder_path=Path(TalosLoader().model_path).parent.parent)
23 | scene.add_robot(talos)
24 | talos.pos[2] = 1.075
25 |
26 | # with scene.animation(fps=30):
27 | with scene.video_recording(filename='/tmp/teaser.mp4', fps=30):
28 | scene.camera_pos = [2, 0, 2.5]
29 | for t in np.linspace(0., 1., 60):
30 | talos['arm_right_1_joint'] = -t
31 | talos['arm_left_1_joint'] = t
32 | panda[0] = t
33 | panda[2] = t
34 | scene.render()
35 |
36 | for t in np.linspace(0, 2 * np.pi, 60):
37 | scene.camera_pos[0] = 2 * np.cos(t)
38 | scene.camera_pos[1] = 2 * np.sin(t)
39 | talos['head_1_joint'] = t
40 | scene.render()
41 |
42 |
--------------------------------------------------------------------------------
/examples/05_teaser_with_human.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | #
3 | # Copyright (c) CTU -- All Rights Reserved
4 | # Created on: 2022-12-8
5 | # Author: Vladimir Petrik
6 | #
7 | import time
8 |
9 | import numpy as np
10 | from pathlib import Path
11 | from example_robot_data.robots_loader import TalosFullLoader as TalosLoader
12 | import pinocchio as pin
13 | import torch
14 | from robomeshcat import Scene, Robot, Human
15 |
16 | scene = Scene()
17 |
18 | smplx_models_path = str(Path(__file__).parent.joinpath('models').joinpath('smplx'))
19 | pose = np.eye(4)
20 | human_default_pose = pin.exp6(np.array([0, 0, 0, np.pi / 2, 0., 0.])).homogeneous
21 | human_default_pose[2, 3] = -.2
22 | human = Human(pose=human_default_pose.copy(), color=[0.6] * 3, model_path=smplx_models_path)
23 |
24 | sad_face = torch.tensor([[-0.4969, -0.2114, 1.5251, 0.1503, 0.4488, 1.7344, 2.1032, -0.3620, -1.2451, 1.8487]])
25 | smile_face = torch.tensor([[3.4081, -1.1111, -1.4181, 0.5018, 0.0286, -0.5347, -0.0042, 0.1118, -0.2230, -0.1172]])
26 | neutral_face = torch.tensor([[-0.5131, 1.0546, 0.6059, -0.6452, 2.7049, 0.8512, 0.0777, 0.8451, -1.4651, 0.3700]])
27 |
28 | human.add_morph(human.get_vertices(expression=sad_face))
29 | human.add_morph(human.get_vertices(expression=smile_face))
30 | human.add_morph(human.get_vertices(expression=neutral_face))
31 | # add some dancing morph
32 | human.add_morph(human.get_vertices(body_pose=torch.randn(human.smplx_model.body_pose.shape) * 0.1))
33 | human.add_morph(human.get_vertices(body_pose=torch.randn(human.smplx_model.body_pose.shape) * 0.1))
34 | human.add_morph(human.get_vertices(body_pose=torch.randn(human.smplx_model.body_pose.shape) * 0.1))
35 | scene.add_object(human)
36 |
37 | talos = Robot(urdf_path=TalosLoader().df_path, mesh_folder_path=Path(TalosLoader().model_path).parent.parent)
38 | scene.add_robot(talos)
39 | talos.pos[0] = 1.
40 | talos.pos[2] = 1.075
41 | talos.rot = pin.utils.rotate('z', np.deg2rad(-90))
42 | talos.opacity = 0.
43 |
44 | q1 = np.array(
45 | [0.57943216, -0.1309057, -0.75505065, 0.78430028, -0.61956061, -0.27349631, 0.13615252, 0.0711049, -0.03615876,
46 | 0.49826378, 0.17217602, 0.50618769, 0.44123115, -0.02707293, -1.18121182, 0.30893653, 2.01942401, -2.13127587,
47 | -0.10865551, 0.30782173, -0.58293303, -0.23586322, 0.42843663, 0.3494325, 0.52727565, 0.50386685, -0.48822942,
48 | 0.09145592, -0.6189864, -0.09982653, -0.33399487, -0.99386967, -0.78832615, 1.12503886, 0.4816953, -0.33853157,
49 | 0.15645548, 0.77799908, 0.25617193, 0.92783777, -0.06406897, 1.03065562, 0.65546472, 0.28488222])
50 |
51 | q2 = np.array(
52 | [0.67953954, -0.23498704, -0.30815908, 1.26050064, -0.75429557, 0.39308716, -0.09183746, -0.3519678, 0.6029438,
53 | 1.92670204, -0.85517111, 0.31218583, 1.12134325, -0.08521749, -0.2414049, 0.41116012, 2.19232313, -0.13271861,
54 | 0.13766665, 0.79690452, -0.64291739, -1.02337668, 0.74399798, 0.32299157, 0.25029159, 0.81949992, -0.4262274,
55 | 0.61293056, 0.01760217, -2.08710036, 0.20761188, -0.27267571, 0.2487861, -0.8711323, -0.19324595, -0.19482248,
56 | 0.06016944, 0.13445533, 1.02400687, 0.02380557, -0.13022461, 0.19958255, 0.60717046, 0.81290787])
57 |
58 | q0 = np.zeros_like(q1)
59 | scene.render()
60 |
61 | with scene.animation(fps=1):
62 | scene.camera_pos = [0, -0.3, 0.2]
63 | scene.render()
64 | human.display_morph(0)
65 | scene.render()
66 | human.display_morph(1)
67 | scene.render()
68 | human.display_morph(2)
69 | human.pos[0] = -.5
70 | human.pos[2] = 1.3
71 | scene.camera_pos = [0, -2., 2.5]
72 | scene.render()
73 | talos.opacity = 1.
74 | scene.render()
75 |
76 | for _ in range(2):
77 | talos[:] = q1
78 | human.display_morph(3)
79 | scene.render()
80 | talos[:] = q2
81 | human.display_morph(4)
82 | scene.render()
83 | talos[:] = q0
84 | human.display_morph(5)
85 | scene.render()
86 |
87 | human.display_morph(None)
88 | human.pose = human_default_pose
89 | scene.camera_pos = [0, -0.3, 0.2]
90 | scene.render()
91 |
92 | time.sleep(3.)
93 |
--------------------------------------------------------------------------------
/examples/06_human.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | #
3 | # Copyright (c) CTU -- All Rights Reserved
4 | # Created on: 2022-11-30
5 | # Author: Vladimir Petrik
6 | #
7 | from pathlib import Path
8 | import numpy as np
9 | import pinocchio as pin
10 | import torch
11 |
12 | from robomeshcat import Scene, Human
13 |
14 | "This examples show how to use the human model and how to animate its pose and color. "
15 | "We show three case studies: "
16 | "(i) the first case study shows how to manipulate human online, i.e. without animation"
17 | "(ii) this case study shows how to animate pose and color of the human in case of uniform color"
18 | "(iii) this case study shows how use per vertex color of the human (only in online mode, no animation yet!)"
19 |
20 | case_study = 2 # chose which case study to visualize
21 | scene = Scene()
22 | "Set smplx_models_path to the directory where are your SMPLX models"
23 | smplx_models_path = str(Path(__file__).parent.joinpath('models').joinpath('smplx'))
24 | human_default_pose = pin.exp6(np.array([0, 0, 0, np.pi / 2, 0., 0.])).homogeneous
25 | human_default_pose[2, 3] = 1.2
26 |
27 | if case_study == 0:
28 | "First let's create the human, arguments are forward to smplx constructor, so you can adjust the human model args"
29 | human = Human(pose=human_default_pose, color=[1., 0., 0.], model_path=smplx_models_path)
30 | scene.add_object(human) # add human to the scene, it will be visualized immediately
31 |
32 | input('Press enter to change the body pose and shape of the human')
33 | human.smplx_model.body_pose.data += 0.1 # modify the pose
34 | human.smplx_model.betas.data += 0.1 # modify shape params
35 | human.smplx_model.expression.data += 0.1 # modify expression param
36 | human.update_vertices() # recreate the geometry model to update in the viewer, this is allowed only in online use
37 |
38 | input('Press enter to change the color, opacity, and position of the human')
39 | human.pos[0] = 1.
40 | human.color = [0, 1, 0]
41 | human.opacity = 0.5
42 | input('Press enter to hide the model and exit.')
43 | human.hide()
44 |
45 | elif case_study == 1:
46 | human = Human(pose=human_default_pose, color=[1., 0., 0.], model_path=smplx_models_path)
47 |
48 | # You need to create all the animation poses of the human in advance of adding the human to the scene
49 | # It's called morphologies of the pose
50 | human.smplx_model.betas.data += 1
51 | human.add_morph(human.get_vertices()) # the first morph changes the shape
52 |
53 | human.smplx_model.body_pose.data += 0.1
54 | human.add_morph(human.get_vertices()) # the second morp changes the body pose
55 |
56 | scene.add_object(human) # add human to the scene, no morphology can be added/modified after this step
57 |
58 | "Let's animate"
59 | with scene.animation(fps=1):
60 | human.display_morph(None) # this will display the human shape that is not affected by morphologies
61 | scene.render()
62 |
63 | human.display_morph(0)
64 | scene.render()
65 |
66 | human.display_morph(1)
67 | scene.render()
68 |
69 | human.color = [0, 0.8, 0]
70 | scene.render()
71 |
72 | # You can also change the .pos, .rot, .opacity, .visible, in animation
73 | elif case_study == 2:
74 | # To have per vertex colors, use attribute use_vertex_colors=True
75 | human = Human(pose=human_default_pose, color=[1., 0., 0.], model_path=smplx_models_path, use_vertex_colors=True)
76 | scene.add_object(human)
77 |
78 | input('press enter to change colors to random')
79 | human.update_vertices(vertices_colors=np.random.rand(human.smplx_model.get_num_verts(), 3))
80 |
81 | input('press enter to change colors to blue')
82 | human.update_vertices(vertices_colors=[[0., 0., 0.75]] * human.smplx_model.get_num_verts())
83 |
84 | input('press enter to display wireframe')
85 | human._show_wireframe = True
86 | human.update_vertices(vertices_colors=[[0., 0., 0.]] * human.smplx_model.get_num_verts())
87 |
88 | input('sample new expressions and store them into video, rotate manually to see the face')
89 | # human.update_vertices(vertices=human.get_vertices(betas=torch.randn([1, 10])))
90 | human._show_wireframe = False
91 | human._vertex_colors[:] = 0.6
92 | with scene.video_recording(filename='/tmp/face_expression.mp4', fps=1):
93 | for _ in range(10):
94 | human.update_vertices(vertices=human.get_vertices(expression=torch.randn([1, 10])))
95 | scene.render()
96 |
--------------------------------------------------------------------------------
/examples/spade.obj:
--------------------------------------------------------------------------------
1 | # Blender v2.81 (sub 16) OBJ File: 'spade.blend'
2 | # www.blender.org
3 | mtllib spade.mtl
4 | o Spade_hull_1
5 | v 14.513958 12.500002 -4.176317
6 | v -46.748081 -12.499999 -72.906143
7 | v -46.748081 -12.500001 -17.268295
8 | v 47.422077 -12.500001 -17.268295
9 | v 51.762924 14.371905 -76.161461
10 | v -51.088928 14.371905 -76.161461
11 | v 47.422077 -12.499999 -72.906143
12 | v -48.743149 14.371903 -13.053298
13 | v 51.762924 14.371903 -15.394273
14 | v -14.642105 -12.500002 -2.285420
15 | v 15.316101 -12.500002 -2.285420
16 | v -39.400303 14.371902 -8.378899
17 | v 44.745724 14.371902 -10.712322
18 | v -51.088928 14.371903 -15.394273
19 | v -13.839962 12.500002 -4.176317
20 | v -36.046089 -12.500002 -10.845088
21 | v 45.274303 -12.500001 -15.124923
22 | vt 0.931774 0.885932
23 | vt 0.645638 1.000000
24 | vt 0.936913 0.826202
25 | vt 1.000000 0.000000
26 | vt 0.042205 0.044065
27 | vt 0.000000 0.000000
28 | vt 0.042205 0.797189
29 | vt 0.957795 0.797189
30 | vt 0.957795 0.044065
31 | vt 0.022807 0.854244
32 | vt 1.000000 0.822556
33 | vt 0.637839 0.974404
34 | vt 0.354362 1.000000
35 | vt 0.113645 0.917517
36 | vt 0.000000 0.822556
37 | vt 0.362161 0.974404
38 | vt 0.146257 0.884135
39 | vn 0.3899 -0.1417 0.9099
40 | vn 0.0000 -0.1203 -0.9927
41 | vn -0.9872 -0.1595 -0.0000
42 | vn 0.9872 -0.1595 -0.0000
43 | vn 0.0000 1.0000 0.0000
44 | vn 0.0000 0.0754 0.9972
45 | vn 0.2059 0.0804 0.9753
46 | vn 0.0087 0.9500 0.3121
47 | vn -0.6971 -0.1613 0.6986
48 | vn 0.0000 0.9135 0.4069
49 | vn -0.1561 0.0795 0.9845
50 | vn -0.0000 -1.0000 -0.0000
51 | vn -0.5071 -0.1702 0.8449
52 | vn -0.4432 -0.1366 0.8859
53 | vn -0.3681 -0.1304 0.9206
54 | vn 0.6971 -0.1613 0.6986
55 | vn 0.5507 -0.1247 0.8253
56 | g Spade_hull_1_Spade_hull_1_Material.001
57 | usemtl Material.001
58 | s off
59 | f 13/1/1 11/2/1 17/3/1
60 | f 5/4/2 2/5/2 6/6/2
61 | f 2/5/3 3/7/3 6/6/3
62 | f 5/4/4 4/8/4 7/9/4
63 | f 2/5/2 5/4/2 7/9/2
64 | f 5/4/5 6/6/5 8/10/5
65 | f 4/8/4 5/4/4 9/11/4
66 | f 5/4/5 8/10/5 9/11/5
67 | f 1/12/6 10/13/6 11/2/6
68 | f 9/11/5 8/10/5 12/14/5
69 | f 1/12/7 11/2/7 13/1/7
70 | f 12/14/8 1/12/8 13/1/8
71 | f 9/11/5 12/14/5 13/1/5
72 | f 6/6/3 3/7/3 14/15/3
73 | f 3/7/9 8/10/9 14/15/9
74 | f 8/10/5 6/6/5 14/15/5
75 | f 10/13/6 1/12/6 15/16/6
76 | f 1/12/10 12/14/10 15/16/10
77 | f 12/14/11 10/13/11 15/16/11
78 | f 3/7/12 2/5/12 16/17/12
79 | f 8/10/13 3/7/13 16/17/13
80 | f 2/5/12 10/13/12 16/17/12
81 | f 12/14/14 8/10/14 16/17/14
82 | f 10/13/15 12/14/15 16/17/15
83 | f 2/5/12 7/9/12 17/3/12
84 | f 7/9/12 4/8/12 17/3/12
85 | f 4/8/16 9/11/16 17/3/16
86 | f 10/13/12 2/5/12 17/3/12
87 | f 11/2/12 10/13/12 17/3/12
88 | f 9/11/17 13/1/17 17/3/17
89 | o Spade_hull_2
90 | v -13.890795 7.457798 -0.596692
91 | v 14.364998 5.018061 223.095016
92 | v 15.084849 -4.570136 228.793839
93 | v -14.683066 -7.166142 230.948853
94 | v 7.350998 -13.684101 0.985001
95 | v 12.128835 9.777925 0.000000
96 | v -4.337171 14.371893 223.095016
97 | v -13.890795 -6.769997 -0.596692
98 | v -6.677002 -13.684111 223.095016
99 | v -4.337171 14.371902 0.985002
100 | v 14.603409 -4.409708 -0.902424
101 | v 13.086816 -9.856565 233.182266
102 | v 5.011167 14.371893 223.095016
103 | v -13.691002 7.357893 223.095016
104 | v -6.677002 -13.684101 0.985001
105 | v 7.350998 -13.684111 223.095016
106 | v 5.011167 14.371902 0.985002
107 | v 14.603409 5.097509 -0.902424
108 | v 9.688083 12.032062 223.095016
109 | v -9.097026 12.135739 0.000001
110 | v -9.014087 12.032062 223.095016
111 | v 12.128835 -9.090124 -0.000000
112 | v 9.771022 12.135739 0.000001
113 | v 12.025167 9.694977 223.095016
114 | vt 0.003855 0.821491
115 | vt 0.956908 0.818705
116 | vt 0.956908 0.897215
117 | vt 0.990459 0.000000
118 | vt 0.001306 0.026615
119 | vt 0.001306 0.026615
120 | vt 0.000000 0.983827
121 | vt 0.956908 0.347552
122 | vt 1.000000 0.932880
123 | vt 0.956908 0.268949
124 | vt 0.981253 1.000000
125 | vt 0.008063 0.347552
126 | vt 0.956908 0.661593
127 | vt 0.956908 0.033327
128 | vt 0.008063 0.268949
129 | vt 0.008063 0.740195
130 | vt 0.956908 0.740195
131 | vt 0.008063 0.661593
132 | vt 0.956908 0.975818
133 | vt 0.000000 0.983827
134 | vt 0.003855 0.187653
135 | vt 0.956908 0.190439
136 | vt 0.003855 0.900698
137 | vt 0.003855 0.900698
138 | vn 0.7071 0.7071 0.0006
139 | vn -1.0000 -0.0000 -0.0034
140 | vn -0.0107 0.0000 -0.9999
141 | vn -0.0402 0.3593 0.9324
142 | vn -0.1243 -0.8221 0.5556
143 | vn 0.9348 -0.3551 -0.0022
144 | vn 0.0000 1.0000 0.0000
145 | vn 0.0000 0.3844 0.9232
146 | vn -0.9976 0.0686 0.0009
147 | vn -0.6915 -0.7223 -0.0036
148 | vn 0.0000 -0.2230 -0.9748
149 | vn -0.6314 -0.7755 -0.0000
150 | vn 0.0000 -1.0000 -0.0000
151 | vn 0.0000 -0.9350 0.3548
152 | vn 0.5551 -0.8318 -0.0000
153 | vn 0.9971 0.0755 0.0011
154 | vn 1.0000 -0.0000 -0.0021
155 | vn 0.2182 0.4362 0.8730
156 | vn -0.4252 0.9051 0.0000
157 | vn -0.0001 0.1267 -0.9919
158 | vn -0.2116 0.4229 0.8811
159 | vn -0.7069 0.7073 0.0009
160 | vn -0.4484 0.4487 0.7731
161 | vn -0.4474 0.8943 0.0006
162 | vn -0.6984 0.7157 0.0006
163 | vn 0.0034 -0.2130 -0.9770
164 | vn 0.0054 -0.1921 -0.9814
165 | vn 0.6925 -0.7214 -0.0052
166 | vn 0.8836 -0.4682 -0.0052
167 | vn 0.0000 0.4031 -0.9152
168 | vn 0.4252 0.9051 0.0000
169 | vn 0.3541 0.3541 -0.8656
170 | vn 0.4474 0.8943 0.0006
171 | vn 0.0000 0.1272 -0.9919
172 | vn 0.7533 0.3769 0.5389
173 | vn 0.5363 0.4098 0.7378
174 | vn 0.8943 0.4474 0.0011
175 | vn 0.8841 0.4673 0.0006
176 | vn 0.4319 0.4319 0.7917
177 | g Spade_hull_2_Spade_hull_2_None
178 | usemtl None
179 | s off
180 | f 40/18/18 36/19/18 41/20/18
181 | f 21/21/19 18/22/19 25/23/19
182 | f 25/23/20 18/22/20 28/24/20
183 | f 24/25/21 21/21/21 29/26/21
184 | f 21/21/22 26/27/22 29/26/22
185 | f 28/24/23 20/28/23 29/26/23
186 | f 27/29/24 24/25/24 30/30/24
187 | f 24/25/25 29/26/25 30/30/25
188 | f 18/22/26 21/21/26 31/31/26
189 | f 21/21/27 25/23/27 32/32/27
190 | f 25/23/28 22/33/28 32/32/28
191 | f 26/27/29 21/21/29 32/32/29
192 | f 22/33/30 26/27/30 32/32/30
193 | f 26/27/30 22/33/30 33/34/30
194 | f 29/26/31 26/27/31 33/34/31
195 | f 22/33/32 29/26/32 33/34/32
196 | f 27/29/24 30/30/24 34/35/24
197 | f 19/36/33 20/28/33 35/37/33
198 | f 20/28/34 28/24/34 35/37/34
199 | f 28/24/20 18/22/20 35/37/20
200 | f 30/30/35 29/26/35 36/19/35
201 | f 24/25/36 27/29/36 37/38/36
202 | f 35/37/37 18/22/37 37/38/37
203 | f 21/21/38 24/25/38 38/39/38
204 | f 18/22/39 31/31/39 38/39/39
205 | f 31/31/40 21/21/40 38/39/40
206 | f 24/25/41 37/38/41 38/39/41
207 | f 37/38/42 18/22/42 38/39/42
208 | f 22/33/43 25/23/43 39/40/43
209 | f 25/23/44 28/24/44 39/40/44
210 | f 29/26/45 22/33/45 39/40/45
211 | f 28/24/46 29/26/46 39/40/46
212 | f 27/29/47 34/35/47 40/18/47
213 | f 34/35/48 30/30/48 40/18/48
214 | f 23/41/49 35/37/49 40/18/49
215 | f 30/30/50 36/19/50 40/18/50
216 | f 37/38/47 27/29/47 40/18/47
217 | f 35/37/51 37/38/51 40/18/51
218 | f 20/28/52 19/36/52 41/20/52
219 | f 29/26/53 20/28/53 41/20/53
220 | f 19/36/54 35/37/54 41/20/54
221 | f 35/37/55 23/41/55 41/20/55
222 | f 36/19/56 29/26/56 41/20/56
223 | f 23/41/18 40/18/18 41/20/18
224 | o Spade_hull_3
225 | v 77.490997 16.702112 262.835297
226 | v 16.708946 -41.740112 255.821991
227 | v 16.708946 5.011197 230.113831
228 | v 14.980774 -18.823496 280.796783
229 | v 75.146820 -23.035883 276.861938
230 | v 61.117447 21.385893 279.201538
231 | v 21.385403 -4.344008 225.440125
232 | v 35.414776 -41.740112 258.156097
233 | v 68.132133 -30.049196 279.201538
234 | v 20.606052 7.141351 249.118271
235 | v 72.808594 9.688798 255.821991
236 | v 77.490997 21.385893 276.861938
237 | v 18.206045 -29.070272 277.819611
238 | v 16.708946 0.333594 223.095032
239 | v 77.490997 -16.016390 279.201538
240 | v 35.414776 -41.740112 262.835297
241 | v 30.738319 5.011197 230.113831
242 | v 30.738319 -39.398224 253.482391
243 | v 70.470360 -2.002120 260.495697
244 | v 56.440987 -34.720619 274.522339
245 | v 77.490997 12.024509 260.495697
246 | v 72.808594 14.366400 255.821991
247 | v 61.117447 21.385893 276.861938
248 | v 19.047174 -2.002118 223.095032
249 | v 16.708946 -25.371593 241.806351
250 | v 63.455673 -32.384911 274.522339
251 | v 54.102760 5.011196 244.140457
252 | v 26.061861 -6.679720 230.113831
253 | v 26.304573 9.573268 251.148285
254 | vt 1.000000 0.738066
255 | vt 1.000000 0.738066
256 | vt 0.812872 0.181151
257 | vt 0.740603 0.027646
258 | vt 0.363030 0.000000
259 | vt 0.774347 0.089990
260 | vt 0.000000 0.027646
261 | vt 0.200707 0.051596
262 | vt 0.185200 0.850283
263 | vt 0.666504 0.027646
264 | vt 0.407498 1.000000
265 | vt 0.296300 0.962499
266 | vt 0.925803 1.000000
267 | vt 1.000000 1.000000
268 | vt 0.000000 0.326891
269 | vt 0.000000 0.326891
270 | vt 0.740603 0.252079
271 | vt 0.592404 0.102457
272 | vt 0.037099 0.252079
273 | vt 0.629503 0.887688
274 | vt 0.814702 0.925094
275 | vt 0.111198 0.663255
276 | vt 0.851703 1.000000
277 | vt 0.888802 0.925094
278 | vt 0.629503 0.065052
279 | vt 0.259299 0.027646
280 | vt 0.148199 0.775472
281 | vt 0.740603 0.625849
282 | vt 0.555403 0.177268
283 | vn -0.3213 0.9470 0.0000
284 | vn -0.9152 0.3758 0.1455
285 | vn -0.5763 0.6793 0.4544
286 | vn -0.8940 -0.3592 0.2677
287 | vn -0.0321 -0.2882 0.9570
288 | vn -0.9994 0.0300 -0.0200
289 | vn 0.0250 0.0110 0.9996
290 | vn 0.0263 -0.0175 0.9995
291 | vn 0.6398 -0.4267 0.6392
292 | vn 1.0000 -0.0000 0.0000
293 | vn 0.1412 0.0618 0.9881
294 | vn 0.0000 -1.0000 0.0000
295 | vn -0.1874 -0.8456 0.4998
296 | vn 0.0000 0.8321 -0.5546
297 | vn 0.5072 -0.4518 -0.7339
298 | vn 0.0619 -0.8662 -0.4959
299 | vn 0.5432 -0.4370 -0.7169
300 | vn 0.6127 -0.3960 -0.6840
301 | vn -0.0337 -0.6639 0.7470
302 | vn -0.0663 -0.7982 0.5987
303 | vn 0.7444 -0.3227 -0.5846
304 | vn 0.9281 -0.2067 -0.3098
305 | vn 0.0013 0.9485 -0.3167
306 | vn 0.0000 0.9397 -0.3420
307 | vn 0.7449 0.2985 -0.5967
308 | vn 0.7064 0.0000 -0.7078
309 | vn 0.0000 1.0000 0.0000
310 | vn 0.0000 0.9486 -0.3165
311 | vn -0.0105 0.9468 -0.3217
312 | vn 0.3313 0.3317 -0.8833
313 | vn 0.2174 -0.5737 -0.7897
314 | vn 0.0225 -0.6262 -0.7793
315 | vn -0.9987 -0.0331 -0.0387
316 | vn -0.9988 -0.0287 -0.0394
317 | vn -0.0181 -0.6503 -0.7595
318 | vn -0.5068 -0.5073 -0.6970
319 | vn 0.5524 -0.5297 -0.6436
320 | vn 0.6336 -0.7244 -0.2717
321 | vn 0.3165 -0.9486 0.0000
322 | vn 0.3121 -0.9372 0.1559
323 | vn 0.3159 -0.9488 0.0015
324 | vn 0.5552 -0.4021 -0.7281
325 | vn 0.5143 0.0414 -0.8566
326 | vn 0.5297 0.0000 -0.8482
327 | vn 0.5347 -0.2684 -0.8012
328 | vn 0.5147 0.0000 -0.8574
329 | vn 0.5079 -0.4514 -0.7337
330 | vn 0.5395 -0.4334 -0.7219
331 | vn 0.5383 -0.4032 -0.7401
332 | vn 0.5407 -0.4276 -0.7244
333 | vn -0.3850 0.9226 -0.0245
334 | vn -0.4378 0.8825 0.1717
335 | vn -0.2597 0.9615 -0.0900
336 | g Spade_hull_3_Spade_hull_3_None
337 | usemtl None
338 | s off
339 | f 47/42/57 64/43/57 70/44/57
340 | f 44/45/58 45/46/58 51/47/58
341 | f 45/46/59 47/42/59 51/47/59
342 | f 45/46/60 43/48/60 54/49/60
343 | f 50/50/61 45/46/61 54/49/61
344 | f 45/46/62 44/45/62 55/51/62
345 | f 47/42/63 45/46/63 56/52/63
346 | f 45/46/64 50/50/64 56/52/64
347 | f 50/50/65 46/53/65 56/52/65
348 | f 42/54/66 53/55/66 56/52/66
349 | f 53/55/67 47/42/67 56/52/67
350 | f 43/48/68 49/56/68 57/57/68
351 | f 54/49/69 43/48/69 57/57/69
352 | f 55/51/70 44/45/70 58/58/70
353 | f 48/59/71 49/56/71 59/60/71
354 | f 49/56/72 43/48/72 59/60/72
355 | f 46/53/73 49/56/73 60/61/73
356 | f 52/62/74 46/53/74 60/61/74
357 | f 50/50/75 54/49/75 61/63/75
358 | f 54/49/76 57/57/76 61/63/76
359 | f 46/53/77 52/62/77 62/64/77
360 | f 42/54/66 56/52/66 62/64/66
361 | f 56/52/78 46/53/78 62/64/78
362 | f 53/55/79 42/54/79 63/65/79
363 | f 58/58/80 44/45/80 63/65/80
364 | f 42/54/81 62/64/81 63/65/81
365 | f 62/64/82 52/62/82 63/65/82
366 | f 47/42/83 53/55/83 64/43/83
367 | f 53/55/84 63/65/84 64/43/84
368 | f 63/65/85 44/45/85 64/43/85
369 | f 55/51/86 58/58/86 65/66/86
370 | f 48/59/87 59/60/87 65/66/87
371 | f 65/66/88 59/60/88 66/67/88
372 | f 43/48/89 45/46/89 66/67/89
373 | f 45/46/90 55/51/90 66/67/90
374 | f 59/60/91 43/48/91 66/67/91
375 | f 55/51/92 65/66/92 66/67/92
376 | f 49/56/93 46/53/93 67/68/93
377 | f 46/53/94 50/50/94 67/68/94
378 | f 57/57/95 49/56/95 67/68/95
379 | f 50/50/96 61/63/96 67/68/96
380 | f 61/63/97 57/57/97 67/68/97
381 | f 52/62/98 60/61/98 68/69/98
382 | f 58/58/99 63/65/99 68/69/99
383 | f 63/65/100 52/62/100 68/69/100
384 | f 48/59/101 65/66/101 68/69/101
385 | f 65/66/102 58/58/102 68/69/102
386 | f 49/56/103 48/59/103 69/70/103
387 | f 60/61/104 49/56/104 69/70/104
388 | f 48/59/105 68/69/105 69/70/105
389 | f 68/69/106 60/61/106 69/70/106
390 | f 44/45/107 51/47/107 70/44/107
391 | f 51/47/108 47/42/108 70/44/108
392 | f 64/43/109 44/45/109 70/44/109
393 | o Spade_hull_4
394 | v -27.721521 2.681664 227.780426
395 | v -76.817001 -20.693991 276.859650
396 | v -76.817001 -20.693991 279.201782
397 | v -18.367004 -41.740112 255.822693
398 | v -60.448029 21.385893 279.201782
399 | v -16.657425 -18.836853 280.774445
400 | v -76.817001 14.366400 258.159546
401 | v -18.367004 9.688798 251.154251
402 | v -34.735977 -41.740112 258.159546
403 | v -18.367004 -4.344008 225.433044
404 | v -55.767906 -34.720619 274.522797
405 | v -76.817001 21.385893 276.859650
406 | v -18.367004 5.011197 230.117279
407 | v -34.735977 0.333594 232.454132
408 | v -19.834827 -29.051123 277.851501
409 | v -69.802544 -30.049196 279.201782
410 | v -76.817001 9.688798 258.159546
411 | v -34.735977 -41.740112 262.833252
412 | v -27.721521 -27.707306 244.143677
413 | v -69.802544 -30.049196 276.859650
414 | v -72.136887 14.366400 255.822693
415 | v -60.448029 21.385893 276.859650
416 | v -23.047123 -2.002118 225.433044
417 | v -23.047123 2.681664 225.433044
418 | v -30.061581 -39.398224 253.485840
419 | v -34.735977 -34.720619 253.485840
420 | v -18.367004 -25.371593 241.806824
421 | vt 0.037099 0.777190
422 | vt 0.222298 0.816088
423 | vt 0.259299 0.971583
424 | vt 1.000000 0.272093
425 | vt 0.333399 0.000000
426 | vt 0.362818 1.000000
427 | vt 0.333399 0.000000
428 | vt 0.888802 0.000000
429 | vt 0.814702 0.971583
430 | vt 1.000000 0.000000
431 | vt 0.740603 0.971583
432 | vt 0.592404 0.971583
433 | vt 0.000000 0.971583
434 | vt 0.201011 0.947184
435 | vt 0.111198 0.349888
436 | vt 0.185200 0.116598
437 | vt 0.814702 0.000000
438 | vt 0.703700 0.816088
439 | vt 0.666504 0.699490
440 | vt 0.000000 0.699490
441 | vt 0.000000 0.699490
442 | vt 0.185200 0.116598
443 | vt 0.888802 0.077795
444 | vt 1.000000 0.272093
445 | vt 0.629503 0.893788
446 | vt 0.703700 0.893788
447 | vt 0.111198 0.699490
448 | vn -0.0415 -0.6186 -0.7846
449 | vn -0.0264 0.0103 0.9996
450 | vn -1.0000 0.0000 0.0000
451 | vn 0.5476 0.6183 0.5638
452 | vn -0.1414 0.0550 0.9884
453 | vn 0.9988 0.0486 -0.0108
454 | vn 0.9995 0.0137 -0.0273
455 | vn 0.8970 -0.3546 0.2640
456 | vn 0.0335 -0.6553 0.7546
457 | vn -0.8001 -0.5999 0.0000
458 | vn -0.0255 -0.0191 0.9995
459 | vn 0.0316 -0.2840 0.9583
460 | vn -0.5262 0.0000 -0.8504
461 | vn -0.5330 -0.0838 -0.8420
462 | vn 0.0000 -1.0000 0.0000
463 | vn 0.2134 -0.8403 0.4983
464 | vn 0.0696 -0.7950 0.6026
465 | vn -0.3147 -0.9492 0.0039
466 | vn -0.3163 -0.9487 0.0000
467 | vn -0.5730 -0.4296 -0.6980
468 | vn -0.1728 0.9221 -0.3461
469 | vn 0.0108 0.9467 -0.3219
470 | vn 0.2678 0.9635 0.0000
471 | vn 0.0000 1.0000 0.0000
472 | vn 0.1374 0.9669 -0.2150
473 | vn 0.0000 0.9486 -0.3165
474 | vn -0.5273 -0.1035 -0.8434
475 | vn -0.2676 -0.5348 -0.8015
476 | vn -0.5258 -0.4360 -0.7304
477 | vn -0.4111 0.4011 -0.8186
478 | vn 0.5578 0.3716 -0.7421
479 | vn -0.4074 0.4104 -0.8158
480 | vn -0.0393 0.9098 -0.4132
481 | vn -0.4488 0.0000 -0.8936
482 | vn 0.0000 0.0000 -1.0000
483 | vn -0.0719 -0.8611 -0.5033
484 | vn -0.4850 -0.4846 -0.7280
485 | vn -0.5380 -0.4337 -0.7228
486 | vn -0.5316 -0.4358 -0.7263
487 | vn -0.5322 -0.4692 -0.7047
488 | vn -0.5389 -0.4347 -0.7216
489 | vn -0.4850 -0.4847 -0.7279
490 | vn 0.9987 -0.0328 -0.0383
491 | vn 0.9988 -0.0302 -0.0388
492 | vn -0.0437 -0.6138 -0.7883
493 | vn 0.0215 -0.6503 -0.7594
494 | g Spade_hull_4_Spade_hull_4_None
495 | usemtl None
496 | s off
497 | f 95/71/110 89/72/110 97/73/110
498 | f 75/74/111 73/75/111 76/76/111
499 | f 72/77/112 73/75/112 77/78/112
500 | f 75/74/113 76/76/113 78/79/113
501 | f 73/75/114 75/74/114 82/80/114
502 | f 77/78/112 73/75/112 82/80/112
503 | f 78/79/115 76/76/115 83/81/115
504 | f 76/76/116 80/82/116 83/81/116
505 | f 74/83/117 76/76/117 85/84/117
506 | f 81/85/118 85/84/118 86/86/118
507 | f 73/75/119 72/77/119 86/86/119
508 | f 76/76/120 73/75/120 86/86/120
509 | f 85/84/121 76/76/121 86/86/121
510 | f 72/77/112 77/78/112 87/87/112
511 | f 77/78/122 71/88/122 87/87/122
512 | f 71/88/123 84/89/123 87/87/123
513 | f 79/90/124 74/83/124 88/91/124
514 | f 74/83/125 85/84/125 88/91/125
515 | f 85/84/126 81/85/126 88/91/126
516 | f 81/85/127 86/86/127 88/91/127
517 | f 86/86/128 79/90/128 88/91/128
518 | f 86/86/119 72/77/119 90/92/119
519 | f 79/90/128 86/86/128 90/92/128
520 | f 72/77/129 87/87/129 90/92/129
521 | f 77/78/130 82/80/130 91/93/130
522 | f 83/81/131 91/93/131 92/94/131
523 | f 75/74/132 78/79/132 92/94/132
524 | f 82/80/133 75/74/133 92/94/133
525 | f 78/79/134 83/81/134 92/94/134
526 | f 91/93/135 82/80/135 92/94/135
527 | f 84/89/136 71/88/136 93/95/136
528 | f 80/82/137 89/72/137 93/95/137
529 | f 89/72/138 84/89/138 93/95/138
530 | f 71/88/139 77/78/139 94/96/139
531 | f 83/81/140 80/82/140 94/96/140
532 | f 77/78/141 91/93/141 94/96/141
533 | f 91/93/142 83/81/142 94/96/142
534 | f 93/95/143 71/88/143 94/96/143
535 | f 80/82/144 93/95/144 94/96/144
536 | f 74/83/145 79/90/145 95/71/145
537 | f 89/72/146 95/71/146 96/97/146
538 | f 87/87/147 84/89/147 96/97/147
539 | f 84/89/148 89/72/148 96/97/148
540 | f 79/90/149 90/92/149 96/97/149
541 | f 90/92/150 87/87/150 96/97/150
542 | f 95/71/151 79/90/151 96/97/151
543 | f 76/76/152 74/83/152 97/73/152
544 | f 80/82/153 76/76/153 97/73/153
545 | f 89/72/154 80/82/154 97/73/154
546 | f 74/83/155 95/71/155 97/73/155
547 | o Spade_hull_5
548 | v -18.367002 -2.001431 223.095032
549 | v -18.367002 -41.740112 255.821991
550 | v 16.702997 -41.740112 255.821991
551 | v 16.702997 9.686740 244.140457
552 | v -18.367002 9.686740 248.819672
553 | v 7.348480 -13.684109 223.095032
554 | v 17.862478 -18.667355 281.056519
555 | v 5.010709 14.371893 260.495697
556 | v -19.526440 -18.667343 281.056458
557 | v 9.682818 12.032064 223.095032
558 | v 16.702997 -2.001431 223.095032
559 | v -17.413399 -29.156620 277.675934
560 | v 15.749385 -29.156610 277.675903
561 | v -4.333510 14.371893 260.495697
562 | v -4.015960 11.827512 225.639160
563 | v -6.674714 -13.684109 223.095032
564 | v -13.684594 7.357894 223.095032
565 | v 16.702997 9.686740 246.480072
566 | v 14.870256 -38.807728 261.613770
567 | vt 0.941675 0.224257
568 | vt 0.941675 0.224257
569 | vt 0.664558 0.052260
570 | vt 0.564633 0.000000
571 | vt 0.564633 0.000000
572 | vt 0.000000 0.500000
573 | vt 0.000000 0.708203
574 | vt 0.999999 0.411191
575 | vt 0.443823 0.916504
576 | vt 0.000000 0.958301
577 | vt 0.645267 1.000000
578 | vt 0.363093 0.916504
579 | vt 0.000000 0.708203
580 | vt 1.000000 0.411191
581 | vt 0.645267 1.000000
582 | vt 0.043894 0.954655
583 | vt 0.000000 0.500000
584 | vt 0.000000 0.875000
585 | vt 0.403458 0.916504
586 | vn 0.0000 -0.8572 0.5150
587 | vn 0.0000 -0.7592 -0.6508
588 | vn -0.9994 -0.0216 -0.0262
589 | vn -0.9996 0.0270 -0.0122
590 | vn 0.0000 0.0000 -1.0000
591 | vn 0.3548 0.9348 -0.0142
592 | vn 0.6218 -0.4979 -0.6046
593 | vn 0.9994 -0.0216 -0.0262
594 | vn 0.9996 0.0237 -0.0132
595 | vn 0.8679 0.4342 -0.2411
596 | vn 0.0000 -0.3067 0.9518
597 | vn -0.9498 -0.2513 0.1862
598 | vn 0.9498 -0.2513 0.1862
599 | vn 0.0000 -0.3068 0.9518
600 | vn 0.0000 0.5284 0.8490
601 | vn -0.6206 0.5997 0.5052
602 | vn 0.0000 0.9980 -0.0624
603 | vn -0.0284 0.9969 -0.0730
604 | vn -0.2609 0.9626 -0.0726
605 | vn -0.5362 -0.5366 -0.6516
606 | vn -0.8764 0.4385 -0.1992
607 | vn -0.1403 0.7013 -0.6989
608 | vn -0.3815 0.9118 -0.1520
609 | vn 0.9992 0.0409 0.0000
610 | vn 0.3720 0.9283 0.0000
611 | vn 0.7245 0.5447 0.4224
612 | vn 0.0000 -0.8922 0.4517
613 | vn -0.0106 -0.8664 0.4993
614 | vn 0.2146 -0.8423 0.4944
615 | g Spade_hull_5_Spade_hull_5_None
616 | usemtl None
617 | s off
618 | f 110/98/156 109/99/156 116/100/156
619 | f 100/101/157 99/102/157 103/103/157
620 | f 98/104/158 99/102/158 106/105/158
621 | f 102/106/159 98/104/159 106/105/159
622 | f 103/103/160 98/104/160 107/107/160
623 | f 105/108/161 101/109/161 107/107/161
624 | f 100/101/162 103/103/162 108/110/162
625 | f 104/111/163 100/101/163 108/110/163
626 | f 101/109/164 104/111/164 108/110/164
627 | f 107/107/165 101/109/165 108/110/165
628 | f 103/103/160 107/107/160 108/110/160
629 | f 104/111/166 106/105/166 109/99/166
630 | f 106/105/167 99/102/167 109/99/167
631 | f 100/101/168 104/111/168 110/98/168
632 | f 104/111/169 109/99/169 110/98/169
633 | f 104/111/170 105/108/170 111/112/170
634 | f 106/105/170 104/111/170 111/112/170
635 | f 102/106/171 106/105/171 111/112/171
636 | f 105/108/172 107/107/172 111/112/172
637 | f 111/112/173 107/107/173 112/113/173
638 | f 102/106/174 111/112/174 112/113/174
639 | f 99/102/175 98/104/175 113/114/175
640 | f 98/104/160 103/103/160 113/114/160
641 | f 103/103/157 99/102/157 113/114/157
642 | f 98/104/176 102/106/176 114/115/176
643 | f 107/107/160 98/104/160 114/115/160
644 | f 112/113/177 107/107/177 114/115/177
645 | f 102/106/178 112/113/178 114/115/178
646 | f 104/111/179 101/109/179 115/116/179
647 | f 101/109/180 105/108/180 115/116/180
648 | f 105/108/181 104/111/181 115/116/181
649 | f 99/102/182 100/101/182 116/100/182
650 | f 109/99/183 99/102/183 116/100/183
651 | f 100/101/184 110/98/184 116/100/184
652 | o Spade_hull_6
653 | v 16.702995 37.746849 393.750061
654 | v -41.747002 -30.050114 279.218475
655 | v 16.702995 -30.050114 279.218475
656 | v 16.702995 47.099770 384.402191
657 | v -41.747002 49.441891 389.081848
658 | v -40.020741 -16.607862 277.884308
659 | v -41.747002 33.062614 386.736298
660 | v 16.702995 -13.678620 283.898132
661 | v 16.702995 -27.707998 286.232239
662 | v -41.747002 -27.707998 286.232239
663 | v -41.747002 40.081188 396.095642
664 | v 16.702995 49.441891 389.081848
665 | v 16.702995 7.361547 344.653625
666 | v 16.702995 -18.355078 279.218475
667 | v -41.868546 -16.127695 278.975433
668 | v -41.747002 47.099770 384.402191
669 | v 16.702995 40.081188 396.095642
670 | v 16.702995 -1.991367 302.605316
671 | v -41.747002 7.361547 344.653625
672 | v 16.702995 33.062614 386.736298
673 | v -41.747002 -30.050114 281.563995
674 | vt 0.070619 0.029464
675 | vt 0.009230 0.175142
676 | vt 0.031128 0.000000
677 | vt 0.980158 0.852878
678 | vt 0.011286 0.000000
679 | vt 0.901080 0.970536
680 | vt 0.011286 0.000000
681 | vt 0.000000 0.169102
682 | vt 0.050874 0.205951
683 | vt 0.070619 0.029464
684 | vt 0.920825 0.793951
685 | vt 1.000000 0.882244
686 | vt 0.940667 1.000000
687 | vt 0.940667 1.000000
688 | vt 0.564830 0.470634
689 | vt 0.011286 0.147122
690 | vt 0.901080 0.970536
691 | vt 1.000000 0.882244
692 | vt 0.209126 0.352976
693 | vt 0.564830 0.470634
694 | vt 0.920825 0.793951
695 | vn -1.0000 -0.0080 0.0040
696 | vn 1.0000 0.0000 0.0000
697 | vn 0.0000 -0.0988 -0.9951
698 | vn -0.0079 -0.8000 0.5999
699 | vn 0.0000 0.8943 -0.4476
700 | vn 0.0000 0.5996 0.8003
701 | vn 0.0000 -0.8574 0.5147
702 | vn 0.0235 0.0000 -0.9997
703 | vn 0.0384 0.7068 -0.7063
704 | vn -0.5121 -0.0195 -0.8587
705 | vn -1.0000 0.0006 0.0008
706 | vn -1.0000 -0.0038 0.0029
707 | vn 0.0000 0.8582 -0.5133
708 | vn -0.0805 0.8549 -0.5126
709 | vn -0.9999 0.0116 -0.0058
710 | vn 0.0000 -0.7088 0.7054
711 | vn 0.0033 0.8574 -0.5146
712 | vn 0.0124 0.8480 -0.5298
713 | vn 0.0000 -0.8534 0.5212
714 | vn -1.0000 -0.0073 0.0045
715 | vn -1.0000 -0.0076 0.0046
716 | vn 0.0000 -0.8316 0.5554
717 | vn 0.0000 -1.0000 0.0000
718 | vn 0.0127 -0.9484 0.3167
719 | vn 0.0000 -0.8938 0.4484
720 | vn -1.0000 -0.0087 0.0000
721 | g Spade_hull_6_Spade_hull_6_None
722 | usemtl None
723 | s off
724 | f 126/117/185 131/118/185 137/119/185
725 | f 117/120/186 119/121/186 120/122/186
726 | f 119/121/187 118/123/187 122/124/187
727 | f 120/122/186 119/121/186 124/125/186
728 | f 119/121/186 117/120/186 125/126/186
729 | f 123/127/188 117/120/188 127/128/188
730 | f 117/120/186 120/122/186 128/129/186
731 | f 120/122/189 121/130/189 128/129/189
732 | f 121/130/190 127/128/190 128/129/190
733 | f 125/126/186 117/120/186 129/131/186
734 | f 126/117/191 125/126/191 129/131/191
735 | f 119/121/192 122/124/192 130/132/192
736 | f 124/125/186 119/121/186 130/132/186
737 | f 122/124/193 124/125/193 130/132/193
738 | f 122/124/194 118/123/194 131/118/194
739 | f 127/128/195 121/130/195 131/118/195
740 | f 123/127/196 127/128/196 131/118/196
741 | f 121/130/189 120/122/189 132/133/189
742 | f 120/122/197 122/124/197 132/133/197
743 | f 122/124/198 131/118/198 132/133/198
744 | f 131/118/199 121/130/199 132/133/199
745 | f 127/128/200 117/120/200 133/134/200
746 | f 117/120/186 128/129/186 133/134/186
747 | f 128/129/190 127/128/190 133/134/190
748 | f 122/124/201 120/122/201 134/135/201
749 | f 120/122/186 124/125/186 134/135/186
750 | f 124/125/202 122/124/202 134/135/202
751 | f 129/131/203 123/127/203 135/136/203
752 | f 126/117/191 129/131/191 135/136/191
753 | f 123/127/204 131/118/204 135/136/204
754 | f 131/118/205 126/117/205 135/136/205
755 | f 117/120/206 123/127/206 136/137/206
756 | f 129/131/186 117/120/186 136/137/186
757 | f 123/127/203 129/131/203 136/137/203
758 | f 118/123/207 119/121/207 137/119/207
759 | f 119/121/208 125/126/208 137/119/208
760 | f 125/126/209 126/117/209 137/119/209
761 | f 131/118/210 118/123/210 137/119/210
762 | o Spade_hull_7
763 | v 16.702999 37.746849 393.750061
764 | v 16.702999 -30.050114 279.218475
765 | v 47.096996 -30.050114 279.218475
766 | v 45.142422 -9.560382 277.473816
767 | v 47.096996 40.081188 396.095642
768 | v 16.702999 49.441891 389.081848
769 | v 47.096996 49.441891 389.081848
770 | v 16.702999 -13.678620 283.898132
771 | v 47.096996 -27.707998 286.232239
772 | v 16.702999 -27.707998 286.232239
773 | v 47.096996 33.062614 386.736298
774 | v 47.213421 -9.145409 278.770782
775 | v 16.702999 7.361547 344.653625
776 | v 16.702999 47.099770 384.402191
777 | v 16.702999 -18.355078 279.218475
778 | v 16.702999 40.081188 396.095642
779 | v 16.702999 -1.991367 302.605316
780 | v 47.096996 7.361547 344.653625
781 | v 16.702999 33.062614 386.736298
782 | v 47.096996 47.099770 384.402191
783 | vt 0.000000 0.257758
784 | vt 0.901422 0.970536
785 | vt 0.901422 0.970536
786 | vt 0.014708 0.000000
787 | vt 0.014708 0.000000
788 | vt 0.980226 0.852878
789 | vt 0.940873 1.000000
790 | vt 1.000000 0.882244
791 | vt 0.940873 1.000000
792 | vt 0.054158 0.205951
793 | vt 0.073835 0.029464
794 | vt 0.073835 0.029464
795 | vt 0.921099 0.793951
796 | vt 0.010934 0.262979
797 | vt 0.566336 0.470634
798 | vt 0.014708 0.147122
799 | vt 1.000000 0.882244
800 | vt 0.211862 0.352976
801 | vt 0.566336 0.470634
802 | vt 0.921099 0.793951
803 | vn 0.0000 0.8836 -0.4682
804 | vn 0.0000 -0.0848 -0.9964
805 | vn -1.0000 0.0000 0.0000
806 | vn 0.0000 0.5996 0.8003
807 | vn 0.0000 -0.9485 0.3167
808 | vn 0.0151 -0.7999 0.5999
809 | vn 1.0000 -0.0030 0.0023
810 | vn 0.5337 -0.0211 -0.8454
811 | vn 1.0000 0.0006 0.0008
812 | vn 1.0000 -0.0055 0.0018
813 | vn 0.0000 -0.8574 0.5147
814 | vn 0.0000 0.8943 -0.4476
815 | vn -0.0612 0.0000 -0.9981
816 | vn -0.2535 0.6842 -0.6838
817 | vn 0.0000 -0.7088 0.7054
818 | vn -0.2357 0.8242 -0.5149
819 | vn -0.2209 0.8362 -0.5019
820 | vn 1.0000 -0.0049 0.0030
821 | vn 1.0000 -0.0051 0.0030
822 | vn 0.0000 -0.8534 0.5212
823 | vn 0.0000 -0.8316 0.5554
824 | vn 0.1166 0.8767 -0.4667
825 | vn 0.9993 0.0344 -0.0172
826 | g Spade_hull_7_Spade_hull_7_None
827 | usemtl None
828 | s off
829 | f 141/138/211 151/139/211 157/140/211
830 | f 140/141/212 139/142/212 141/138/212
831 | f 139/142/213 138/143/213 143/144/213
832 | f 143/144/214 142/145/214 144/146/214
833 | f 139/142/213 143/144/213 145/147/213
834 | f 139/142/215 140/141/215 146/148/215
835 | f 138/143/213 139/142/213 147/149/213
836 | f 139/142/215 146/148/215 147/149/215
837 | f 142/145/216 138/143/216 148/150/216
838 | f 142/145/217 148/150/217 149/151/217
839 | f 140/141/218 141/138/218 149/151/218
840 | f 144/146/219 142/145/219 149/151/219
841 | f 146/148/220 140/141/220 149/151/220
842 | f 138/143/213 147/149/213 150/152/213
843 | f 147/149/221 146/148/221 150/152/221
844 | f 143/144/222 144/146/222 151/139/222
845 | f 145/147/213 143/144/213 151/139/213
846 | f 141/138/223 139/142/223 152/153/223
847 | f 139/142/213 145/147/213 152/153/213
848 | f 145/147/224 141/138/224 152/153/224
849 | f 138/143/225 142/145/225 153/154/225
850 | f 143/144/213 138/143/213 153/154/213
851 | f 142/145/214 143/144/214 153/154/214
852 | f 141/138/226 145/147/226 154/155/226
853 | f 151/139/227 141/138/227 154/155/227
854 | f 145/147/213 151/139/213 154/155/213
855 | f 149/151/228 148/150/228 155/156/228
856 | f 146/148/229 149/151/229 155/156/229
857 | f 150/152/221 146/148/221 155/156/221
858 | f 148/150/230 150/152/230 155/156/230
859 | f 148/150/231 138/143/231 156/157/231
860 | f 138/143/213 150/152/213 156/157/213
861 | f 150/152/230 148/150/230 156/157/230
862 | f 149/151/232 141/138/232 157/140/232
863 | f 144/146/233 149/151/233 157/140/233
864 | f 151/139/222 144/146/222 157/140/222
865 | o Spade_hull_8
866 | v 84.501106 26.060055 375.054291
867 | v 47.100883 -30.050117 279.218475
868 | v 70.473106 -30.050117 279.218475
869 | v 61.128883 23.720686 281.563995
870 | v 47.100883 49.436394 393.750061
871 | v 86.842995 58.793892 377.388397
872 | v 79.825104 19.041939 290.911896
873 | v 47.100883 37.748222 393.750061
874 | v 46.386471 -7.421408 277.117462
875 | v 86.842995 40.087597 396.095642
876 | v 77.487106 -20.692621 279.218475
877 | v 68.135101 58.793892 377.388397
878 | v 75.149101 -27.710745 286.232239
879 | v 76.810699 20.930420 282.977631
880 | v 86.842995 58.793892 386.736298
881 | v 47.100883 49.436398 389.081848
882 | v 47.100883 -27.710745 286.232239
883 | v 48.457745 -2.901729 275.948273
884 | v 86.842995 40.087601 384.402191
885 | v 77.487106 -25.371370 288.577789
886 | v 61.128883 30.730108 300.259766
887 | v 47.100883 0.344342 332.994537
888 | v 61.128883 21.381313 279.218475
889 | v 68.135101 58.793892 386.736298
890 | v 84.501106 33.069481 386.736298
891 | v 47.100883 40.087597 396.095642
892 | v 86.842995 56.454517 389.081848
893 | v 47.100883 33.069481 386.736298
894 | v 79.825104 -6.665080 321.301086
895 | v 86.842995 37.748222 393.750061
896 | v 77.487106 -25.371370 283.898132
897 | vt 0.980477 0.763117
898 | vt 0.105117 0.052663
899 | vt 0.066168 0.052663
900 | vt 0.027218 0.000000
901 | vt 0.027218 0.000000
902 | vt 0.085595 0.026331
903 | vt 0.046740 0.605227
904 | vt 0.844298 1.000000
905 | vt 0.058506 0.573821
906 | vt 0.124544 0.552565
907 | vt 0.027218 0.105325
908 | vt 1.000000 0.789448
909 | vt 0.922101 1.000000
910 | vt 0.844298 1.000000
911 | vt 0.009731 0.254702
912 | vt 0.980477 0.894675
913 | vt 0.941623 0.894675
914 | vt 0.085595 0.026331
915 | vt 0.000000 0.305574
916 | vt 0.902674 0.789448
917 | vt 0.202347 0.684123
918 | vt 0.474803 0.342110
919 | vt 0.027218 0.578896
920 | vt 0.922101 1.000000
921 | vt 1.000000 0.789448
922 | vt 0.980477 0.763117
923 | vt 0.941623 0.973669
924 | vt 0.922101 0.710454
925 | vt 0.922101 0.710454
926 | vt 0.377477 0.263215
927 | vt 0.824871 0.631558
928 | vn 0.9892 -0.1466 0.0000
929 | vn 0.0000 -0.9486 0.3164
930 | vn 0.1952 0.9029 -0.3829
931 | vn 0.8906 0.3814 -0.2476
932 | vn 0.9374 0.0464 -0.3451
933 | vn 1.0000 -0.0000 0.0000
934 | vn 0.0000 1.0000 0.0000
935 | vn -0.9999 0.0126 0.0000
936 | vn -0.4065 0.9137 0.0000
937 | vn -0.9995 -0.0306 0.0102
938 | vn 0.0000 -0.1196 -0.9928
939 | vn -0.3191 -0.0976 -0.9427
940 | vn 0.0768 -0.0576 -0.9954
941 | vn -0.8615 0.4568 -0.2217
942 | vn -0.9109 0.3703 -0.1822
943 | vn 0.9973 -0.0259 -0.0690
944 | vn 0.9970 -0.0392 -0.0660
945 | vn 0.0311 0.9359 -0.3509
946 | vn 0.0000 0.9397 -0.3419
947 | vn -0.6978 0.6707 -0.2515
948 | vn -0.5074 0.8236 -0.2536
949 | vn -0.9995 -0.0277 0.0166
950 | vn 0.0000 -0.8575 0.5145
951 | vn 0.1863 0.6956 -0.6938
952 | vn 0.2346 0.0912 -0.9678
953 | vn 0.1461 0.0568 -0.9876
954 | vn -0.7614 0.4590 -0.4578
955 | vn -1.0000 0.0014 0.0055
956 | vn -0.9999 -0.0100 0.0100
957 | vn 0.0000 0.2434 0.9699
958 | vn 0.0000 -0.7080 0.7062
959 | vn 0.0384 0.3936 0.9185
960 | vn -0.0192 0.6270 0.7788
961 | vn 0.0000 0.7080 0.7062
962 | vn -0.9997 -0.0219 0.0146
963 | vn -0.9995 -0.0272 0.0166
964 | vn 0.0000 -0.8319 0.5549
965 | vn 0.0000 -0.8541 0.5201
966 | vn 0.3754 -0.8160 0.4396
967 | vn 0.0002 -0.8575 0.5146
968 | vn 0.0863 -0.8543 0.5126
969 | vn 0.0023 -0.8548 0.5189
970 | vn 0.1697 -0.8450 0.5070
971 | vn 0.3482 -0.8139 0.4652
972 | vn 0.8288 -0.5092 0.2318
973 | vn 0.6862 -0.5144 -0.5143
974 | vn 0.6175 -0.7712 -0.1544
975 | vn 0.9968 -0.0562 -0.0562
976 | vn 0.7073 -0.7069 0.0000
977 | vn 0.9944 -0.1027 -0.0257
978 | g Spade_hull_8_Spade_hull_8_None
979 | usemtl None
980 | s off
981 | f 187/158/234 177/159/234 188/160/234
982 | f 159/161/235 160/162/235 170/163/235
983 | f 161/164/236 163/165/236 171/166/236
984 | f 163/165/237 164/167/237 171/166/237
985 | f 164/167/238 168/168/238 171/166/238
986 | f 167/169/239 163/165/239 172/170/239
987 | f 163/165/240 169/171/240 172/170/240
988 | f 166/172/241 162/173/241 173/174/241
989 | f 162/173/242 169/171/242 173/174/242
990 | f 166/172/243 159/161/243 174/175/243
991 | f 159/161/235 170/163/235 174/175/235
992 | f 160/162/244 159/161/244 175/176/244
993 | f 159/161/245 166/172/245 175/176/245
994 | f 168/168/246 160/162/246 175/176/246
995 | f 173/174/247 161/164/247 175/176/247
996 | f 166/172/248 173/174/248 175/176/248
997 | f 164/167/249 163/165/249 176/177/249
998 | f 163/165/239 167/169/239 176/177/239
999 | f 168/168/250 164/167/250 176/177/250
1000 | f 163/165/251 161/164/251 178/178/251
1001 | f 169/171/252 163/165/252 178/178/252
1002 | f 161/164/253 173/174/253 178/178/253
1003 | f 173/174/254 169/171/254 178/178/254
1004 | f 166/172/255 174/175/255 179/179/255
1005 | f 174/175/256 170/163/256 179/179/256
1006 | f 161/164/257 171/166/257 180/180/257
1007 | f 171/166/258 168/168/258 180/180/258
1008 | f 168/168/259 175/176/259 180/180/259
1009 | f 175/176/260 161/164/260 180/180/260
1010 | f 169/171/242 162/173/242 181/181/242
1011 | f 172/170/240 169/171/240 181/181/240
1012 | f 162/173/261 166/172/261 183/182/261
1013 | f 166/172/262 165/183/262 183/182/262
1014 | f 167/169/263 162/173/263 183/182/263
1015 | f 165/183/264 167/169/264 183/182/264
1016 | f 162/173/265 167/169/265 184/184/265
1017 | f 167/169/239 172/170/239 184/184/239
1018 | f 181/181/266 162/173/266 184/184/266
1019 | f 172/170/267 181/181/267 184/184/267
1020 | f 165/183/268 166/172/268 185/185/268
1021 | f 166/172/269 179/179/269 185/185/269
1022 | f 182/186/270 165/183/270 185/185/270
1023 | f 179/179/271 182/186/271 185/185/271
1024 | f 170/163/272 177/159/272 186/187/272
1025 | f 179/179/273 170/163/273 186/187/273
1026 | f 158/188/274 182/186/274 186/187/274
1027 | f 182/186/275 179/179/275 186/187/275
1028 | f 167/169/264 165/183/264 187/158/264
1029 | f 176/177/239 167/169/239 187/158/239
1030 | f 182/186/276 158/188/276 187/158/276
1031 | f 165/183/270 182/186/270 187/158/270
1032 | f 158/188/277 186/187/277 187/158/277
1033 | f 186/187/278 177/159/278 187/158/278
1034 | f 160/162/279 168/168/279 188/160/279
1035 | f 170/163/280 160/162/280 188/160/280
1036 | f 168/168/281 176/177/281 188/160/281
1037 | f 177/159/282 170/163/282 188/160/282
1038 | f 176/177/283 187/158/283 188/160/283
1039 | o Spade_hull_9
1040 | v -86.168999 44.757645 396.095642
1041 | v -69.804825 -30.050117 279.218475
1042 | v -60.453514 -30.050117 279.218475
1043 | v -60.453514 23.720686 281.563995
1044 | v -60.453514 40.087597 396.095642
1045 | v -79.151108 23.720686 281.563995
1046 | v -67.468887 58.793892 377.388397
1047 | v -76.815170 -27.710745 286.232239
1048 | v -86.168999 58.793892 377.388397
1049 | v -86.168999 33.069481 386.736298
1050 | v -60.453514 49.436394 393.750061
1051 | v -60.453514 -13.683200 309.619110
1052 | v -86.168999 58.793892 386.736298
1053 | v -79.151108 -13.683200 300.259766
1054 | v -86.168999 51.775772 368.040497
1055 | v -76.815170 -23.031996 279.218475
1056 | v -60.453514 33.069481 386.736298
1057 | v -67.468887 58.793892 384.402191
1058 | v -86.168999 40.087597 396.095642
1059 | v -86.168999 33.069481 379.722504
1060 | v -65.130432 49.436398 351.667419
1061 | v -79.151108 19.041941 281.563995
1062 | v -60.453514 49.436398 389.081848
1063 | v -60.453514 -27.710745 286.232239
1064 | vt 0.260108 0.184221
1065 | vt 0.060010 0.026331
1066 | vt 0.060010 0.026331
1067 | vt 0.000000 0.000000
1068 | vt 0.020068 0.605227
1069 | vt 1.000000 0.789448
1070 | vt 0.000000 0.000000
1071 | vt 1.000000 0.842013
1072 | vt 0.839941 1.000000
1073 | vt 0.919922 0.710454
1074 | vt 0.979931 0.894675
1075 | vt 0.839941 1.000000
1076 | vt 0.919922 1.000000
1077 | vt 0.180029 0.184221
1078 | vt 0.020068 0.605227
1079 | vt 0.759961 0.921006
1080 | vt 0.000000 0.078994
1081 | vt 0.919922 0.710454
1082 | vt 0.899951 1.000000
1083 | vt 1.000000 0.789448
1084 | vt 0.859912 0.710454
1085 | vt 0.619873 0.894675
1086 | vt 0.020068 0.552565
1087 | vt 0.939990 0.894675
1088 | vn 0.0000 -0.8576 0.5144
1089 | vn 1.0000 0.0000 0.0000
1090 | vn 0.0000 -0.9486 0.3164
1091 | vn -1.0000 -0.0000 0.0000
1092 | vn 0.0441 0.2431 0.9690
1093 | vn -0.0053 -0.8559 0.5171
1094 | vn 0.0000 1.0000 0.0000
1095 | vn -0.0250 0.5546 0.8317
1096 | vn -0.9830 -0.1827 0.0190
1097 | vn -0.9728 0.1852 -0.1390
1098 | vn 0.0000 0.0000 -1.0000
1099 | vn 0.0187 0.0436 -0.9989
1100 | vn 0.0000 0.0501 -0.9987
1101 | vn -0.6399 -0.6392 -0.4264
1102 | vn -0.9929 -0.0992 -0.0662
1103 | vn 0.0000 -0.8001 0.5999
1104 | vn 0.0000 -0.8551 0.5184
1105 | vn 0.8001 0.5998 0.0000
1106 | vn 0.0840 0.7351 0.6728
1107 | vn 0.0000 0.0000 1.0000
1108 | vn -0.9889 -0.1484 0.0000
1109 | vn -0.9971 -0.0402 -0.0644
1110 | vn 0.0000 0.9388 -0.3444
1111 | vn -0.0028 0.9390 -0.3439
1112 | vn 0.0000 0.9397 -0.3419
1113 | vn -0.9971 -0.0380 -0.0665
1114 | vn -0.9967 0.0000 -0.0809
1115 | vn -0.7086 0.0000 -0.7056
1116 | vn -0.9948 -0.0503 -0.0881
1117 | vn 0.8001 0.5999 0.0000
1118 | vn 0.8809 0.4604 -0.1101
1119 | vn 0.8550 0.5075 -0.1069
1120 | g Spade_hull_9_Spade_hull_9_None
1121 | usemtl None
1122 | s off
1123 | f 200/189/284 196/190/284 212/191/284
1124 | f 191/192/285 192/193/285 193/194/285
1125 | f 190/195/286 191/192/286 196/190/286
1126 | f 189/196/287 197/197/287 198/198/287
1127 | f 189/196/288 193/194/288 199/199/288
1128 | f 193/194/285 192/193/285 199/199/285
1129 | f 191/192/285 193/194/285 200/189/285
1130 | f 198/198/289 196/190/289 200/189/289
1131 | f 195/200/290 197/197/290 201/201/290
1132 | f 197/197/287 189/196/287 201/201/287
1133 | f 189/196/291 199/199/291 201/201/291
1134 | f 196/190/292 198/198/292 202/202/292
1135 | f 197/197/293 194/203/293 203/204/293
1136 | f 198/198/287 197/197/287 203/204/287
1137 | f 191/192/294 190/195/294 204/205/294
1138 | f 192/193/295 191/192/295 204/205/295
1139 | f 194/203/296 192/193/296 204/205/296
1140 | f 190/195/297 196/190/297 204/205/297
1141 | f 196/190/298 202/202/298 204/205/298
1142 | f 193/194/299 198/198/299 205/206/299
1143 | f 200/189/285 193/194/285 205/206/285
1144 | f 198/198/300 200/189/300 205/206/300
1145 | f 199/199/301 195/200/301 206/207/301
1146 | f 195/200/290 201/201/290 206/207/290
1147 | f 201/201/302 199/199/302 206/207/302
1148 | f 193/194/303 189/196/303 207/208/303
1149 | f 189/196/287 198/198/287 207/208/287
1150 | f 198/198/299 193/194/299 207/208/299
1151 | f 202/202/304 198/198/304 208/209/304
1152 | f 198/198/287 203/204/287 208/209/287
1153 | f 203/204/305 202/202/305 208/209/305
1154 | f 192/193/306 194/203/306 209/210/306
1155 | f 194/203/307 197/197/307 209/210/307
1156 | f 197/197/308 195/200/308 209/210/308
1157 | f 202/202/309 203/204/309 210/211/309
1158 | f 203/204/310 194/203/310 210/211/310
1159 | f 194/203/311 204/205/311 210/211/311
1160 | f 204/205/312 202/202/312 210/211/312
1161 | f 199/199/285 192/193/285 211/212/285
1162 | f 195/200/313 199/199/313 211/212/313
1163 | f 192/193/314 209/210/314 211/212/314
1164 | f 209/210/315 195/200/315 211/212/315
1165 | f 196/190/286 191/192/286 212/191/286
1166 | f 191/192/285 200/189/285 212/191/285
1167 | o Spade_hull_10
1168 | v -60.450996 -30.050114 279.207031
1169 | v -41.748833 -30.050114 279.207031
1170 | v -61.000923 8.457848 278.863281
1171 | v -41.748833 -11.346113 290.897034
1172 | v -60.450996 -25.370455 288.556976
1173 | v -41.748833 -13.678621 279.207031
1174 | v -41.748833 -25.370455 288.556976
1175 | v -61.244213 6.406167 283.785858
1176 | v -61.243397 -6.273876 291.392334
1177 | v -59.332413 -21.632908 290.197845
1178 | v -53.435856 2.681887 279.207031
1179 | v -60.450996 -30.050114 281.547089
1180 | v -58.111397 7.357888 281.547089
1181 | vt 0.485718 1.000000
1182 | vt 0.850006 0.400523
1183 | vt 0.971435 0.160695
1184 | vt 0.000000 1.000000
1185 | vt 0.000000 0.040687
1186 | vt 1.000000 0.012479
1187 | vt 0.425146 1.000000
1188 | vt 0.121524 1.000000
1189 | vt 0.946721 0.000000
1190 | vt 0.617437 0.000042
1191 | vt 0.218584 0.098064
1192 | vt 0.121524 0.040687
1193 | vt 0.000000 0.040687
1194 | vn 0.7266 0.6810 0.0909
1195 | vn 0.0000 -0.0089 -1.0000
1196 | vn 0.0178 0.0000 -0.9998
1197 | vn 1.0000 0.0000 0.0000
1198 | vn -0.9984 -0.0148 -0.0555
1199 | vn -0.9994 -0.0180 -0.0299
1200 | vn 0.1538 0.5083 0.8473
1201 | vn 0.0570 -0.1643 0.9848
1202 | vn 0.0000 -0.4020 0.9156
1203 | vn 0.0053 -0.0769 0.9970
1204 | vn -0.6425 -0.1386 0.7536
1205 | vn 0.0992 0.0709 -0.9925
1206 | vn 0.8083 0.5774 -0.1152
1207 | vn 0.0000 -1.0000 0.0000
1208 | vn 0.0559 -0.8928 0.4469
1209 | vn 0.0000 -0.8317 0.5552
1210 | vn -0.9985 -0.0460 0.0307
1211 | vn -0.9994 -0.0333 0.0000
1212 | vn -0.0057 0.9231 0.3845
1213 | vn 0.3154 0.6304 0.7093
1214 | vn 0.5836 0.7451 -0.3229
1215 | g Spade_hull_10_Spade_hull_10_None
1216 | usemtl None
1217 | s off
1218 | f 216/213/316 223/214/316 225/215/316
1219 | f 214/216/317 213/217/317 215/218/317
1220 | f 214/216/318 215/218/318 218/219/318
1221 | f 216/213/319 214/216/319 218/219/319
1222 | f 214/216/319 216/213/319 219/220/319
1223 | f 215/218/320 213/217/320 220/221/320
1224 | f 220/221/321 213/217/321 221/222/321
1225 | f 216/213/322 220/221/322 221/222/322
1226 | f 219/220/323 216/213/323 222/223/323
1227 | f 217/224/324 219/220/324 222/223/324
1228 | f 216/213/325 221/222/325 222/223/325
1229 | f 221/222/326 217/224/326 222/223/326
1230 | f 218/219/327 215/218/327 223/214/327
1231 | f 216/213/328 218/219/328 223/214/328
1232 | f 213/217/329 214/216/329 224/225/329
1233 | f 214/216/330 219/220/330 224/225/330
1234 | f 219/220/331 217/224/331 224/225/331
1235 | f 217/224/332 221/222/332 224/225/332
1236 | f 221/222/333 213/217/333 224/225/333
1237 | f 215/218/334 220/221/334 225/215/334
1238 | f 220/221/335 216/213/335 225/215/335
1239 | f 223/214/336 215/218/336 225/215/336
1240 | o Spade_hull_11
1241 | v -41.748833 40.077068 396.086487
1242 | v -60.450996 -23.036114 290.897064
1243 | v -41.748833 -23.036114 290.897064
1244 | v -61.708733 -4.037617 297.106079
1245 | v -60.450996 37.750053 393.748962
1246 | v -41.748833 47.100685 384.398773
1247 | v -60.450996 49.441891 389.073853
1248 | v -41.748833 -6.668969 295.592743
1249 | v -41.748833 -20.694912 297.930298
1250 | v -60.450996 -20.694912 297.930298
1251 | v -41.748833 33.067650 386.736328
1252 | v -60.566914 -11.648576 290.245087
1253 | v -41.748833 49.441891 389.073853
1254 | v -41.748833 -11.344282 290.897064
1255 | v -60.450996 0.347547 332.993439
1256 | v -60.450996 40.077068 396.086487
1257 | v -41.748833 -1.993655 302.605377
1258 | v -60.450996 47.100685 384.398773
1259 | v -60.450996 33.067650 386.736328
1260 | vt 0.403891 0.322631
1261 | vt 0.911659 0.774080
1262 | vt 0.911659 0.774080
1263 | vt 1.000000 0.870791
1264 | vt 0.006160 0.000000
1265 | vt 0.889573 0.967698
1266 | vt 0.050525 0.225822
1267 | vt 0.072611 0.032302
1268 | vt 0.006160 0.000000
1269 | vt 0.064823 0.262128
1270 | vt 0.072611 0.032302
1271 | vt 0.977915 0.838684
1272 | vt 0.000000 0.157117
1273 | vt 0.933744 1.000000
1274 | vt 0.933744 1.000000
1275 | vt 0.006160 0.161316
1276 | vt 1.000000 0.870791
1277 | vt 0.116781 0.290329
1278 | vt 0.889573 0.967698
1279 | vn 0.0000 -0.8541 0.5200
1280 | vn 1.0000 0.0000 0.0000
1281 | vn 0.0000 -0.9488 0.3158
1282 | vn -0.9969 -0.0741 0.0247
1283 | vn 0.0246 -0.7999 0.5996
1284 | vn 0.0000 -0.0572 -0.9984
1285 | vn -0.9894 -0.0183 -0.1443
1286 | vn 0.0000 0.5994 0.8005
1287 | vn 0.0000 0.8941 -0.4478
1288 | vn 0.0399 0.7081 -0.7050
1289 | vn 0.0346 0.0000 -0.9994
1290 | vn 0.0148 0.6707 -0.7416
1291 | vn 0.0000 -0.8574 0.5146
1292 | vn -0.9964 -0.0731 0.0438
1293 | vn 0.0084 -0.8554 0.5179
1294 | vn 0.0000 -0.7087 0.7055
1295 | vn -0.9995 -0.0231 0.0230
1296 | vn -0.9999 0.0071 0.0095
1297 | vn 0.0539 0.8562 -0.5139
1298 | vn 0.0675 0.8301 -0.5535
1299 | vn 0.0000 0.8628 -0.5055
1300 | vn -0.9825 0.1665 -0.0834
1301 | vn -0.9978 -0.0552 0.0368
1302 | vn 0.0000 -0.8316 0.5553
1303 | vn -0.9965 -0.0718 0.0437
1304 | g Spade_hull_11_Spade_hull_11_Material.001
1305 | usemtl Material.001
1306 | s off
1307 | f 240/226/337 236/227/337 244/228/337
1308 | f 226/229/338 228/230/338 231/231/338
1309 | f 231/231/338 228/230/338 233/232/338
1310 | f 228/230/338 226/229/338 234/233/338
1311 | f 227/234/339 228/230/339 234/233/339
1312 | f 229/235/340 227/234/340 235/236/340
1313 | f 227/234/339 234/233/339 235/236/339
1314 | f 226/229/341 230/237/341 236/227/341
1315 | f 234/233/338 226/229/338 236/227/338
1316 | f 228/230/342 227/234/342 237/238/342
1317 | f 227/234/343 229/235/343 237/238/343
1318 | f 226/229/338 231/231/338 238/239/338
1319 | f 232/240/344 226/229/344 238/239/344
1320 | f 231/231/345 232/240/345 238/239/345
1321 | f 233/232/338 228/230/338 239/241/338
1322 | f 229/235/346 233/232/346 239/241/346
1323 | f 228/230/347 237/238/347 239/241/347
1324 | f 237/238/348 229/235/348 239/241/348
1325 | f 235/236/349 234/233/349 240/226/349
1326 | f 229/235/350 235/236/350 240/226/350
1327 | f 234/233/351 236/227/351 240/226/351
1328 | f 230/237/352 226/229/352 241/242/352
1329 | f 229/235/353 230/237/353 241/242/353
1330 | f 226/229/344 232/240/344 241/242/344
1331 | f 232/240/354 229/235/354 241/242/354
1332 | f 229/235/355 231/231/355 242/243/355
1333 | f 233/232/356 229/235/356 242/243/356
1334 | f 231/231/338 233/232/338 242/243/338
1335 | f 231/231/357 229/235/357 243/244/357
1336 | f 229/235/358 232/240/358 243/244/358
1337 | f 232/240/345 231/231/345 243/244/345
1338 | f 230/237/359 229/235/359 244/228/359
1339 | f 236/227/360 230/237/360 244/228/360
1340 | f 229/235/361 240/226/361 244/228/361
1341 |
--------------------------------------------------------------------------------
/examples/texture.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/petrikvladimir/RoboMeshCat/4c803c4c457d4e5db51ac8b11b04eadfec19239d/examples/texture.png
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [build-system]
2 | requires = ["setuptools>=45", "setuptools_scm[toml]>=6.2"]
3 | build-backend = "setuptools.build_meta"
4 |
5 | [tool.setuptools_scm]
6 |
7 | [project]
8 | name = "robomeshcat"
9 | dynamic = ["version"]
10 | license = { text = "BSD-2-Clause" }
11 | authors = [
12 | { name = "Vladimir Petrik", email = "vladimir.petrik@cvut.cz" },
13 | ]
14 | description = "RoboMeshCat - Set of utilities for visualizing robots in web-based visualizer MeshCat."
15 | readme = "README.md"
16 | requires-python = ">=3.7" # we use future annotations supported from 3.7
17 | dependencies = ["pin", "meshcat", "trimesh", "imageio", "imageio-ffmpeg"]
18 | classifiers = [
19 | "Topic :: Scientific/Engineering :: Visualization",
20 | "Framework :: Robot Framework",
21 | ]
22 |
23 | [project.urls]
24 | "Homepage" = "https://github.com/petrikvladimir/RoboMeshCat"
25 | "Bug Tracker" = "https://github.com/petrikvladimir/RoboMeshCat/issues"
26 |
27 | [project.optional-dependencies]
28 | dev = [
29 | "pycollada", # used to load 'dae' meshes
30 | ]
31 |
32 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Copyright (c) CTU -- All Rights Reserved
4 | # Created on: 2022-10-11
5 | # Author: Vladimir Petrik
6 | #
7 |
8 | from setuptools import setup
9 |
10 | if __name__ == '__main__':
11 | setup()
12 |
--------------------------------------------------------------------------------
/src/robomeshcat/__init__.py:
--------------------------------------------------------------------------------
1 | from .object import Object
2 | from .robot import Robot
3 | from .scene import Scene
4 | from .human import Human
5 |
--------------------------------------------------------------------------------
/src/robomeshcat/human.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | #
3 | # Copyright (c) CTU -- All Rights Reserved
4 | # Created on: 2022-12-6
5 | # Author: Vladimir Petrik
6 | #
7 | from __future__ import annotations
8 |
9 | import numpy as np
10 | from meshcat import geometry as g
11 |
12 | from . import Object
13 |
14 |
15 | class Human(Object):
16 | def __init__(
17 | self,
18 | pose=None,
19 | color: list[float] | None = None,
20 | opacity: float = 1.0,
21 | name: str | None = None,
22 | use_vertex_colors: bool = False,
23 | show_wireframe: bool = False,
24 | **kwargs,
25 | ) -> None:
26 | from smplx import SMPLX # we import SMPLX here on purpose, so that smplx dependencies are optional
27 |
28 | self.smplx_model = SMPLX(**kwargs)
29 | super().__init__(None, pose, color if not use_vertex_colors else [1, 1, 1], None, opacity, name)
30 |
31 | self._use_vertex_colors = use_vertex_colors
32 | self._show_wireframe = show_wireframe
33 | clr = self._color_from_input(color)[np.newaxis, :]
34 | self._vertex_colors = np.repeat(clr, self.smplx_model.get_num_verts(), 0) if use_vertex_colors else None
35 | self.update_vertices(set_object=False) # this will create a geometry
36 |
37 | "Additional properties that are modifiable "
38 | self._morph_target_influences = None
39 |
40 | @property
41 | def _material(self):
42 | mat = super()._material
43 | mat.wireframe = self._show_wireframe
44 | mat.vertexColors = self._use_vertex_colors
45 | return mat
46 |
47 | def get_vertices(self, **kwargs):
48 | """Return vertices of the mesh for the given smplx parameters."""
49 | output = self.smplx_model(return_verts=True, **kwargs)
50 | return output.vertices.detach().cpu().numpy().squeeze()
51 |
52 | def update_vertices(self, vertices=None, vertices_colors=None, set_object=True):
53 | if self._is_animation():
54 | print(
55 | 'Update vertices of the mesh will recreate the geometry. It cannot be used in animation for '
56 | 'which you should use this.add_morph function'
57 | )
58 | return
59 | self._geometry = TriangularMeshGeometryWithMorphAttributes(
60 | vertices=np.asarray(vertices) if vertices is not None else self.get_vertices(),
61 | faces=self.smplx_model.faces,
62 | color=np.asarray(vertices_colors) if vertices_colors is not None else self._vertex_colors,
63 | morph_positions=[],
64 | morph_colors=[],
65 | )
66 | if set_object:
67 | self._set_object()
68 |
69 | def add_morph(self, vertices, vertex_colors=None):
70 | """Add new morphology through which we can create animations."""
71 | self._geometry.morph_positions.append(vertices)
72 | if vertex_colors is not None:
73 | self._geometry.morph_colors.append(np.asarray(vertex_colors))
74 |
75 | def display_morph(self, morph_id):
76 | """Set morphTargetInfluences to display only the given morph_id."""
77 | self._morph_target_influences = [0] * self._geometry.number_of_morphs()
78 | if morph_id is not None:
79 | self._morph_target_influences[morph_id] = 1
80 |
81 | def _set_morph_property(self):
82 | if self._morph_target_influences is None:
83 | self._morph_target_influences = [0] * len(self._geometry.morph_positions)
84 | self._set_property('morphTargetInfluences', self._morph_target_influences, 'vector')
85 |
86 | def _reset_all_properties(self):
87 | super()._reset_all_properties()
88 | self._set_morph_property()
89 |
90 |
91 | class TriangularMeshGeometryWithMorphAttributes(g.TriangularMeshGeometry):
92 | def __init__(self, morph_positions=None, morph_colors=None, **kwargs):
93 | super(TriangularMeshGeometryWithMorphAttributes, self).__init__(**kwargs)
94 | self.morph_positions = morph_positions
95 | self.morph_colors = morph_colors
96 |
97 | def number_of_morphs(self) -> int:
98 | return max(len(self.morph_colors), len(self.morph_positions))
99 |
100 | def lower(self, object_data):
101 | ret = super(TriangularMeshGeometryWithMorphAttributes, self).lower(object_data=object_data)
102 | ret[u"data"][u"morphAttributes"] = {}
103 | if self.morph_positions is not None:
104 | ret[u"data"][u"morphAttributes"][u"position"] = [g.pack_numpy_array(pos.T) for pos in self.morph_positions]
105 | if self.morph_colors is not None:
106 | ret[u"data"][u"morphAttributes"][u"color"] = [g.pack_numpy_array(c.T) for c in self.morph_colors]
107 | return ret
108 |
--------------------------------------------------------------------------------
/src/robomeshcat/object.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | #
3 | # Copyright (c) CTU -- All Rights Reserved
4 | # Created on: 2022-10-11
5 | # Author: Vladimir Petrik
6 | #
7 |
8 | from __future__ import annotations
9 |
10 | import itertools
11 | from pathlib import Path
12 | import trimesh
13 | import numpy as np
14 | from PIL import Image
15 | import io
16 | import meshcat.geometry as g
17 | from meshcat.animation import AnimationFrameVisualizer
18 |
19 |
20 | class Object:
21 | """Represent an object with arbitrary geometry that can be rendered in the meshcat."""
22 |
23 | id_iterator = itertools.count()
24 |
25 | def __init__(
26 | self,
27 | geometry=None,
28 | pose=None,
29 | color: list[float] | None = None,
30 | texture: g.ImageTexture | Path | None = None,
31 | opacity: float = 1.0,
32 | name: str | None = None,
33 | ) -> None:
34 | """Create an object with given geometry, pose, color and opacity.
35 | :param geometry
36 | :param pose - 4x4 pose of the object
37 | :param color - RGB color of the object either tuple of integers [0-255] or tuple of floats [0. - 1.]
38 | :param texture - alternative to color, you can specify texture that object uses to render as meshcat texture or
39 | as a path to png file.
40 | :param opacity - transparency of the object from 0. to 1.
41 | :param name - unique name of the object, it's created automatically if not specified
42 | """
43 | super().__init__()
44 | self.name = f'obj{next(self.id_iterator)}' if name is None else name
45 | self._vis = None # either a visualization tree or the frame of the animation, is set by the scene
46 |
47 | "List of properties that could be updated for all objects"
48 | self._pose = ArrayWithCallbackOnSetItem(np.eye(4) if pose is None else pose, cb=self._set_transform)
49 | self._color = ArrayWithCallbackOnSetItem(self._color_from_input(color), cb=self._color_reset_on_set_item)
50 | self._opacity = opacity
51 | self._visible = True
52 |
53 | "Not updatable properties."
54 | self._texture = g.ImageTexture(g.PngImage.from_file(texture)) if isinstance(texture, (Path, str)) else texture
55 | self._geometry = geometry
56 |
57 | @staticmethod
58 | def _color_from_input(clr: list[float] | np.ndarray | None, default=None):
59 | """Modify input color to always be represented by numpy array with values from 0 to 1"""
60 | if clr is None:
61 | clr = np.random.uniform(0, 1, 3) if default is None else default
62 | clr = np.asarray(clr)
63 | assert clr.shape == (3,)
64 | if np.any(clr > 1) and clr.dtype == np.int:
65 | clr = clr.astype(dtype=np.float) / 255
66 | clr = clr.clip(0.0, 1.0)
67 | return clr
68 |
69 | def _set_vis(self, vis):
70 | """Set visualizer. Used internally to create frames of animation."""
71 | self._vis = vis[self.name]
72 |
73 | def _assert_vis(self):
74 | assert (
75 | self._vis is not None
76 | ), 'The properties of the object cannot be modified unless object is added to the scene.'
77 |
78 | def _set_object(self):
79 | """Create an object in meshcat and set all the initial properties."""
80 | self._assert_vis()
81 | self._vis.set_object(self._geometry, self._material)
82 | self._set_transform()
83 |
84 | def _delete_object(self):
85 | """Delete an object from meshcat."""
86 | self._vis.delete()
87 |
88 | def _set_transform(self):
89 | """Update transformation in the meshcat."""
90 | self._assert_vis()
91 | self._vis.set_transform(self._pose)
92 |
93 | def _set_property(self, key, value, prop_type='number', subpath='