Skip to content

Instantly share code, notes, and snippets.

@jackd
Last active December 1, 2022 09:04
Show Gist options
  • Save jackd/4fed392bb28621f9d8d18c36e4f9f423 to your computer and use it in GitHub Desktop.
Save jackd/4fed392bb28621f9d8d18c36e4f9f423 to your computer and use it in GitHub Desktop.
Demo of trimesh loading issue with missing map_Kd
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import numpy as np
try:
from PIL import Image
except ImportError:
Image = None
import trimesh
from trimesh import util
from trimesh.visual.texture import SimpleMaterial
from trimesh.constants import log
trimesh.constants.tol.strict = True
TOL_ZERO = 1e-12
def parse_mtl(mtl, resolver=None):
"""
Parse a loaded MTL file.
Parameters
-------------
mtl : str or bytes
Data from an MTL file
Returns
------------
mtllibs : list of dict
Each dict has keys: newmtl, map_Kd, Kd
"""
# decode bytes into string if necessary
if hasattr(mtl, 'decode'):
mtl = mtl.decode('utf-8')
# current material
material = None
# materials referenced by name
materials = {}
# use universal newline splitting
lines = str.splitlines(str(mtl).strip())
for line in lines:
# split by white space
split = line.strip().split()
# needs to be at least two values
if len(split) <= 1:
continue
# the first value is the parameter name
key = split[0]
# start a new material
if key == 'newmtl':
# material name extracted from line like:
# newmtl material_0
name = split[1]
if material is not None:
# save the old material by old name and remove key
materials[material.pop('newmtl')] = material
# start a fresh new material
material = {'newmtl': split[1]}
elif key == 'map_Kd':
# represents the file name of the texture image
try:
file_data = resolver.get(split[1])
# load the bytes into a PIL image
# an image file name
material['image'] = Image.open(
util.wrap_as_stream(file_data))
except BaseException:
log.warning('failed to load image', exc_info=True)
elif key in ['Kd', 'Ka', 'Ks']:
# remap to kwargs for SimpleMaterial
mapped = {'Kd': 'diffuse',
'Ka': 'ambient',
'Ks': 'specular'}
try:
# diffuse, ambient, and specular float RGB
material[mapped[key]] = [float(x) for x in split[1:]]
except BaseException:
log.warning('failed to convert color!', exc_info=True)
elif material is not None:
# save any other unspecified keys
material[key] = split[1:]
# reached EOF so save any existing materials
if material:
materials[material.pop('newmtl')] = material
return materials
def unmerge(faces, faces_tex):
"""
Textured meshes can come with faces referencing vertex
indices (`v`) and an array the same shape which references
vertex texture indices (`vt`).
Parameters
-------------
faces : (n, d) int
References vertex indices
faces_tex : (n, d) int
References a list of UV coordinates
Returns
-------------
new_faces : (m, d) int
New faces for masked vertices
mask_v : (p,) int
A mask to apply to vertices
mask_vt : (p,) int
A mask to apply to vt array to get matching UV coordinates
"""
# stack into pairs of (vertex index, texture index)
stack = np.column_stack((faces.reshape(-1),
faces_tex.reshape(-1)))
# find unique pairs: we're trying to avoid merging
# vertices that have the same position but different
# texture coordinates
unique, inverse = trimesh.grouping.unique_rows(stack)
# only take the unique pairts
pairs = stack[unique]
# try to maintain original vertex order
order = pairs[:, 0].argsort()
# apply the order to the pairs
pairs = pairs[order]
# the mask for vertices, and mask for vt to generate uv coordinates
mask_v, mask_uv = pairs.T
# we re-ordered the vertices to try to maintain
# the original vertex order as much as possible
# so to reconstruct the faces we need to remap
remap = np.zeros(len(order), dtype=np.int64)
remap[order] = np.arange(len(order))
# the faces are just the inverse with the new order
new_faces = remap[inverse].reshape((-1, 3))
return new_faces, mask_v, mask_uv
def _parse_faces(lines):
"""
Use a slow but more flexible looping method to process
face lines as a fallback option to faster vectorized methods.
Parameters
-------------
lines : (n,) str
List of lines with face information
Returns
-------------
faces : (m, 3) int
Clean numpy array of face triangles
"""
# collect vertex, texture, and vertex normal indexes
v, vt, vn = [], [], []
# loop through every line starting with a face
for line in lines:
# remove leading newlines then
# take first bit before newline then split by whitespace
split = line.strip().split('\n')[0].split()
# split into: ['76/558/76', '498/265/498', '456/267/456']
if len(split) == 4:
# triangulate quad face
split = [split[0],
split[1],
split[2],
split[2],
split[3],
split[0]]
elif len(split) != 3:
log.warning('only triangle and quad faces supported!')
continue
# f is like: '76/558/76'
for f in split:
# vertex, vertex texture, vertex normal
split = f.split('/')
# we always have a vertex reference
v.append(int(split[0]))
# faster to try/except than check in loop
try:
vt.append(int(split[1]))
except BaseException:
pass
try:
# vertex normal is the third index
vn.append(int(split[2]))
except BaseException:
pass
# shape into triangles and switch to 0-indexed
faces = np.array(v, dtype=np.int64).reshape((-1, 3)) - 1
faces_tex, normals = None, None
if len(vt) == len(v):
faces_tex = np.array(vt, dtype=np.int64).reshape((-1, 3)) - 1
if len(vn) == len(v):
normals = np.array(vn, dtype=np.int64).reshape((-1, 3)) - 1
return faces, faces_tex, normals
def load_obj(file_obj, resolver=None):
# Load Materials
materials = None
text = file_obj.read()
mtl_position = text.find('mtllib')
if mtl_position >= 0:
# take the line of the material file after `mtllib`
# which should be the file location of the .mtl file
mtl_path = text[mtl_position + 6:text.find('\n', mtl_position)]
# use the resolver to get the data, then parse the MTL
material_kwargs = parse_mtl(resolver[mtl_path], resolver=resolver)
materials = {k: SimpleMaterial(**v)
for k, v in material_kwargs.items()}
# Load Vertices
# aggressivly reduce blob to only part with vertices
# the first position of a vertex in the text blob
v_start = text.find('\nv ') - 3
# we only need to search from the start of the file
# up to the location of out our first vertex
vn_start = text.find('\nvn ', 0, v_start) - 4
vt_start = text.find('\nvt ', 0, v_start) - 4
start = min(i for i in [v_start, vt_start, vn_start] if i > 0)
# search for the first newline past the last vertex
v_end = text.find('\n', text.rfind('\nv ') + 3)
# we only need to search from the last
# vertex up until the end of the file
vt_end = text.find('\n', text.rfind('\nvt ', v_end) + 4)
vn_end = text.find('\n', text.rfind('\nvn ', v_end) + 4)
# take the last position of any vertex property
end = max(i for i in [v_end, vt_end, vn_end] if i > 0)
# make a giant string numpy array of each "word"
words = np.array(text[start:end].split())
# find indexes of the three values after a "vertex" key
# this strategy avoids having to loop through the giant
# vertex array but does discard vertex colors if specified
v_idx = np.nonzero(words == 'v')[0].reshape((-1, 1))
# do the type conversion with built- in map/list/float
# vs np.astype, which is roughly 2x slower and these
# are some of the most expensive operations in the whole loader
# due to the fact that they are executed on every vertex
# In [17]: %timeit np.array(list(map(float, a.ravel().tolist())),
# dtype=np.float64)
# 1 loop, best of 3: 208 ms per loop
# In [18]: %timeit a.astype(np.float64)
# 1 loop, best of 3: 375 ms per loop
#
# get vertices as list of strings
v_list = words[v_idx + np.arange(1, 4)].ravel().tolist()
# run the conversion using built- in functions then re-numpy it
v = np.array(list(map(float, v_list)), dtype=np.float64).reshape((-1, 3))
# vertex colors are stored right after the vertices
vc = None
try:
# try just one line, which will raise before
# we try to do the whole array
words[v_idx[0] + np.arange(4, 7)].astype(np.float64)
# we made it past one line, try to get a color for every vertex
vc_list = words[v_idx + np.arange(4, 7)].ravel().tolist()
vc = np.array(list(map(float, vc_list)), dtype=np.float64).reshape((-1, 3))
except BaseException:
pass
vt = None
if vt_end >= 0:
# if we have vertex textures specified convert to numpy array
vt_idx = np.nonzero(words == 'vt')[0].reshape((-1, 1))
vt_list = words[vt_idx + np.arange(1, 3)].ravel().tolist()
vt = np.array(list(map(float, vt_list)), dtype=np.float64).reshape((-1, 2))
vn = None
if vn_end >= 0:
# if we have vertex normals specified convert to numpy array
vn_idx = np.nonzero(words == 'vn')[0].reshape((-1, 1))
if len(vn_idx) == len(v):
vn_list = words[vn_idx + np.arange(1, 4)].ravel().tolist()
vn = np.array(list(map(float, vn_list)), dtype=np.float64).reshape((-1, 3))
# Pre-Process Face Text
# Rather than looking at each line in a loop we're
# going to split lines by directives which indicate
# a new mesh, specifically 'usemtl' and 'o' keys
# search for materials, objects, faces, or groups
starters = ['\nusemtl ', '\no ', '\nf ', '\ng ', '\ns ']
f_start = len(text)
# first index of material, object, face, group, or smoother
for st in starters:
current = text.find(st, 0, f_start)
if current < 0:
continue
current += len(st)
if current < f_start:
f_start = current
# index in blob of the newline after the last face
f_end = text.find('\n', text.rfind('\nf ') + 3)
# get the chunk of the file that has face information
if f_end >= 0:
# clip to the newline after the last face
f_chunk = text[f_start:f_end]
else:
# no newline after last face
f_chunk = text[f_start:]
# start with undefined objects and material
current_object = None
current_material = None
# where we're going to store result tuples
# containing (material, object, face lines)
face_tuples = []
# two things cause new meshes to be created: objects and materials
# first divide faces into groups split by material and objects
# face chunks using different materials will be treated
# as different meshes
for m_chunk in f_chunk.split('\nusemtl '):
# if empty continue
if len(m_chunk) == 0:
continue
# find the first newline in the chunk
# everything before it will be the usemtl direction
newline = m_chunk.find('\n')
current_material = m_chunk[:newline].strip()
# material chunk contains multiple objects
o_split = m_chunk.split('\no ')
if len(o_split) > 1:
for o_chunk in o_split:
# set the object label
current_object = o_chunk[
:o_chunk.find('\n')].strip()
# find the first face in the chunk
f_idx = o_chunk.find('\nf ')
# if we have any faces append it to our search tuple
if f_idx >= 0:
face_tuples.append(
(current_material,
current_object,
o_chunk[f_idx:]))
else:
# if there are any faces in this chunk add them
f_idx = m_chunk.find('\nf ')
if f_idx >= 0:
face_tuples.append(
(current_material,
current_object,
m_chunk[f_idx:]))
# Load Faces
# now we have clean- ish faces grouped by material and object
# so now we have to turn them into numpy arrays and kwargs
# for trimesh mesh and scene objects
kwargs = []
for material, obj, chunk in face_tuples:
# do wangling in string form
# we need to only take the face line before a newline
# using builtin functions in a list comprehension
# is pretty fast relative to other options
# this operation is the only one that is O(len(faces))
# [i[:i.find('\n')] ... requires a conditional
face_lines = [i.split('\n')[0] for i in chunk.split('\nf ')[1:]]
# then we are going to replace all slashes with spaces
joined = ' '.join(face_lines).replace('/', ' ')
# the fastest way to get to a numpy array
# processes the whole string at once into a 1D array
# also wavefront is 1- indexed (vs 0- indexed) so offset
array = np.fromstring(
joined, sep=' ', dtype=np.int64) - 1
# get the number of columns rounded and converted to int
columns = int(np.round(
float(len(array) / len(face_lines))))
# make sure we have the right number of values
if len(array) == (columns * len(face_lines)):
# reshape to columns
array = array.reshape((-1, columns))
# faces are going to be the first value
index = np.arange(3) * int(columns / 3.0)
# slice the faces out of the blob array
faces = array[:, index]
faces_tex, normals = None, None
if columns == 6:
# if we have two values per vertex the second
# one is index of texture coordinate (`vt`)
# count how many delimeters are in the first face line
# to see if our second value is texture or normals
count = face_lines[0].count('/')
if count == columns:
# case where each face line looks like:
# ' 75//139 76//141 77//141'
# which is vertex/nothing/normal
normals = array[:, index + 1]
elif count == int(columns / 2):
# case where each face line looks like:
# '75/139 76/141 77/141'
# which is vertex/texture
faces_tex = array[:, index + 1]
else:
log.warning('face lines are weird: {}'.format(
face_lines[0]))
elif columns == 9:
# if we have three values per vertex
# second value is always texture
faces_tex = array[:, index + 1]
# third value is reference to vertex normal (`vn`)
normals = array[:, index + 2]
else:
# if we had something annoying like mixed in quads
# or faces that differ per-line we have to loop
log.warning('inconsistent faces!')
# TODO: allow fallback, and find a mesh we can test it on
assert False
faces, faces_tex, normals = _parse_faces(face_lines)
# try to get usable texture
visual = None
if faces_tex is not None:
# texture is referencing vt
new_faces, mask_v, mask_vt = unmerge(faces=faces, faces_tex=faces_tex)
# we should NOT have messed up the faces
# note: this is EXTREMELY slow due to the numerous
# float comparisons so only use in unit tests
if trimesh.tol.strict:
assert np.allclose(v[faces], v[mask_v][new_faces])
try:
visual = trimesh.visual.TextureVisuals(
uv=vt[mask_vt], material=materials[material])
except BaseException:
log.warning('visual creation failed for submesh!',
exc_info=True)
visual = None
# mask vertices and use new faces
kwargs.append({'vertices': v[mask_v],
'vertex_normals': normals,
'visual': visual,
'faces': new_faces})
else:
# otherwise just use unmasked vertices
kwargs.append({'vertices': v,
'faces': faces,
'vertex_normals': normals})
return kwargs
def to_mesh(m):
if isinstance(m, trimesh.Trimesh):
return m
elif isinstance(m, (list, tuple)):
return trimesh.util.concatenate([to_mesh(mi) for mi in m])
elif isinstance(m, dict):
return trimesh.Trimesh(**m)
else:
raise TypeError("Unrecognized output type '%s'" % type(mesh))
path = 'model.obj'
with open(path, 'r') as fp:
mesh = load_obj(fp, trimesh.visual.resolvers.FilePathResolver(path))
# mesh = trimesh.load(path)
mesh = to_mesh(mesh)
mesh.show()
# mesh = [trimesh.Trimesh(**m) for m in mesh]
# for m in mesh:
# m.show()
# Blender MTL File: 'blank.blend'
# Material Count: 3
newmtl material_0_1_8
Ns 96.078431
Ka 0.000000 0.000000 0.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ni -1.000000
d 1.000000
illum 2
map_Kd ./texture0.jpg
newmtl material_1_24
Ns 96.078431
Ka 0.000000 0.000000 0.000000
Kd 0.800000 0.800000 0.800000
Ks 0.500000 0.500000 0.500000
Ni -1.000000
d 1.000000
illum 2
newmtl material_2_2_8
Ns 96.078431
Ka 0.000000 0.000000 0.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ni -1.000000
d 1.000000
illum 2
map_Kd ./texture1.jpg
# Blender v2.71 (sub 0) OBJ File: 'blank.blend'
# www.blender.org
mtllib ./model.mtl
o mesh1_mesh1-geometry
v -0.221803 -0.316862 0.316862
v 0.0950589 -0.316862 -0.316862
v 0.0950589 -0.316862 0.316862
v -0.221803 -0.316862 -0.316862
v 0.0950589 -0.0164769 -0.017662
v 0.0950589 -0.158431 0.316862
v -0.221803 -0.301019 -0.301019
v -0.221803 -0.158431 -0.316862
v 0.0950589 -0.0131939 -0.0201807
v 0.0950589 -0.0189929 -0.0143823
v -0.221803 -0.158431 0.316862
v 0.0950589 0.11497 0.0613163
v -0.221803 -0.301019 0.301019
v 0.0950589 -0.158431 -0.316862
v 0.0950589 -0.0093731 -0.0217619
v 0.221803 -0.0164769 -0.017662
v 0.0950589 -0.0205768 -0.0105609
v 0.0950589 0.316862 2.69123e-07
v -0.221803 -0.163228 0.301019
v 0.0950589 0.0985661 0.0591551
v 0.0950589 0.131371 0.0591551
v -0.221803 -0.163228 -0.301019
v -0.207953 -0.301019 0.301019
v -0.221803 0.316862 2.69123e-07
v 0.0950589 0.0985661 -0.0632709
v 0.221803 -0.0131939 -0.0201807
v 0.221803 -0.0189929 -0.0143823
v 0.0950589 -0.0211158 -0.006461
v 0.0950589 0.159781 0.0427543
v 0.0950589 0.0832838 0.0528243
v 0.0792157 0.11497 0.0613163
v 0.0950589 0.146656 0.0528243
v 0.0792157 0.131371 0.0591551
v 0.0792157 -0.163228 -0.301019
v -0.207953 -0.301019 -0.301019
v 0.0950589 0.131371 -0.0632709
v 0.0950589 0.11497 -0.0654288
v 0.0950589 0.0832838 -0.0569401
v 0.221803 -0.0093731 -0.0217619
v 0.221803 0.00592836 -0.017662
v 0.221803 0.00844763 -0.0143823
v 0.221803 -0.0205768 -0.0105609
v 0.0950589 -0.0205768 -0.00236055
v 0.0950589 0.169851 0.02963
v -0.221803 0.2883 2.69123e-07
v 0.0950589 0.0701595 0.0427543
v 0.0792157 0.0985661 0.0591551
v 0.0792157 0.146656 0.0528243
v 0.0792157 0.2883 2.69123e-07
v 0.0792157 -0.301019 0.301019
v 0.0950589 0.146656 -0.0569401
v 0.0792157 0.0985661 -0.0632709
v 0.0792157 0.0832838 -0.0569401
v 0.0950589 0.0701595 -0.0468701
v 0.0950589 -0.00527264 -0.0223036
v 0.221803 0.00264865 -0.0201807
v 0.221803 0.0100316 -0.0105609
v 0.221803 -0.0211158 -0.006461
v 0.0950589 -0.0189929 0.00146084
v 0.0950589 0.176181 0.0143445
v 0.0792157 0.159781 0.0427543
v -0.20596 0.0625359 0.15051
v 0.0950589 0.0600863 0.02963
v 0.0792157 0.0832838 0.0528243
v 0.0792157 0.0757172 0.141723
v 0.0792157 -0.301019 -0.301019
v 0.0792157 0.131371 -0.0632709
v 0.0792157 -0.163228 0.301019
v 0.0950589 0.159781 -0.0468701
v 0.0792157 0.11497 -0.0654288
v 0.0792157 0.0701595 -0.0468701
v 0.0950589 0.0600863 -0.0337425
v 0.221803 -0.00527264 -0.0223036
v 0.221803 -0.00117219 -0.0217619
v 0.0950589 0.00592836 -0.017662
v 0.0950589 0.00844763 -0.0143823
v 0.221803 0.0105705 -0.006461
v 0.221803 -0.0205768 -0.00236055
v 0.0950589 -0.0164769 0.00474329
v 0.0950589 0.178343 -0.00205626
v 0.0792157 0.169851 0.02963
v -0.20596 0.0757172 0.141723
v 0.0950589 0.0537555 0.0143445
v 0.0792157 0.0600863 0.02963
v 0.0792157 0.0701595 0.0427543
v 0.0792157 0.146656 -0.0569401
v 0.0792157 0.0625359 0.15051
v 0.0950589 0.169851 -0.0337425
v 0.0792157 -0.0447157 -0.0444748
v 0.0950589 -0.00117219 -0.0217619
v 0.0950589 0.0537555 -0.0184603
v 0.0950589 0.00264865 -0.0201807
v 0.0950589 0.0100316 -0.0105609
v 0.221803 0.0100316 -0.00236055
v 0.221803 -0.0189929 0.00146084
v 0.0950589 -0.0131939 0.00725926
v 0.0950589 0.176181 -0.0184603
v 0.0792157 0.176181 0.0143445
v -0.20596 -0.0579004 -0.035685
v 0.0950589 -0.00527264 0.00938214
v 0.0792157 0.0537555 0.0143445
v 0.0792157 -0.0579004 -0.035685
v 0.0792157 0.159781 -0.0468701
v 0.0792157 0.169851 -0.0337425
v 0.0792157 0.0600863 -0.0337425
v 0.0950589 0.0515975 -0.00205626
v 0.0950589 0.0105705 -0.006461
v 0.221803 0.00844763 0.00146084
v 0.221803 -0.0164769 0.00474329
v 0.221803 -0.0131939 0.00725926
v 0.0950589 -0.0093731 0.00884374
v 0.0792157 0.178343 -0.00205626
v -0.20596 -0.0447157 -0.0444748
v 0.0950589 -0.00117219 0.00884374
v 0.0792157 0.0515975 -0.00205626
v 0.0792157 0.176181 -0.0184603
v 0.0792157 0.0537555 -0.0184603
v 0.0950589 0.0100316 -0.00236055
v 0.221803 0.00592836 0.00474329
v 0.221803 -0.0093731 0.00884374
v 0.221803 -0.00527264 0.00938214
v 0.0950589 0.00264865 0.00725926
v 0.221803 -0.00117219 0.00884374
v 0.0950589 0.00844763 0.00146084
v 0.221803 0.00264865 0.00725926
v 0.0950589 0.00592836 0.00474329
vt -0.409471 -1.188335
vt -0.343854 -1.450802
vt -0.343854 -1.188335
vt -0.409471 -1.450802
vt 0.725401 0.000000
vt 0.663442 0.124410
vt 0.594167 0.000000
vt -0.343854 0.065617
vt -0.409471 0.000000
vt -0.343854 0.000000
vt -0.722120 0.006562
vt -0.725401 0.000000
vt -0.594167 0.000000
vt 0.409471 0.065617
vt 0.343854 0.000000
vt 0.409471 0.000000
vt 0.663963 0.125769
vt 0.662762 0.123367
vt -0.409471 0.065617
vt 0.647087 0.178850
vt 0.594167 0.065617
vt -0.725401 0.065617
vt -0.597448 0.006562
vt 0.343854 0.065617
vt 0.664291 0.127351
vt 0.661971 0.122712
vt -0.343854 0.950351
vt -0.409471 0.713766
vt -0.343854 0.713766
vt -0.594167 0.065617
vt -0.597448 0.063630
vt 0.647534 0.172057
vt 0.647534 0.185643
vt -0.722120 0.063630
vt 0.406603 -1.194896
vt 0.409471 -1.444240
vt 0.409471 -1.194896
vt 0.409471 -0.513579
vt 0.343854 -0.750164
vt 0.409471 -0.750164
vt 0.659784 0.262467
vt 0.725401 0.065617
vt 0.672886 0.172057
vt 0.661122 0.122488
vt -0.409471 0.950351
vt 0.650931 0.197409
vt -0.659784 0.262467
vt 0.648845 0.165727
vt 0.648845 0.191973
vt 0.406603 -1.444240
vt 0.343854 -0.513579
vt 0.672886 0.185643
vt 0.673334 0.178850
vt 0.671575 0.165727
vt 0.663442 0.133690
vt 0.662762 0.134732
vt 0.660273 0.122712
vt 0.653648 0.201580
vt -0.659784 0.250638
vt 0.650931 0.160291
vt 0.671575 0.191973
vt 0.669490 0.160291
vt 0.663963 0.132331
vt 0.661971 0.135388
vt 0.659481 0.123367
vt 0.656814 0.204202
vt 0.653648 0.156120
vt 0.669490 0.197409
vt 0.666772 0.156120
vt 0.664403 0.129050
vt 0.664291 0.130748
vt 0.661122 0.135611
vt 0.658802 0.124410
vt 0.660210 0.205097
vt 0.656814 0.153498
vt 0.666772 0.201580
vt 0.663607 0.153498
vt 0.660273 0.135388
vt 0.658281 0.125769
vt 0.663607 0.204202
vt 0.657841 0.129050
vt 0.660210 0.152603
vt 0.659481 0.134732
vt 0.657953 0.127351
vt 0.657953 0.130748
vt 0.658802 0.133690
vt 0.658281 0.132331
vt 0.001718 0.052493
vt 0.002577 0.000000
vt 0.002577 0.052493
vt 0.003436 0.052493
vt 0.000859 0.052493
vt 0.001718 0.000000
vt 0.003436 0.000000
vt 0.004295 0.052493
vt 0.024050 0.000000
vt 0.020614 0.006562
vt 0.020614 0.000000
vt 0.017178 0.006562
vt 0.017178 -0.000000
vt 0.000859 0.000000
vt 0.004295 0.000000
vt 0.005154 0.052493
vt 0.027486 0.000000
vt 0.024050 0.006562
vt 0.013743 0.006562
vt 0.013743 0.000000
vt 0.061842 0.000000
vt 0.058407 0.006562
vt 0.058407 0.000000
vt 0.054971 0.006562
vt 0.054971 0.000000
vt -0.000000 0.052493
vt 0.005154 0.000000
vt 0.006012 0.052493
vt 0.010307 0.006562
vt 0.006871 -0.000000
vt 0.010307 -0.000000
vt 0.030921 -0.000000
vt 0.027486 0.006562
vt 0.068714 0.000000
vt 0.065278 0.006562
vt 0.065278 0.000000
vt 0.061842 0.006562
vt 0.051535 0.006562
vt 0.051535 -0.000000
vt -0.000000 0.000000
vt 0.018896 0.000000
vt 0.018037 0.052493
vt 0.018037 0.000000
vt 0.017178 0.052493
vt 0.016320 0.000000
vt 0.006012 0.000000
vt 0.006871 0.052493
vt 0.006871 0.006562
vt 0.034357 0.006562
vt 0.034357 -0.000000
vt 0.030921 0.006562
vt 0.072149 -0.000000
vt 0.068714 0.006562
vt 0.048100 -0.000000
vt 0.019755 0.052493
vt 0.020614 0.052493
vt 0.019755 -0.000000
vt 0.018896 0.052493
vt 0.016320 0.052493
vt 0.015461 0.000000
vt 0.007730 0.052493
vt 0.003436 0.006562
vt 0.037793 0.006562
vt 0.037793 -0.000000
vt 0.075585 0.006562
vt 0.075585 -0.000000
vt 0.072149 0.006562
vt 0.048100 0.006562
vt 0.044664 0.000000
vt 0.015461 0.052493
vt 0.014602 0.000000
vt 0.007730 -0.000000
vt 0.008589 0.000000
vt 0.008589 0.052493
vt 0.082457 0.006562
vt 0.079021 -0.000000
vt 0.082457 -0.000000
vt 0.000000 0.006562
vt 0.041228 0.006562
vt 0.079021 0.006562
vt 0.044664 0.006562
vt 0.014602 0.052493
vt 0.009448 0.000000
vt 0.009448 0.052493
vt 0.010307 0.052493
vt 0.011166 0.000000
vt 0.011166 0.052493
vt 0.041228 -0.000000
vt 0.013743 0.052493
vt 0.012884 0.000000
vt 0.012025 0.000000
vt 0.012025 0.052493
vt 0.012884 0.052493
vt -0.409471 0.003281
vt -0.347135 0.031815
vt -0.409471 0.031815
vt 0.409471 0.031815
vt 0.406603 0.003281
vt 0.409471 0.003281
vt -0.406603 0.003281
vt -0.347135 -0.261711
vt -0.409471 -0.374088
vt -0.347135 -0.374088
vt 0.347135 0.003281
vt 0.347135 -0.597448
vt 0.406603 -0.722120
vt 0.406603 -0.597448
vt 0.406190 0.414065
vt 0.409471 0.357876
vt 0.409471 0.470254
vt -0.647534 0.086028
vt -0.630436 0.081297
vt -0.647087 0.089425
vt -0.647534 0.092822
vt -0.648845 0.095987
vt -0.409471 -0.261711
vt -0.347135 0.003281
vt -0.722120 0.031815
vt -0.672886 0.092822
vt -0.659784 0.125319
vt 0.347135 0.031815
vt 0.347135 -0.722120
vt 0.347135 0.357876
vt 0.406190 0.417346
vt -0.648845 0.082863
vt -0.650931 0.098705
vt 0.347135 0.417346
vt 0.347135 0.470254
vt -0.597448 0.003281
vt -0.722120 0.003281
vt -0.673334 0.089425
vt -0.671575 0.095987
vt -0.628616 0.078567
vt -0.597448 0.031815
vt -0.668994 0.056357
vt -0.672886 0.086028
vt -0.671575 0.082863
vt -0.669490 0.080145
vt -0.653648 0.100790
vt 0.347135 0.414065
vt -0.667174 0.053627
vt -0.650931 0.080145
vt -0.669490 0.098705
vt -0.666772 0.078060
vt -0.656814 0.102101
vt -0.406190 -0.485153
vt -0.347135 -0.531074
vt -0.347135 -0.485153
vt 0.347135 -0.485199
vt 0.406190 -0.531120
vt 0.406190 -0.485199
vt -0.406190 -0.531074
vt -0.653648 0.078060
vt -0.656814 0.076749
vt -0.666772 0.100790
vt -0.663607 0.076749
vt -0.660210 0.102548
vt 0.347135 -0.531120
vt 0.406190 0.417983
vt 0.347135 0.414702
vt 0.406190 0.414702
vt -0.660210 0.076302
vt 0.347135 0.417983
vt -0.663607 0.102101
usemtl material_0_1_8
s off
f 1/1 2/2 3/3
f 2/2 1/1 4/4
f 2/5 5/6 3/7
f 6/8 1/9 3/10
f 7/11 4/12 1/13
f 8/14 2/15 4/16
f 5/6 2/5 9/17
f 3/7 5/6 10/18
f 1/9 6/8 11/19
f 3/7 12/20 6/21
f 4/12 7/11 8/22
f 7/11 1/13 13/23
f 2/15 8/14 14/24
f 9/17 2/5 15/25
f 3/7 10/18 17/26
f 18/27 11/28 6/29
f 11/30 19/31 1/13
f 3/7 20/32 12/20
f 21/33 6/21 12/20
f 22/34 8/22 7/11
f 13/23 1/13 19/31
f 23/35 7/36 13/37
f 24/38 14/39 8/40
f 18/41 2/5 14/42
f 15/25 2/5 25/43
f 3/7 17/26 28/44
f 11/28 18/27 24/45
f 29/46 18/41 6/21
f 24/47 19/31 11/30
f 3/7 30/48 20/32
f 32/49 6/21 21/33
f 8/22 22/34 24/47
f 7/36 23/35 35/50
f 14/39 24/38 18/51
f 2/5 18/41 36/52
f 25/43 2/5 37/53
f 15/25 25/43 38/54
f 26/17 40/55 16/6
f 16/6 41/56 27/18
f 3/7 28/44 43/57
f 44/58 18/41 29/46
f 29/46 6/21 32/49
f 24/47 45/59 19/31
f 3/7 46/60 30/48
f 24/47 22/34 45/59
f 36/52 18/41 51/61
f 2/5 36/52 37/53
f 15/25 38/54 54/62
f 39/25 56/63 26/17
f 26/17 56/63 40/55
f 16/6 40/55 41/56
f 27/18 41/56 57/64
f 27/18 57/64 42/26
f 3/7 43/57 59/65
f 60/66 18/41 44/58
f 3/7 63/67 46/60
f 51/61 18/41 69/68
f 15/25 54/62 72/69
f 15/25 72/69 55/70
f 39/25 74/71 56/63
f 42/26 57/64 77/72
f 42/26 77/72 58/44
f 3/7 59/65 79/73
f 80/74 18/41 60/66
f 3/7 83/75 63/67
f 69/68 18/41 88/76
f 74/71 39/25 73/70
f 91/77 55/70 72/69
f 58/44 77/72 94/78
f 58/44 94/78 78/57
f 3/7 79/73 96/79
f 97/80 18/41 80/74
f 3/7 100/81 83/75
f 88/76 18/41 97/80
f 55/70 91/77 90/71
f 92/63 91/77 75/55
f 75/55 106/82 76/56
f 76/56 106/82 93/64
f 78/57 94/78 108/83
f 78/57 108/83 95/65
f 3/7 96/79 111/84
f 3/7 111/84 100/81
f 114/85 83/75 100/81
f 90/71 91/77 92/63
f 75/55 91/77 106/82
f 93/64 106/82 107/72
f 95/65 108/83 119/86
f 95/65 119/86 109/73
f 122/87 83/75 114/85
f 107/72 106/82 118/78
f 109/73 119/86 125/87
f 109/73 125/87 110/79
f 110/79 123/85 120/84
f 126/86 83/75 122/87
f 124/83 106/82 83/75
f 118/78 106/82 124/83
f 110/79 125/87 123/85
f 120/84 123/85 121/81
f 124/83 83/75 126/86
s 1
f 9/88 16/89 5/90
f 16/89 10/91 5/90
f 15/92 26/93 9/88
f 16/89 9/88 26/93
f 10/91 16/89 27/94
f 27/94 17/95 10/91
f 20/96 31/97 12/98
f 12/98 33/99 21/100
f 26/93 15/92 39/101
f 17/95 27/94 42/102
f 42/102 28/103 17/95
f 30/104 47/105 20/96
f 31/97 20/96 47/105
f 33/99 12/98 31/97
f 21/100 48/106 32/107
f 48/106 21/100 33/99
f 37/108 52/109 25/110
f 25/110 53/111 38/112
f 55/113 39/101 15/92
f 28/103 42/102 58/114
f 58/114 43/115 28/103
f 61/116 44/117 29/118
f 32/107 61/116 29/118
f 46/119 64/120 30/104
f 47/105 30/104 64/120
f 61/116 32/107 48/106
f 51/121 67/122 36/123
f 36/123 70/124 37/108
f 52/109 37/108 70/124
f 53/111 25/110 52/109
f 71/125 38/112 53/111
f 38/112 71/125 54/126
f 39/101 55/113 73/127
f 56/128 75/129 40/130
f 75/129 41/100 40/130
f 76/131 57/132 41/100
f 43/115 58/114 78/133
f 78/133 59/134 43/115
f 81/135 60/94 44/117
f 44/117 61/116 81/135
f 84/136 46/119 63/137
f 64/120 46/119 85/138
f 69/139 86/140 51/121
f 67/122 51/121 86/140
f 70/124 36/123 67/122
f 71/125 72/141 54/126
f 90/142 73/98 55/143
f 74/144 92/145 56/128
f 75/129 56/128 92/145
f 41/100 75/129 76/131
f 57/132 76/131 93/146
f 93/146 77/147 57/132
f 59/134 78/133 95/117
f 95/117 79/148 59/134
f 98/149 80/127 60/94
f 60/94 81/135 98/149
f 101/150 63/137 83/151
f 46/119 84/136 85/138
f 63/137 101/150 84/136
f 104/152 69/139 88/153
f 86/140 69/139 103/154
f 72/141 71/125 105/155
f 73/98 90/142 74/144
f 105/155 91/156 72/141
f 92/145 74/144 90/142
f 77/147 93/146 107/157
f 107/157 94/158 77/147
f 79/148 95/117 109/159
f 79/148 110/160 96/161
f 112/162 97/163 80/164
f 80/127 98/149 112/165
f 83/151 115/166 101/150
f 116/167 88/153 97/163
f 69/139 104/152 103/154
f 88/153 116/167 104/152
f 91/156 105/155 117/168
f 94/158 107/157 118/169
f 118/169 108/107 94/158
f 110/160 79/148 109/159
f 120/170 96/161 110/160
f 96/161 120/170 111/171
f 97/163 112/162 116/167
f 111/171 121/118 100/172
f 100/172 123/173 114/174
f 115/166 83/151 106/175
f 117/168 106/175 91/156
f 108/107 118/169 124/176
f 124/176 119/177 108/107
f 121/118 111/171 120/170
f 123/173 100/172 121/118
f 114/174 125/178 122/179
f 125/178 114/174 123/173
f 106/175 117/168 115/166
f 119/177 124/176 126/180
f 119/177 122/179 125/178
f 122/179 119/177 126/180
usemtl material_1_24
s off
f 3/127 2/127 1/127
f 4/127 1/127 2/127
f 3/127 5/127 2/127
f 3/127 1/127 6/127
f 1/127 4/127 7/127
f 4/127 2/127 8/127
f 9/127 2/127 5/127
f 10/127 5/127 3/127
f 11/127 6/127 1/127
f 6/127 12/127 3/127
f 8/127 7/127 4/127
f 13/127 1/127 7/127
f 14/127 8/127 2/127
f 15/127 2/127 9/127
f 17/127 10/127 3/127
f 6/127 11/127 18/127
f 1/127 19/127 11/127
f 12/127 20/127 3/127
f 12/127 6/127 21/127
f 7/127 8/127 22/127
f 19/127 1/127 13/127
f 13/127 7/127 23/127
f 8/127 14/127 24/127
f 14/127 2/127 18/127
f 25/127 2/127 15/127
f 28/127 17/127 3/127
f 24/127 18/127 11/127
f 6/127 18/127 29/127
f 11/127 19/127 24/127
f 20/127 30/127 3/127
f 21/127 6/127 32/127
f 24/127 22/127 8/127
f 22/127 34/127 7/127
f 13/127 23/127 19/127
f 35/127 23/127 7/127
f 18/127 24/127 14/127
f 36/127 18/127 2/127
f 37/127 2/127 25/127
f 38/127 25/127 15/127
f 16/127 40/127 26/127
f 27/127 41/127 16/127
f 43/127 28/127 3/127
f 29/127 18/127 44/127
f 32/127 6/127 29/127
f 19/127 45/127 24/127
f 30/127 46/127 3/127
f 45/127 22/127 24/127
f 7/127 34/127 35/127
f 34/127 22/127 49/127
f 50/127 19/127 23/127
f 23/127 35/127 50/127
f 51/127 18/127 36/127
f 37/127 36/127 2/127
f 54/127 38/127 15/127
f 26/127 56/127 39/127
f 40/127 56/127 26/127
f 41/127 40/127 16/127
f 57/127 41/127 27/127
f 42/127 57/127 27/127
f 59/127 43/127 3/127
f 44/127 18/127 60/127
f 45/127 19/127 62/127
f 46/127 63/127 3/127
f 31/127 65/127 47/127
f 31/127 33/127 65/127
f 33/127 48/127 65/127
f 45/127 49/127 22/127
f 66/127 35/127 34/127
f 49/127 67/127 34/127
f 68/127 19/127 50/127
f 66/127 50/127 35/127
f 69/127 18/127 51/127
f 72/127 54/127 15/127
f 55/127 72/127 15/127
f 56/127 74/127 39/127
f 77/127 57/127 42/127
f 58/127 77/127 42/127
f 79/127 59/127 3/127
f 60/127 18/127 80/127
f 68/127 62/127 19/127
f 82/127 45/127 62/127
f 63/127 83/127 3/127
f 47/127 65/127 64/127
f 48/127 61/127 65/127
f 49/127 45/127 65/127
f 66/127 34/127 50/127
f 70/127 34/127 67/127
f 86/127 67/127 49/127
f 68/127 50/127 87/127
f 88/127 18/127 69/127
f 52/127 89/127 70/127
f 53/127 89/127 52/127
f 71/127 89/127 53/127
f 73/127 39/127 74/127
f 72/127 55/127 91/127
f 94/127 77/127 58/127
f 78/127 94/127 58/127
f 96/127 79/127 3/127
f 80/127 18/127 97/127
f 61/127 81/127 49/127
f 62/127 68/127 87/127
f 65/127 45/127 82/127
f 62/127 99/127 82/127
f 83/127 100/127 3/127
f 64/127 65/127 85/127
f 65/127 61/127 49/127
f 102/127 50/127 34/127
f 70/127 89/127 34/127
f 103/127 86/127 49/127
f 87/127 50/127 102/127
f 97/127 18/127 88/127
f 71/127 105/127 89/127
f 90/127 91/127 55/127
f 75/127 91/127 92/127
f 76/127 106/127 75/127
f 93/127 106/127 76/127
f 108/127 94/127 78/127
f 95/127 108/127 78/127
f 111/127 96/127 3/127
f 81/127 98/127 49/127
f 87/127 102/127 62/127
f 82/127 113/127 65/127
f 113/127 82/127 99/127
f 99/127 62/127 102/127
f 100/127 111/127 3/127
f 100/127 83/127 114/127
f 85/127 65/127 84/127
f 84/127 65/127 101/127
f 89/127 102/127 34/127
f 104/127 103/127 49/127
f 117/127 89/127 105/127
f 92/127 91/127 90/127
f 106/127 91/127 75/127
f 107/127 106/127 93/127
f 119/127 108/127 95/127
f 109/127 119/127 95/127
f 98/127 112/127 49/127
f 89/127 65/127 113/127
f 99/127 102/127 113/127
f 114/127 83/127 122/127
f 101/127 89/127 115/127
f 65/127 89/127 101/127
f 89/127 113/127 102/127
f 116/127 104/127 49/127
f 115/127 89/127 117/127
f 118/127 106/127 107/127
f 125/127 119/127 109/127
f 110/127 125/127 109/127
f 120/127 123/127 110/127
f 112/127 116/127 49/127
f 122/127 83/127 126/127
f 83/127 106/127 124/127
f 124/127 106/127 118/127
f 123/127 125/127 110/127
f 121/127 123/127 120/127
f 126/127 83/127 124/127
s 1
f 5/127 16/127 9/127
f 5/127 10/127 16/127
f 9/127 26/127 15/127
f 26/127 9/127 16/127
f 27/127 16/127 10/127
f 10/127 17/127 27/127
f 12/127 31/127 20/127
f 21/127 33/127 12/127
f 39/127 15/127 26/127
f 42/127 27/127 17/127
f 17/127 28/127 42/127
f 20/127 47/127 30/127
f 47/127 20/127 31/127
f 31/127 12/127 33/127
f 32/127 48/127 21/127
f 33/127 21/127 48/127
f 25/127 52/127 37/127
f 38/127 53/127 25/127
f 15/127 39/127 55/127
f 58/127 42/127 28/127
f 28/127 43/127 58/127
f 29/127 44/127 61/127
f 29/127 61/127 32/127
f 30/127 64/127 46/127
f 64/127 30/127 47/127
f 48/127 32/127 61/127
f 36/127 67/127 51/127
f 37/127 70/127 36/127
f 70/127 37/127 52/127
f 52/127 25/127 53/127
f 53/127 38/127 71/127
f 54/127 71/127 38/127
f 73/127 55/127 39/127
f 40/127 75/127 56/127
f 40/127 41/127 75/127
f 41/127 57/127 76/127
f 78/127 58/127 43/127
f 43/127 59/127 78/127
f 44/127 60/127 81/127
f 81/127 61/127 44/127
f 63/127 46/127 84/127
f 85/127 46/127 64/127
f 51/127 86/127 69/127
f 86/127 51/127 67/127
f 67/127 36/127 70/127
f 54/127 72/127 71/127
f 55/127 73/127 90/127
f 56/127 92/127 74/127
f 92/127 56/127 75/127
f 76/127 75/127 41/127
f 93/127 76/127 57/127
f 57/127 77/127 93/127
f 95/127 78/127 59/127
f 59/127 79/127 95/127
f 60/127 80/127 98/127
f 98/127 81/127 60/127
f 83/127 63/127 101/127
f 85/127 84/127 46/127
f 84/127 101/127 63/127
f 88/127 69/127 104/127
f 103/127 69/127 86/127
f 105/127 71/127 72/127
f 74/127 90/127 73/127
f 72/127 91/127 105/127
f 90/127 74/127 92/127
f 107/127 93/127 77/127
f 77/127 94/127 107/127
f 109/127 95/127 79/127
f 96/127 110/127 79/127
f 80/127 97/127 112/127
f 112/127 98/127 80/127
f 101/127 115/127 83/127
f 97/127 88/127 116/127
f 103/127 104/127 69/127
f 104/127 116/127 88/127
f 117/127 105/127 91/127
f 118/127 107/127 94/127
f 94/127 108/127 118/127
f 109/127 79/127 110/127
f 110/127 96/127 120/127
f 111/127 120/127 96/127
f 116/127 112/127 97/127
f 100/127 121/127 111/127
f 114/127 123/127 100/127
f 106/127 83/127 115/127
f 91/127 106/127 117/127
f 124/127 118/127 108/127
f 108/127 119/127 124/127
f 120/127 111/127 121/127
f 121/127 100/127 123/127
f 122/127 125/127 114/127
f 123/127 114/127 125/127
f 115/127 117/127 106/127
f 126/127 124/127 119/127
f 125/127 122/127 119/127
f 126/127 119/127 122/127
usemtl material_2_2_8
s off
f 7/181 34/182 22/183
f 19/184 23/185 13/186
f 35/187 34/182 7/181
f 49/188 22/189 34/190
f 23/185 19/184 50/191
f 50/192 35/193 23/194
f 62/195 19/196 45/197
f 47/198 65/199 31/200
f 65/199 33/201 31/200
f 65/199 48/202 33/201
f 22/189 49/188 45/203
f 34/182 35/187 66/204
f 34/205 67/206 49/207
f 50/191 19/184 68/208
f 35/193 50/192 66/209
f 19/196 62/195 68/210
f 62/195 45/197 82/211
f 64/212 65/199 47/198
f 65/199 61/213 48/202
f 65/214 45/197 49/215
f 50/216 34/205 66/217
f 67/206 34/205 70/218
f 49/207 67/206 86/219
f 87/220 50/216 68/221
f 70/218 89/222 52/223
f 52/223 89/222 53/224
f 53/224 89/222 71/225
f 49/207 81/226 61/213
f 87/227 68/210 62/195
f 82/211 45/197 65/214
f 82/199 99/228 62/220
f 85/229 65/199 64/212
f 49/207 61/213 65/199
f 34/205 50/216 102/228
f 34/205 89/222 70/218
f 49/207 86/219 103/230
f 102/228 50/216 87/220
f 89/222 105/231 71/225
f 49/207 98/232 81/226
f 62/233 102/234 87/235
f 65/236 113/237 82/238
f 99/228 82/199 113/222
f 102/234 62/233 99/239
f 84/240 65/199 85/229
f 101/241 65/199 84/240
f 34/205 102/228 89/222
f 49/207 103/230 104/242
f 105/231 89/222 117/243
f 49/207 112/244 98/232
f 113/237 65/236 89/245
f 113/246 102/247 99/248
f 115/249 89/222 101/241
f 101/241 89/222 65/199
f 102/247 113/246 89/250
f 49/207 104/242 116/251
f 117/243 89/222 115/249
f 49/207 116/251 112/244
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment