Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 040ea64f86 | |||
| bd154c1575 | |||
| bbc20e2353 | |||
| 716347b497 | |||
| 58283439ab | |||
| 0c29a2ec0a | |||
| 4ec62d4168 | |||
| 8d596823ed | |||
| ccd62e3452 | |||
| 1bd08497e6 | |||
| 3e2ac4b5b6 | |||
| 26927ca6f4 | |||
| 928bc15ff1 | |||
| e410e40060 |
@@ -6,6 +6,9 @@ from ..operations.load_operation import load_operation
|
||||
from ..utils.model_card_utils import (
|
||||
delete_model_card_objects,
|
||||
update_model_card_objects,
|
||||
store_visibility_settings,
|
||||
store_uv_mappings,
|
||||
store_modifier_settings,
|
||||
)
|
||||
|
||||
|
||||
@@ -27,6 +30,10 @@ class SPECKLE_OT_load_model_card(bpy.types.Operator):
|
||||
self.report({"ERROR"}, "Model card not found")
|
||||
return {"CANCELLED"}
|
||||
|
||||
store_visibility_settings(model_card)
|
||||
store_modifier_settings(model_card)
|
||||
store_uv_mappings(model_card)
|
||||
|
||||
delete_model_card_objects(model_card, context)
|
||||
|
||||
# set wm
|
||||
|
||||
@@ -102,7 +102,7 @@ def load_operation(
|
||||
|
||||
traversal_function = create_default_traversal_function()
|
||||
|
||||
root_collection_name = f"{wm.selected_model_name} - {wm.selected_version_id[:8]}"
|
||||
root_collection_name = f"{wm.selected_model_name} - {wm.selected_version_id}"
|
||||
root_collection = bpy.data.collections.new(root_collection_name)
|
||||
context.scene.collection.children.link(root_collection)
|
||||
|
||||
@@ -139,7 +139,7 @@ def load_operation(
|
||||
speckle_root_id = speckle_obj.id
|
||||
|
||||
collection_name = getattr(
|
||||
speckle_obj, "name", f"Collection_{speckle_obj.id[:8]}"
|
||||
speckle_obj, "name", f"Collection_{speckle_obj.id}"
|
||||
)
|
||||
|
||||
parent_id = None
|
||||
@@ -183,6 +183,8 @@ def load_operation(
|
||||
if speckle_root_id and speckle_root_id in collection_hierarchy:
|
||||
collection_hierarchy[speckle_root_id]["blender_collection"] = root_collection
|
||||
converted_objects[speckle_root_id] = root_collection
|
||||
# Add root collection name as key for UV mapping preservation
|
||||
converted_objects[root_collection.name] = root_collection
|
||||
|
||||
# create collections in depth order (skip the root that's already mapped)
|
||||
for coll_id in sorted_collections:
|
||||
@@ -212,6 +214,8 @@ def load_operation(
|
||||
|
||||
coll_info["blender_collection"] = blender_collection
|
||||
converted_objects[coll_id] = blender_collection
|
||||
# Add collection name as key for UV mapping preservation
|
||||
converted_objects[blender_collection.name] = blender_collection
|
||||
|
||||
conversion_count = 0
|
||||
for traversal_item in traversal_function.traverse(version_data):
|
||||
@@ -261,6 +265,8 @@ def load_operation(
|
||||
converted_objects[speckle_obj.id] = blender_obj
|
||||
if hasattr(speckle_obj, "applicationId"):
|
||||
converted_objects[speckle_obj.applicationId] = blender_obj
|
||||
# Add object name as key for UV mapping preservation
|
||||
converted_objects[blender_obj.name] = blender_obj
|
||||
|
||||
if not isinstance(blender_obj, bpy.types.Collection):
|
||||
try:
|
||||
|
||||
@@ -1,13 +1,216 @@
|
||||
import bpy
|
||||
import json
|
||||
from bpy.types import Context
|
||||
from typing import Dict
|
||||
import json
|
||||
from ..utils.property_groups import speckle_model_card
|
||||
|
||||
|
||||
def find_layer_collection(layer_collection, collection_name):
|
||||
"""
|
||||
Recursively find a layer collection by collection name
|
||||
"""
|
||||
if layer_collection.collection.name == collection_name:
|
||||
return layer_collection
|
||||
for child in layer_collection.children:
|
||||
result = find_layer_collection(child, collection_name)
|
||||
if result:
|
||||
return result
|
||||
return None
|
||||
|
||||
|
||||
def get_object_by_application_id(app_id: str):
|
||||
"""
|
||||
Find a Blender object by its applicationId stored in custom property
|
||||
"""
|
||||
if not app_id:
|
||||
return None
|
||||
|
||||
for obj in bpy.data.objects:
|
||||
if "applicationId" in obj and obj["applicationId"] == app_id:
|
||||
return obj
|
||||
return None
|
||||
|
||||
|
||||
def get_objects_by_application_ids(app_ids: list):
|
||||
"""
|
||||
Find multiple Blender objects by their applicationIds
|
||||
"""
|
||||
if not app_ids:
|
||||
return {}
|
||||
|
||||
result = {}
|
||||
for obj in bpy.data.objects:
|
||||
if "applicationId" in obj and obj["applicationId"] in app_ids:
|
||||
result[obj["applicationId"]] = obj
|
||||
return result
|
||||
|
||||
|
||||
def store_visibility_settings(model_card: speckle_model_card):
|
||||
"""
|
||||
Store current visibility settings of model card objects and collections
|
||||
This is used to restore the visibility settings of the loaded objects after loading a new version
|
||||
"""
|
||||
for s_obj in model_card.objects:
|
||||
blender_obj = get_object_by_application_id(s_obj.applicationId)
|
||||
if blender_obj:
|
||||
s_obj.hide_get = blender_obj.hide_get()
|
||||
s_obj.hide_viewport = blender_obj.hide_viewport
|
||||
s_obj.hide_select = blender_obj.hide_select
|
||||
s_obj.hide_render = blender_obj.hide_render
|
||||
|
||||
for s_col in model_card.collections:
|
||||
blender_col = bpy.data.collections.get(s_col.name)
|
||||
if blender_col:
|
||||
# For collections, visibility is controlled through the view layer system
|
||||
view_layer = bpy.context.view_layer
|
||||
if view_layer:
|
||||
# Find the layer collection for this collection
|
||||
layer_col = find_layer_collection(
|
||||
view_layer.layer_collection, blender_col.name
|
||||
)
|
||||
if layer_col:
|
||||
s_col.hide_viewport = layer_col.hide_viewport
|
||||
s_col.hide_select = layer_col.collection.hide_select
|
||||
s_col.hide_render = layer_col.collection.hide_render
|
||||
s_col.exclude_from_view_layer = layer_col.exclude
|
||||
else:
|
||||
s_col.hide_viewport = False
|
||||
s_col.hide_select = False
|
||||
s_col.hide_render = False
|
||||
s_col.exclude_from_view_layer = False
|
||||
|
||||
|
||||
def store_uv_mappings(model_card: speckle_model_card):
|
||||
"""
|
||||
Store current UV mapping data of model card mesh objects
|
||||
This is used to restore the UV mappings after loading a new version
|
||||
"""
|
||||
for s_obj in model_card.objects:
|
||||
blender_obj = get_object_by_application_id(s_obj.applicationId)
|
||||
|
||||
if blender_obj and blender_obj.type == "MESH" and blender_obj.data:
|
||||
mesh = blender_obj.data
|
||||
|
||||
uv_data = {"active_uv_layer": "", "uv_layers": []}
|
||||
|
||||
# Store active UV layer name
|
||||
if mesh.uv_layers.active:
|
||||
uv_data["active_uv_layer"] = mesh.uv_layers.active.name
|
||||
|
||||
# Store UV data for each UV layer
|
||||
for uv_layer in mesh.uv_layers:
|
||||
# Extract UV coordinates for all loops in this layer
|
||||
uv_coords = []
|
||||
for uv_loop in uv_layer.data:
|
||||
uv_coords.extend([uv_loop.uv.x, uv_loop.uv.y])
|
||||
|
||||
uv_data["uv_layers"].append(
|
||||
{"name": uv_layer.name, "uv_coords": uv_coords}
|
||||
)
|
||||
|
||||
# Serialize complete UV data as JSON string
|
||||
s_obj.uv_data_serialized = json.dumps(uv_data)
|
||||
|
||||
|
||||
def restore_uv_mappings(
|
||||
model_card: speckle_model_card,
|
||||
converted_objects: Dict[str, bpy.types.Object | bpy.types.Collection],
|
||||
):
|
||||
"""
|
||||
Restore UV mapping data to reloaded mesh objects
|
||||
"""
|
||||
# First, collect UV mapping data from property groups before they are cleared
|
||||
uv_mapping_data = {}
|
||||
for s_obj in model_card.objects:
|
||||
if s_obj.uv_data_serialized: # Only process objects that have UV data stored
|
||||
try:
|
||||
uv_data = json.loads(s_obj.uv_data_serialized)
|
||||
uv_mapping_data[s_obj.applicationId] = uv_data
|
||||
except (json.JSONDecodeError, ValueError):
|
||||
# Skip invalid UV data
|
||||
continue
|
||||
|
||||
# Now restore UV mappings to the new objects
|
||||
for app_id, uv_data in uv_mapping_data.items():
|
||||
# Find the blender object by applicationId in converted_objects
|
||||
blender_obj = None
|
||||
for obj in converted_objects.values():
|
||||
if isinstance(obj, bpy.types.Object) and obj.get("applicationId") == app_id:
|
||||
blender_obj = obj
|
||||
break
|
||||
|
||||
if blender_obj:
|
||||
# Only process mesh objects
|
||||
if (
|
||||
isinstance(blender_obj, bpy.types.Object)
|
||||
and blender_obj.type == "MESH"
|
||||
and blender_obj.data
|
||||
):
|
||||
mesh = blender_obj.data
|
||||
|
||||
# Restore UV layers
|
||||
for uv_layer_data in uv_data.get("uv_layers", []):
|
||||
layer_name = uv_layer_data["name"]
|
||||
uv_coords = uv_layer_data["uv_coords"]
|
||||
|
||||
# Find or create the UV layer
|
||||
uv_layer = mesh.uv_layers.get(layer_name)
|
||||
if not uv_layer:
|
||||
uv_layer = mesh.uv_layers.new(name=layer_name)
|
||||
|
||||
# Restore UV coordinates
|
||||
expected_coords = len(mesh.loops) * 2 # 2 coords per loop
|
||||
|
||||
if len(uv_coords) == expected_coords:
|
||||
for i, uv_loop in enumerate(uv_layer.data):
|
||||
coord_idx = i * 2
|
||||
if coord_idx + 1 < len(uv_coords):
|
||||
uv_loop.uv = (
|
||||
uv_coords[coord_idx],
|
||||
uv_coords[coord_idx + 1],
|
||||
)
|
||||
|
||||
# Restore active UV layer
|
||||
active_uv_layer = uv_data.get("active_uv_layer", "")
|
||||
if active_uv_layer and mesh.uv_layers.get(active_uv_layer):
|
||||
mesh.uv_layers.active = mesh.uv_layers[active_uv_layer]
|
||||
|
||||
|
||||
def update_model_card_objects(
|
||||
model_card: speckle_model_card,
|
||||
converted_objects: Dict[str, bpy.types.Object | bpy.types.Collection],
|
||||
):
|
||||
# Restore UV mappings before clearing property groups
|
||||
restore_uv_mappings(model_card, converted_objects)
|
||||
|
||||
# Store visibility settings from property group before clearing
|
||||
visibility_settings = {}
|
||||
for s_obj in model_card.objects:
|
||||
if s_obj.applicationId:
|
||||
visibility_settings[s_obj.applicationId] = {
|
||||
"hide_get": s_obj.hide_get,
|
||||
"hide_viewport": s_obj.hide_viewport,
|
||||
"hide_select": s_obj.hide_select,
|
||||
"hide_render": s_obj.hide_render,
|
||||
}
|
||||
|
||||
# Store modifier settings from property group before clearing
|
||||
modifier_settings = {}
|
||||
for s_obj in model_card.objects:
|
||||
if s_obj.applicationId:
|
||||
modifier_settings[s_obj.applicationId] = s_obj.modifiers
|
||||
|
||||
# Store collection visibility settings from property group before clearing
|
||||
collection_visibility_settings = {}
|
||||
for s_col in model_card.collections:
|
||||
collection_visibility_settings[s_col.name] = {
|
||||
"hide_viewport": s_col.hide_viewport,
|
||||
"hide_select": s_col.hide_select,
|
||||
"hide_render": s_col.hide_render,
|
||||
"exclude_from_view_layer": s_col.exclude_from_view_layer,
|
||||
}
|
||||
|
||||
# clear model card objects
|
||||
model_card.objects.clear()
|
||||
model_card.collections.clear()
|
||||
@@ -23,20 +226,101 @@ def update_model_card_objects(
|
||||
continue
|
||||
s_col = model_card.collections.add()
|
||||
s_col.name = obj.name
|
||||
|
||||
# Restore collection visibility settings if they exist
|
||||
if obj.name in collection_visibility_settings:
|
||||
s_col.hide_viewport = collection_visibility_settings[obj.name][
|
||||
"hide_viewport"
|
||||
]
|
||||
s_col.hide_select = collection_visibility_settings[obj.name][
|
||||
"hide_select"
|
||||
]
|
||||
s_col.hide_render = collection_visibility_settings[obj.name][
|
||||
"hide_render"
|
||||
]
|
||||
s_col.exclude_from_view_layer = collection_visibility_settings[
|
||||
obj.name
|
||||
]["exclude_from_view_layer"]
|
||||
|
||||
# Apply the visibility settings to the new collection through view layer
|
||||
view_layer = bpy.context.view_layer
|
||||
if view_layer:
|
||||
# Find the layer collection for this collection
|
||||
layer_col = find_layer_collection(
|
||||
view_layer.layer_collection, obj.name
|
||||
)
|
||||
if layer_col:
|
||||
# Apply viewport visibility (controlled by layer collection)
|
||||
layer_col.hide_viewport = collection_visibility_settings[
|
||||
obj.name
|
||||
]["hide_viewport"]
|
||||
# Apply selectability and render visibility (controlled by collection)
|
||||
obj.hide_select = collection_visibility_settings[obj.name][
|
||||
"hide_select"
|
||||
]
|
||||
obj.hide_render = collection_visibility_settings[obj.name][
|
||||
"hide_render"
|
||||
]
|
||||
# Apply view layer exclusion
|
||||
layer_col.exclude = collection_visibility_settings[obj.name][
|
||||
"exclude_from_view_layer"
|
||||
]
|
||||
|
||||
# if its an object, add it to the objects field of model card
|
||||
if isinstance(obj, bpy.types.Object):
|
||||
if obj.name in (o.name for o in model_card.objects):
|
||||
continue
|
||||
s_obj = model_card.objects.add()
|
||||
s_obj.name = obj.name
|
||||
s_obj.applicationId = obj.get("applicationId", "")
|
||||
# Restore visibility settings if they exist
|
||||
if s_obj.applicationId and s_obj.applicationId in visibility_settings:
|
||||
s_obj.hide_get = visibility_settings[s_obj.applicationId]["hide_get"]
|
||||
s_obj.hide_viewport = visibility_settings[s_obj.applicationId][
|
||||
"hide_viewport"
|
||||
]
|
||||
s_obj.hide_select = visibility_settings[s_obj.applicationId][
|
||||
"hide_select"
|
||||
]
|
||||
s_obj.hide_render = visibility_settings[s_obj.applicationId][
|
||||
"hide_render"
|
||||
]
|
||||
|
||||
# Apply the visibility settings to the new object
|
||||
obj.hide_set(visibility_settings[s_obj.applicationId]["hide_get"])
|
||||
obj.hide_viewport = visibility_settings[s_obj.applicationId][
|
||||
"hide_viewport"
|
||||
]
|
||||
obj.hide_select = visibility_settings[s_obj.applicationId][
|
||||
"hide_select"
|
||||
]
|
||||
obj.hide_render = visibility_settings[s_obj.applicationId][
|
||||
"hide_render"
|
||||
]
|
||||
|
||||
# Restore modifier settings if they exist
|
||||
if s_obj.applicationId and s_obj.applicationId in modifier_settings:
|
||||
s_obj.modifiers = modifier_settings[s_obj.applicationId]
|
||||
restore_modifier_settings(obj, modifier_settings[s_obj.applicationId])
|
||||
|
||||
|
||||
def delete_model_card_objects(model_card: speckle_model_card, context: Context) -> None:
|
||||
"""
|
||||
deletes the model card objects
|
||||
"""
|
||||
select_model_card_objects(model_card, context)
|
||||
bpy.ops.object.delete()
|
||||
# Delete objects directly without requiring selection
|
||||
for obj in model_card.objects:
|
||||
blender_obj = get_object_by_application_id(obj.applicationId)
|
||||
if not blender_obj:
|
||||
continue
|
||||
|
||||
# Remove object from all collections first
|
||||
for collection in blender_obj.users_collection:
|
||||
collection.objects.unlink(blender_obj)
|
||||
|
||||
# Delete the object directly
|
||||
bpy.data.objects.remove(blender_obj)
|
||||
|
||||
# delete model card/currently loaded collections
|
||||
for col in model_card.collections:
|
||||
coll = bpy.data.collections.get(col.name)
|
||||
@@ -54,7 +338,7 @@ def select_model_card_objects(model_card, context: Context):
|
||||
bpy.ops.object.select_all(action="DESELECT")
|
||||
# select objects in model card
|
||||
for obj in model_card.objects:
|
||||
blender_obj = bpy.data.objects.get(obj.name)
|
||||
blender_obj = get_object_by_application_id(obj.applicationId)
|
||||
if not blender_obj:
|
||||
continue
|
||||
if blender_obj.name in context.view_layer.objects:
|
||||
@@ -86,3 +370,120 @@ def model_card_exists(
|
||||
):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def serialize_modifier(modifier):
|
||||
"""
|
||||
Serialize a Blender modifier to a dictionary
|
||||
"""
|
||||
modifier_data = {
|
||||
"name": modifier.name,
|
||||
"type": modifier.type,
|
||||
"show_viewport": modifier.show_viewport,
|
||||
"show_render": modifier.show_render,
|
||||
"show_in_editmode": modifier.show_in_editmode,
|
||||
"show_on_cage": modifier.show_on_cage,
|
||||
"properties": {},
|
||||
}
|
||||
|
||||
# Store all modifier-specific properties
|
||||
for prop_name in modifier.bl_rna.properties.keys():
|
||||
if prop_name in [
|
||||
"rna_type",
|
||||
"name",
|
||||
"type",
|
||||
"show_viewport",
|
||||
"show_render",
|
||||
"show_in_editmode",
|
||||
"show_on_cage",
|
||||
]:
|
||||
continue
|
||||
try:
|
||||
prop_value = getattr(modifier, prop_name)
|
||||
# Handle different property types
|
||||
if isinstance(prop_value, (int, float, bool, str)):
|
||||
modifier_data["properties"][prop_name] = prop_value
|
||||
elif hasattr(prop_value, "name"): # Object references
|
||||
modifier_data["properties"][prop_name] = prop_value.name
|
||||
elif (
|
||||
hasattr(prop_value, "__len__") and len(prop_value) <= 4
|
||||
): # Vectors/colors
|
||||
modifier_data["properties"][prop_name] = list(prop_value)
|
||||
except (AttributeError, TypeError):
|
||||
# Skip properties that can't be serialized
|
||||
continue
|
||||
|
||||
return modifier_data
|
||||
|
||||
|
||||
def deserialize_modifier(obj, modifier_data):
|
||||
"""
|
||||
Recreate a modifier from serialized data
|
||||
"""
|
||||
try:
|
||||
modifier = obj.modifiers.new(modifier_data["name"], modifier_data["type"])
|
||||
|
||||
# Set visibility properties
|
||||
modifier.show_viewport = modifier_data.get("show_viewport", True)
|
||||
modifier.show_render = modifier_data.get("show_render", True)
|
||||
modifier.show_in_editmode = modifier_data.get("show_in_editmode", True)
|
||||
modifier.show_on_cage = modifier_data.get("show_on_cage", False)
|
||||
|
||||
# Set modifier-specific properties
|
||||
for prop_name, prop_value in modifier_data.get("properties", {}).items():
|
||||
try:
|
||||
if hasattr(modifier, prop_name):
|
||||
current_value = getattr(modifier, prop_name)
|
||||
# Handle object references
|
||||
if hasattr(current_value, "name") and isinstance(prop_value, str):
|
||||
referenced_obj = bpy.data.objects.get(prop_value)
|
||||
if referenced_obj:
|
||||
setattr(modifier, prop_name, referenced_obj)
|
||||
else:
|
||||
setattr(modifier, prop_name, prop_value)
|
||||
except (AttributeError, TypeError):
|
||||
# Skip properties that can't be set
|
||||
continue
|
||||
|
||||
return modifier
|
||||
except Exception as e:
|
||||
print(f"Error deserializing modifier {modifier_data['name']}: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def store_modifier_settings(model_card: speckle_model_card):
|
||||
"""
|
||||
Store current modifier settings of model card objects
|
||||
This is used to restore the modifier settings of the loaded objects after loading a new version
|
||||
"""
|
||||
for s_obj in model_card.objects:
|
||||
blender_obj = get_object_by_application_id(s_obj.applicationId)
|
||||
if blender_obj and hasattr(blender_obj, "modifiers"):
|
||||
modifiers_data = []
|
||||
for modifier in blender_obj.modifiers:
|
||||
modifier_data = serialize_modifier(modifier)
|
||||
modifiers_data.append(modifier_data)
|
||||
|
||||
# Store as JSON string
|
||||
s_obj.modifiers = json.dumps(modifiers_data)
|
||||
|
||||
|
||||
def restore_modifier_settings(blender_obj, modifier_data_json):
|
||||
"""
|
||||
Restore modifier settings to a Blender object
|
||||
"""
|
||||
if not modifier_data_json or not hasattr(blender_obj, "modifiers"):
|
||||
return
|
||||
|
||||
try:
|
||||
modifiers_data = json.loads(modifier_data_json)
|
||||
|
||||
# Clear existing modifiers
|
||||
blender_obj.modifiers.clear()
|
||||
|
||||
# Recreate modifiers
|
||||
for modifier_data in modifiers_data:
|
||||
deserialize_modifier(blender_obj, modifier_data)
|
||||
|
||||
except (json.JSONDecodeError, KeyError, TypeError) as e:
|
||||
print(f"Error restoring modifiers for {blender_obj.name}: {e}")
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import bpy
|
||||
from typing import Dict, Any
|
||||
|
||||
|
||||
class speckle_project(bpy.types.PropertyGroup):
|
||||
@@ -37,18 +36,31 @@ class speckle_version(bpy.types.PropertyGroup):
|
||||
|
||||
class speckle_object(bpy.types.PropertyGroup):
|
||||
"""
|
||||
PropertyGroup for storing object names
|
||||
PropertyGroup for storing object names, visibility settings, and UV mapping data
|
||||
"""
|
||||
|
||||
name: bpy.props.StringProperty() # type: ignore
|
||||
applicationId: bpy.props.StringProperty(name="Application ID", default="") # type: ignore
|
||||
hide_get: bpy.props.BoolProperty(name="Hide Get", default=False) # type: ignore
|
||||
hide_viewport: bpy.props.BoolProperty(name="Hide Viewport", default=False) # type: ignore
|
||||
hide_select: bpy.props.BoolProperty(name="Hide Select", default=False) # type: ignore
|
||||
hide_render: bpy.props.BoolProperty(name="Hide Render", default=False) # type: ignore
|
||||
modifiers: bpy.props.StringProperty(name="Modifiers", default="") # type: ignore
|
||||
uv_data_serialized: bpy.props.StringProperty() # type: ignore
|
||||
|
||||
|
||||
class speckle_collection(bpy.types.PropertyGroup):
|
||||
"""
|
||||
PropertyGroup for storing collection information
|
||||
PropertyGroup for storing collection information and visibility settings
|
||||
"""
|
||||
|
||||
name: bpy.props.StringProperty() # type: ignore
|
||||
hide_viewport: bpy.props.BoolProperty(name="Hide Viewport", default=False) # type: ignore
|
||||
hide_select: bpy.props.BoolProperty(name="Hide Select", default=False) # type: ignore
|
||||
hide_render: bpy.props.BoolProperty(name="Hide Render", default=False) # type: ignore
|
||||
exclude_from_view_layer: bpy.props.BoolProperty(
|
||||
name="Exclude From View Layer", default=False
|
||||
) # type: ignore
|
||||
|
||||
|
||||
class speckle_model_card(bpy.types.PropertyGroup):
|
||||
|
||||
@@ -186,7 +186,7 @@ def convert_to_native(
|
||||
# Store Speckle ID in custom property
|
||||
converted_object["speckle_id"] = speckle_object.id
|
||||
if hasattr(speckle_object, "applicationId"):
|
||||
converted_object["speckle_application_id"] = speckle_object.applicationId
|
||||
converted_object["applicationId"] = speckle_object.applicationId
|
||||
|
||||
return converted_object
|
||||
|
||||
@@ -320,7 +320,9 @@ def _members_to_native(
|
||||
|
||||
for item in others:
|
||||
try:
|
||||
blender_object = convert_to_native(item, material_mapping, instance_loading_mode="INSTANCE_PROXIES")
|
||||
blender_object = convert_to_native(
|
||||
item, material_mapping, instance_loading_mode="INSTANCE_PROXIES"
|
||||
)
|
||||
if blender_object:
|
||||
# If the parent is a DataObject, override the name of the converted child
|
||||
if is_data_object:
|
||||
@@ -1219,7 +1221,7 @@ def instance_definition_proxy_to_native(
|
||||
"Must be 'INSTANCE_PROXIES' or 'LINKED_DUPLICATES'"
|
||||
)
|
||||
assert isinstance(material_mapping, dict), "material_mapping must be a dictionary"
|
||||
|
||||
|
||||
processed_definitions = processed_definitions or {}
|
||||
definition_collections = {}
|
||||
converted_objects = {}
|
||||
@@ -1240,7 +1242,7 @@ def instance_definition_proxy_to_native(
|
||||
sorted_components = sort_instance_components(definitions, [])
|
||||
|
||||
for _, _, def_id, definition in sorted_components:
|
||||
collection_name = getattr(definition, "name", f"Definition_{def_id[:8]}")
|
||||
collection_name = getattr(definition, "name", f"Definition_{def_id}")
|
||||
|
||||
if def_id in processed_definitions:
|
||||
definition_collections[def_id] = processed_definitions[def_id]
|
||||
@@ -1272,10 +1274,10 @@ def instance_definition_proxy_to_native(
|
||||
nested_def = definitions[found_obj.definitionId]
|
||||
max_depth = getattr(nested_def, "maxDepth", 0)
|
||||
if max_depth > 0: # Only process if max_depth allows
|
||||
assert found_obj.definitionId in definition_collections, (
|
||||
f"Definition collection not found for nested instance {found_obj.definitionId}"
|
||||
)
|
||||
|
||||
assert (
|
||||
found_obj.definitionId in definition_collections
|
||||
), f"Definition collection not found for nested instance {found_obj.definitionId}"
|
||||
|
||||
if instance_loading_mode == "LINKED_DUPLICATES":
|
||||
blender_obj = instance_proxy_to_linked_duplicates(
|
||||
found_obj,
|
||||
@@ -1293,7 +1295,11 @@ def instance_definition_proxy_to_native(
|
||||
if blender_obj:
|
||||
converted_objects[obj_id] = blender_obj
|
||||
else:
|
||||
blender_obj = convert_to_native(found_obj, material_mapping, instance_loading_mode="INSTANCE_PROXIES")
|
||||
blender_obj = convert_to_native(
|
||||
found_obj,
|
||||
material_mapping,
|
||||
instance_loading_mode="INSTANCE_PROXIES",
|
||||
)
|
||||
if blender_obj:
|
||||
definition_collection.objects.link(blender_obj)
|
||||
converted_objects[obj_id] = blender_obj
|
||||
@@ -1398,16 +1404,16 @@ def instance_proxy_to_linked_duplicates(
|
||||
@ mathutils.Matrix.Diagonal(scale_vector).to_4x4()
|
||||
)
|
||||
|
||||
instance_name = f"Instance_{speckle_instance.id[:8]}"
|
||||
instance_name = f"Instance_{speckle_instance.id}"
|
||||
parent_empty = bpy.data.objects.new(instance_name, None)
|
||||
parent_empty.empty_display_type = 'PLAIN_AXES'
|
||||
parent_empty.empty_display_type = "PLAIN_AXES"
|
||||
parent_empty.empty_display_size = 0.1
|
||||
|
||||
|
||||
parent_empty.matrix_world = final_matrix
|
||||
|
||||
|
||||
# link parent to root collection
|
||||
root_collection.objects.link(parent_empty)
|
||||
|
||||
|
||||
parent_empty["speckle_id"] = speckle_instance.id
|
||||
parent_empty["speckle_type"] = speckle_instance.speckle_type
|
||||
parent_empty["definition_id"] = speckle_instance.definitionId
|
||||
@@ -1418,14 +1424,14 @@ def instance_proxy_to_linked_duplicates(
|
||||
for obj in definition_collection.objects:
|
||||
# create a copy of the object with linked data
|
||||
duplicate_obj = obj.copy()
|
||||
|
||||
|
||||
duplicate_obj.name = f"{obj.name}_{speckle_instance.id[:8]}"
|
||||
|
||||
|
||||
root_collection.objects.link(duplicate_obj)
|
||||
|
||||
|
||||
# apply the instance transformation directly to each object
|
||||
duplicate_obj.matrix_world = final_matrix @ obj.matrix_world
|
||||
|
||||
|
||||
duplicated_objects.append(duplicate_obj)
|
||||
|
||||
return parent_empty
|
||||
@@ -1492,7 +1498,7 @@ def instance_proxy_to_native(
|
||||
|
||||
instance_obj.empty_display_size = 0
|
||||
|
||||
instance_name = f"Instance_{speckle_instance.id[:8]}"
|
||||
instance_name = f"Instance_{speckle_instance.id}"
|
||||
instance_obj.name = instance_name
|
||||
|
||||
if instance_obj.name not in root_collection.objects:
|
||||
|
||||
Reference in New Issue
Block a user