Skip to content

Commit

Permalink
Add option for exporting manual LOD levels
Browse files Browse the repository at this point in the history
  • Loading branch information
sercero committed Feb 8, 2024
1 parent 038f73f commit 7dcb527
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 18 deletions.
8 changes: 7 additions & 1 deletion io_ogre/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@
('4', 'generate with parity', 'Generate with parity')
]

LOD_METHODS = [
('0', 'meshtools', 'Generate LODs using OgreMesh Tools: does LOD by removing edges, which allows only changing the index buffer and re-use the vertex-buffer (storage efficient)'),
('1', 'blender', 'Generate LODs using Blenders "Decimate" Modifier: does LOD by collapsing vertices, which can result in a visually better LOD, but needs different vertex-buffers per LOD'),
('2', 'manual', 'Generate LODs by manually crafting the lower LODs: needs different vertex-buffers per LOD')
]

CONFIG_PATH = bpy.utils.user_resource('CONFIG', path='scripts', create=True)
CONFIG_FILENAME = 'io_ogre.pickle'
CONFIG_FILEPATH = os.path.join(CONFIG_PATH, CONFIG_FILENAME)
Expand Down Expand Up @@ -75,10 +81,10 @@
'OPTIMISE_VERTEX_BUFFERS_OPTIONS' : 'puqs',

# LOD
'LOD_GENERATION': '0',
'LOD_LEVELS' : 0,
'LOD_DISTANCE' : 300,
'LOD_PERCENT' : 40,
'LOD_MESH_TOOLS' : False,

# Pose Animation
'SHAPE_ANIMATIONS' : True,
Expand Down
58 changes: 54 additions & 4 deletions io_ogre/ogre/mesh.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,8 @@ def dot_mesh(ob, path, force_name=None, ignore_shape_animation=False, normals=Tr
overwrite = kwargs.get('overwrite', False)

# Don't export hidden or unselected objects unless told to
if not isLOD and (
if not isLOD and (
(config.get('LOD_GENERATION') == '2' and "_LOD_" in ob.name) or
(not config.get("EXPORT_HIDDEN") and ob not in bpy.context.visible_objects) or
(config.get("SELECTED_ONLY") and not ob.select_get())
):
Expand Down Expand Up @@ -447,8 +448,57 @@ def dot_mesh(ob, path, force_name=None, ignore_shape_animation=False, normals=Tr

logger.info('- Done at %s seconds' % util.timer_diff_str(start))

# Generate lod levels
if isLOD == False and ob.type == 'MESH' and config.get('LOD_LEVELS') > 0 and config.get('LOD_MESH_TOOLS') == False:
# Generate LOD levels for manual LOD meshes
if isLOD == False and ob.type == 'MESH' and config.get('LOD_LEVELS') > 0 and config.get('LOD_GENERATION') == '2':
lod_levels = config.get('LOD_LEVELS')
lod_distance = config.get('LOD_DISTANCE')

lod_generated = []
lod_current_distance = lod_distance

for level in range(lod_levels + 1)[1:]:
lod_ob_name = obj_name + '_LOD_' + str(level)
lod_manual_ob = bpy.context.scene.objects.get(lod_ob_name)

if lod_manual_ob:
logger.info("- Found LOD Manual object: %s" % lod_ob_name)
lod_generated.append({ 'level': level, 'distance': lod_current_distance, 'lod_manual_ob': lod_manual_ob })
lod_current_distance += lod_distance

else:
failure = 'FAILED to manually create LOD levels, manual LOD with name %s NOT FOUND!' % lod_ob_name
Report.warnings.append( failure )
logger.error( failure )
break

# Create lod .mesh files and generate LOD XML to the original .mesh.xml
if len(lod_generated) > 0:
# 'manual' means if the geometry gets loaded from a different file than this LOD list references
doc.start_tag('levelofdetail', {
'strategy' : 'default',
'numlevels' : str(len(lod_generated) + 1), # The main mesh is + 1 (kind of weird Ogre logic)
'manual' : "true"
})

logger.info('- Generating: %s LOD meshes. Original: vertices %s, faces: %s' % (len(lod_generated), len(mesh.vertices), len(mesh.loop_triangles)))
for lod in lod_generated:
lod_manual_ob = lod['lod_manual_ob']

logger.info("- Writing LOD %s for distance %s, with %s vertices and %s faces" %
(lod['level'], lod['distance'], len(lod_manual_ob.data.vertices), len(lod_manual_ob.data.loop_triangles)))

dot_mesh(lod_manual_ob, path, lod_manual_ob.data.name, ignore_shape_animation, normals, tangents, isLOD=True)

# 'value' is the distance this LOD kicks in for the 'Distance' strategy.
doc.leaf_tag('lodmanual', {
'value' : str(lod['distance']),
'meshname' : lod_manual_ob.data.name + ".mesh"
})

doc.end_tag('levelofdetail')

# Generate LOD levels automatically using Blenders "Decimate" Modifier
if isLOD == False and ob.type == 'MESH' and config.get('LOD_LEVELS') > 0 and config.get('LOD_GENERATION') == '1':
lod_levels = config.get('LOD_LEVELS')
lod_distance = config.get('LOD_DISTANCE')
lod_ratio = config.get('LOD_PERCENT') / 100.0
Expand Down Expand Up @@ -799,7 +849,7 @@ def replaceInplace(f,searchExp,replaceExp):
logger.info('- Created %s.mesh in total time %s seconds' % (obj_name, util.timer_diff_str(start)))

# 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
if ((config.get('LOD_LEVELS') > 0 and config.get('LOD_GENERATION') == '0') or
(config.get('GENERATE_EDGE_LISTS') == True)):
target_mesh_file = os.path.join(path, '%s.mesh' % obj_name )
util.mesh_upgrade_tool(target_mesh_file)
Expand Down
13 changes: 6 additions & 7 deletions io_ogre/ui/export.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ def draw(self, context):
"Textures" : ["EX_DDS_MIPS", "EX_FORCE_IMAGE_FORMAT"],
"Armature" : ["EX_ARMATURE_ANIMATION", "EX_SHARED_ARMATURE", "EX_ONLY_KEYFRAMES", "EX_ONLY_DEFORMABLE_BONES", "EX_ONLY_KEYFRAMED_BONES", "EX_OGRE_INHERIT_SCALE", "EX_TRIM_BONE_WEIGHTS"],
"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"],
"LOD" : ["EX_LOD_GENERATION", "EX_LOD_LEVELS", "EX_LOD_DISTANCE", "EX_LOD_PERCENT"],
"Shape Animation" : ["EX_SHAPE_ANIMATIONS", "EX_SHAPE_NORMALS"],
"Logging" : ["EX_Vx_ENABLE_LOGGING", "EX_Vx_DEBUG_LOGGING"]
}
Expand Down Expand Up @@ -397,6 +397,11 @@ def execute(self, context):
default=config.get('OPTIMISE_VERTEX_BUFFERS_OPTIONS')) = {}

# LOD
EX_LOD_GENERATION : EnumProperty(
items=config.LOD_METHODS,
name='LOD Generation Method',
description='Method of generating LOD levels',
default=config.get('LOD_METHODS')) = {}
EX_LOD_LEVELS : IntProperty(
name="LOD Levels",
description="Number of LOD levels",
Expand All @@ -412,12 +417,6 @@ def execute(self, context):
description="LOD percentage reduction",
min=0, max=99,
default=config.get('LOD_PERCENT')) = {}
EX_LOD_MESH_TOOLS : BoolProperty(
name="Use OgreMesh Tools",
description="""Use OgreMeshUpgrader/OgreMeshTool instead of Blender to generate the mesh LODs.
OgreMeshUpgrader/OgreMeshTool does LOD by removing edges, which allows only changing the index buffer and re-use the vertex-buffer (storage efficient).
Blenders decimate does LOD by collapsing vertices, which can result in a visually better LOD, but needs different vertex-buffers per LOD.""",
default=config.get('LOD_MESH_TOOLS')) = {}

# Pose Animation
EX_SHAPE_ANIMATIONS : BoolProperty(
Expand Down
12 changes: 6 additions & 6 deletions io_ogre/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ def mesh_upgrade_tool(infile):
if not os.path.exists(infile):
logger.warn("Cannot find file mesh file: %s, unable run OgreMeshUpgrader" % filename)

if config.get('LOD_MESH_TOOLS') == True:
if config.get('LOD_GENERATION') == '0':
Report.warnings.append("OgreMeshUpgrader failed, LODs will not be generated for this mesh: %s" % filename)

if config.get('GENERATE_EDGE_LISTS') == True:
Expand All @@ -107,7 +107,7 @@ def mesh_upgrade_tool(infile):

cmd = [exe]

if config.get('LOD_LEVELS') > 0 and config.get('LOD_MESH_TOOLS') == True:
if config.get('LOD_LEVELS') > 0 and config.get('LOD_GENERATION') == '0':
cmd.append('-l')
cmd.append(str(config.get('LOD_LEVELS')))

Expand All @@ -134,7 +134,7 @@ def mesh_upgrade_tool(infile):
# Finally, specify input file
cmd.append(infile)

if config.get('LOD_LEVELS') > 0 and config.get('LOD_MESH_TOOLS') == True:
if config.get('LOD_LEVELS') > 0 and config.get('LOD_GENERATION') == '0':
logger.info("* Generating %s LOD levels for mesh: %s" % (config.get('LOD_LEVELS'), filename))

if config.get('GENERATE_EDGE_LISTS') == True:
Expand All @@ -153,7 +153,7 @@ def mesh_upgrade_tool(infile):
if proc.returncode != 0:
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:
if config.get('LOD_LEVELS') > 0 and config.get('LOD_GENERATION') == '0':
Report.warnings.append("OgreMeshUpgrader failed, LODs will not be generated for this mesh: %s" % filename)

if config.get('GENERATE_EDGE_LISTS') == True:
Expand All @@ -163,7 +163,7 @@ def mesh_upgrade_tool(infile):
logger.error(error)
logger.warn(output)
else:
if config.get('LOD_LEVELS') > 0 and config.get('LOD_MESH_TOOLS') == True:
if config.get('LOD_LEVELS') > 0 and config.get('LOD_GENERATION') == '0':
logger.info("- Generated %s LOD levels for mesh: %s" % (config.get('LOD_LEVELS'), filename))

if config.get('GENERATE_EDGE_LISTS') == True:
Expand Down Expand Up @@ -341,7 +341,7 @@ def xml_convert(infile, has_uvs=False):
cmd.append('-%s' %config.get('MESH_TOOL_VERSION'))

# If requested by the user, generate LOD levels through OgreMeshUpgrader/OgreMeshTool
if config.get('LOD_LEVELS') > 0 and config.get('LOD_MESH_TOOLS') == True:
if config.get('LOD_LEVELS') > 0 and config.get('LOD_GENERATION') == '0':
cmd.append('-l')
cmd.append(str(config.get('LOD_LEVELS')))

Expand Down

0 comments on commit 7dcb527

Please sign in to comment.