-
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathproperties.py
More file actions
608 lines (530 loc) · 18.8 KB
/
properties.py
File metadata and controls
608 lines (530 loc) · 18.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
# properties.py
"""
This module defines properties for exporting meshes in Blender.
It includes properties for export path, format, scale, coordinate system,
triangulation, LOD generation, and more.
"""
import bpy
from bpy.props import (
StringProperty,
EnumProperty,
FloatProperty,
IntProperty,
BoolProperty,
PointerProperty,
)
from bpy.types import PropertyGroup
# Module-level cache for preset enum items
# This prevents expensive file I/O on every UI draw/hover event
_preset_items_cache = None
def refresh_preset_items_cache():
"""Refresh the cached preset enum items.
This should be called after any operation that changes the preset list
(save, load, delete, rename).
Note:
Updates the module-level _preset_items_cache variable.
"""
global _preset_items_cache
from . import operators
items = []
try:
all_presets = operators.get_all_preset_names()
for i, preset_name in enumerate(all_presets):
icon = operators.get_preset_icon(preset_name)
desc = operators.get_preset_description(preset_name) or preset_name
items.append((preset_name, preset_name, desc, icon, i))
except Exception as e:
import logging
logger = logging.getLogger(__name__)
logger.warning(f"Failed to refresh preset items cache: {e}")
if not items:
items.append(("NONE", "No Presets", "No presets available", "SETTINGS", 0))
_preset_items_cache = items
def clear_indicators_if_disabled(self, context):
"""Callback to clear all indicators when the checkbox is unchecked.
Args:
self: The property group instance containing mesh_export_show_indicators
context: The current Blender context
Note:
Silently fails if the clear operator hasn't been registered yet during addon startup.
"""
if not self.mesh_export_show_indicators:
# Clear all export indicators
try:
bpy.ops.mesh.clear_export_indicators()
except (AttributeError, RuntimeError):
# Operator might not be registered yet during addon startup
pass
def get_preset_items(self, context):
"""Generate enum items from available presets.
Returns:
List of tuples (identifier, name, description, icon, number)
Note:
This is called dynamically to populate the preset selector enum.
Uses cached items to avoid expensive file I/O on every draw/hover event.
"""
global _preset_items_cache
# If cache is empty, refresh it
if _preset_items_cache is None:
refresh_preset_items_cache()
# Return cached items (always returns a valid list)
return (
_preset_items_cache
if _preset_items_cache
else [("NONE", "No Presets", "No presets available", "SETTINGS", 0)]
)
def load_preset_on_change(self, context):
"""Load preset when enum selection changes.
Args:
self: The property group instance
context: The current Blender context
Note:
Auto-loads the selected preset when user clicks a different preset button.
"""
if self.mesh_export_preset_selector and self.mesh_export_preset_selector != "NONE":
# Only load if different from current
if self.mesh_export_preset_selector != self.mesh_export_current_preset:
try:
bpy.ops.mesh.load_export_preset(
preset_name=self.mesh_export_preset_selector
)
except (AttributeError, RuntimeError) as e:
import logging
logger = logging.getLogger(__name__)
logger.warning(f"Failed to auto-load preset: {e}")
class MeshExporterSettings(PropertyGroup):
# Export path property
# Default to the current blend file directory
# with a subfolder "exported_meshes"
mesh_export_path: StringProperty(
name="Export Path",
description="Path to export meshes",
default="//exported_meshes/",
subtype="DIR_PATH",
)
# Export format property
mesh_export_format: EnumProperty(
name="Format",
description="File format to export meshes",
items=[
("FBX", "FBX", "Export as FBX"),
("OBJ", "OBJ", "Export as OBJ"),
("GLTF", "glTF", "Export as glTF"),
("USD", "USD", "Export as USD"),
("STL", "STL", "Export as STL"),
],
default="FBX",
)
# GLTF type property
mesh_export_gltf_type: EnumProperty(
name="glTF Type",
description="Type of glTF to export",
items=[
("GLB", "Binary", "Export as binary glTF (GLB)"),
("GLTF_SEPARATE", "JSON", "Export as JSON glTF (GLTF)"),
],
default="GLB",
)
# GLTF export materials property
mesh_export_gltf_materials: EnumProperty(
name="Export Materials",
description="Export materials with glTF",
items=[
("EXPORT", "Export", "Export all materials used by included objects"),
(
"PLACEHOLDER",
"Placeholder",
"Do not export materials, but write multiple primitive groups per mesh, keeping material slot information",
),
(
"NONE",
"No export",
"Do not export materials, and combine mesh primitive groups, losing material slot information",
),
],
default="EXPORT",
)
# GLTF Draco compression property
mesh_export_use_draco_compression: BoolProperty(
name="Draco Compression",
description="Enable Draco mesh compression for smaller file sizes. Note: Godot has its own runtime compression, so this is typically not needed for Godot imports",
default=False,
)
# GLTF batch export property
mesh_export_gltf_batch_mode: EnumProperty(
name="Batching",
description="Choose how to export multiple selected meshes in glTF format",
items=[
(
"COMBINE",
"Combine",
"Export all selected meshes into a single glTF file (recommended for Godot)",
),
(
"INDIVIDUAL",
"Individual",
"Export each selected mesh as a separate glTF file",
),
],
default="COMBINE",
)
# Scale property
mesh_export_scale: FloatProperty(
name="Scale",
description="Scale factor for exported meshes",
default=1.0,
min=0.001,
max=1000.0,
soft_min=0.01,
soft_max=100.0,
)
# Units property
mesh_export_units: EnumProperty(
name="Units",
description="Unit system for exported meshes",
items=[
("METERS", "m", "Use meters as export unit"),
("CENTIMETERS", "cm", "Use centimeters as export unit"),
],
default="METERS",
)
# Coordinate system properties
mesh_export_coord_up: EnumProperty(
name="Up Axis",
description="Up axis for exported meshes",
items=[
("X", "X", ""),
("Y", "Y", ""),
("Z", "Z", ""),
("-X", "-X", ""),
("-Y", "-Y", ""),
("-Z", "-Z", ""),
],
default="Z",
)
mesh_export_coord_forward: EnumProperty(
name="Forward Axis",
description="Forward axis for exported meshes",
items=[
("X", "X", ""),
("Y", "Y", ""),
("Z", "Z", ""),
("-X", "-X", ""),
("-Y", "-Y", ""),
("-Z", "-Z", ""),
],
default="X",
)
mesh_export_smoothing: EnumProperty(
name="Smoothing",
description="Smoothing method for exported meshes",
items=[
(
"OFF",
"Off",
"Export only normals instead of writing edge or face smoothing data",
),
("FACE", "Face", "Write face smoothing"),
("EDGE", "Edge", "Write edge smoothing"),
("SMOOTH_GROUP", "Smoothing Groups", "Write smoothing groups"),
],
default="FACE",
)
# Zero location property
mesh_export_zero_location: BoolProperty(
name="Zero Location",
description="Zero location of the object copy before export",
default=True,
)
# Triangulate properties
mesh_export_tri: BoolProperty(
name="Triangulate Faces",
description="Convert all faces to triangles on the copy",
default=True,
)
# Triangulate method property
mesh_export_tri_method: EnumProperty(
name="Method",
description="Method used for triangulating quads",
items=[
("BEAUTY", "Beauty", "Triangulate with the best-looking result"),
("FIXED", "Fixed", "Split the quad from the first to third vertices"),
(
"FIXED_ALTERNATE",
"Fixed Alternate",
"Split the quad from the second to fourth vertices",
),
(
"SHORTEST_DIAGONAL",
"Shortest Diagonal",
"Split the quad along the shortest diagonal",
),
],
default="BEAUTY",
)
# Keep normals property
mesh_export_keep_normals: BoolProperty(
name="Keep Normals",
description="Preserve normal vectors during triangulation",
default=True,
)
# Modifier application property
mesh_export_apply_modifiers: EnumProperty(
name="Modifier Mode",
description="Which modifiers to apply during export",
items=[
("NONE", "None", "Don't apply any modifiers to the exported copy"),
("VISIBLE", "Visible", "Apply only modifiers visible in viewport"),
("RENDER", "Render", "Apply only modifiers enabled for rendering"),
],
default="VISIBLE",
)
# Prefix and suffix properties
mesh_export_prefix: StringProperty(
name="Prefix", description="Prefix for exported file names", default=""
)
mesh_export_suffix: StringProperty(
name="Suffix", description="Suffix for exported file names", default=""
)
mesh_export_naming_convention: EnumProperty(
name="Naming Convention",
description="Apply specific naming conventions to exported files",
items=[
("DEFAULT", "Default", "Keep original naming"),
("GODOT", "Godot", "snake_case naming (my_mesh_name)"),
("UNITY", "Unity", "Capitalised words with underscores (My_Mesh_Name)"),
("UNREAL", "Unreal Engine", "PascalCase naming (MyMeshName)"),
],
default="DEFAULT",
)
# Export indicators property
mesh_export_show_indicators: BoolProperty(
name="Show Export Indicators",
description="Display colour indicators in viewport for recently exported objects",
default=True,
update=lambda self, context: clear_indicators_if_disabled(self, context),
)
# LOD properties
mesh_export_lod: BoolProperty(
name="Generate LODs",
description="Generate additional LODs using Decimate (modifies copy)",
default=False,
)
# LOD count property
mesh_export_lod_count: IntProperty(
name="Additional LODs",
description="How many additional LODs to generate (LOD1 to LOD4)",
default=4,
min=1,
max=4, # Max 4 due to 4 ratio properties
)
# LOD symmetry property
mesh_export_lod_symmetry: BoolProperty(
name="Symmetry", description="Use symmetry for LOD generation", default=False
)
# LOD symmetry axis property
mesh_export_lod_symmetry_axis: EnumProperty(
name="Symmetry Axis",
description="Axis of symmetry for LOD generation",
items=[("X", "X", "X axis"), ("Y", "Y", "Y axis"), ("Z", "Z", "Z axis")],
default="X",
)
# LOD type property
# Note: I've excluded "UNSUBDIVIDE" and "DISSOLVE"
mesh_export_lod_type: EnumProperty(
name="Decimation Type",
description="Type of decimation to use for generating LODs",
items=[
("COLLAPSE", "Collapse", "Collapse edges (Ratio)"),
# ("UNSUBDIVIDE", "Unsubdivide", "Unsubdivide (Iterations)"),
# ("DISSOLVE", "Planar", "Dissolve planar faces (Angle Limit)")
],
default="COLLAPSE",
)
# Texture resizing property
mesh_export_resize_textures: BoolProperty(
name="Resize Textures for LODs",
description="Automatically resize textures for LODs",
default=True,
)
# Texture quality property
mesh_export_texture_quality: IntProperty(
name="Texture Compression",
description="Quality for lossy formats like JPEG (0-100). Does not affect PNG or other lossless formats",
default=85,
min=0,
max=100,
subtype="PERCENTAGE",
)
# LOD texture size properties
mesh_export_lod1_texture_size: EnumProperty(
name="LOD1 Texture Size",
description="Maximum texture size for LOD1",
items=[
("8192", "8K", "8192x8192"),
("4096", "4K", "4096x4096"),
("2048", "2K", "2048x2048"),
("1024", "1K", "1024x1024"),
],
default="2048",
)
mesh_export_lod2_texture_size: EnumProperty(
name="LOD2 Texture Size",
description="Maximum texture size for LOD2",
items=[
("4096", "4K", "4096x4096"),
("2048", "2K", "2048x2048"),
("1024", "1K", "1024x1024"),
("512", "512", "512x512"),
],
default="1024",
)
mesh_export_lod3_texture_size: EnumProperty(
name="LOD3 Texture Size",
description="Maximum texture size for LOD3",
items=[
("2048", "2K", "2048x2048"),
("1024", "1K", "1024x1024"),
("512", "512", "512x512"),
("256", "256", "256x256"),
],
default="512",
)
mesh_export_lod4_texture_size: EnumProperty(
name="LOD4 Texture Size",
description="Maximum texture size for LOD4",
items=[
("1024", "1K", "1024x1024"),
("512", "512", "512x512"),
("256", "256", "256x256"),
("128", "128", "128x128"),
],
default="256",
)
# Normal map handling
mesh_export_preserve_normal_maps: BoolProperty(
name="Preserve Normal Map Quality",
description="Keep normal maps at one LOD level higher resolution",
default=True,
)
# Texture embedding property
mesh_export_embed_textures: BoolProperty(
name="Embed Textures",
description="Embed textures in the exported file (recommended for game development)",
default=True,
)
# LOD hierarchy export property (for game engines)
mesh_export_lod_hierarchy: BoolProperty(
name="Export as Hierarchy",
description="Export collection as single mesh with LODs in parent/child structure (Unity/Unreal workflow)",
default=True,
)
# LOD ratio properties
mesh_export_lod_ratio_01: FloatProperty(
name="LOD1 Ratio",
description="Decimate factor for LOD 1",
default=0.75,
min=0.0,
max=1.0,
subtype="FACTOR",
)
mesh_export_lod_ratio_02: FloatProperty(
name="LOD2 Ratio",
description="Decimate factor for LOD 2",
default=0.50,
min=0.0,
max=1.0,
subtype="FACTOR",
)
mesh_export_lod_ratio_03: FloatProperty(
name="LOD3 Ratio",
description="Decimate factor for LOD 3",
default=0.25,
min=0.0,
max=1.0,
subtype="FACTOR",
)
mesh_export_lod_ratio_04: FloatProperty(
name="LOD4 Ratio",
description="Decimate factor for LOD 4",
default=0.10,
min=0.0,
max=1.0,
subtype="FACTOR",
)
# --- Attachment Points & Slot Empties ---
mesh_export_include_empties: BoolProperty(
name="Include Attachment Points",
description="Include empty children of exported objects as attachment points",
default=True,
)
mesh_export_empty_filter: EnumProperty(
name="Empty Filter",
description="Which empty children to include",
items=[
("ALL", "All Empties", "Include all empty children"),
("PREFIXED", "Prefixed Only", "Only empties with specific prefix"),
],
default="ALL",
)
mesh_export_empty_prefix: StringProperty(
name="Empty Prefix",
description="Only include empties starting with this prefix",
default="attach_",
)
mesh_export_create_slots: BoolProperty(
name="Create Slot Empties",
description="Create slot empties marking where child meshes attach",
default=False,
)
mesh_export_slot_prefix: StringProperty(
name="Slot Prefix",
description="Prefix for generated slot empties",
default="slot_",
)
# Preset system properties
mesh_export_current_preset: StringProperty(
name="Current Preset", description="Currently active export preset", default=""
)
mesh_export_preset_modified: BoolProperty(
name="Preset Modified",
description="True if current settings differ from saved preset",
default=False,
)
mesh_export_preset_is_builtin: BoolProperty(
name="Is Built-in Preset",
description="True if current preset is a built-in factory preset",
default=False,
)
mesh_export_preset_selector: EnumProperty(
name="Preset",
description="Select and load a preset",
items=get_preset_items,
update=load_preset_on_change,
)
def register_properties():
"""Register the property group and create the Scene property.
Creates a pointer property on bpy.types.Scene called 'mesh_exporter' that
stores all export settings. Prints verification message to console.
Raises:
Exception: If registration fails, error is caught and printed to console
"""
try:
bpy.utils.register_class(MeshExporterSettings)
bpy.types.Scene.mesh_exporter = PointerProperty(type=MeshExporterSettings)
# Verification
test = bpy.types.Scene.bl_rna.properties.get("mesh_exporter")
if test:
print(f"Successfully registered mesh_exporter: {test}")
else:
print("Failed to register mesh_exporter property")
except Exception as e:
print(f"Error registering properties: {e}")
def unregister_properties():
"""Unregister the property group and remove the Scene property.
Removes the 'mesh_exporter' property from bpy.types.Scene and unregisters
the MeshExporterSettings class. Safe to call even if property doesn't exist.
"""
if hasattr(bpy.types.Scene, "mesh_exporter"):
delattr(bpy.types.Scene, "mesh_exporter")
bpy.utils.unregister_class(MeshExporterSettings)