Skip to content

Commit

Permalink
convex hull making script
Browse files Browse the repository at this point in the history
  • Loading branch information
galmetzer committed May 25, 2020
1 parent 413978d commit 0e3d288
Show file tree
Hide file tree
Showing 2 changed files with 199 additions and 0 deletions.
100 changes: 100 additions & 0 deletions scripts/process_data/blender_scripts/blender_hull.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@

import bpy
import os
import sys


'''
@input:
<obj_file>
<octree_res> number of target faces / resolution of octree (larger equals higher)
<outfile> name of convex hull .obj file
@output:
convex hull, which is uniform
'''

class Process:
def __init__(self, obj_file, oct_depth, export_name):
mesh = self.load_obj(obj_file)
self.convex_hull(mesh)
# self.simplify(mesh, target_faces)
self.remesh(mesh, oct_depth)
self.clean(mesh)
self.export_obj(mesh, export_name)

def load_obj(self, obj_file):
bpy.ops.import_scene.obj(filepath=obj_file, axis_forward='-Z', axis_up='Y', filter_glob="*.obj;*.mtl", use_edges=True,
use_smooth_groups=True, use_split_objects=False, use_split_groups=False,
use_groups_as_vgroups=False, use_image_search=True, split_mode='ON')
ob = bpy.context.selected_objects[0]
return ob

def subsurf(self, mesh):
# subdivide mesh
bpy.context.view_layer.objects.active = mesh
mod = mesh.modifiers.new(name='Subsurf', type='SUBSURF')
mod.subdivision_type = 'SIMPLE'
bpy.ops.object.modifier_apply(modifier=mod.name)
# now triangulate
mod = mesh.modifiers.new(name='Triangluate', type='TRIANGULATE')
bpy.ops.object.modifier_apply(modifier=mod.name)

def simplify(self, mesh, target_faces):
bpy.context.view_layer.objects.active = mesh
mod = mesh.modifiers.new(name='Decimate', type='DECIMATE')
bpy.context.object.modifiers['Decimate'].use_collapse_triangulate = True
#
nfaces = len(mesh.data.polygons)
print('nfaces: ', len(mesh.data.polygons))
if nfaces < target_faces:
self.subsurf(mesh)
nfaces = len(mesh.data.polygons)
ratio = target_faces / float(nfaces)
mod.ratio = float('%s' % ('%.6g' % (ratio)))
print('faces: ', mod.face_count, mod.ratio)
bpy.ops.object.modifier_apply(modifier=mod.name)
print('nfaces: ', len(mesh.data.polygons))

def remesh(self, mesh, oct_depth):
bpy.context.view_layer.objects.active = mesh
bpy.ops.object.modifier_add(type='REMESH')
bpy.context.object.modifiers["Remesh"].octree_depth = oct_depth
bpy.ops.object.modifier_apply(modifier='Remesh')
print('nfaces remesh: ', len(mesh.data.polygons))
# now triangulate
mod = mesh.modifiers.new(name='Triangluate', type='TRIANGULATE')
bpy.ops.object.modifier_apply(modifier=mod.name)
print('nfaces after tri: ', len(mesh.data.polygons))

def convex_hull(self, mesh):
bpy.context.view_layer.objects.active = mesh
bpy.ops.object.editmode_toggle()
bpy.ops.mesh.convex_hull()
bpy.ops.object.editmode_toggle()

def clean(self, mesh):
bpy.context.view_layer.objects.active = mesh
bpy.ops.object.editmode_toggle()
bpy.ops.mesh.dissolve_degenerate()
bpy.ops.object.editmode_toggle()

def export_obj(self, mesh, export_name):
outpath = os.path.dirname(export_name)
if not os.path.isdir(outpath): os.makedirs(outpath)
print('EXPORTING', export_name)
bpy.ops.object.select_all(action='DESELECT')
mesh.select_set(state=True)
bpy.ops.export_scene.obj(filepath=export_name, check_existing=False, filter_glob="*.obj;*.mtl",
use_selection=True, use_animation=False, use_mesh_modifiers=True, use_edges=True,
use_smooth_groups=False, use_smooth_groups_bitflags=False, use_normals=True,
use_uvs=False, use_materials=False, use_triangles=True, use_nurbs=False,
use_vertex_groups=False, use_blen_objects=True, group_by_object=False,
group_by_material=False, keep_vertex_order=True, global_scale=1, path_mode='AUTO',
axis_forward='-Z', axis_up='Y')

obj_file = sys.argv[-3]
target_faces = int(sys.argv[-2])
export_name = sys.argv[-1]

print('args: ', obj_file, target_faces, export_name)
blender = Process(obj_file, target_faces, export_name)
99 changes: 99 additions & 0 deletions scripts/process_data/convex_hull.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import trimesh
from models.layers.mesh import *
import argparse
import pathlib
from utils import *
import warnings


def run(args):
xyz, _ = read_pts(args.i)

m = trimesh.convex.convex_hull(xyz[:, :3])
vs, faces = m.vertices, m.faces

export(args.o, vs, faces)

if args.blender:
blender_rehull(args.o, args.o, args.blender_res, args.blender_path)
else:
inplace_manifold(args.o, args.manifold_res, args.manifold_path)

num_faces = count_faces(args.o)
if args.blender:
num_faces /= 2
num_faces = int(num_faces)
if num_faces < args.faces:
software = 'blender' if args.blender else 'manifold'
warnings.warn(f'only {num_faces} faces where generated by {software}. '
f'try increasing --{software}-res to achieve the desired target of {args.faces} faces')
else:
inplace_simplify(args.o, args.faces, args.manifold_path)

print('*** Done! ****')


def check_args(args):
if not args.i.exists():
raise FileNotFoundError('can\' find input file')

if args.blender:
if not (args.blender_path / 'blender').exists():
raise FileNotFoundError('can\' find blender')
else:
if not (args.manifold_path / 'manifold').exists():
raise FileNotFoundError('can\' find manifold software')

if not (args.manifold_path / 'simplify').exists():
raise FileNotFoundError('can\' find simplify software')

if not args.o:
args.o = args.i.with_name('.'.join(args.i.name.split('.')[:-1]) + '_hull.obj')
else:
args.o = Path(args.o)


def count_faces(path: Path) -> int:
with open(path, 'r') as file:
lines = file.read().split('\n')
return sum(map(lambda x: x.startswith('f'), lines))


def inplace_manifold(path: Path, res: int, manifold_software_path: Path):
cmd = f'{manifold_software_path}/manifold {path} {path} {res}'
os.system(cmd)


def inplace_simplify(path: Path, faces: int, manifold_software_path: Path):
cmd = f'{manifold_software_path}/simplify -i {path} -o {path} -f {faces}'
os.system(cmd)


def blender_rehull(target: Path, dest: Path, res: int, blender_path: Path):
base_path = pathlib.Path(__file__).parent.absolute()
cmd = f'{blender_path}/blender --background --python {base_path}/blender_scripts/blender_hull.py' \
f' {target} {res} {dest} > /dev/null 2>&1'
os.system(cmd)


if __name__ == '__main__':
base_path = os.path.dirname(os.path.abspath(__file__))
parser = argparse.ArgumentParser(description='Convex hull maker')
parser.add_argument('--i', type=Path, required=True,
help='path to read .xyz/.npts or .ply from')
parser.add_argument('--faces', type=int, required=True, help='#target of faces for the convex hull')

parser.add_argument('--o', type=str, required=False,
help='path to output convex hull obj to', default='')
parser.add_argument('--manifold-path', type=Path, required=False,
help='path to build folder containing manifold and simplify software')
parser.add_argument('--manifold-res', type=int, default=5000, required=False,
help='resolution for Manifold software')
parser.add_argument('--blender', action='store_true')
parser.add_argument('--blender-res', type=int, default=5, required=False,
help='resolution for making convex hulls with blender software')
parser.add_argument('--blender-path', type=Path, required=False, help='path to folder containing blender')

args = parser.parse_args()
check_args(args)
run(args)

0 comments on commit 0e3d288

Please sign in to comment.