Compare commits

...

14 Commits

Author SHA1 Message Date
Mucahit Bilal GOKER c3976b6962 Merge remote-tracking branch 'origin/v3-dev' into bilal/cnx-1895-load-properties-in-blender 2025-09-21 16:39:49 +03:00
Mucahit Bilal GOKER 6b9420f5f1 instance proxies -> collection instances 2025-09-21 16:26:30 +03:00
Mucahit Bilal GOKER fedc7a8e67 extract properties only for data objects 2025-09-21 16:22:51 +03:00
Mucahit Bilal GOKER 3c09fec185 remove unnecessary boolean check 2025-09-21 16:13:37 +03:00
Mucahit Bilal GOKER 31e8b838dd Merge pull request #303 from specklesystems/bilal/bump-specklepy
Release workflow / Build Zip (push) Has been cancelled
Release workflow / deploy-installers (push) Has been cancelled
bump specklepy to 3.0.4
2025-09-08 12:32:32 +03:00
bimgeek baf7f32c2a bump specklepy to 3.0.4 2025-09-08 12:27:02 +03:00
Mucahit Bilal GOKER ad1d58bd4c Merge pull request #302 from specklesystems/bilal/null-check-on-active-workspace
null check on active workspace
2025-09-08 12:14:51 +03:00
Mucahit Bilal GOKER ec86688750 null check on active workspace 2025-09-05 16:26:22 +03:00
Mucahit Bilal GOKER 84098f4c42 Merge pull request #301 from specklesystems/bilal/update-docs
replace docs links
2025-08-26 11:38:12 +03:00
bimgeek 77f9d73698 replace xyz with app 2025-08-26 11:33:28 +03:00
bimgeek 812e8dd2f3 replace docs links 2025-08-26 11:30:22 +03:00
Mucahit Bilal GOKER 4de853f89a Merge branch 'v3-dev' into bilal/cnx-1895-load-properties-in-blender 2025-07-22 12:44:08 +03:00
Mucahit Bilal GOKER 9a1469afe9 exclude archicad composite structure 2025-07-11 17:47:53 +03:00
Mucahit Bilal GOKER 97d425e59f property extraction and conversion functions 2025-07-11 17:24:42 +03:00
8 changed files with 114 additions and 22 deletions
+4 -5
View File
@@ -7,7 +7,7 @@
</h3>
<p align="center"><b>Speckle</b> is the data infrastructure for the AEC industry.</p><br/>
<p align="center"><a href="https://twitter.com/SpeckleSystems"><img src="https://img.shields.io/twitter/follow/SpeckleSystems?style=social" alt="Twitter Follow"></a> <a href="https://speckle.community"><img src="https://img.shields.io/discourse/users?server=https%3A%2F%2Fspeckle.community&amp;style=flat-square&amp;logo=discourse&amp;logoColor=white" alt="Community forum users"></a> <a href="https://speckle.systems"><img src="https://img.shields.io/badge/https://-speckle.systems-royalblue?style=flat-square" alt="website"></a> <a href="https://speckle.guide/dev/"><img src="https://img.shields.io/badge/docs-speckle.guide-orange?style=flat-square&amp;logo=read-the-docs&amp;logoColor=white" alt="docs"></a></p>
<p align="center"><a href="https://twitter.com/SpeckleSystems"><img src="https://img.shields.io/twitter/follow/SpeckleSystems?style=social" alt="Twitter Follow"></a> <a href="https://speckle.community"><img src="https://img.shields.io/discourse/users?server=https%3A%2F%2Fspeckle.community&amp;style=flat-square&amp;logo=discourse&amp;logoColor=white" alt="Community forum users"></a> <a href="https://speckle.systems"><img src="https://img.shields.io/badge/https://-speckle.systems-royalblue?style=flat-square" alt="website"></a> <a href="https://docs.speckle.systems/dev/"><img src="https://img.shields.io/badge/docs-docs.speckle.systems-orange?style=flat-square&amp;logo=read-the-docs&amp;logoColor=white" alt="docs"></a></p>
<p align="center"><a href="https://github.com/specklesystems/speckle-blender/"><img src="https://circleci.com/gh/specklesystems/speckle-blender.svg?style=svg&amp;circle-token=76eabd350ea243575cbb258b746ed3f471f7ac29" alt="Speckle-Next"></a> </p>
# About Speckle
@@ -25,20 +25,19 @@ What is Speckle? Check our ![YouTube Video Views](https://img.shields.io/youtube
- **GraphQL API:** get what you need anywhere you want it
- **Webhooks:** the base for a automation and next-gen pipelines
- **Built for developers:** we are building Speckle with developers in mind and got tools for every stack
- **Built for the AEC industry:** Speckle connectors are plugins for the most common software used in the industry such as Revit, Rhino, Grasshopper, AutoCAD, Civil 3D, Excel, Unreal Engine, Unity, QGIS, Blender and more!
- **Built for the AEC industry:** Speckle connectors are plugins for the most common software used in the industry such as Revit, Rhino, Grasshopper, AutoCAD, Civil 3D, Blender and more!
### Try Speckle now!
Give Speckle a try in no time by:
- [![speckle XYZ](https://img.shields.io/badge/https://-speckle.xyz-0069ff?style=flat-square&logo=hackthebox&logoColor=white)](https://speckle.xyz) ⇒ creating an account at our public server
- [![create a droplet](https://img.shields.io/badge/Create%20a%20Droplet-0069ff?style=flat-square&logo=digitalocean&logoColor=white)](https://marketplace.digitalocean.com/apps/speckle-server?refcode=947a2b5d7dc1) ⇒ deploying an instance in 1 click
- [![speckle XYZ](https://img.shields.io/badge/https://-app.speckle.systems-0069ff?style=flat-square&logo=hackthebox&logoColor=white)](https://app.speckle.systems) ⇒ creating an account at our public server
### Resources
- [![Community forum users](https://img.shields.io/badge/community-forum-green?style=for-the-badge&logo=discourse&logoColor=white)](https://speckle.community) for help, feature requests or just to hang with other speckle enthusiasts, check out our community forum!
- [![website](https://img.shields.io/badge/tutorials-speckle.systems-royalblue?style=for-the-badge&logo=youtube)](https://speckle.systems) our tutorials portal is full of resources to get you started using Speckle
- [![docs](https://img.shields.io/badge/docs-speckle.guide-orange?style=for-the-badge&logo=read-the-docs&logoColor=white)](https://speckle.guide/user/blender.html) reference on almost any end-user and developer functionality
- [![docs](https://img.shields.io/badge/docs-docs.speckle.systems-orange?style=for-the-badge&logo=read-the-docs&logoColor=white)](https://docs.speckle.systems/connectors/blender) reference on almost any end-user and developer functionality
# Blender Connector
@@ -20,7 +20,7 @@ class SPECKLE_OT_load(bpy.types.Operator):
description="Choose how to load instances",
items=[
(
"INSTANCE_PROXIES",
"COLLECTION_INSTANCES",
"Collection Instances",
"Load objects as collection instances",
),
@@ -30,7 +30,7 @@ class SPECKLE_OT_load(bpy.types.Operator):
"Get objects as linked duplicates",
),
],
default="INSTANCE_PROXIES",
default="COLLECTION_INSTANCES",
)
def draw(self, context: Context) -> None:
@@ -23,7 +23,7 @@ from typing import Dict, Union
def load_operation(
context: Context, instance_loading_mode: str = "INSTANCE_PROXIES"
context: Context, instance_loading_mode: str = "COLLECTION_INSTANCES"
) -> Dict[str, Union[bpy.types.Collection, bpy.types.Object]]:
"""
load objects from Speckle and maintain hierarchy.
@@ -123,7 +123,11 @@ def update_workspaces_list(context: Context) -> None:
workspace: speckle_workspace = wm.speckle_workspaces.add()
workspace.id = id
workspace.name = name
wm.selected_workspace.id = get_active_workspace(wm.selected_account_id)["id"]
active_workspace = get_active_workspace(wm.selected_account_id)
if active_workspace:
wm.selected_workspace.id = active_workspace["id"]
else:
wm.selected_workspace.id = "personal"
print("Updated Workspaces List!")
@@ -120,10 +120,13 @@ class SPECKLE_OT_project_selection_dialog(bpy.types.Operator):
if wm.selected_account_id == "":
wm.selected_account_id = get_default_account_id()
wm.selected_workspace.id = get_active_workspace(wm.selected_account_id)["id"]
wm.selected_workspace.name = get_active_workspace(wm.selected_account_id)[
"name"
]
active_workspace = get_active_workspace(wm.selected_account_id)
if active_workspace:
wm.selected_workspace.id = active_workspace["id"]
wm.selected_workspace.name = active_workspace["name"]
else:
wm.selected_workspace.id = "personal"
wm.selected_workspace.name = "Personal Projects"
# Fetch projects from server
projects: List[Tuple[str, str, str, str, bool]] = get_projects_for_account(
@@ -96,7 +96,7 @@ class speckle_model_card(bpy.types.PropertyGroup):
instance_loading_mode: bpy.props.StringProperty(
name="Instance Loading Mode",
description="Mode of loading instances",
default="INSTANCE_PROXIES",
default="COLLECTION_INSTANCES",
) # type: ignore
apply_modifiers: bpy.props.BoolProperty(
name="Apply Modifiers",
+93 -7
View File
@@ -85,7 +85,7 @@ def convert_to_native(
material_mapping: Optional[Dict[str, bpy.types.Material]] = None,
definition_collections: Optional[Dict[str, bpy.types.Collection]] = None,
root_collection: Optional[bpy.types.Collection] = None,
instance_loading_mode: str = "INSTANCE_PROXIES",
instance_loading_mode: str = "COLLECTION_INSTANCES",
) -> Optional[Object]:
"""
converts a speckle object to blender object with material support
@@ -114,7 +114,7 @@ def convert_to_native(
converted_object = instance_proxy_to_linked_duplicates(
speckle_object, coll, root_collection, scale
)
else: # INSTANCE_PROXIES (default)
else: # COLLECTION_INSTANCES (default)
converted_object = instance_proxy_to_native(
speckle_object, coll, root_collection, scale
)
@@ -188,9 +188,95 @@ def convert_to_native(
if hasattr(speckle_object, "applicationId"):
converted_object["applicationId"] = speckle_object.applicationId
# Extract and store Speckle properties
speckle_properties = extract_speckle_properties(speckle_object)
for prop_name, prop_value in speckle_properties.items():
try:
converted_object[prop_name] = prop_value
except Exception as e:
# Skip problematic properties
print(f"Warning: Could not set property '{prop_name}': {e}")
return converted_object
def convert_property_value(value: Any) -> Any:
"""Convert property values to appropriate Python types."""
if isinstance(value, str):
# Return string as-is
return value
elif isinstance(value, (int, float)):
# Return numeric values as-is
return value
elif isinstance(value, bool):
# Return boolean as-is
return value
else:
# Convert everything else to string
return str(value)
def _traverse_properties(obj: Any, parent_name: str = "") -> Dict[str, Any]:
"""Recursively traverse properties, collecting name/value pairs with duplicate handling."""
properties = {}
for key, value in obj.items():
# Skip excluded sections
if key == "Material Quantities":
continue
if key == "Composite Structure":
continue
if parent_name == "Type Parameters" and key == "Structure":
continue
if isinstance(value, dict):
# Check if this is a complex property (has name and value)
if "name" in value and "value" in value:
# Extract only name and value, ignore other fields
prop_name = value.get("name", key)
prop_value = convert_property_value(value["value"])
# Handle duplicates by adding parent suffix
final_name = prop_name
if final_name in properties:
final_name = f"{prop_name}_{key}" # Use the dict key as suffix
properties[final_name] = prop_value
else:
# Recurse into nested structure
nested_props = _traverse_properties(value, key)
for nested_name, nested_value in nested_props.items():
# Handle duplicates by adding parent suffix
final_name = nested_name
if final_name in properties:
final_name = f"{nested_name}_{key}"
properties[final_name] = nested_value
else:
# Simple property - store directly with key as name
final_name = key
if final_name in properties:
final_name = f"{key}_{parent_name}" if parent_name else key
properties[final_name] = convert_property_value(value)
return properties
def extract_speckle_properties(speckle_object: Base) -> Dict[str, Any]:
"""Extract properties from Speckle object properties field only."""
if not isinstance(speckle_object, DataObject):
return {}
try:
return _traverse_properties(speckle_object.properties)
except Exception as e:
# Silently handle any extraction errors
print(f"Warning: Failed to extract properties: {e}")
return {}
def display_value_to_native(
speckle_object: Base,
object_name: str,
@@ -321,7 +407,7 @@ def _members_to_native(
for item in others:
try:
blender_object = convert_to_native(
item, material_mapping, instance_loading_mode="INSTANCE_PROXIES"
item, material_mapping, instance_loading_mode="COLLECTION_INSTANCES"
)
if blender_object:
# If the parent is a DataObject, override the name of the converted child
@@ -1210,13 +1296,13 @@ def instance_definition_proxy_to_native(
root_object: Base,
material_mapping: Dict[str, Any],
processed_definitions: Dict[str, Any] = None,
instance_loading_mode: str = "INSTANCE_PROXIES",
instance_loading_mode: str = "COLLECTION_INSTANCES",
) -> Tuple[Dict[str, bpy.types.Collection], Dict[str, Any]]:
"""
converts instance definition proxies to Blender collections recursively
"""
# Validate instance loading mode
assert instance_loading_mode in ["INSTANCE_PROXIES", "LINKED_DUPLICATES"], (
assert instance_loading_mode in ["COLLECTION_INSTANCES", "LINKED_DUPLICATES"], (
f"Invalid instance_loading_mode: {instance_loading_mode}. "
"Must be 'INSTANCE_PROXIES' or 'LINKED_DUPLICATES'"
)
@@ -1285,7 +1371,7 @@ def instance_definition_proxy_to_native(
definition_collection,
scale=1.0,
)
else: # INSTANCE_PROXIES (default)
else: # COLLECTION_INSTANCES (default)
blender_obj = instance_proxy_to_native(
found_obj,
definition_collections[found_obj.definitionId],
@@ -1298,7 +1384,7 @@ def instance_definition_proxy_to_native(
blender_obj = convert_to_native(
found_obj,
material_mapping,
instance_loading_mode="INSTANCE_PROXIES",
instance_loading_mode="COLLECTION_INSTANCES",
)
if blender_obj:
definition_collection.objects.link(blender_obj)
+1 -1
View File
@@ -5,7 +5,7 @@ description = "Next-Gen Speckle connector for Blender!"
requires-python = ">=3.11.9, <4.0.0"
license = "Apache-2.0"
dependencies = [
"specklepy>=3.0.3",
"specklepy>=3.0.4",
]
[dependency-groups]