Skip to content

Commit 7303b6b

Browse files
fix problem with inconsistent scaling, added units enum to interface with M or CM options.
1 parent c4bcdff commit 7303b6b

File tree

4 files changed

+130
-30
lines changed

4 files changed

+130
-30
lines changed

export_indicators.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,8 @@ def update_all_export_statuses():
316316
continue
317317

318318
elapsed_time = current_time - export_time
319-
old_status_val = obj.get(EXPORT_STATUS_PROP, ExportStatus.NONE.value)
319+
old_status_val = obj.get(EXPORT_STATUS_PROP,
320+
ExportStatus.NONE.value)
320321
old_status_name = (ExportStatus(old_status_val).name
321322
if isinstance(old_status_val, int)
322323
else "UNKNOWN")
@@ -408,7 +409,11 @@ def update_timer_callback():
408409
try:
409410
# Force update for all area types
410411
area.tag_redraw()
411-
except:
412+
except Exception as e:
413+
# Log potential errors during redraw
414+
# without stopping timer
415+
logger.debug(f"Error redrawing area "
416+
f"{area.type}: {e}")
412417
pass
413418
else:
414419
logger.warning("Timer callback couldn't redraw: "
@@ -459,7 +464,8 @@ def execute(self, context):
459464
# Trigger redraw after clearing
460465
if context and context.window_manager:
461466
for window in context.window_manager.windows:
462-
if not window.screen: continue
467+
if not window.screen:
468+
continue
463469
for area in window.screen.areas:
464470
try:
465471
area.tag_redraw()

operators.py

Lines changed: 94 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -115,17 +115,53 @@ def create_export_copy(original_obj, context):
115115
if not original_obj or original_obj.type != "MESH":
116116
raise ValueError("Invalid object provided for copying.")
117117

118-
try:
119-
# Direct API calls - no mode switching needed
120-
copy_obj = original_obj.copy()
121-
copy_obj.data = original_obj.data.copy()
122-
context.collection.objects.link(copy_obj)
123-
logger.info(f"Created copy: {copy_obj.name} from {original_obj.name}")
124-
return copy_obj
125-
except Exception as e:
126-
raise RuntimeError(
127-
f"Failed to create copy of {original_obj.name}: {e}"
128-
) from e
118+
logger.info(f"Attempting to duplicate '{original_obj.name}' using operator...")
119+
120+
# Use the context manager to handle selection and active object
121+
with temp_selection_context(context,
122+
active_object=original_obj,
123+
selected_objects=[original_obj]):
124+
try:
125+
# Duplicate the active object (linked)
126+
# more direct and conventional way to perform a non-interactive
127+
# linked duplication than bpy.ops.object.duplicate(linked=True)
128+
bpy.ops.object.duplicate_move_linked(
129+
OBJECT_OT_duplicate={"linked":True, "mode":'TRANSLATION'},
130+
TRANSFORM_OT_translate={"value":(0, 0, 0)} # No actual move
131+
)
132+
133+
# The new duplicate becomes the active object
134+
# after the operator runs
135+
copy_obj = context.view_layer.objects.active
136+
logger.info(f"Successfully created duplicate "
137+
f"'{copy_obj.name}' via operator.")
138+
139+
# Make Mesh Data Single User
140+
if copy_obj and copy_obj.data and copy_obj.data.users > 1:
141+
logger.info(f"Making mesh data single user for "
142+
f"'{copy_obj.name}'")
143+
copy_obj.data = copy_obj.data.copy()
144+
145+
return copy_obj
146+
147+
except Exception as e:
148+
logger.error(f"Error using duplicate_move_linked operator for "
149+
f"'{original_obj.name}': {e}", exc_info=True)
150+
# Attempt cleanup if copy_obj was created but failed later
151+
copy_obj_ref = None
152+
try:
153+
copy_obj_ref = context.view_layer.objects.active
154+
if copy_obj_ref and copy_obj_ref != original_obj:
155+
logger.info(f"Attempting cleanup of partially created "
156+
f"copy: {copy_obj_ref.name}")
157+
bpy.data.objects.remove(copy_obj_ref, do_unlink=True)
158+
except Exception as cleanup_e:
159+
logger.warning(f"Issue during cleanup after copy "
160+
f"failure: {cleanup_e}")
161+
162+
raise RuntimeError(
163+
f"Failed to create operator copy of {original_obj.name}: {e}"
164+
) from e
129165

130166

131167
def sanitise_filename(name):
@@ -182,10 +218,48 @@ def setup_export_object(obj, original_obj_name, scene_props, lod_level=None):
182218
obj.name = final_name
183219
logger.info(f"Renamed to: {obj.name}")
184220

221+
# Check if object's scale is already 1.0
222+
if obj.scale != (1.0, 1.0, 1.0):
223+
# Apply scale transform if not 1.0
224+
logger.info(f"Object scale is not 1.0: {obj.scale}, applying...")
225+
with bpy.context.temp_override(
226+
selected_editable_objects=[obj],
227+
selected_objects=[obj], active_object=obj):
228+
bpy.ops.object.transform_apply(location=False,
229+
rotation=False,
230+
scale=True)
231+
232+
# Zero location if specified in scene properties
185233
if scene_props.mesh_export_zero_location:
186234
obj.location = (0.0, 0.0, 0.0)
187235
logger.info(f"Zeroed location for {obj.name}")
188236

237+
# Calculate final scale factor
238+
final_scale_factor = scene_props.mesh_export_scale
239+
if scene_props.mesh_export_units == "CENTIMETERS":
240+
# Apply 100x scale for Meters (Blender default)
241+
# to Centimeters (UE default)
242+
final_scale_factor *= 100.0
243+
logger.info("Applying M to CM scale factor (x100)")
244+
245+
# Set the object's scale
246+
if abs(final_scale_factor - 1.0) > 1e-6: # Check if scaling is needed
247+
obj.scale = (final_scale_factor,
248+
final_scale_factor,
249+
final_scale_factor)
250+
logger.info(f"Set object scale to {final_scale_factor:.2f}")
251+
else:
252+
logger.info(f"Final scale factor is 1.0. No scaling needed.")
253+
254+
# Apply the calculated scale transform using temp_override
255+
with bpy.context.temp_override(
256+
selected_editable_objects=[obj],
257+
selected_objects=[obj], active_object=obj):
258+
bpy.ops.object.transform_apply(location=False,
259+
rotation=True,
260+
scale=True)
261+
logger.info(f"Applied final scale transform for {obj.name}")
262+
189263
return obj.name, base_name
190264
except Exception as e:
191265
raise RuntimeError(
@@ -428,7 +502,8 @@ def restore_original_textures(obj):
428502
bpy.data.images.remove(img)
429503

430504

431-
def apply_decimate_modifier(obj, ratio, decimate_type, sym_axis="X", sym=False):
505+
def apply_decimate_modifier(obj, ratio, decimate_type,
506+
sym_axis="X", sym=False):
432507
"""
433508
Adds, configures, and applies a Decimate modifier.
434509
@@ -476,7 +551,8 @@ def apply_decimate_modifier(obj, ratio, decimate_type, sym_axis="X", sym=False):
476551
# Ensure axis is valid before setting
477552
axis_upper = sym_axis.upper()
478553
if axis_upper not in {'X', 'Y', 'Z'}:
479-
logger.error(f"Invalid symmetry axis value received: {sym_axis}. Aborting modifier application.")
554+
logger.error(f"Invalid symmetry axis value received: "
555+
f"{sym_axis}. Aborting modifier application.")
480556
obj.modifiers.remove(dec_mod) # Clean up modifier
481557
raise ValueError(f"Invalid symmetry axis: {sym_axis}")
482558

@@ -604,7 +680,7 @@ def export_object(obj, file_path, scene_props):
604680
# logger.info(f"[[quality: {export_quality}]]")
605681
# logger.info(f"[[downscale: {downscale_size}]]")
606682

607-
logger.info(f"File path: {file_path}")
683+
# logger.info(f"File path: {file_path}")
608684
logger.info(
609685
f"Exporting {os.path.basename(export_filepath)} ({fmt})..."
610686
)
@@ -616,9 +692,10 @@ def export_object(obj, file_path, scene_props):
616692
bpy.ops.export_scene.fbx(
617693
filepath=export_filepath,
618694
use_selection=True,
619-
global_scale=scene_props.mesh_export_scale,
695+
global_scale=1.0, # Scale applied setup_export_object
620696
axis_forward=scene_props.mesh_export_coord_forward,
621697
axis_up=scene_props.mesh_export_coord_up,
698+
apply_unit_scale=False,
622699
apply_scale_options="FBX_SCALE_ALL",
623700
object_types={"MESH"},
624701
path_mode="COPY",
@@ -630,7 +707,7 @@ def export_object(obj, file_path, scene_props):
630707
bpy.ops.wm.obj_export(
631708
filepath=export_filepath,
632709
export_selected_objects=True,
633-
global_scale=scene_props.mesh_export_scale,
710+
global_scale=1.0, # Scale applied setup_export_object
634711
forward_axis=scene_props.mesh_export_coord_forward,
635712
up_axis=scene_props.mesh_export_coord_up,
636713
export_materials=True,
@@ -675,7 +752,7 @@ def export_object(obj, file_path, scene_props):
675752
bpy.ops.wm.stl_export(
676753
filepath=export_filepath,
677754
export_selected_objects=True,
678-
global_scale=scene_props.mesh_export_scale,
755+
global_scale=1.0, # Scale applied setup_export_object
679756
forward_axis=scene_props.mesh_export_coord_forward,
680757
up_axis=scene_props.mesh_export_coord_up,
681758
apply_modifiers=False, # Handled by apply_mesh_modifiers

panels.py

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ def draw(self, context):
4141
layout = self.layout
4242

4343
# --- Debugging Start ---
44-
logger.info("--- Drawing MESH_PT_exporter_panel ---")
44+
# logger.info("--- Drawing MESH_PT_exporter_panel ---")
4545
if not hasattr(context.scene, "mesh_exporter"):
4646
logger.error("context.scene has no 'mesh_exporter' attribute!")
4747
layout.label(text="Error: Property group not registered?")
@@ -54,13 +54,13 @@ def draw(self, context):
5454
layout.label(text="Error: Property group is None?")
5555
return # Stop drawing if the group is None
5656

57-
logger.info(f"Settings object: {settings}")
58-
try:
57+
# logger.info(f"Settings object: {settings}")
58+
# try:
5959
# Try accessing a property directly
60-
path_value = settings.mesh_export_path
61-
logger.info(f"Value of mesh_export_path: {path_value}")
62-
except AttributeError:
63-
logger.error("Could not access settings.mesh_export_path!")
60+
# path_value = settings.mesh_export_path
61+
# logger.info(f"Value of mesh_export_path: {path_value}")
62+
# except AttributeError:
63+
# logger.error("Could not access settings.mesh_export_path!")
6464
# --- Debugging End ---
6565

6666
layout.use_property_split = True
@@ -79,10 +79,16 @@ def draw(self, context):
7979
row.prop(settings, "mesh_export_coord_forward", expand=True)
8080

8181
# Scale settings
82-
if self.format_has_scale(settings.mesh_export_format):
83-
col = layout.column(heading="Scale", align=True)
84-
col.prop(settings, "mesh_export_scale")
82+
# if self.format_has_scale(settings.mesh_export_format):
83+
col = layout.column(heading="Scale", align=True)
84+
col.prop(settings, "mesh_export_scale")
85+
86+
# Units settings
87+
col = layout.column(heading="Units", align=True)
88+
row = col.row(align=True)
89+
row.prop(settings, "mesh_export_units", expand=True)
8590

91+
# Zero location settings
8692
col = layout.column(align=True)
8793
col.prop(settings, "mesh_export_zero_location")
8894

properties.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,17 @@ class MeshExporterSettings(PropertyGroup):
4747
soft_min=0.01,
4848
soft_max=100.0
4949
)
50+
51+
# Units property
52+
mesh_export_units: EnumProperty(
53+
name="Units",
54+
description="Unit system for exported meshes",
55+
items=[
56+
("METERS", "M", "Use meters as unit"),
57+
("CENTIMETERS", "CM", "Use centimeters as unit"),
58+
],
59+
default="METERS",
60+
)
5061

5162
# Coordinate system properties
5263
mesh_export_coord_up: EnumProperty(

0 commit comments

Comments
 (0)