Utility_Apps/Blender/simple scripts/Link Multiple Assets into a File/Link Multiple Assets into a File.py
2025-07-11 08:27:54 -05:00

125 lines
3.7 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import bpy
import os
import math
import re
from mathutils import Vector
# --- Settings ---
source_dir = r"D:\Dropbox\Gitea_OD\Entourage\People\WIP - Lowpoly People - 2"
spacing = 2.0
# --- Helpers ---
def extract_base_name(name):
# if re.fullmatch(r".+\.\d{3}", name): # e.g. Tree.001
# return name[:-4]
# if re.fullmatch(r".+\d{3}", name): # e.g. Tree001
# return re.sub(r"\d{3}$", "", name)
return name
def get_collection_bounds(collection):
depsgraph = bpy.context.evaluated_depsgraph_get()
points = []
for obj in collection.objects:
obj_eval = obj.evaluated_get(depsgraph)
points += [obj_eval.matrix_world @ Vector(v) for v in obj_eval.bound_box]
if not points:
return 0, 0, 0
x_vals = [v.x for v in points]
y_vals = [v.y for v in points]
z_vals = [v.z for v in points]
width = max(x_vals) - min(x_vals)
depth = max(y_vals) - min(y_vals)
height = max(z_vals) - min(z_vals)
return width, depth, height
# --- Main ---
collection_instances = []
global_existing_names = set()
print(f"\n🔍 Searching for .blend files in: {source_dir}\n")
for filename in sorted(os.listdir(source_dir)):
if not filename.endswith(".blend"):
continue
blend_path = os.path.join(source_dir, filename)
blend_base = os.path.splitext(filename)[0]
print(f"\n📄 Processing file: {filename}")
# Load all objects from file
with bpy.data.libraries.load(blend_path, link=True) as (data_from, data_to):
base_name_seen = set()
unique_object_names = []
for name in data_from.objects:
base = extract_base_name(name)
if base in base_name_seen:
print(f" ⛔ Skipping variant: {name} (base: {base})")
continue
if name in global_existing_names:
print(f" 🔁 Already linked globally: {name}")
continue
base_name_seen.add(base)
unique_object_names.append(name)
data_to.objects = unique_object_names
if not data_to.objects:
print(f"⚠️ No unique objects to import from {filename}")
continue
# Create a collection and instance per object
for obj in data_to.objects:
if not obj:
continue
obj_name = obj.name
col_name = f"COL_{blend_base}_{obj_name}"
inst_name = f"Instance_{blend_base}_{obj_name}"
# Create and link the collection
new_col = bpy.data.collections.new(col_name)
bpy.context.scene.collection.children.link(new_col)
new_col.objects.link(obj)
global_existing_names.add(obj.name)
print(f" Imported object: {obj.name}{col_name}")
# Create instance of the collection
instance_obj = bpy.data.objects.new(name=inst_name, object_data=None)
instance_obj.instance_type = 'COLLECTION'
instance_obj.instance_collection = new_col
bpy.context.collection.objects.link(instance_obj)
collection_instances.append((instance_obj, new_col))
print(f" 🔗 Created instance: {inst_name}")
# --- Arrange in Grid ---
print("\n📐 Arranging collection instances in grid...\n")
cols = math.ceil(math.sqrt(len(collection_instances)))
x_cursor = 0
y_cursor = 0
max_row_depth = 0
col = 0
for instance_obj, collection in collection_instances:
width, depth, _ = get_collection_bounds(collection)
instance_obj.location = (x_cursor, y_cursor, 0)
print(f"📍 Placing '{instance_obj.name}' at (X: {x_cursor:.2f}, Y: {y_cursor:.2f})")
x_cursor += width + spacing
max_row_depth = max(max_row_depth, depth)
col += 1
if col >= cols:
col = 0
x_cursor = 0
y_cursor += max_row_depth + spacing
max_row_depth = 0
print("\n✅ Done.")