diff --git a/scripts/bakemyscan.py b/scripts/bakemyscan.py index 234a26c..083ee4a 100644 --- a/scripts/bakemyscan.py +++ b/scripts/bakemyscan.py @@ -96,16 +96,16 @@ def check_valid_file(_file, _exts=None, _image=False, _can_create=False): parser.print_help() print('ERROR: invalid method name') sys.exit(5) - if args.method == "MMGS" and bpy.context.user_preferences.addons["BakeMyScan"].preferences.mmgs == "": + if args.method == "MMGS" and bpy.types.Scene.executables["mmgs"] == "": print("MMGS is not configured in the user preferences") sys.exit(6) - if args.method == "INSTANT" and bpy.context.user_preferences.addons["BakeMyScan"].preferences.instant == "": + if args.method == "INSTANT" and bpy.types.Scene.executables["instant"] == "": print("Instant Meshes is not configured in the user preferences") sys.exit(7) - if args.method == "QUADRIFLOW" and bpy.context.user_preferences.addons["BakeMyScan"].preferences.quadriflow == "": + if args.method == "QUADRIFLOW" and bpy.types.Scene.executables["quadriflow"] == "": print("Quadriflow is not configured in the user preferences") sys.exit(8) - if args.method == "MESHLAB" and bpy.context.user_preferences.addons["BakeMyScan"].preferences.meshlabserver == "": + if args.method == "MESHLAB" and bpy.types.Scene.executables["meshlabserver"] == "": print("Meshlabserver is not configured in the user preferences") sys.exit(9) diff --git a/src/PREFS.py b/src/PREFS.py index 34e6f86..d6e7b36 100644 --- a/src/PREFS.py +++ b/src/PREFS.py @@ -1,21 +1,93 @@ import bpy +import os +import json + +def absolute_paths(self, context): + #Make the paths absolute + bpy.types.Scene.executables["mmgs"] = os.path.abspath(bpy.path.abspath(self.mmgs)) if self.mmgs!="" else "" + bpy.types.Scene.executables["instant"] = os.path.abspath(bpy.path.abspath(self.instant)) if self.instant!="" else "" + bpy.types.Scene.executables["quadriflow"] = os.path.abspath(bpy.path.abspath(self.quadriflow)) if self.quadriflow!="" else "" + bpy.types.Scene.executables["meshlabserver"] = os.path.abspath(bpy.path.abspath(self.meshlabserver)) if self.meshlabserver!="" else "" + bpy.types.Scene.executables["colmap"] = os.path.abspath(bpy.path.abspath(self.colmap)) if self.colmap!="" else "" + bpy.types.Scene.executables["interfacevisualsfm"] = os.path.abspath(bpy.path.abspath(self.interfacevisualsfm)) if self.interfacevisualsfm!="" else "" + bpy.types.Scene.executables["densifypointcloud"] = os.path.abspath(bpy.path.abspath(self.densifypointcloud)) if self.densifypointcloud!="" else "" + bpy.types.Scene.executables["reconstructmesh"] = os.path.abspath(bpy.path.abspath(self.reconstructmesh)) if self.reconstructmesh!="" else "" + bpy.types.Scene.executables["texturemesh"] = os.path.abspath(bpy.path.abspath(self.texturemesh)) if self.texturemesh!="" else "" + bpy.types.Scene.executables["openmvsdir"] = os.path.abspath(bpy.path.abspath(self.openmvsdir)) if self.openmvsdir!="" else "" + #Write to a .json file to keep even after updating the addon + path = os.path.join(bpy.utils.resource_path('USER'), "bakemyscan.config") + with open(path, 'w') as fp: + json.dump(bpy.types.Scene.executables, fp, sort_keys=True, indent=4) + return None +def find_openmvs_executables(self, context): + bpy.types.Scene.executables["openmvsdir"] = os.path.abspath(bpy.path.abspath(self.openmvsdir)) if self.openmvsdir!="" else "" + for f in os.listdir(bpy.types.Scene.executables["openmvsdir"]): + if "InterfaceVisualSFM" in f: + self.interfacevisualsfm = os.path.join(bpy.types.Scene.executables["openmvsdir"], f) + if "DensifyPointCloud" in f: + self.densifypointcloud = os.path.join(bpy.types.Scene.executables["openmvsdir"], f) + if "ReconstructMesh" in f: + self.reconstructmesh = os.path.join(bpy.types.Scene.executables["openmvsdir"], f) + if "TextureMesh" in f: + self.texturemesh = os.path.join(bpy.types.Scene.executables["openmvsdir"], f) + return None class BakeMyScanPrefs(bpy.types.AddonPreferences): bl_idname = 'BakeMyScan' - mmgs = bpy.props.StringProperty(name="MMGS Executable", subtype='FILE_PATH') - instant = bpy.props.StringProperty(name="Instant Meshes Executable", subtype='FILE_PATH') - quadriflow = bpy.props.StringProperty(name="Quadriflow Executable", subtype='FILE_PATH') - meshlabserver = bpy.props.StringProperty(name="Meshlabserver Executable", subtype='FILE_PATH') + executables = {} + #Remeshers + mmgs = bpy.props.StringProperty(name="MMGS Executable", subtype='FILE_PATH', update=absolute_paths) + instant = bpy.props.StringProperty(name="Instant Meshes Executable", subtype='FILE_PATH', update=absolute_paths) + quadriflow = bpy.props.StringProperty(name="Quadriflow Executable", subtype='FILE_PATH', update=absolute_paths) + meshlabserver = bpy.props.StringProperty(name="Meshlabserver Executable", subtype='FILE_PATH', update=absolute_paths) + #Scanning executables + colmap = bpy.props.StringProperty(name="Colmap Executable", subtype='FILE_PATH', update=absolute_paths) + openmvsdir = bpy.props.StringProperty(name="OpenMVS directory", subtype='DIR_PATH', update=find_openmvs_executables) + #OpenMVS executables + interfacevisualsfm = bpy.props.StringProperty(name="InterfaceVisualSFM", subtype='FILE_PATH', update=absolute_paths) + densifypointcloud = bpy.props.StringProperty(name="DensifyPointCloud", subtype='FILE_PATH', update=absolute_paths) + reconstructmesh = bpy.props.StringProperty(name="ReconstructMesh", subtype='FILE_PATH', update=absolute_paths) + texturemesh = bpy.props.StringProperty(name="TextureMesh", subtype='FILE_PATH', update=absolute_paths) + + def check(self, context): + return True def draw(self, context): layout = self.layout - layout.label(text="Executable paths") + layout.label(text="Remeshing tools") layout.prop(self, "instant") layout.prop(self, "mmgs") layout.prop(self, "quadriflow") layout.prop(self, "meshlabserver") + layout.label(text="Photogrammetry") + layout.prop(self, "colmap") + layout.prop(self, "openmvsdir") + #Display the OpenMVS executables + if self.openmvsdir != "": + layout.prop(self, "interfacevisualsfm") + layout.prop(self, "densifypointcloud") + layout.prop(self, "reconstructmesh") + layout.prop(self, "texturemesh") def register(): + bpy.types.Scene.executables = {} bpy.utils.register_class(BakeMyScanPrefs) + PREFS = bpy.context.user_preferences.addons["BakeMyScan"].preferences + + #Print the preferences + for x in PREFS.keys(): + print(x, PREFS[x]) + + #Try to read in the preferences + path = os.path.join(bpy.utils.resource_path('USER'), "bakemyscan.config") + if os.path.exists(path): + with open(path, 'r') as fp: + bpy.types.Scene.executables = json.load(fp) + #Assign them to the variables + for x in bpy.types.Scene.executables.keys(): + if PREFS[x] == "": + PREFS[x] = bpy.types.Scene.executables[x] + print(bpy.types.Scene.executables) def unregister(): bpy.utils.unregister_class(BakeMyScanPrefs) + del bpy.types.Scene.executables diff --git a/src/fn_soft.py b/src/fn_soft.py index bade8aa..a8c697f 100644 --- a/src/fn_soft.py +++ b/src/fn_soft.py @@ -142,10 +142,10 @@ def colmap_auto( sparse=1, dense=1, mesher="delaunay", - executable="colmap", + colmap="colmap", gpu=False ): - cmd = '"' + executable + '" automatic_reconstructor ' + cmd = '"' + colmap + '" automatic_reconstructor ' cmd += "--workspace_path %s " % workspace cmd += "--image_path %s " % images cmd += "--quality %s " % quality @@ -165,7 +165,12 @@ def colmap_openmvs( sparse=1, dense=1, mesher="delaunay", - executable="colmap" + colmap="colmap", + interfacevisualsfm = "InterfaceVisualSFM", + densifypointcloud = "DensifyPointCloud", + reconstructmesh = "ReconstructMesh", + texturemesh = "TextureMesh", + meshlabserver = "meshlabserver" ): DB = os.path.join(workspace, "database.db") SP = os.path.join(workspace, "sparse") @@ -175,23 +180,21 @@ def colmap_openmvs( os.chdir(workspace) #Colmap - cmd1 = executable + " feature_extractor --database_path %s --image_path %s " % (DB, images) - cmd2 = executable + " exhaustive_matcher --database_path %s" % DB - cmd3 = executable + " mapper --database_path %s --image_path %s --output_path %s" % ( DB, images, SP ) - cmd4 = executable + " model_converter --input_path %s --output_path %s --output_type NVM" % (os.path.join(SP, "0"), os.path.join(workspace, "model.nvm")) + cmd1 = colmap + " feature_extractor --database_path %s --image_path %s " % (DB, images) + cmd2 = colmap + " exhaustive_matcher --database_path %s" % DB + cmd3 = colmap + " mapper --database_path %s --image_path %s --output_path %s" % ( DB, images, SP ) + cmd4 = colmap + " model_converter --input_path %s --output_path %s --output_type NVM" % (os.path.join(SP, "0"), os.path.join(workspace, "model.nvm")) #OpenMVS - cmd5 = "InterfaceVisualSFM -w %s -i %s" % (workspace, os.path.join(workspace, "model.nvm")) - cmd6 = "DensifyPointCloud -w %s -i %s --resolution-level %d" % (workspace, os.path.join(workspace, "model.mvs"), 2) - cmd7 = "ReconstructMesh -w %s -i %s" % (workspace, os.path.join(workspace, "model_dense.mvs")) - cmd8 = "TextureMesh -w %s -i %s" % (workspace, os.path.join(workspace, "model_dense_mesh.mvs")) + cmd5 = interfacevisualsfm + " -w %s -i %s" % (workspace, os.path.join(workspace, "model.nvm")) + cmd6 = densifypointcloud + " -w %s -i %s --resolution-level %d" % (workspace, os.path.join(workspace, "model.mvs"), 2) + cmd7 = reconstructmesh + " -w %s -i %s" % (workspace, os.path.join(workspace, "model_dense.mvs")) + cmd8 = texturemesh + " -w %s -i %s" % (workspace, os.path.join(workspace, "model_dense_mesh.mvs")) #Conversion - cmd9 = "meshlabserver -om wt -i %s -o %s" % (os.path.join(workspace, "model_dense_mesh_texture.ply"), os.path.join(workspace, "model_dense_mesh_texture.obj")) + cmd9 = meshlabserver + " -om wt -i %s -o %s" % (os.path.join(workspace, "model_dense_mesh_texture.ply"), os.path.join(workspace, "model_dense_mesh_texture.obj")) os.chdir(old) - - for cmd in [cmd1, cmd2, cmd3, cmd4, cmd5, cmd6, cmd7, cmd8]: out, err, code = run(cmd) if code!=0: diff --git a/src/op_REMESHERS.py b/src/op_REMESHERS.py index c151d93..69edc4d 100644 --- a/src/op_REMESHERS.py +++ b/src/op_REMESHERS.py @@ -145,7 +145,7 @@ def draw(self, context): #Overriden methods def setexe(self, context): - self.executable = context.user_preferences.addons["BakeMyScan"].preferences.quadriflow + self.executable = bpy.types.Scene.executables["quadriflow"] def export(self, context): bpy.ops.export_scene.obj( filepath = os.path.join(self.tmp.name, "tmp.obj"), @@ -217,7 +217,7 @@ def draw(self, context): #Overriden methods def setexe(self, context): - self.executable = context.user_preferences.addons["BakeMyScan"].preferences.instant + self.executable = bpy.types.Scene.executables["instant"] def export(self, context): bpy.ops.export_scene.obj( filepath = os.path.join(self.tmp.name, "tmp.obj"), @@ -312,7 +312,7 @@ def draw(self, context): #Overriden methods def setexe(self, context): - self.executable = context.user_preferences.addons["BakeMyScan"].preferences.mmgs + self.executable = bpy.types.Scene.executables["mmgs"] def export(self, context): obj = context.active_object self.maxDim = max( max( obj.dimensions[0], obj.dimensions[1]) , obj.dimensions[2] ) @@ -353,7 +353,7 @@ def draw(self, context): #Overriden methods def setexe(self, context): - self.executable = context.user_preferences.addons["BakeMyScan"].preferences.meshlabserver + self.executable = bpy.types.Scene.executables["meshlabserver"] def export(self, context): bpy.ops.export_scene.obj( filepath = os.path.join(self.tmp.name, "tmp.obj"), @@ -759,6 +759,28 @@ def remesh(self, context): +""" +class Ultimate(bpy.types.Operator): + bl_idname = "bakemyscan.retopo" + bl_label = "Ultimate retopo" + bl_options = {"REGISTER"} + + #For executable remeshers + tmp = tempfile.TemporaryDirectory() + executable = None + results = [] + keepMaterials = False + + #For remeshers which need to duplicate the object + workonduplis = False + + method = bpy.props.EnumProperty( + items= ( + ('iterative', 'iterative', 'iterative'), + ("2", "2", "2"), + ("4", "4", "4"), ("6", "6", "6")) , name="r", description="Orientation symmetry type", default="r0") +""" + def register() : bpy.utils.register_class(BaseRemesher) bpy.utils.register_class(Quadriflow) diff --git a/src/op_SCAN.py b/src/op_SCAN.py index 979b19c..d7aec13 100644 --- a/src/op_SCAN.py +++ b/src/op_SCAN.py @@ -41,13 +41,15 @@ def poll(self, context): return 0 if len([imghdr.what(os.path.join(D,x)) for x in os.listdir(D) if not os.path.isdir(os.path.join(D,x))]) == 0: return 0 + if bpy.types.Scene.executables["colmap"] == "": + return 0 return 1 def execute(self, context): if True: D = bpy.types.Scene.imgpaths self.results = fn_soft.colmap_auto( - executable = "colmap", + colmap = bpy.types.Scene.executables["colmap"], workspace = D, images = D, mesher = self.mesher, @@ -98,13 +100,24 @@ def poll(self, context): return 0 if len([imghdr.what(os.path.join(D,x)) for x in os.listdir(D) if not os.path.isdir(os.path.join(D,x))]) == 0: return 0 + if bpy.types.Scene.executables["colmap"] == "": + return 0 + if bpy.types.Scene.executables["densifypointcloud"] == "": + return 0 + if bpy.types.Scene.executables["interfacevisualsfm"] == "": + return 0 + if bpy.types.Scene.executables["reconstructmesh"] == "": + return 0 + if bpy.types.Scene.executables["texturemesh"] == "": + return 0 + if bpy.types.Scene.executables["meshlabserver"] == "": + return 0 return 1 def execute(self, context): if True: D = bpy.types.Scene.imgpaths self.results = fn_soft.colmap_openmvs( - executable = "colmap", workspace = D, images = D, mesher = self.mesher, @@ -112,6 +125,12 @@ def execute(self, context): sparse = 1 if self.sparse else 0, dense = 1 if self.sparse else 0, single_camera = 1 if self.single else 0, + colmap=bpy.types.Scene.executables["colmap"], + interfacevisualsfm = bpy.types.Scene.executables["interfacevisualsfm"], + densifypointcloud = bpy.types.Scene.executables["densifypointcloud"], + reconstructmesh = bpy.types.Scene.executables["reconstructmesh"], + texturemesh = bpy.types.Scene.executables["texturemesh"], + meshlabserver = bpy.types.Scene.executables["meshlabserver"], ) return{'FINISHED'} else: diff --git a/tests/tests.py b/tests/tests.py index df7504b..de12090 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -14,7 +14,7 @@ import json #Do we stop the execution on error? -_BREAK = True +_BREAK = False #Path to the local_directory _DIR = os.path.dirname(__file__) def _PATH(f):