def test_ignoring_unrecognized_lines(self): # Given gibberish = a file containing: # """ # There was a young lady named Bright # who traveled much faster than light. # She set out one day # in a relative way, # and came back the previous night. # """ parser = ObjFile.parse_obj_file("tests/obj_test_files/gibberish.obj") self.assertEqual(parser.ignored_lines, 5)
def test_triangles_in_groups(self): parser = ObjFile.parse_obj_file("tests/obj_test_files/triangles.obj") g1 = parser.named_groups["FirstGroup"] g2 = parser.named_groups["SecondGroup"] t1 = g1.members[0] t2 = g2.members[0] self.assertEqual(t1.p1, parser.vertices[1]) self.assertEqual(t1.p2, parser.vertices[2]) self.assertEqual(t1.p3, parser.vertices[3]) self.assertEqual(t2.p1, parser.vertices[1]) self.assertEqual(t2.p2, parser.vertices[3]) self.assertEqual(t2.p3, parser.vertices[4])
def test_parsing_trangle_faces(self): parser = ObjFile.parse_obj_file( "tests/obj_test_files/triangle_faces.obj") g = parser.default_group t1 = g.members[0] t2 = g.members[1] self.assertEqual(t1.p1, parser.vertices[1]) self.assertEqual(t1.p2, parser.vertices[2]) self.assertEqual(t1.p3, parser.vertices[3]) self.assertEqual(t2.p1, parser.vertices[1]) self.assertEqual(t2.p2, parser.vertices[3]) self.assertEqual(t2.p3, parser.vertices[4])
def test_vertex_records(self): # Given file = a file containing: # """ # v -1 1 0 # v -1.0000 0.5000 0.0000 # v 1 0 0 # v 1 1 0 # """ parser = ObjFile.parse_obj_file("tests/obj_test_files/vertex.obj") self.assertEqual(parser.vertices[1], Point(-1, 1, 0)) self.assertEqual(parser.vertices[2], Point(-1, 0.5, 0)) self.assertEqual(parser.vertices[3], Point(1, 0, 0)) self.assertEqual(parser.vertices[4], Point(1, 1, 0))
def test_faces_normals(self): parser = ObjFile.parse_obj_file( "tests/obj_test_files/faces_normals.obj") g = parser.default_group t1 = g.members[0] t2 = g.members[1] self.assertEqual(t1.p1, parser.vertices[1]) self.assertEqual(t1.p2, parser.vertices[2]) self.assertEqual(t1.p3, parser.vertices[3]) self.assertEqual(t1.n1, parser.normals[3]) self.assertEqual(t1.n2, parser.normals[1]) self.assertEqual(t1.n3, parser.normals[2]) self.assertEqual(t2, t1)
def test_triangulating_polygons(self): parser = ObjFile.parse_obj_file( "tests/obj_test_files/triangulating_polygons.obj") g = parser.default_group t1 = g.members[0] t2 = g.members[1] t3 = g.members[2] self.assertEqual(t1.p1, parser.vertices[1]) self.assertEqual(t1.p2, parser.vertices[2]) self.assertEqual(t1.p3, parser.vertices[3]) self.assertEqual(t2.p1, parser.vertices[1]) self.assertEqual(t2.p2, parser.vertices[3]) self.assertEqual(t2.p3, parser.vertices[4]) self.assertEqual(t3.p1, parser.vertices[1]) self.assertEqual(t3.p2, parser.vertices[4]) self.assertEqual(t3.p3, parser.vertices[5])
def convex_decomposition(mesh, cache_dir='', name='mesh'): """ Performs a convex deomposition of the mesh using V-HACD. Parameters ---------- cache_dir : str a directory to store the intermediate files name : str the name of the mesh for the cache file Returns ------- :obj:`list` of :obj:`Mesh3D` list of mesh objects comprising the convex pieces of the object, or None if vhacd failed :obj:`list` of str string file roots of the convex pieces float total volume of the convex pieces """ # save to file if not os.path.exists(cache_dir): os.mkdir(cache_dir) obj_filename = os.path.join(cache_dir, '%s.obj' % (name)) vhacd_out_filename = os.path.join(cache_dir, '%s_vhacd.obj' % (name)) log_filename = os.path.join(cache_dir, 'vhacd_log.txt') print obj_filename ObjFile(obj_filename).write(mesh) # use v-hacd for convex decomposition cvx_decomp_cmd = 'vhacd --input %s --output %s --log %s' % ( obj_filename, vhacd_out_filename, log_filename) vhacd_process = Popen(cvx_decomp_cmd, bufsize=-1, close_fds=True, shell=True) vhacd_process.wait() # check success if not os.path.exists(vhacd_out_filename): logging.error( 'Output mesh file %s not found. V-HACD failed. Is V-HACD installed?' % (vhacd_out_filename)) return None # create separate convex piece files convex_piece_files = split_vhacd_output(vhacd_out_filename) # read convex pieces convex_piece_meshes = [] convex_piece_filenames = [] convex_pieces_volume = 0.0 # read in initial meshes for global properties for convex_piece_filename in convex_piece_files: # read in meshes obj_file_path, obj_file_root = os.path.split(convex_piece_filename) of = ObjFile(convex_piece_filename) convex_piece = of.read() convex_pieces_volume += convex_piece.total_volume() convex_piece_meshes.append(of.read()) convex_piece_filenames.append(obj_file_root) return convex_piece_meshes, convex_piece_filenames, convex_pieces_volume
def write_pieces(self, meshes, center_of_mass=np.zeros(3), density=1.0): """Writes a list of Mesh3D object to a .urdf file. Parameters ---------- meshes : :obj:`list` of :obj:`Mesh3D` The Mesh3D objects to write to the .urdf file. center_of_mass : :obj:`numpy.ndarray` The center of mass of the combined object. Defaults to zero. desnity : float The density fo the mesh pieces Note ---- Does not support moveable joints. """ # create output directory out_dir = self.filepath_ if not os.path.exists(out_dir): os.mkdir(out_dir) # read convex pieces mesh_filenames = [] # write meshes to reference with URDF files for i, mesh in enumerate(meshes): # read in meshes obj_file_root = '%s_%04d.obj' % (self.name_, i) obj_filename = os.path.join(out_dir, obj_file_root) ObjFile(obj_filename).write(mesh) mesh_filenames.append(obj_file_root) # open an XML tree root = et.Element('robot', name='root') # loop through all pieces prev_piece_name = None for mesh, filename in zip(meshes, mesh_filenames): # set the mass properties mesh.center_of_mass = center_of_mass mesh.density = density _, file_root = os.path.split(filename) file_root, _ = os.path.splitext(file_root) obj_filename = 'package://%s/%s' % (self.name_, filename) # write to xml piece_name = 'link_%s' % (file_root) I = mesh.inertia link = et.SubElement(root, 'link', name=piece_name) inertial = et.SubElement(link, 'inertial') origin = et.SubElement(inertial, 'origin', xyz="0 0 0", rpy="0 0 0") mass = et.SubElement(inertial, 'mass', value='%.2E' % mesh.mass) inertia = et.SubElement(inertial, 'inertia', ixx='%.2E' % I[0, 0], ixy='%.2E' % I[0, 1], ixz='%.2E' % I[0, 2], iyy='%.2E' % I[1, 1], iyz='%.2E' % I[1, 2], izz='%.2E' % I[2, 2]) visual = et.SubElement(link, 'visual') origin = et.SubElement(visual, 'origin', xyz="0 0 0", rpy="0 0 0") geometry = et.SubElement(visual, 'geometry') mesh_element = et.SubElement(geometry, 'mesh', filename=obj_filename) material = et.SubElement(visual, 'material', name='') color = et.SubElement(material, 'color', rgba="0.75 0.75 0.75 1") collision = et.SubElement(link, 'collision') origin = et.SubElement(collision, 'origin', xyz="0 0 0", rpy="0 0 0") geometry = et.SubElement(collision, 'geometry') mesh_element = et.SubElement(geometry, 'mesh', filename=obj_filename) if prev_piece_name is not None: joint = et.SubElement(root, 'joint', name='%s_joint' % (piece_name), type='fixed') origin = et.SubElement(joint, 'origin', xyz="0 0 0", rpy="0 0 0") parent = et.SubElement(joint, 'parent', link=prev_piece_name) child = et.SubElement(joint, 'child', link=piece_name) prev_piece_name = piece_name # write URDF file tree = et.ElementTree(root) tree.write(self.urdf_filename) # write config file root = et.Element('model') model = et.SubElement(root, 'name') model.text = self.name_ version = et.SubElement(root, 'version') version.text = '1.0' sdf = et.SubElement(root, 'sdf', version='1.4') urdf_root, urdf_ext = os.path.splitext(self.urdf_filename) urdf_path, urdf_name = os.path.split(urdf_root) sdf.text = urdf_name author = et.SubElement(root, 'author') et.SubElement(author, 'name').text = 'AUTOLAB meshpy' et.SubElement(author, 'email').text = '*****@*****.**' description = et.SubElement(root, 'description') description.text = 'My awesome %s' % (self.name_) tree = et.ElementTree(root) config_filename = os.path.join(out_dir, 'model.config') tree.write(config_filename)
def test_vertex_normal_records(self): parser = ObjFile.parse_obj_file( "tests/obj_test_files/vertex_normal.obj") self.assertEqual(parser.normals[1], Vector(0, 0, 1)) self.assertEqual(parser.normals[2], Vector(0.707, 0, -0.707)) self.assertEqual(parser.normals[3], Vector(1, 2, 3))
def test_converting_obj_file_to_group(self): parser = ObjFile.parse_obj_file("tests/obj_test_files/triangles.obj") g = ObjFile.obj_to_group(parser) self.assertIn(parser.named_groups["FirstGroup"], g.members) self.assertIn(parser.named_groups["SecondGroup"], g.members)
from camera import Camera from color import Color from light import PointLight from obj_file import ObjFile from tuple import * from world import World if __name__ == '__main__': parser = ObjFile.parse_obj_file("teapot-low.obj") teapot = ObjFile.obj_to_group(parser) teapot.material.ambient = .3 teapot.material.color = Color(.75, .1, .1) teapot.divide(1) # The light source is white, shining from above and to the left world = World() world.objects = [teapot] world.light = PointLight(Point(-5, 30, 25), Color(1, 1, 1)) # Camera camera = Camera(200, 150, math.pi / 3) # camera = Camera(50, 50, math.pi / 3) camera.transform = World.view_transform(Point(0, 30, 25), Point(0, 0, 0), Vector(0, 0, 1)) # render the result to a canvas canvas = Camera.render(camera, world) canvas.canvas_to_ppm()
def import_obj(): file_path, user_id, stream_id, branch_name, commit_message = sys.argv[1:] print(f'ImportOBJ argv[1:]: {sys.argv[1:]}') # Parse input obj = ObjFile(file_path) print( f'Parsed obj with {len(obj.faces)} faces ({len(obj.vertices) * 3} vertices)' ) speckle_root = Base() speckle_root['@objects'] = [] for objname in obj.objects: print(f' Converting {objname}...') speckle_obj = Base() speckle_obj.name = objname speckle_obj['@displayValue'] = [] speckle_root['@objects'].append(speckle_obj) for obj_mesh in obj.objects[objname]: speckle_vertices = [ coord for point in obj_mesh['vertices'] for coord in point ] speckle_faces = [] for obj_face in obj_mesh['faces']: if len(obj_face) == 3: speckle_faces.append(0) elif len(obj_face) == 4: speckle_faces.append(1) else: speckle_faces.append(len(obj_face)) speckle_faces.extend(obj_face) has_vertex_colors = False for vc in obj_mesh['vertex_colors']: if vc is not None: has_vertex_colors = True colors = [] if has_vertex_colors: for vc in obj_mesh['vertex_colors']: if vc is None: r, g, b = (1.0, 1.0, 1.0) else: r, g, b = vc argb = (1.0, r, g, b) color = int.from_bytes([int(val * 255) for val in argb], byteorder="big", signed=True) colors.append(color) speckle_mesh = Mesh(vertices=speckle_vertices, faces=speckle_faces, colors=colors, textureCoordinates=[]) obj_material = obj_mesh['material'] if obj_material: speckle_mesh['renderMaterial'] = convert_material(obj_material) speckle_obj['@displayValue'].append(speckle_mesh) # Commit client = SpeckleClient(host=os.getenv('SPECKLE_SERVER_URL', 'localhost:3000'), use_ssl=False) client.authenticate(os.environ['USER_TOKEN']) if not client.branch.get(stream_id, branch_name): client.branch.create( stream_id, branch_name, 'File upload branch' if branch_name == 'uploads' else '') transport = ServerTransport(client=client, stream_id=stream_id) id = operations.send(base=speckle_root, transports=[transport]) commit_id = client.commit.create(stream_id=stream_id, object_id=id, branch_name=(branch_name or DEFAULT_BRANCH), message=(commit_message or 'OBJ file upload'), source_application='OBJ') return commit_id