From 0cc1425e7bdd9c0776dfe0d60c1a1d4d22b2e498 Mon Sep 17 00:00:00 2001 From: sercero Date: Sat, 18 Feb 2023 22:34:01 -0300 Subject: [PATCH] Fix issue #135, where Edge Lists where not being generated for OGRE > 1.9 --- io_ogre/__init__.py | 3 +- io_ogre/config.py | 4 +-- io_ogre/ogre/mesh.py | 17 +++++---- io_ogre/ui/export.py | 39 ++++++++++---------- io_ogre/util.py | 84 ++++++++++++++++++++++++++++++-------------- 5 files changed, 91 insertions(+), 56 deletions(-) diff --git a/io_ogre/__init__.py b/io_ogre/__init__.py index f28b3bd..0b0c369 100644 --- a/io_ogre/__init__.py +++ b/io_ogre/__init__.py @@ -104,8 +104,7 @@ def draw(self, context): layout.prop(self, "SHADER_PROGRAMS") def register(): - #logging.basicConfig(stream=sys.stdout, level=logging.DEBUG) - logging.basicConfig(stream=sys.stdout, level=logging.DEBUG, format='[%(levelname)5s] %(message)s', datefmt='%H:%M:%S') + logging.basicConfig(stream=sys.stdout, level=logging.INFO, format='[%(levelname)5s] %(message)s', datefmt='%H:%M:%S') logging.info('Starting io_ogre %s', bl_info["version"]) diff --git a/io_ogre/config.py b/io_ogre/config.py index 77a7575..3da0deb 100644 --- a/io_ogre/config.py +++ b/io_ogre/config.py @@ -85,8 +85,8 @@ # Logging 'ENABLE_LOGGING' : False, - #'DEBUG_LOGGING' : False, - 'SHOW_LOG_NAME' : False, + 'DEBUG_LOGGING' : False, + #'SHOW_LOG_NAME' : False, # Import 'IMPORT_NORMALS' : True, diff --git a/io_ogre/ogre/mesh.py b/io_ogre/ogre/mesh.py index ec5c343..010750b 100644 --- a/io_ogre/ogre/mesh.py +++ b/io_ogre/ogre/mesh.py @@ -737,15 +737,17 @@ def duplicate_object(scene, name, copyobj): doc.end_tag('animations') logger.info('- Done at %s seconds' % timer_diff_str(start)) - ## Clean up and save - if ob.modifiers != None: - #bpy.context.collection.objects.unlink(copy) + ## If we made a copy of the object, clean it up + if ob != copy: + #bpy.context.collection.objects.unlink(copy) # Blender 2.7x + #bpy.context.scene.collection.objects.unlink(copy) # Blender 2.8+ copy.user_clear() logger.debug("Removing temporary object: %s" % copy.name) bpy.data.objects.remove(copy) del copy - # Reenable disabled modifiers + # Reenable disabled modifiers + if ob.modifiers != None: for mod in ob.modifiers: if mod.type in disable_mods and mod.show_viewport == False: logger.debug("Enabling Modifier: %s" % mod.name) @@ -779,10 +781,11 @@ def replaceInplace(f,searchExp,replaceExp): logger.info('- Created %s.mesh in total time %s seconds' % (obj_name, timer_diff_str(start))) - # If requested by the user, generate LOD levels through OgreMeshUpgrader - if config.get('LOD_LEVELS') > 0 and config.get('LOD_MESH_TOOLS') == True: + # If requested by the user, generate LOD levels / Edge Lists / Vertex buffer optimization through OgreMeshUpgrader + if ((config.get('LOD_LEVELS') > 0 and config.get('LOD_MESH_TOOLS') == True) or + (config.get('GENERATE_EDGE_LISTS') == True)): target_mesh_file = os.path.join(path, '%s.mesh' % obj_name ) - util.lod_create(target_mesh_file) + util.mesh_upgrade_tool(target_mesh_file) # Note that exporting the skeleton does not happen here anymore # It was moved to the function dot_skeleton in its own module (skeleton.py) diff --git a/io_ogre/ui/export.py b/io_ogre/ui/export.py index 75a9869..3ac59f4 100644 --- a/io_ogre/ui/export.py +++ b/io_ogre/ui/export.py @@ -100,7 +100,7 @@ def draw(self, context): "Armature" : "ARMATURE_DATA", "Mesh" : "MESH_DATA", "LOD" : "LATTICE_DATA", "Shape Animation" : "POSE_HLT", "Logging" : "TEXT" } - + # Options associated with each section section_options = { "General" : ["EX_SWAP_AXIS", "EX_V2_MESH_TOOL_VERSION", "EX_XML_DELETE"], @@ -111,9 +111,9 @@ def draw(self, context): "Mesh" : ["EX_MESH", "EX_MESH_OVERWRITE", "EX_ARRAY", "EX_V1_EXTREMITY_POINTS", "EX_Vx_GENERATE_EDGE_LISTS", "EX_GENERATE_TANGENTS", "EX_Vx_OPTIMISE_ANIMATIONS", "EX_V2_OPTIMISE_VERTEX_BUFFERS", "EX_V2_OPTIMISE_VERTEX_BUFFERS_OPTIONS"], "LOD" : ["EX_LOD_LEVELS", "EX_LOD_DISTANCE", "EX_LOD_PERCENT", "EX_LOD_MESH_TOOLS"], "Shape Animation" : ["EX_SHAPE_ANIMATIONS", "EX_SHAPE_NORMALS"], - "Logging" : ["EX_Vx_ENABLE_LOGGING"] + "Logging" : ["EX_Vx_ENABLE_LOGGING", "EX_Vx_DEBUG_LOGGING"] } - + for section in sections: row = layout.row() box = row.box() @@ -130,7 +130,7 @@ def draw(self, context): box.prop(self, prop) elif prop.startswith('EX_'): box.prop(self, prop) - + def execute(self, context): # Add warinng about missing XML converter Report.reset() @@ -155,23 +155,23 @@ def execute(self, context): config.update(**kw) print ("_" * 80,"\n") - + target_path, target_file_name = os.path.split(os.path.abspath(self.filepath)) target_file_name = clean_object_name(target_file_name) target_file_name_no_ext = os.path.splitext(target_file_name)[0] file_handler = None - + # Add a file handler to all Logger instances if config.get('ENABLE_LOGGING') == True: log_file = ("%s/blender2ogre.log" % target_path) logger.info("* Writing log file to: %s" % log_file) file_handler = logging.FileHandler(filename=log_file, mode='w', encoding='utf-8', delay=False) - + # Show the python file name from where each log message originated SHOW_LOG_NAME = False - + if SHOW_LOG_NAME: file_formatter = logging.Formatter(fmt='%(asctime)s %(name)9s.py [%(levelname)5s] %(message)s', datefmt='%Y-%m-%d %H:%M:%S') else: @@ -179,8 +179,14 @@ def execute(self, context): file_handler.setFormatter(file_formatter) + if config.get('DEBUG_LOGGING') == True: + level = logging.DEBUG + else: + level = logging.INFO + for logger_name in logging.Logger.manager.loggerDict.keys(): logging.getLogger(logger_name).addHandler(file_handler) + logging.getLogger(logger_name).setLevel(level) logger.info("* Target path: %s" % target_path) logger.info("* Target file name: %s" % target_file_name) @@ -201,10 +207,7 @@ def execute(self, context): # Flush and close all logging file handlers if config.get('ENABLE_LOGGING') == True: for logger_name in logging.Logger.manager.loggerDict.keys(): - logger_instance = logging.getLogger(logger_name) - - # Remove handlers - logger_instance.handlers.clear() + logging.getLogger(logger_name).handlers.clear() file_handler.flush() file_handler.close() @@ -356,8 +359,8 @@ def execute(self, context): min=0, max=65536, default=config.get('EXTREMITY_POINTS')) = {} EX_Vx_GENERATE_EDGE_LISTS : BoolProperty( - name="Edge Lists", - description="Generate edge lists (for stencil shadows)", + name="Generate Edge Lists", + description="Generate Edge Lists (for Stencil Shadows)", default=config.get('GENERATE_EDGE_LISTS')) = {} EX_GENERATE_TANGENTS : EnumProperty( items=config.TANGENT_MODES, @@ -423,10 +426,10 @@ def execute(self, context): default=config.get('ENABLE_LOGGING')) = {} # It seems that it is not possible to exclude DEBUG when selecting a log level - #EX_Vx_DEBUG_LOGGING : BoolProperty( - # name="Debug Logging", - # description="Whether to show DEBUG log messages", - # default=config.get('DEBUG_LOGGING')) = {} + EX_Vx_DEBUG_LOGGING : BoolProperty( + name="Debug Logging", + description="Whether to show DEBUG log messages", + default=config.get('DEBUG_LOGGING')) = {} # It was decided to make this an option that is not user-facing #EX_Vx_SHOW_LOG_NAME : BoolProperty( diff --git a/io_ogre/util.py b/io_ogre/util.py index ada1ab1..ee0e66d 100644 --- a/io_ogre/util.py +++ b/io_ogre/util.py @@ -67,20 +67,28 @@ def mesh_tool_parameters(): def mesh_tool_version(): return mesh_tool_parameters()[1] -# Calls OgreMeshUpgrader to generate the LOD levels -def lod_create(infile): +# Calls OgreMeshUpgrader to perform: +# - Edge List / LOD generation +# - Optimize vertex buffers for shaders +def mesh_upgrade_tool(infile): exe = config.get('OGRETOOLS_MESH_UPGRADER') # OgreMeshUpgrader only works in tandem with OgreXMLConverter, which are both Ogre v1.x tools. - # For Ogre v2.x we will use OgreMeshTool to generate the LODs + # For Ogre v2.x we will use OgreMeshTool, which can perform the same operations if detect_converter_type() != "OgreXMLConverter": return output_path, filename = os.path.split(infile) if not os.path.exists(infile): - logger.warn("Cannot find file mesh file: %s, unable to generate LOD" % filename) - Report.warnings.append("OgreMeshUpgrader failed, LODs will not be generated for this mesh: %s" % filename) + logger.warn("Cannot find file mesh file: %s, unable run OgreMeshUpgrader" % filename) + + if config.get('LOD_MESH_TOOLS') == True: + Report.warnings.append("OgreMeshUpgrader failed, LODs will not be generated for this mesh: %s" % filename) + + if config.get('GENERATE_EDGE_LISTS') == True: + Report.warnings.append("OgreMeshUpgrader failed, Edge Lists will not be generated for this mesh: %s" % filename) + return # Extract converter type from its output @@ -91,25 +99,27 @@ def lod_create(infile): output = output.decode('utf-8') except: output = "" - + # Check to see if the executable is actually OgreMeshUpgrader if output.find("OgreMeshUpgrader") == -1: - logger.warn("Cannot find suitable OgreMeshUpgrader executable, unable to generate LOD") + logger.warn("Cannot find suitable OgreMeshUpgrader executable, unable to generate LODs / Edge Lists / Vertex buffer optimization") return cmd = [exe] - cmd.append('-l') - cmd.append(str(config.get('LOD_LEVELS'))) - - cmd.append('-d') - cmd.append(str(config.get('LOD_DISTANCE'))) + if config.get('LOD_LEVELS') > 0 and config.get('LOD_MESH_TOOLS') == True: + cmd.append('-l') + cmd.append(str(config.get('LOD_LEVELS'))) + + cmd.append('-d') + cmd.append(str(config.get('LOD_DISTANCE'))) - cmd.append('-p') - cmd.append(str(config.get('LOD_PERCENT'))) + cmd.append('-p') + cmd.append(str(config.get('LOD_PERCENT'))) - # Edge lists should be generated (or not) by mesh.py, not OgreMeshUpgrader - cmd.append('-e') + # Don't generate Edge Lists (-e = DON'T generate edge lists (for stencil shadows)) + if config.get('GENERATE_EDGE_LISTS') == False: + cmd.append('-e') # Put logfile into output directory use_logger = False @@ -124,10 +134,15 @@ def lod_create(infile): # Finally, specify input file cmd.append(infile) - logger.info("* Generating %s LOD levels for mesh: %s" % (config.get('LOD_LEVELS'), filename)) + if config.get('LOD_LEVELS') > 0 and config.get('LOD_MESH_TOOLS') == True: + logger.info("* Generating %s LOD levels for mesh: %s" % (config.get('LOD_LEVELS'), filename)) + + if config.get('GENERATE_EDGE_LISTS') == True: + logger.info("* Generating Edge Lists for mesh: %s" % filename) # First try to execute with the -log option - proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) + logger.debug("%s" % " ".join(cmd)) + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True) output, error = proc.communicate() if use_logger == False: @@ -136,13 +151,23 @@ def lod_create(infile): log.write(output) if proc.returncode != 0: - logger.warn("OgreMeshUpgrader failed, LODs will not be generated for this mesh: %s" % filename) - Report.warnings.append("OgreMeshUpgrader failed, LODs will not be generated for this mesh: %s" % filename) + logger.warn("OgreMeshUpgrader failed, LODs / Edge Lists / Vertex buffer optimizations will not be generated for this mesh: %s" % filename) + + if config.get('LOD_LEVELS') > 0 and config.get('LOD_MESH_TOOLS') == True: + Report.warnings.append("OgreMeshUpgrader failed, LODs will not be generated for this mesh: %s" % filename) + + if config.get('GENERATE_EDGE_LISTS') == True: + Report.warnings.append("OgreMeshUpgrader failed, Edge Lists will not be generated for this mesh: %s" % filename) + if error != None: logger.error(error) logger.warn(output) else: - logger.info("- Generated %s LOD levels for mesh: %s" % (config.get('LOD_LEVELS'), filename)) + if config.get('LOD_LEVELS') > 0 and config.get('LOD_MESH_TOOLS') == True: + logger.info("- Generated %s LOD levels for mesh: %s" % (config.get('LOD_LEVELS'), filename)) + + if config.get('GENERATE_EDGE_LISTS') == True: + logger.info("- Generated Edge Lists for mesh: %s" % filename) def detect_converter_type(): @@ -218,6 +243,7 @@ def mesh_convert(infile): # OgreMeshTool must be run from its own directory (so setting cwd accordingly) # otherwise it will complain about missing render system (missing plugins_tools.cfg) exe_path, name = os.path.split(exe) + logger.debug("%s" % " ".join(cmd)) proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, cwd=exe_path) output, error = proc.communicate() @@ -261,9 +287,10 @@ def xml_convert(infile, has_uvs=False): cmd.append('-x') cmd.append(config.get('EXTREMITY_POINTS')) - if version < (1,10,0) or converter_type == "OgreMeshTool": - if not config.get('GENERATE_EDGE_LISTS'): - cmd.append('-e') + # OgreMeshTool (OGRE v2): -e = DON'T generate edge lists (for stencil shadows) + # OgreXMLConverter (OGRE < 1.10): -e = DON'T generate edge lists (for stencil shadows) + if config.get('GENERATE_EDGE_LISTS') == False and (version < (1,10,0) or converter_type == "OgreMeshTool"): + cmd.append('-e') if config.get('GENERATE_TANGENTS') != "0" and converter_type == "OgreMeshTool": cmd.append('-t') @@ -293,16 +320,18 @@ def xml_convert(infile, has_uvs=False): # Finally, specify input file cmd.append(infile) - ret = subprocess.call(cmd) + logger.debug("%s" % " ".join(cmd)) + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True) + output, error = proc.communicate() # Instead of asserting, report an error - if ret != 0: + if proc.returncode != 0: logger.error("OgreXMLConverter returned with non-zero status, check OgreXMLConverter.log") logger.info(" ".join(cmd)) Report.errors.append("OgreXMLConverter finished with non-zero status converting mesh: (%s), it might not have been properly generated" % name) # Clean up .xml file after successful conversion - if ret == 0 and config.get('XML_DELETE') == True: + if proc.returncode == 0 and config.get('XML_DELETE') == True: logger.info("Removing generated xml file after conversion: %s" % infile) os.remove(infile) @@ -330,6 +359,7 @@ def xml_convert(infile, has_uvs=False): # OgreMeshTool must be run from its own directory (so setting cwd accordingly) # otherwise it will complain about missing render system (missing plugins_tools.cfg) exe_path, name = os.path.split(exe) + logger.debug("%s" % " ".join(cmd)) proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, cwd=exe_path) output, error = proc.communicate()