Skip to content

Commit e72ac3d

Browse files
committed
Properly export tilesheet meshes
1 parent 6bf2fef commit e72ac3d

1 file changed

Lines changed: 105 additions & 0 deletions

File tree

armory/blender/arm/exporter.py

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -557,6 +557,108 @@ def export_material_ref(self, bobject: bpy.types.Object, material, index, o):
557557
self.material_array.append(material)
558558
o['material_refs'].append(arm.utils.asset_name(material))
559559

560+
def collect_tilesheet_meshes(self, scene: bpy.types.Scene):
561+
"""Collects meshes and materials referenced by tilesheet actions.
562+
563+
When a tilesheet action references a mesh for swapping, that mesh and its
564+
materials need to be exported even though they're not directly in the scene.
565+
"""
566+
print(f"[Tilesheet] Collecting meshes from scene: {scene.name}")
567+
568+
# Collect all objects including those inside collection instances
569+
objects_to_check = list(scene.collection.all_objects.values())
570+
571+
# Also check inside collection instances (for linked collections)
572+
for bobject in scene.collection.all_objects.values():
573+
if bobject.instance_type == 'COLLECTION' and bobject.instance_collection is not None:
574+
for cobj in bobject.instance_collection.all_objects:
575+
if cobj not in objects_to_check:
576+
objects_to_check.append(cobj)
577+
578+
for bobject in objects_to_check:
579+
if bobject.type != 'MESH':
580+
continue
581+
if not bobject.arm_tilesheet_enabled:
582+
continue
583+
584+
print(f"[Tilesheet] Found tilesheet object: {bobject.name}")
585+
print(f"[Tilesheet] Actions count: {len(bobject.arm_tilesheet_actionlist)}")
586+
587+
# Get the library path if this object's mesh is linked
588+
library_path = None
589+
if bobject.data and bobject.data.library:
590+
library_path = bobject.data.library.filepath
591+
print(f"[Tilesheet] Library path: {library_path}")
592+
593+
for action in bobject.arm_tilesheet_actionlist:
594+
print(f"[Tilesheet] Action: {action.name}, mesh_prop: '{action.mesh_prop}'")
595+
if action.mesh_prop == '':
596+
continue
597+
598+
# Look up the mesh in bpy.data.meshes
599+
mesh_data = bpy.data.meshes.get(action.mesh_prop)
600+
601+
# If mesh not found and we have a library path, try to link it
602+
if mesh_data is None and library_path is not None:
603+
print(f"[Tilesheet] Mesh '{action.mesh_prop}' not in scene, linking from library...")
604+
mesh_data = self.link_mesh_from_library(library_path, action.mesh_prop)
605+
606+
print(f"[Tilesheet] Mesh lookup result: {mesh_data}")
607+
if mesh_data is None:
608+
print(f"[Tilesheet] WARNING: Mesh '{action.mesh_prop}' not found!")
609+
continue
610+
611+
# Add mesh to mesh_array if not already present
612+
if mesh_data not in self.mesh_array:
613+
struct_name = arm.utils.asset_name(mesh_data)
614+
self.mesh_array[mesh_data] = {"structName": struct_name, "objectTable": [bobject]}
615+
print(f"[Tilesheet] Added mesh to export: {struct_name}")
616+
617+
# Collect materials from the mesh
618+
for mat in mesh_data.materials:
619+
if mat is None:
620+
continue
621+
# Create tilesheet variant if needed
622+
if mat.library is not None:
623+
# For linked materials, set the flag directly
624+
mat.arm_tilesheet_flag = True
625+
mat.arm_cached = False
626+
if mat not in self.material_array:
627+
self.material_array.append(mat)
628+
print(f"[Tilesheet] Added linked material: {mat.name}")
629+
else:
630+
# For local materials, create a variant with _armtile suffix
631+
variant_name = mat.name + '_armtile'
632+
variant_mat = bpy.data.materials.get(variant_name)
633+
if variant_mat is None:
634+
variant_mat = mat.copy()
635+
variant_mat.name = variant_name
636+
variant_mat.arm_tilesheet_flag = True
637+
if variant_mat not in self.material_array:
638+
self.material_array.append(variant_mat)
639+
print(f"[Tilesheet] Added material variant: {variant_mat.name}")
640+
641+
def link_mesh_from_library(self, library_path: str, mesh_name: str):
642+
"""Links a mesh from an external library file.
643+
644+
Returns the linked mesh data or None if not found.
645+
"""
646+
try:
647+
# Use link instead of append to maintain the library reference
648+
with bpy.data.libraries.load(library_path, link=True) as (data_from, data_to):
649+
if mesh_name in data_from.meshes:
650+
data_to.meshes = [mesh_name]
651+
print(f"[Tilesheet] Linking mesh '{mesh_name}' from {library_path}")
652+
else:
653+
print(f"[Tilesheet] Mesh '{mesh_name}' not found in library. Available: {data_from.meshes[:10]}")
654+
return None
655+
656+
# Return the newly linked mesh
657+
return bpy.data.meshes.get(mesh_name)
658+
except Exception as e:
659+
print(f"[Tilesheet] Error linking mesh: {e}")
660+
return None
661+
560662
def export_particle_system_ref(self, psys: bpy.types.ParticleSystem, out_object):
561663
if psys.settings.instance_object is None or psys.settings.render_type != 'OBJECT' or not psys.settings.instance_object.arm_export:
562664
return
@@ -2772,6 +2874,9 @@ def execute(self):
27722874
else:
27732875
self.output['gravity'] = [0.0, 0.0, 0.0]
27742876

2877+
# Collect meshes and materials referenced by tilesheet actions
2878+
self.collect_tilesheet_meshes(self.scene)
2879+
27752880
self.export_objects(self.scene)
27762881

27772882
# Create Viewport camera

0 commit comments

Comments
 (0)