forked from ranahanocka/point2mesh
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
199 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |