Compare commits

...

22 Commits

Author SHA1 Message Date
JR-Morgan 548b3ad352 CI fix for macos slugs 2022-10-24 21:48:25 +01:00
JR-Morgan d2deecf099 added deploy steps for macos 2022-10-24 21:27:38 +01:00
JR-Morgan fbda0110cd fix(connector): 2.9.0 update 2022-10-11 18:43:36 +01:00
Jedd Morgan ba9ad9ac07 Merge pull request #118 from specklesystems/gergo/workflowMetrics
add workflow tracking to receive operations
2022-10-11 17:56:40 +01:00
Jedd Morgan d13db2b44a Merge pull request #117 from specklesystems/jrm/receive-script-update
Jrm/receive script update
2022-10-11 17:54:33 +01:00
JR-Morgan 2c127c85f3 feat: Updated recieve scripts to use context rather than scene 2022-10-07 16:03:10 +01:00
Gergő Jedlicska cc099c0ff1 add workflow tracking to receive operations 2022-10-07 16:08:27 +02:00
JR-Morgan 448ab70c3b Merge remote-tracking branch 'origin/jrm/clean-mesh' into jrm/receive-script-update 2022-10-04 18:28:57 +01:00
JR-Morgan 020eba2727 Merge remote-tracking branch 'origin/main' into jrm/receive-script-update 2022-10-04 17:53:01 +01:00
JR-Morgan d8117e2c30 feat(converter): Updated Recieve Script callback functions to allow for access to Base object, and for callback on ToNative completion 2022-10-04 17:50:57 +01:00
Matteo Cominetti 57a6d88e6b ci 2022-09-27 10:27:50 +01:00
Matteo Cominetti 74cb3e5f85 ci: change shell 2022-09-27 10:15:58 +01:00
Matteo Cominetti d0aeecc863 Update README.md 2022-09-27 10:08:55 +01:00
Matteo Cominetti 2803308c5e Update README.md 2022-09-27 10:08:06 +01:00
Matteo Cominetti 326f04f67d ci: adds signtool param 2022-09-27 10:07:01 +01:00
JR-Morgan 68972ba8f9 Merge remote-tracking branch 'origin' into jrm/clean-mesh 2022-09-23 13:58:07 +01:00
Alan Rynne 73a028b56f Merge pull request #112 from specklesystems/ci/force-run
ci: Activated both mac runs on build
2022-09-23 14:56:02 +02:00
Alan Rynne 33890ef0ee fix: went too far 2022-09-23 14:28:51 +02:00
Alan Rynne 53fe676ab6 ci: Prettified steps 2022-09-23 14:27:50 +02:00
Alan Rynne f027c7eca4 ci: Activated both mac runs on build 2022-09-23 14:11:19 +02:00
JR-Morgan ea2b6dfb0e feat(connector): Added clean mesh option to recieve geometry with limited disolve + merge verts 2022-09-23 12:36:55 +01:00
JR-Morgan 83610cec38 Added type hinting to codebase 2022-09-21 22:22:07 +01:00
13 changed files with 681 additions and 834 deletions
+90 -60
View File
@@ -68,7 +68,10 @@ jobs:
New-Item -Force "speckle-sharp-ci-tools/Installers/blender/$channel.yml" -ItemType File -Value "version: $semver"
echo $semver
python patch_version.py $semver
speckle-sharp-ci-tools\InnoSetup\ISCC.exe speckle-sharp-ci-tools\blender.iss
- run:
name: Installer
shell: cmd.exe #does not work in powershell
command: speckle-sharp-ci-tools\InnoSetup\ISCC.exe speckle-sharp-ci-tools\blender.iss /Sbyparam=$p
- when:
condition: << parameters.installer >>
steps:
@@ -120,7 +123,7 @@ jobs:
- when:
condition:
and:
- << parameters.installer >>
# - << parameters.installer >>
- equal: [osx-x64, << parameters.runtime >>]
steps:
- run:
@@ -182,33 +185,22 @@ jobs:
paths:
- speckle-sharp-ci-tools
deploy: # Uploads all installers found to S3
docker:
- image: cimg/base:2021.01
steps:
- attach_workspace:
at: ./
- run:
name: List contents
command: ls -R speckle-sharp-ci-tools/Installers
- aws-s3/copy:
arguments: "--recursive --endpoint=https://$SPACES_REGION.digitaloceanspaces.com --acl public-read"
aws-access-key-id: SPACES_KEY
aws-region: SPACES_REGION
aws-secret-access-key: SPACES_SECRET
from: '"speckle-sharp-ci-tools/Installers/"'
to: s3://speckle-releases/installers/
deploy-manager2:
deploy-connector-new:
docker:
- image: mcr.microsoft.com/dotnet/sdk:6.0
parameters:
slug:
type: string
file_slug:
type: string
os:
type: string
extension:
type: string
arch:
type: string
default: Any
steps:
- checkout
- attach_workspace:
@@ -221,7 +213,13 @@ jobs:
command: |
TAG=$(if [ "${CIRCLE_TAG}" ]; then echo $CIRCLE_TAG; else echo "0.0.0"; fi;)
SEMVER=$(echo "$TAG" | sed -e 's/\/[a-zA-Z-]*//')
/root/.dotnet/tools/Speckle.Manager.Feed deploy -s << parameters.slug >> -v ${SEMVER} -u https://releases.speckle.dev/installers/<< parameters.slug >>/<< parameters.slug >>-${SEMVER}.<< parameters.extension >> -o << parameters.os >> -f speckle-sharp-ci-tools/Installers/<< parameters.slug >>/<< parameters.slug >>-${SEMVER}.<< parameters.extension >>
/root/.dotnet/tools/Speckle.Manager.Feed deploy \
-s << parameters.slug >> \
-v ${SEMVER} \
-u https://releases.speckle.dev/installers/<< parameters.file_slug >>/<< parameters.file_slug >>-${SEMVER}.<< parameters.extension >> \
-o << parameters.os >> \
-a << parameters.arch >> \
-f speckle-sharp-ci-tools/Installers/<< parameters.file_slug >>/<< parameters.file_slug >>-${SEMVER}.<< parameters.extension >>
workflows:
build: # build the installers, but don't persist to workspace for deployment
@@ -233,6 +231,7 @@ workflows:
- main
- /ci\/.*/
- build-connector-win:
name: Windows Build
requires:
- get-ci-tools
filters:
@@ -241,14 +240,17 @@ workflows:
- main
- /ci\/.*/
- build-connector-mac:
name: Mac ARM Build
slug: blender-mac-arm
runtime: osx-arm64
requires:
- get-ci-tools
- build-connector-mac:
name: Mac Intel Build
slug: blender-mac-intel
runtime: osx-x64
requires:
- get-ci-tools
filters:
branches:
only:
- main
- /ci\/.*/
deploy: # build installers and deploy
jobs:
- get-ci-tools:
@@ -259,7 +261,7 @@ workflows:
ignore: /.*/
- build-connector-win:
name: build-deploy-connector-win
name: Windows Build
slug: blender
installer: true
requires:
@@ -270,51 +272,79 @@ workflows:
branches:
ignore: /.*/
# - build-connector-mac:
# name: build-deploy-connector-mac-arm
# slug: blender-mac-arm
# runtime: osx-arm64
# installer: true
# requires:
# - get-ci-tools
# filters:
# tags:
# only: /^(all|mac)\/([0-9]+)\.([0-9]+)\.([0-9]+)(?:-\w+)?$/
# branches:
# ignore: /.*/
# - build-connector-mac:
# name: build-deploy-connector-mac-intel
# slug: blender-mac-intel
# runtime: osx-x64
# installer: true
# requires:
# - get-ci-tools
# filters:
# tags:
# only: /^(all|mac)\/([0-9]+)\.([0-9]+)\.([0-9]+)(?:-\w+)?$/
# branches:
# ignore: /.*/
- deploy:
- build-connector-mac:
name: Mac ARM Build
slug: blender-mac-arm
runtime: osx-arm64
installer: true
requires:
- get-ci-tools
- build-deploy-connector-win
filters:
tags:
only: /([0-9]+)\.([0-9]+)\.([0-9]+)(?:-\w+)?$/
branches:
ignore: /.*/
- deploy-manager2:
slug: blender
os: Win
extension: exe
- build-connector-mac:
name: Mac Intel Build
slug: blender-mac-intel
runtime: osx-x64
installer: true
requires:
- get-ci-tools
- deploy
filters:
tags:
only: /([0-9]+)\.([0-9]+)\.([0-9]+)(?:-\w+)?$/
branches:
ignore: /.*/
- deploy-connector-new:
name: deploy-win
slug: blender
file_slug: blender
os: Win
extension: exe
arch: Any
requires:
- Windows Build
- Mac ARM Build
- Mac Intel Build
filters:
tags:
only: /([0-9]+)\.([0-9]+)\.([0-9]+)(?:-\w+)?$/
branches:
ignore: /.*/ # For testing only! /ci\/.*/
- deploy-connector-new:
name: deploy-mac-intel
slug: blender
file_slug: blender-mac-intel
os: OSX
arch: Intel
extension: zip
requires:
- Windows Build
- Mac ARM Build
- Mac Intel Build
filters:
tags:
only: /([0-9]+)\.([0-9]+)\.([0-9]+)(?:-\w+)?$/
branches:
ignore: /.*/ # For testing only! /ci\/.*/
- deploy-connector-new:
name: deploy-mac-arm
slug: blender
file_slug: blender-mac-arm
os: OSX
arch: Arm
extension: zip
requires:
- Windows Build
- Mac ARM Build
- Mac Intel Build
filters:
tags:
only: /([0-9]+)\.([0-9]+)\.([0-9]+)(?:-\w+)?$/
branches:
ignore: /.*/ # For testing only! /ci\/.*/
+2 -1
View File
@@ -10,4 +10,5 @@ __pycache__/
# dev
.venv
Installers/
modules/
modules/
.tool-versions
+2 -2
View File
@@ -3,7 +3,7 @@
Speckle | Blender
</h1>
<h3 align="center">
Connector for Blender 2.92 & 2.93
Connector for Blender
</h3>
<p align="center"><b>Speckle</b> is the data infrastructure for the AEC industry.</p><br/>
@@ -91,4 +91,4 @@ The Speckle Community hangs out on [the forum](https://discourse.speckle.works),
Unless otherwise described, the code in this repository is licensed under the Apache-2.0 License. Please note that some modules, extensions or code herein might be otherwise licensed. This is indicated either in the root of the containing folder under a different license file, or in the respective file's header. If you have any questions, don't hesitate to get in touch with us via [email](mailto:hello@speckle.systems).
## Notes
SpeckleBlender is written and maintained by [Tom Svilans](http://tomsvilans.com) ([Github](https://github.com/tsvilans)).
Thanks to [Tom Svilans](http://tomsvilans.com) ([Github](https://github.com/tsvilans)) for the original v1 contribution!
+4 -1
View File
@@ -1,4 +1,7 @@
"""
Permanent handle on all user clients
"""
speckle_clients = []
from specklepy.api.client import SpeckleClient
speckle_clients: list[SpeckleClient] = []
+2 -2
View File
@@ -1,7 +1,7 @@
from bpy_speckle.convert.to_native import convert_to_native
from specklepy.objects.base import Base
def get_speckle_subobjects(attr, scale, name):
def get_speckle_subobjects(attr: dict | Base, scale: float, name: str) -> list:
subobjects = []
keys = attr.keys() if isinstance(attr, dict) else attr.get_dynamic_member_names()
for key in keys:
+24 -20
View File
@@ -4,6 +4,7 @@ import mathutils
import bpy, bmesh, bpy_types
from specklepy.objects.other import *
from specklepy.objects.geometry import *
from bpy.types import Object
from .util import (
add_blender_material,
add_custom_properties,
@@ -25,7 +26,7 @@ CAN_CONVERT_TO_NATIVE = (
)
def can_convert_to_native(speckle_object):
def can_convert_to_native(speckle_object: Base) -> bool:
if type(speckle_object) in CAN_CONVERT_TO_NATIVE:
return True
if getattr(
@@ -37,7 +38,7 @@ def can_convert_to_native(speckle_object):
return False
def convert_to_native(speckle_object, name=None):
def convert_to_native(speckle_object: Base, name: str = None) -> list | Object | None:
speckle_type = type(speckle_object)
speckle_name = (
name
@@ -52,7 +53,7 @@ def convert_to_native(speckle_object, name=None):
)
if not elements and not display:
_report(f"Could not convert unsupported Speckle object: {speckle_object}")
return
return None
if isinstance(display, list):
elements.extend(display)
else:
@@ -62,6 +63,8 @@ def convert_to_native(speckle_object, name=None):
# not making it hidden, so it will get added on send as i think it might be helpful? can reconsider
converted = []
for item in elements:
if(item is None):
continue
item.parent_speckle_type = speckle_object.speckle_type
blender_object = convert_to_native(item)
if isinstance(blender_object, list):
@@ -75,7 +78,7 @@ def convert_to_native(speckle_object, name=None):
# convert breps
if speckle_type is Brep:
meshes = getattr(
speckle_object, "displayValue", getattr(speckle_object, "displayMesh", None)
speckle_object, "displayValue", getattr(speckle_object, "displayMesh", iter([]))
)
if material := getattr(speckle_object, "renderMaterial", getattr(speckle_object, "@renderMaterial", None),):
for mesh in meshes:
@@ -83,18 +86,19 @@ def convert_to_native(speckle_object, name=None):
return [convert_to_native(mesh) for mesh in meshes]
scale = 1.0
if units := getattr(speckle_object, "units", None):
scale = get_scale_length(units) / bpy.context.scene.unit_settings.scale_length
# convert supported geometry
if speckle_type is Mesh:
if isinstance(speckle_object, Mesh):
obj_data = mesh_to_native(speckle_object, name=speckle_name, scale=scale)
elif speckle_type in SUPPORTED_CURVES:
obj_data = icurve_to_native(speckle_object, name=speckle_name, scale=scale)
elif speckle_type is Transform:
elif isinstance(speckle_object, Transform):
obj_data = transform_to_native(speckle_object, scale=scale)
elif speckle_type is BlockDefinition:
elif isinstance(speckle_object, BlockDefinition):
obj_data = block_def_to_native(speckle_object, scale=scale)
elif speckle_type is BlockInstance:
elif isinstance(speckle_object, BlockInstance): # speckle_type is BlockInstance:
obj_data = block_instance_to_native(speckle_object, scale=scale)
else:
_report(f"Unsupported type {speckle_type}")
@@ -130,7 +134,7 @@ def convert_to_native(speckle_object, name=None):
return blender_object
def mesh_to_native(speckle_mesh: Mesh, name, scale=1.0):
def mesh_to_native(speckle_mesh: Mesh, name: str, scale=1.0) -> bpy.types.Mesh:
if name in bpy.data.meshes.keys():
blender_mesh = bpy.data.meshes[name]
@@ -150,7 +154,7 @@ def mesh_to_native(speckle_mesh: Mesh, name, scale=1.0):
return blender_mesh
def line_to_native(speckle_curve, blender_curve, scale):
def line_to_native(speckle_curve: Line, blender_curve: bpy.types.Curve, scale: float) -> bpy.types.Spline | None:
line = blender_curve.splines.new("POLY")
line.points.add(1)
@@ -173,7 +177,7 @@ def line_to_native(speckle_curve, blender_curve, scale):
return line
def polyline_to_native(scurve, bcurve, scale):
def polyline_to_native(scurve: Polyline, bcurve: bpy.types.Curve, scale: float) -> bpy.types.Spline | None:
if value := scurve.value:
N = len(value) // 3
@@ -197,7 +201,7 @@ def polyline_to_native(scurve, bcurve, scale):
return polyline
def nurbs_to_native(scurve, bcurve, scale):
def nurbs_to_native(scurve: Curve, bcurve: bpy.types.Curve, scale: float) -> bpy.types.Spline | None:
if points := scurve.points:
N = len(points) // 3
@@ -226,12 +230,12 @@ def nurbs_to_native(scurve, bcurve, scale):
return nurbs
def arc_to_native(rcurve, bcurve, scale):
def arc_to_native(rcurve: Arc, bcurve: bpy.types.Curve, scale: float) -> bpy.types.Spline | None:
# TODO: improve Blender representation of arc
plane = rcurve.plane
if not plane:
return
return None
normal = mathutils.Vector([plane.normal.x, plane.normal.y, plane.normal.z])
@@ -288,7 +292,7 @@ def arc_to_native(rcurve, bcurve, scale):
return arc
def polycurve_to_native(scurve, bcurve, scale):
def polycurve_to_native(scurve: Polycurve, bcurve: bpy.types.Curve, scale: float):
"""
Convert Polycurve object
"""
@@ -307,7 +311,7 @@ def polycurve_to_native(scurve, bcurve, scale):
return curves
def icurve_to_native_spline(speckle_curve, blender_curve, scale=1.0):
def icurve_to_native_spline(speckle_curve: Base, blender_curve: bpy.types.Curve, scale=1.0):
curve_type = type(speckle_curve)
if curve_type is Line:
return line_to_native(speckle_curve, blender_curve, scale)
@@ -321,7 +325,7 @@ def icurve_to_native_spline(speckle_curve, blender_curve, scale=1.0):
return arc_to_native(speckle_curve, blender_curve, scale)
def icurve_to_native(speckle_curve, name=None, scale=1.0):
def icurve_to_native(speckle_curve: Base, name=None, scale=1.0) -> Curve | None:
curve_type = type(speckle_curve)
if curve_type not in SUPPORTED_CURVES:
_report(f"Unsupported curve type: {curve_type}")
@@ -340,7 +344,7 @@ def icurve_to_native(speckle_curve, name=None, scale=1.0):
return blender_curve
def transform_to_native(transform: Transform, scale=1.0):
def transform_to_native(transform: Transform, scale=1.0) -> mathutils.Matrix:
mat = mathutils.Matrix(
[
transform.value[:4],
@@ -355,7 +359,7 @@ def transform_to_native(transform: Transform, scale=1.0):
return mat
def block_def_to_native(definition: BlockDefinition, scale=1.0):
def block_def_to_native(definition: BlockDefinition, scale=1.0) -> bpy.types.Collection:
native_def = bpy.data.collections.get(definition.name)
if native_def:
return native_def
@@ -373,7 +377,7 @@ def block_def_to_native(definition: BlockDefinition, scale=1.0):
return native_def
def block_instance_to_native(instance: BlockInstance, scale=1.0):
def block_instance_to_native(instance: BlockInstance, scale=1.0) -> bpy.types.Object:
"""
Convert BlockInstance to native
"""
+24 -16
View File
@@ -1,4 +1,5 @@
import bpy
from bpy.types import MeshVertColor, MeshVertex, Object
from specklepy.objects.geometry import Mesh, Curve, Interval, Box, Point, Polyline
from specklepy.objects.other import *
from bpy_speckle.functions import _report
@@ -13,12 +14,12 @@ UNITS = "m"
CAN_CONVERT_TO_SPECKLE = ("MESH", "CURVE", "EMPTY")
def convert_to_speckle(blender_object, scale, units, desgraph=None):
def convert_to_speckle(blender_object: Object, scale: float, units: str, desgraph=None) -> list | None:
global UNITS
UNITS = units
blender_type = blender_object.type
if blender_type not in CAN_CONVERT_TO_SPECKLE:
return
return None
speckle_objects = []
speckle_material = material_to_speckle(blender_object)
@@ -54,10 +55,11 @@ def convert_to_speckle(blender_object, scale, units, desgraph=None):
return speckle_objects
def mesh_to_speckle(blender_object, data, scale=1.0):
def mesh_to_speckle(blender_object: Object, data: bpy.types.Mesh, scale=1.0) -> List[Mesh]:
if data.loop_triangles is None or len(data.loop_triangles) < 1:
data.calc_loop_triangles()
mat = blender_object.matrix_world
verts = [tuple(mat @ x.co * scale) for x in data.vertices]
@@ -89,10 +91,16 @@ def mesh_to_speckle(blender_object, data, scale=1.0):
sm.faces.append(n)
sm.faces.extend(f)
# TODO: figure out how to align vertex colors and vertices consistantly in receiving applications
# we are seeing the same issue as with texture coordinate alignment
#if data.color_attributes.active_color:
# sm.colors = [to_argb_int(x.color) for x in data.color_attributes.active_color.data]
return [sm]
def bezier_to_speckle(matrix, spline, scale, name=None):
def bezier_to_speckle(matrix: List[float], spline: bpy.types.Spline, scale: float, name:str = None) -> Curve:
degree = 3
closed = spline.use_cyclic_u
@@ -141,7 +149,7 @@ def bezier_to_speckle(matrix, spline, scale, name=None):
)
def nurbs_to_speckle(matrix, spline, scale, name=None):
def nurbs_to_speckle(matrix: List[float], spline: bpy.types.Spline, scale: float, name:str = None) -> Curve:
knots = make_knots(spline)
points = [tuple(matrix @ pt.co.xyz * scale) for pt in spline.points]
degree = spline.order_u - 1
@@ -167,7 +175,7 @@ def nurbs_to_speckle(matrix, spline, scale, name=None):
)
def poly_to_speckle(matrix, spline, scale, name=None):
def poly_to_speckle(matrix: List[float], spline: bpy.types.Spline, scale: float, name: str = None) -> Polyline:
points = [tuple(matrix @ pt.co.xyz * scale) for pt in spline.points]
length = spline.calc_length()
@@ -184,7 +192,7 @@ def poly_to_speckle(matrix, spline, scale, name=None):
)
def icurve_to_speckle(blender_object, data, scale=1.0):
def icurve_to_speckle(blender_object: Object, data: bpy.types.Curve, scale=1.0) -> List[Base] | None:
UNITS = "m" if bpy.context.scene.unit_settings.system == "METRIC" else "ft"
if blender_object.type != "CURVE":
@@ -213,7 +221,7 @@ def icurve_to_speckle(blender_object, data, scale=1.0):
return curves
def ngons_to_speckle_polylines(blender_object, data, scale=1.0):
def ngons_to_speckle_polylines(blender_object: Object, data: bpy.types.Mesh, scale=1.0) -> List[Polyline] | None:
UNITS = "m" if bpy.context.scene.unit_settings.system == "METRIC" else "ft"
if blender_object.type != "MESH":
@@ -245,14 +253,14 @@ def ngons_to_speckle_polylines(blender_object, data, scale=1.0):
return polylines
def material_to_speckle(blender_object) -> RenderMaterial:
def material_to_speckle(blender_object: Object) -> RenderMaterial | None:
"""Create and return a render material from a blender object"""
if not getattr(blender_object.data, "materials", None):
return
return None
blender_mat = blender_object.data.materials[0]
blender_mat: bpy.types.Material = blender_object.data.materials[0]
if not blender_mat:
return
return None
speckle_mat = RenderMaterial()
speckle_mat.name = blender_mat.name
@@ -275,7 +283,7 @@ def material_to_speckle(blender_object) -> RenderMaterial:
return speckle_mat
def transform_to_speckle(blender_transform, scale=1.0):
def transform_to_speckle(blender_transform: List[float], scale=1.0) -> Transform:
value = [y for x in blender_transform for y in x]
# scale the translation
for i in (3, 7, 11):
@@ -284,7 +292,7 @@ def transform_to_speckle(blender_transform, scale=1.0):
return Transform(value=value, units=UNITS)
def block_def_to_speckle(blender_definition, scale=1.0):
def block_def_to_speckle(blender_definition: bpy.types.Collection, scale=1.0) -> BlockDefinition:
geometry = []
for geo in blender_definition.objects:
geometry.extend(convert_to_speckle(geo, scale, UNITS))
@@ -299,7 +307,7 @@ def block_def_to_speckle(blender_definition, scale=1.0):
return block_def
def block_instance_to_speckle(blender_instance, scale=1.0):
def block_instance_to_speckle(blender_instance: Object, scale=1.0):
return BlockInstance(
blockDefinition=block_def_to_speckle(
blender_instance.instance_collection, scale
@@ -310,7 +318,7 @@ def block_instance_to_speckle(blender_instance, scale=1.0):
)
def empty_to_speckle(blender_object, scale=1.0):
def empty_to_speckle(blender_object: Object, scale=1.0) -> BlockInstance | None:
# probably an instance collection (block) so let's try it
try:
geo = blender_object.instance_collection.objects.items()
+10 -6
View File
@@ -1,9 +1,13 @@
import math
from typing import Tuple
from bmesh.types import BMesh
import bpy, struct, idprop
from specklepy.objects.base import Base
from specklepy.objects.geometry import Mesh
from specklepy.serialization.base_object_serializer import BaseObjectSerializer
from bpy_speckle.functions import _report
from bpy.types import Object
IGNORED_PROPERTY_KEYS = {
"id",
@@ -38,7 +42,7 @@ def to_argb_int(diffuse_colour) -> int:
return int.from_bytes(diffuse_colour, byteorder="big", signed=True)
def add_custom_properties(speckle_object, blender_object):
def add_custom_properties(speckle_object: Base, blender_object: Object):
if blender_object is None:
return
@@ -69,7 +73,7 @@ def add_custom_properties(speckle_object, blender_object):
blender_object[k] = v
def add_blender_material(speckle_object, blender_object) -> None:
def add_blender_material(speckle_object: Base, blender_object: Object) -> None:
"""Add material to a blender object if the corresponding speckle object has a render material"""
if blender_object.data is None:
return
@@ -107,7 +111,7 @@ def add_blender_material(speckle_object, blender_object) -> None:
blender_object.data.materials.append(blender_mat)
def add_vertices(speckle_mesh, blender_mesh, scale=1.0):
def add_vertices(speckle_mesh: Mesh, blender_mesh: BMesh, scale=1.0):
sverts = speckle_mesh.vertices
if sverts and len(sverts) > 0:
@@ -123,7 +127,7 @@ def add_vertices(speckle_mesh, blender_mesh, scale=1.0):
blender_mesh.verts.ensure_lookup_table()
def add_faces(speckle_mesh, blender_mesh, smooth=False):
def add_faces(speckle_mesh: Mesh, blender_mesh: BMesh, smooth=False):
sfaces = speckle_mesh.faces
if sfaces and len(sfaces) > 0:
@@ -147,7 +151,7 @@ def add_faces(speckle_mesh, blender_mesh, smooth=False):
blender_mesh.verts.index_update()
def add_colors(speckle_mesh, blender_mesh):
def add_colors(speckle_mesh: Mesh, blender_mesh: BMesh):
scolors = speckle_mesh.colors
@@ -178,7 +182,7 @@ def add_colors(speckle_mesh, blender_mesh):
loop[color_layer] = colors[loop.vert.index]
def add_uv_coords(speckle_mesh, blender_mesh):
def add_uv_coords(speckle_mesh: Mesh, blender_mesh: BMesh):
s_uvs = speckle_mesh.textureCoordinates
if not s_uvs:
return
+1 -1
View File
@@ -33,7 +33,7 @@ def _report(msg):
print("SpeckleBlender: {}".format(msg))
def get_scale_length(units):
def get_scale_length(units: str) -> float:
if units.lower() in unit_scale.keys():
return unit_scale[units]
_report("Units <{}> are not supported.".format(units))
+136 -23
View File
@@ -2,7 +2,8 @@
Stream operators
"""
from itertools import chain
from typing import Dict
from math import radians
from typing import Callable, Dict, Iterable
import bpy
from specklepy.api.models import Commit
import webbrowser
@@ -10,10 +11,10 @@ from bpy.props import (
StringProperty,
BoolProperty,
)
from bpy.types import Context, Object
from bpy_speckle.convert.to_native import can_convert_to_native, convert_to_native
from bpy_speckle.convert.to_speckle import (
convert_to_speckle,
ngons_to_speckle_polylines,
)
from bpy_speckle.functions import (
_check_speckle_client_user_stream,
@@ -24,15 +25,16 @@ from bpy_speckle.convert import get_speckle_subobjects
from bpy_speckle.clients import speckle_clients
from bpy_speckle.operators.users import add_user_stream
from specklepy.api import operations
from specklepy.api import operations, host_applications
from specklepy.api.wrapper import StreamWrapper
from specklepy.api.resources.stream import Stream
from specklepy.transports.server import ServerTransport
from specklepy.objects.geometry import *
from specklepy.logging.exceptions import SpeckleException
from specklepy.logging import metrics
def get_objects_collections(base) -> Dict:
def get_objects_collections(base: Base) -> Dict[str, list]:
"""Create collections based on the dynamic members on a root commit object"""
collections = {}
for name in base.get_dynamic_member_names():
@@ -47,7 +49,7 @@ def get_objects_collections(base) -> Dict:
return collections
def get_objects_nested_lists(items, parent_col=None) -> List:
def get_objects_nested_lists(items: list, parent_col: bpy.types.Collection = None) -> List:
"""For handling the weird nested lists that come from Grasshopper"""
objects = []
@@ -64,7 +66,7 @@ def get_objects_nested_lists(items, parent_col=None) -> List:
return objects
def get_objects_collections_recursive(base, parent_col=None) -> List:
def get_objects_collections_recursive(base: Base, parent_col: bpy.types.Collection = 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
if can_convert_to_native(base):
@@ -94,7 +96,47 @@ def get_objects_collections_recursive(base, parent_col=None) -> List:
return objects
def bases_to_native(context, collections, scale, stream_id, func=None):
ObjectCallback = Callable[[bpy.types.Context, Object, Base], Object] | None
ReceiveCompleteCallback = Callable[[bpy.types.Context, Dict[str, Object]], None] | None
def get_receive_funcs(context: Context, created_objects: Dict[str, Object]) -> tuple[ObjectCallback, ReceiveCompleteCallback]:
"""
Fetches the injected callback functions from user specified "Receive Script"
"""
objectCallback: ObjectCallback = None
receiveCompleteCallback: ReceiveCompleteCallback = None
if context.scene.speckle.receive_script in bpy.data.texts:
mod = bpy.data.texts[context.scene.speckle.receive_script].as_module()
if hasattr(mod, "execute_for_each"):
objectCallback = mod.execute_for_each
elif hasattr(mod, "execute"):
objectCallback = lambda c, o, _ : mod.execute(c.scene, o)
if hasattr(mod, "execute_for_all"):
receiveCompleteCallback = mod.execute_for_all
progress = 0
def for_each_object(context: bpy.types.Context, obj: Object, base: Base) -> Object:
nonlocal progress
nonlocal created_objects
nonlocal objectCallback
progress += 1 #TODO: Progress bar never reaches 100 because func is only called for convertible objects
context.window_manager.progress_update(progress)
created_objects[obj.name] = obj
if objectCallback:
return objectCallback(context, obj, base)
else:
return obj
return (for_each_object, receiveCompleteCallback)
def bases_to_native(context: bpy.types.Context, collections: Dict[str, list], scale: float, stream_id: str, func: ObjectCallback = None):
for col_name, objects in collections.items():
col = bpy.data.collections[col_name]
existing = get_existing_collection_objs(col)
@@ -126,7 +168,15 @@ def bases_to_native(context, collections, scale, stream_id, func=None):
context.area.tag_redraw()
def base_to_native(context, base, scale, stream_id, col, existing, func=None):
def base_to_native(context: bpy.types.Context,
base: Base,
scale: float,
stream_id: str,
col: bpy.types.Collection,
existing: Dict[str, Object],
func: ObjectCallback = None
):
new_objects = convert_to_native(base)
if not isinstance(new_objects, list):
new_objects = [new_objects]
@@ -149,7 +199,7 @@ def base_to_native(context, base, scale, stream_id, col, existing, func=None):
Run injected function
"""
if func:
new_object = func(context.scene, new_object)
new_object = func(context, new_object, base) #this base object isn't the right one for hosted elements!
if (
new_object is None
@@ -170,7 +220,7 @@ def base_to_native(context, base, scale, stream_id, col, existing, func=None):
col.objects.link(new_object)
def create_collection(name, clear_collection=True):
def create_collection(name: str, clear_collection=True) -> bpy.types.Collection:
if name in bpy.data.collections:
col = bpy.data.collections[name]
if clear_collection:
@@ -182,13 +232,13 @@ def create_collection(name, clear_collection=True):
return col
def create_child_collections(parent_col, children_names):
def create_child_collections(parent_col: bpy.types.Collection, children_names: Iterable[str]):
for name in children_names:
col = create_collection(name)
parent_col.children.link(col)
def get_existing_collection_objs(col):
def get_existing_collection_objs(col: bpy.types.Collection) -> Dict[str, bpy.types.Object]:
return {
obj.speckle.object_id: obj for obj in col.objects if obj.speckle.object_id != ""
}
@@ -229,7 +279,6 @@ def create_nested_hierarchy(base, hierarchy, objects):
return base
class ReceiveStreamObjects(bpy.types.Operator):
"""
Receive stream objects
@@ -240,6 +289,49 @@ class ReceiveStreamObjects(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
bl_description = "Receive objects from active stream"
clean_meshes: BoolProperty(name="Clean Meshes", default=False)
def draw(self, context):
layout = self.layout
col = layout.column()
col.prop(self, "clean_meshes")
def invoke(self, context, event):
return context.window_manager.invoke_props_dialog(self)
@staticmethod
def clean_converted_meshes(context: bpy.types.Context, convertedObjects: dict[str, Object]):
bpy.ops.object.select_all(action='DESELECT')
active = None
for obj in convertedObjects.values():
if obj.type != 'MESH':
continue
# This seems to be required inorder to select the object here
if obj.name not in context.scene.collection.objects:
context.scene.collection.objects.link(obj)
obj.select_set(True, view_layer=context.scene.view_layers[0])
active = obj
if active == None:
return
context.view_layer.objects.active = active
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.remove_doubles()
bpy.ops.mesh.dissolve_limited(angle_limit=radians(0.1))
# Reset state to previous (not quite sure if this is 100% necessary)
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
bpy.context.view_layer.objects.active = None
def execute(self, context):
bpy.context.view_layer.objects.active = None
@@ -266,10 +358,19 @@ class ReceiveStreamObjects(bpy.types.Operator):
_report("No commits found. Probably an empty stream.")
return {"CANCELLED"}
commit = branch.commits.items[int(bbranch.commit)]
commit: Commit = branch.commits.items[int(bbranch.commit)]
transport = ServerTransport(stream.id, client)
stream_data = operations.receive(commit.referencedObject, transport)
metrics.track(
metrics.RECEIVE,
getattr(transport, "account", None),
custom_props={
"sourceHostApp": host_applications.get_host_app_from_string(commit.sourceApplication).slug,
"sourceHostAppVersion": commit.sourceApplication
},
)
stream_data = operations._untracked_receive(commit.referencedObject, transport)
client.commit.received(
bstream.id,
commit.id,
@@ -277,6 +378,9 @@ class ReceiveStreamObjects(bpy.types.Operator):
message="received commit from Speckle Blender",
)
context.window_manager.progress_begin(0, stream_data.totalChildrenCount)
"""
Create or get Collection for stream objects
"""
@@ -288,8 +392,8 @@ class ReceiveStreamObjects(bpy.types.Operator):
name = "{} [ {} @ {} ]".format(stream.name, branch.name, commit.id)
col = create_collection(name)
col.speckle.stream_id = stream.id
col.speckle.name = stream.name
col.speckle.units = stream_data.units
col.speckle.units = stream_data.units or "m"
if col.name not in bpy.context.scene.collection.children:
bpy.context.scene.collection.children.link(col)
@@ -302,25 +406,34 @@ class ReceiveStreamObjects(bpy.types.Operator):
Set conversion scale from stream units
"""
scale = (
get_scale_length(stream_data.units)
get_scale_length(col.speckle.units)
/ context.scene.unit_settings.scale_length
)
"""
Get script from text editor for injection
"""
func = None
if context.scene.speckle.receive_script in bpy.data.texts:
mod = bpy.data.texts[context.scene.speckle.receive_script].as_module()
if hasattr(mod, "execute"):
func = mod.execute
created_objects = {}
(func, on_complete) = get_receive_funcs(context, created_objects)
"""
Iterate through retrieved resources
"""
bases_to_native(context, collections, scale, stream.id, func)
context.window_manager.progress_end()
if self.clean_meshes:
self.clean_converted_meshes(context, created_objects)
if on_complete:
on_complete(context, created_objects)
return {"FINISHED"}
class SendStreamObjects(bpy.types.Operator):
+2 -1
View File
@@ -5,6 +5,7 @@ import bpy
from bpy_speckle.functions import _report
from bpy_speckle.clients import speckle_clients
from specklepy.api.client import SpeckleClient
from specklepy.api.models import Stream, User
from specklepy.api.credentials import get_local_accounts
from datetime import datetime
@@ -59,7 +60,7 @@ class LoadUsers(bpy.types.Operator):
return {"FINISHED"}
def add_user_stream(user, stream):
def add_user_stream(user: User, stream: Stream):
s = user.streams.add()
s.name = stream.name
s.id = stream.id
Generated
+379 -699
View File
File diff suppressed because it is too large Load Diff
+5 -2
View File
@@ -2,12 +2,12 @@
name = "speckle-blender"
version = "2.0.0"
description = "the Speckle 2.0 connector for Blender!"
authors = ["izzy lyseggen <izzy.lyseggen@gmail.com>"]
authors = ["izzy lyseggen <izzy.lyseggen@gmail.com>", "Gergő Jedlicska <gergo@jedlicska.com>"]
license = "Apache-2.0"
[tool.poetry.dependencies]
python = ">=3.8, <4.0.0"
specklepy = "^2.6.6"
specklepy = "^2.9.0"
[tool.poetry.dev-dependencies]
devtools = "^0.6.1"
@@ -16,6 +16,9 @@ fake-bpy-module-latest = "^20220401"
black = "^21.12b0"
pylint = "^2.12.2"
[tool.poetry.group.local_specklepy.dependencies]
specklepy = {path = "../specklepy", develop = true}
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"