Compare commits

...

11 Commits

Author SHA1 Message Date
izzy lyseggen c00635d093 Merge pull request #48 from specklesystems/izzy/receive-fixes
fix(cols): try catch linking children
2021-08-04 10:26:34 +01:00
izzy lyseggen a07d4f0a8e fix(cols): try catch linking children 2021-08-04 10:25:33 +01:00
izzy lyseggen 61e721716f Merge pull request #47 from specklesystems/izzy/receive-fixes
fix(receive): clear matrix and get mat name
2021-08-04 10:10:15 +01:00
izzy lyseggen 91d12b5a6c fix: remove devtools ref 2021-08-04 10:09:52 +01:00
izzy lyseggen f331846138 fix(receive): clear matrix and get mat name 2021-08-04 10:08:59 +01:00
izzy lyseggen d350887860 Merge pull request #46 from specklesystems/izzy/materials-again
fix(receive): adding and assigning materials
2021-07-30 11:17:48 +01:00
izzy lyseggen 8ad607a8e0 docs(readme): update info on materials 2021-07-30 11:12:46 +01:00
izzy lyseggen 13f242e47f fix(user): make sure default is first active 2021-07-29 16:35:32 +01:00
izzy lyseggen 9f55acd02d fix(mats): get both detached and attached by name 2021-07-28 18:03:59 +01:00
izzy lyseggen 476f947b68 Merge pull request #45 from specklesystems/izzy/gh-receive-bug
fix(receive): resolve bug when receiveing 🦗 CSO
2021-07-19 18:20:55 +01:00
izzy lyseggen f56dea0a35 fix(receive): resolve bug when receiveing 🦗 CSO 2021-07-19 18:19:15 +01:00
5 changed files with 98 additions and 62 deletions
+2 -2
View File
@@ -1,5 +1,5 @@
# SpeckleBlender 2.0
Speckle add-on for Blender 2.92
Speckle add-on for Blender 2.92 & 2.93
[![Twitter Follow](https://img.shields.io/twitter/follow/SpeckleSystems?style=social)](https://twitter.com/SpeckleSystems) [![Community forum users](https://img.shields.io/discourse/users?server=https%3A%2F%2Fdiscourse.speckle.works&style=flat-square&logo=discourse&logoColor=white)](https://discourse.speckle.works) [![website](https://img.shields.io/badge/https://-speckle.systems-royalblue?style=flat-square)](https://speckle.systems) [![docs](https://img.shields.io/badge/docs-speckle.guide-orange?style=flat-square&logo=read-the-docs&logoColor=white)](https://speckle.guide/dev/)
@@ -45,7 +45,7 @@ This code is WIP and as such should be used with extreme caution on non-sensitiv
## Custom properties
- **SpeckleBlender** will look for a `texture_coordinates` property and use that to create a UV layer for the imported object. These texture coordinates are a space-separated list of floats (`[u v u v u v etc...]`) that is encoded as a base64 blob. This is subject to change as **SpeckleBlender** develops.
- If a `material` property is found, **SpeckleBlender** will create a material named using the sub-property `material.name`. If a material with that name already exists in Blender, **SpeckleBlender** will just assign that existing material to the object. This allows geometry to be updated without having to re-assign and re-create materials.
- If a `renderMaterial` property is found, **SpeckleBlender** will create a material named using the sub-property `renderMaterial.name`. If a material with that name already exists in Blender, **SpeckleBlender** will just assign that existing material to the object. This allows geometry to be updated without having to re-assign and re-create materials.
- Vertex colors are supported. The `colors` list from Speckle meshes is translated to a vertex color layer.
- Speckle properties will be imported as custom properties on Blender objects. Nested dictionaries are expanded to individual properties by flattening their key hierarchy. I.e. `propA:{'propB': {'propC':10, 'propD':'foobar'}}` is flattened to `propA.propB.propC = 10` and `propA.propB.propD = "foobar"`.
+6 -3
View File
@@ -9,6 +9,7 @@ from bpy_speckle.functions import _report, get_scale_length
from specklepy.objects.geometry import *
from specklepy.objects.other import RenderMaterial
FROM_SPECKLE_SCHEMAS = {
Mesh: import_mesh,
Brep: import_brep,
@@ -48,13 +49,14 @@ def add_blender_material(smesh, blender_object) -> None:
if blender_object.data is None:
return
if not hasattr(smesh, "renderMaterial"):
if not hasattr(smesh, "renderMaterial") and not hasattr(smesh, "@renderMaterial"):
return
speckle_mat = smesh.renderMaterial
mat_name = getattr(speckle_mat, "name", None)
speckle_mat = getattr(smesh, "renderMaterial", None) or smesh["@renderMaterial"]
mat_name = getattr(speckle_mat, "name", None) or speckle_mat.__dict__.get("@name")
if not mat_name:
mat_name = speckle_mat.applicationId or speckle_mat.id
blender_mat = bpy.data.materials.get(mat_name)
if not blender_mat:
blender_mat = bpy.data.materials.new(mat_name)
@@ -196,6 +198,7 @@ def from_speckle_object(speckle_object, scale, name=None):
if speckle_name in bpy.data.objects.keys():
blender_object = bpy.data.objects[speckle_name]
blender_object.data = obdata
blender_object.matrix_world = Matrix()
if hasattr(obdata, "materials"):
blender_object.data.materials.clear()
else:
+1 -1
View File
@@ -157,7 +157,7 @@ def import_mesh(speckle_mesh, scale=1.0, name=None):
if not name:
name = speckle_mesh.geometryHash or speckle_mesh.id
if name in bpy.data.meshes.keys() and False:
if name in bpy.data.meshes.keys():
mesh = bpy.data.meshes[name]
else:
mesh = bpy.data.meshes.new(name=name)
+87 -56
View File
@@ -2,6 +2,7 @@
Stream operators
"""
from itertools import chain
from typing import Dict
import bpy, bmesh, os
import webbrowser
from bpy.props import (
@@ -31,14 +32,14 @@ from specklepy.objects import Base
from specklepy.objects.geometry import *
def get_objects_collections(base):
def get_objects_collections(base) -> Dict:
"""Create collections based on the dynamic members on a root commit object"""
collections = {}
for name in base.get_dynamic_member_names():
value = base[name]
if isinstance(value, list):
col = create_collection(name)
collections[name] = get_objects_nested_lists(value)
collections[name] = get_objects_nested_lists(value, col)
if isinstance(value, Base):
col = create_collection(name)
collections[name] = get_objects_collections_recursive(value, col)
@@ -46,22 +47,37 @@ def get_objects_collections(base):
return collections
def get_objects_nested_lists(items):
def get_objects_nested_lists(items, parent_col=None) -> List:
"""For handling the weird nested lists that come from Grasshopper"""
objects = []
if isinstance(items[0], list):
items = list(chain.from_iterable(items))
objects.extend(get_objects_nested_lists(items))
objects.extend(get_objects_nested_lists(items, parent_col))
else:
objects = [item for item in items if isinstance(item, Base)]
objects = [
get_objects_collections_recursive(item, parent_col)
for item in items
if isinstance(item, Base)
]
return objects
def get_objects_collections_recursive(base, parent_col=None):
def get_objects_collections_recursive(base, parent_col=None) -> List:
"""Recursively create collections based on the dynamic members on nested `Base` objects within the root commit object"""
# if it's a convertable (registered) class and not just a plain `Base`, return the object itself
object_type = Base.get_registered_type(base.speckle_type)
if (
(object_type and object_type != Base)
or hasattr(base, "displayMesh")
or hasattr(base, "displayValue")
):
return [base]
# if it's an unknown type, try to drill further down to find convertable objects
objects = []
for name in base.get_dynamic_member_names():
value = base[name]
if isinstance(value, list):
@@ -69,8 +85,10 @@ def get_objects_collections_recursive(base, parent_col=None):
if isinstance(item, Base):
objects.append(item)
if isinstance(value, Base):
col = create_collection(name)
parent_col.children.link(col)
col = parent_col.children.get(name)
if not parent_col.children.get(name):
col = create_collection(name)
parent_col.children.link(col)
objects.append({name: get_objects_collections_recursive(value, col)})
return objects
@@ -85,60 +103,71 @@ def bases_to_native(context, collections, scale, stream_id, func=None):
elif isinstance(objects, list):
for obj in objects:
if isinstance(obj, dict):
bases_to_native(context, obj, scale, stream_id)
bases_to_native(context, obj, scale, stream_id, func)
elif isinstance(obj, list):
for item in obj:
if isinstance(item, dict):
bases_to_native(context, item, scale, stream_id, func)
elif isinstance(item, Base):
base_to_native(
context, item, scale, stream_id, col, existing, func
)
elif isinstance(obj, Base):
base_to_native(context, obj, scale, stream_id, col, existing, func)
else:
new_objects = [from_speckle_object(obj, scale)]
_report(
f"Something went wrong when receiving collection: {col_name}"
)
if hasattr(obj, "properties") and obj.properties is not None:
new_objects.extend(
get_speckle_subobjects(obj.properties, scale, obj.id)
)
elif isinstance(obj, dict) and "properties" in obj.keys():
new_objects.extend(
get_speckle_subobjects(obj["properties"], scale, obj["id"])
)
"""
Set object Speckle settings
"""
for new_object in new_objects:
if new_object is None:
continue
"""
Run injected function
"""
if func:
new_object = func(context.scene, new_object)
if (
new_object is None
): # Make sure that the injected function returned an object
new_obj = new_object
_report(
"Script '{}' returned None.".format(func.__module__)
)
continue
new_object.speckle.stream_id = stream_id
new_object.speckle.send_or_receive = "receive"
if new_object.speckle.object_id in existing.keys():
name = existing[new_object.speckle.object_id].name
existing[new_object.speckle.object_id].name = (
name + "__deleted"
)
new_object.name = name
col.objects.unlink(existing[new_object.speckle.object_id])
if new_object.name not in col.objects:
col.objects.link(new_object)
bpy.context.view_layer.update()
if context.area:
context.area.tag_redraw()
def base_to_native(context, base, scale, stream_id, col, existing, func=None):
new_objects = [from_speckle_object(base, scale)]
if hasattr(base, "properties") and base.properties is not None:
new_objects.extend(get_speckle_subobjects(base.properties, scale, base.id))
elif isinstance(base, dict) and "properties" in base.keys():
new_objects.extend(
get_speckle_subobjects(base["properties"], scale, base["id"])
)
"""
Set object Speckle settings
"""
for new_object in new_objects:
if new_object is None:
continue
"""
Run injected function
"""
if func:
new_object = func(context.scene, new_object)
if (
new_object is None
): # Make sure that the injected function returned an object
new_obj = new_object
_report("Script '{}' returned None.".format(func.__module__))
continue
new_object.speckle.stream_id = stream_id
new_object.speckle.send_or_receive = "receive"
if new_object.speckle.object_id in existing.keys():
name = existing[new_object.speckle.object_id].name
existing[new_object.speckle.object_id].name = name + "__deleted"
new_object.name = name
col.objects.unlink(existing[new_object.speckle.object_id])
if new_object.name not in col.objects:
col.objects.link(new_object)
def create_collection(name, clear_collection=True):
if name in bpy.data.collections:
col = bpy.data.collections[name]
@@ -257,8 +286,10 @@ class ReceiveStreamObjects(bpy.types.Operator):
bpy.context.scene.collection.children.link(col)
for child_col in collections.keys():
col.children.link(bpy.data.collections[child_col])
try:
col.children.link(bpy.data.collections[child_col])
except:
pass
"""
Set conversion scale from stream units
"""
+2
View File
@@ -55,6 +55,8 @@ class LoadUsers(bpy.types.Operator):
except Exception as ex:
_report(ex)
users.remove(len(users) - 1)
if profile.isDefault:
context.scene.speckle.active_user = str(len(users) - 1)
context.scene.speckle.active_user_index = int(context.scene.speckle.active_user)
bpy.ops.speckle.load_user_streams()