@@ -31,13 +31,32 @@ def remesh_and_bake_file(input_path, output_path, voxel_size=0.002, export_forma
3131 Export format ('DAE' or 'STL'). Default is 'DAE'.
3232 """
3333 import math
34+ import xml .etree .ElementTree as ET
3435
3536 input_path = Path (input_path )
3637 output_path = Path (output_path )
3738
39+ # Detect original coordinate system from DAE file
40+ original_up_axis = 'Y_UP' # default
41+ if export_format .upper () == 'DAE' and input_path .suffix .lower () == '.dae' :
42+ try :
43+ tree = ET .parse (input_path )
44+ root = tree .getroot ()
45+ # Find up_axis element (handle namespace)
46+ ns = {'collada' : 'http://www.collada.org/2005/11/COLLADASchema' }
47+ up_axis_elem = root .find ('.//collada:up_axis' , ns )
48+ if up_axis_elem is None :
49+ up_axis_elem = root .find ('.//up_axis' )
50+ if up_axis_elem is not None and up_axis_elem .text :
51+ original_up_axis = up_axis_elem .text .strip ()
52+ print (f"Detected original coordinate system: { original_up_axis } " )
53+ except Exception as e :
54+ print (f"Warning: Could not detect coordinate system: { e } " )
55+
3856 clear_scene ()
3957
40- # Import with fix_orientation to maintain Blender's Z-up coordinate system
58+ # Import with fix_orientation to ensure proper handling in Blender
59+ # Blender uses Z-up, so this converts Y-up to Z-up properly
4160 bpy .ops .wm .collada_import (
4261 filepath = str (input_path ),
4362 fix_orientation = True ,
@@ -53,31 +72,73 @@ def remesh_and_bake_file(input_path, output_path, voxel_size=0.002, export_forma
5372
5473 # Build a map of face center positions to colors
5574 face_color_data = []
75+ objects_with_vertex_colors = 0
76+ objects_with_materials = 0
5677
5778 for obj in source_objects :
5879 world_matrix = obj .matrix_world
5980
60- # Get vertex colors if available
61- if obj .data .vertex_colors :
81+ # Try to get colors from vertex colors first, then from materials
82+ has_vertex_colors = obj .data .vertex_colors and len (obj .data .vertex_colors ) > 0
83+
84+ if has_vertex_colors :
85+ objects_with_vertex_colors += 1
6286 vc = obj .data .vertex_colors .active .data
6387
6488 for poly in obj .data .polygons :
65- # Get average color for this face
89+ # Get average color for this face from vertex colors
6690 color_sum = [0.0 , 0.0 , 0.0 , 0.0 ]
6791 for loop_index in poly .loop_indices :
6892 for i in range (4 ):
6993 color_sum [i ] += vc [loop_index ].color [i ]
70-
7194 avg_color = tuple (c / len (poly .loop_indices ) for c in color_sum )
95+ face_color = avg_color [:3 ]
96+
97+ # Get face center in world space
98+ face_center = world_matrix @ poly .center
99+
100+ face_color_data .append ({
101+ 'center' : face_center .copy (),
102+ 'color' : face_color
103+ })
104+ elif obj .data .materials :
105+ objects_with_materials += 1
106+
107+ for poly in obj .data .polygons :
108+ # Get color from material
109+ face_color = (0.5 , 0.5 , 0.5 ) # default fallback
110+
111+ if poly .material_index < len (obj .data .materials ):
112+ mat = obj .data .materials [poly .material_index ]
113+ if mat and mat .use_nodes :
114+ # Get base color from Principled BSDF
115+ bsdf = mat .node_tree .nodes .get ("Principled BSDF" )
116+ if bsdf and 'Base Color' in bsdf .inputs :
117+ base_color = bsdf .inputs ['Base Color' ].default_value
118+ face_color = (base_color [0 ], base_color [1 ], base_color [2 ])
72119
73120 # Get face center in world space
74121 face_center = world_matrix @ poly .center
75122
76123 face_color_data .append ({
77124 'center' : face_center .copy (),
78- 'color' : avg_color [: 3 ]
125+ 'color' : face_color
79126 })
80127
128+ msg = f"Color collection: { objects_with_vertex_colors } objects with vertex colors"
129+ msg += f'{ objects_with_materials } with materials'
130+ msg += f"Collected { len (face_color_data )} face color samples"
131+ print (msg )
132+
133+ # Print color statistics
134+ if face_color_data :
135+ unique_colors = set (data ['color' ] for data in face_color_data )
136+ print (f"Unique colors in source: { len (unique_colors )} " )
137+ # Sample some colors
138+ sample_colors = list (unique_colors )[:5 ]
139+ for i , c in enumerate (sample_colors ):
140+ print (f" Sample { i + 1 } : ({ c [0 ]:.3f} , { c [1 ]:.3f} , { c [2 ]:.3f} )" )
141+
81142 # --- 2. JOIN ALL OBJECTS ---
82143 bpy .ops .object .select_all (action = 'DESELECT' )
83144 for obj in source_objects :
@@ -121,7 +182,8 @@ def remesh_and_bake_file(input_path, output_path, voxel_size=0.002, export_forma
121182 nearest_color = face_color_data [nearest_index ]['color' ]
122183
123184 # Round color to reduce number of materials
124- rounded_color = tuple (round (c * 50 ) / 50 for c in nearest_color )
185+ # Use finer quantization (0.01 steps) to preserve more color detail
186+ rounded_color = tuple (round (c * 100 ) / 100 for c in nearest_color )
125187
126188 # Create material if it doesn't exist
127189 if rounded_color not in color_to_material :
@@ -137,18 +199,28 @@ def remesh_and_bake_file(input_path, output_path, voxel_size=0.002, export_forma
137199 # Assign material to face
138200 poly .material_index = color_to_material [rounded_color ]
139201
202+ print (f"Created { len (color_to_material )} materials from { len (face_color_data )} source colors" )
203+ print ("Material color samples:" )
204+ for i , (color , mat_idx ) in enumerate (list (color_to_material .items ())[:5 ]):
205+ print (f" Material { mat_idx } : ({ color [0 ]:.3f} , { color [1 ]:.3f} , { color [2 ]:.3f} )" )
206+
140207 # Apply smooth shading
141208 if joined_obj .data .polygons :
142209 joined_obj .data .polygons .foreach_set ('use_smooth' , [True ] * len (joined_obj .data .polygons ))
143210
144211 # --- 5. EXPORT ---
145- # Convert from Blender Z-up to Collada Y-up by rotating X-axis -90 degrees
146- joined_obj .rotation_euler [0 ] -= math .pi / 2
147-
148- # Apply transformation to vertices
212+ # Convert back to original coordinate system if needed
213+ if export_format .upper () == 'DAE' and original_up_axis == 'Y_UP' :
214+ # Convert from Blender Z-up back to Y-up
215+ # Rotate -90 degrees around X axis
216+ joined_obj .rotation_euler [0 ] -= math .pi / 2
217+ bpy .context .view_layer .objects .active = joined_obj
218+ joined_obj .select_set (True )
219+ bpy .ops .object .transform_apply (location = False , rotation = True , scale = False )
220+
221+ # Ensure object is selected for export
149222 bpy .context .view_layer .objects .active = joined_obj
150223 joined_obj .select_set (True )
151- bpy .ops .object .transform_apply (location = False , rotation = True , scale = False )
152224
153225 if export_format .upper () == 'DAE' :
154226 bpy .ops .wm .collada_export (
@@ -162,6 +234,25 @@ def remesh_and_bake_file(input_path, output_path, voxel_size=0.002, export_forma
162234 use_object_instantiation = False ,
163235 sort_by_name = False
164236 )
237+
238+ # Fix coordinate system in exported DAE file to match original
239+ # Use text replacement to preserve all XML formatting and content
240+ try :
241+ with open (output_path , 'r' , encoding = 'utf-8' ) as f :
242+ content = f .read ()
243+
244+ # Replace Z_UP with original coordinate system
245+ content = content .replace ('<up_axis>Z_UP</up_axis>' , f'<up_axis>{ original_up_axis } </up_axis>' )
246+ # Also handle namespace prefix variations
247+ content = content .replace (':up_axis>Z_UP</' , f':up_axis>{ original_up_axis } </' )
248+
249+ with open (output_path , 'w' , encoding = 'utf-8' ) as f :
250+ f .write (content )
251+
252+ print (f"Restored coordinate system to: { original_up_axis } " )
253+ except Exception as e :
254+ print (f"Warning: Could not fix coordinate system in output: { e } " )
255+
165256 elif export_format .upper () == 'STL' :
166257 bpy .ops .export_mesh .stl (
167258 filepath = str (output_path ),
0 commit comments