Compare commits
22 Commits
2.7.0
...
2.9.0-beta2
| Author | SHA1 | Date | |
|---|---|---|---|
| 548b3ad352 | |||
| d2deecf099 | |||
| fbda0110cd | |||
| ba9ad9ac07 | |||
| d13db2b44a | |||
| 2c127c85f3 | |||
| cc099c0ff1 | |||
| 448ab70c3b | |||
| 020eba2727 | |||
| d8117e2c30 | |||
| 57a6d88e6b | |||
| 74cb3e5f85 | |||
| d0aeecc863 | |||
| 2803308c5e | |||
| 326f04f67d | |||
| 68972ba8f9 | |||
| 73a028b56f | |||
| 33890ef0ee | |||
| 53fe676ab6 | |||
| f027c7eca4 | |||
| ea2b6dfb0e | |||
| 83610cec38 |
+90
-60
@@ -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
@@ -10,4 +10,5 @@ __pycache__/
|
||||
# dev
|
||||
.venv
|
||||
Installers/
|
||||
modules/
|
||||
modules/
|
||||
.tool-versions
|
||||
@@ -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!
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
"""
|
||||
Permanent handle on all user clients
|
||||
"""
|
||||
speckle_clients = []
|
||||
from specklepy.api.client import SpeckleClient
|
||||
|
||||
|
||||
speckle_clients: list[SpeckleClient] = []
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
"""
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
+5
-2
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user