Files

139 lines
4.6 KiB
Python

import os
import sys
import bpy
def split_mesh_by_texture(input_filepath, output_dir):
print(f"[*] Loading {input_filepath}...")
# 1. Clear background scene
bpy.ops.wm.read_factory_settings(use_empty=True)
# 2. Import
ext = os.path.splitext(input_filepath)[1].lower()
if ext in ['.glb', '.gltf']:
bpy.ops.import_scene.gltf(filepath=input_filepath)
elif ext == '.obj':
bpy.ops.wm.obj_import(filepath=input_filepath)
else:
print(f"Error: Unsupported extension '{ext}'. Need .glb or .obj")
sys.exit(1)
meshes = [o for o in bpy.data.objects if o.type == 'MESH']
if not meshes:
print("Error: No mesh geometry found inside the file.")
sys.exit(1)
obj = meshes[0]
bpy.context.view_layer.objects.active = obj
obj.select_set(True)
mesh = obj.data
uv_layer = mesh.uv_layers.active
if not uv_layer:
print("Error: Model has no UV mapping.")
sys.exit(1)
mat = obj.active_material
if not mat or not mat.use_nodes:
print("Error: Model has no active Material with an image texture.")
sys.exit(1)
img_node = next((n for n in mat.node_tree.nodes if n.type == 'TEX_IMAGE' and getattr(n, 'image', None)), None)
if not img_node:
print("Error: No Image Texture found baked into the material.")
sys.exit(1)
img = img_node.image
width, height = img.size
print(f"[*] Found baked texture map: {width}x{height}")
# Fast pixel extraction
pixels = [0.0] * (width * height * 4)
img.pixels.foreach_get(pixels)
print("[*] Analyzing face colors and mathematically generating bounds...")
bpy.ops.object.mode_set(mode='OBJECT')
unique_colors = {}
mesh.materials.clear()
for poly in mesh.polygons:
# Centroid sampling
u_sum = v_sum = 0.0
for loop_idx in poly.loop_indices:
uv = uv_layer.data[loop_idx].uv
u_sum += uv.x
v_sum += uv.y
u_center = u_sum / len(poly.loop_indices)
v_center = v_sum / len(poly.loop_indices)
u = u_center % 1.0
v = v_center % 1.0
x = min(int(u * width), width - 1)
y = min(int(v * height), height - 1)
idx = (y * width + x) * 4
# Color crushing (1.0 = primary basic colors)
steps = 1.0
r = round(pixels[idx] * steps) / steps
g = round(pixels[idx+1] * steps) / steps
b = round(pixels[idx+2] * steps) / steps
color_key = (r, g, b)
if color_key not in unique_colors:
mat_name = f"Segment_{len(unique_colors) + 1}"
new_mat = bpy.data.materials.new(name=mat_name)
new_mat.use_nodes = True
if new_mat.node_tree:
bsdf = new_mat.node_tree.nodes.get("Principled BSDF")
if bsdf:
bsdf.inputs['Base Color'].default_value = (r, g, b, 1.0)
mesh.materials.append(new_mat)
unique_colors[color_key] = len(mesh.materials) - 1
poly.material_index = unique_colors[color_key]
print(f"[*] Identified {len(unique_colors)} solid color groups.")
print("[*] Slicing mesh geometry...")
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.separate(type='MATERIAL')
bpy.ops.object.mode_set(mode='OBJECT')
# 4. EXPORTING
os.makedirs(output_dir, exist_ok=True)
basename = os.path.splitext(os.path.basename(input_filepath))[0]
# Find all the new split pieces in the scene
separated = [o for o in bpy.context.scene.objects if o.type == 'MESH']
print(f"[*] Successfully sliced into {len(separated)} pieces. Exporting independent files...")
for i, part in enumerate(separated):
bpy.ops.object.select_all(action='DESELECT')
part.select_set(True)
bpy.context.view_layer.objects.active = part
out_path = os.path.join(output_dir, f"{basename}_part_{i+1}.glb")
bpy.ops.export_scene.gltf(
filepath=out_path,
use_selection=True,
export_materials='EXPORT',
export_format='GLB'
)
print(f" -> Saved: {out_path}")
print(f"[*] FINISHED! All pieces saved to folder: {os.path.abspath(output_dir)}")
if __name__ == "__main__":
if len(sys.argv) < 3:
print("Usage: python headless_split.py <input_mesh.glb> <output_folder_path>")
sys.exit(1)
split_mesh_by_texture(sys.argv[1], sys.argv[2])