Compare commits
38 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fbda0110cd | |||
| ba9ad9ac07 | |||
| d13db2b44a | |||
| 2c127c85f3 | |||
| cc099c0ff1 | |||
| 448ab70c3b | |||
| 020eba2727 | |||
| d8117e2c30 | |||
| 57a6d88e6b | |||
| 74cb3e5f85 | |||
| d0aeecc863 | |||
| 2803308c5e | |||
| 326f04f67d | |||
| 68972ba8f9 | |||
| 73a028b56f | |||
| 33890ef0ee | |||
| 53fe676ab6 | |||
| f027c7eca4 | |||
| ea2b6dfb0e | |||
| 83610cec38 | |||
| 2ed0685b10 | |||
| 877e616188 | |||
| 2e3e258a9a | |||
| 1e1c790eb4 | |||
| 2148fe8dee | |||
| 41c87a8661 | |||
| 96c9add526 | |||
| f425316e60 | |||
| abf363894e | |||
| 8fc8b97b7a | |||
| 64b4175585 | |||
| 4eefda3305 | |||
| f7275140d5 | |||
| 17a36f4fc2 | |||
| ba931e8205 | |||
| 572925cfbb | |||
| 03f94d6371 | |||
| c220337aec |
+248
-63
@@ -7,21 +7,25 @@ orbs:
|
||||
# Upload artifacts to s3
|
||||
aws-s3: circleci/aws-s3@2.0.0
|
||||
|
||||
jobs:
|
||||
build-connector: # Reusable job for basic connectors
|
||||
executor:
|
||||
name: win/default # comes with python 3.7.3
|
||||
shell: cmd.exe
|
||||
commands:
|
||||
install-specklepy-windows: # Reusable job for installing `specklepy` for windows machines
|
||||
parameters:
|
||||
slug:
|
||||
python-version:
|
||||
type: string
|
||||
default: ""
|
||||
default: "" # leave blank for blender v2.93
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: ./
|
||||
- run:
|
||||
name: Install specklepy with python 3.7
|
||||
- when:
|
||||
condition: << parameters.python-version >>
|
||||
steps:
|
||||
- run:
|
||||
name: Upgrade python version << parameters.python-version >>
|
||||
shell: powershell.exe
|
||||
command: |
|
||||
choco upgrade python --version=<< parameters.python-version >>
|
||||
refreshenv
|
||||
python --version
|
||||
- run:
|
||||
name: Install specklepy into modules directory
|
||||
shell: powershell.exe
|
||||
command: |
|
||||
$pyarr=(python --version).split(' ')[1].split('.')
|
||||
@@ -29,58 +33,146 @@ jobs:
|
||||
echo "using python version:" $pyver
|
||||
$specklepy=(python patch_version.py)
|
||||
python -m pip install --target=./modules-$pyver specklepy==$specklepy
|
||||
- run:
|
||||
name: Install python 3.9 and specklepy
|
||||
shell: powershell.exe
|
||||
command: |
|
||||
choco upgrade python --version=3.9.7
|
||||
refreshenv
|
||||
$pyarr=(py --version).split(' ')[1].split('.')
|
||||
$pyver=($pyarr[0..1] -join '.')
|
||||
echo "using python version:" $pyver
|
||||
$specklepy=(python patch_version.py)
|
||||
py -m pip install --target=./modules-$pyver specklepy==$specklepy
|
||||
|
||||
jobs:
|
||||
build-connector-win: # Reusable job for basic connectors
|
||||
executor:
|
||||
name: win/default # comes with python 3.7.3
|
||||
shell: cmd.exe
|
||||
parameters:
|
||||
slug:
|
||||
type: string
|
||||
default: "blender"
|
||||
installer:
|
||||
type: boolean
|
||||
default: false
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: ./
|
||||
- install-specklepy-windows
|
||||
- install-specklepy-windows:
|
||||
python-version: "3.9.10"
|
||||
- install-specklepy-windows:
|
||||
python-version: "3.10.2"
|
||||
- run:
|
||||
name: Patch
|
||||
shell: powershell.exe
|
||||
command:
|
||||
| # If no tag, use 0.0.0.1 and don't make any YML (for testing only!)
|
||||
$tag = if([string]::IsNullOrEmpty($env:CIRCLE_TAG)) { "0.0.1" } else { $env:CIRCLE_TAG }
|
||||
$semver = $tag.replace("-beta","")
|
||||
$version = "$($semver).$($env:CIRCLE_BUILD_NUM)"
|
||||
$channel = "latest"
|
||||
if($tag -like "*-beta") { $channel = "beta" }
|
||||
# only create the yml if we have a tag
|
||||
New-Item -Force "speckle-sharp-ci-tools/Installers/blender/$channel.yml" -ItemType File -Value "version: $version"
|
||||
echo $version
|
||||
ls
|
||||
python patch_version.py $version
|
||||
speckle-sharp-ci-tools\InnoSetup\ISCC.exe speckle-sharp-ci-tools\blender.iss
|
||||
- persist_to_workspace:
|
||||
root: ./
|
||||
paths:
|
||||
- speckle-sharp-ci-tools/Installers
|
||||
$tag = if([string]::IsNullOrEmpty($env:CIRCLE_TAG)) { "0.0.0" } else { $env:CIRCLE_TAG }
|
||||
$semver = if($tag.Contains('/')) {$tag.Split("/")[1] } else { $tag }
|
||||
$ver = if($semver.Contains('-')) {$semver.Split("-")[0] } else { $semver }
|
||||
$version = "$($ver).$($env:CIRCLE_BUILD_NUM)"
|
||||
$channel = if($semver.Contains('-')) { "prerelease" } else { "latest" }
|
||||
New-Item -Force "speckle-sharp-ci-tools/Installers/blender/$channel.yml" -ItemType File -Value "version: $semver"
|
||||
echo $semver
|
||||
python patch_version.py $semver
|
||||
- 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:
|
||||
- persist_to_workspace:
|
||||
root: ./
|
||||
paths:
|
||||
- speckle-sharp-ci-tools/Installers
|
||||
|
||||
install-specklepy: # due to ujson dep, we need to match the py version to the install
|
||||
docker:
|
||||
- image: "cimg/python:<<parameters.tag>>"
|
||||
build-connector-mac:
|
||||
macos:
|
||||
xcode: 12.5.1
|
||||
parameters:
|
||||
tag:
|
||||
default: "3.9"
|
||||
slug:
|
||||
type: string
|
||||
default: "blender-mac"
|
||||
installername:
|
||||
type: string
|
||||
default: "SpeckleBlenderInstall"
|
||||
runtime:
|
||||
type: string
|
||||
default: "osx-x64"
|
||||
installer:
|
||||
type: boolean
|
||||
default: false
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: ./
|
||||
- run:
|
||||
name: upgrade pip and install specklepy
|
||||
name: Install mono
|
||||
command: |
|
||||
specklepyver=$(python patch_version.py)
|
||||
echo installing specklepy $specklepyver
|
||||
python -m pip install --target=./modules-<<parameters.tag>> specklepy==$specklepyver
|
||||
- persist_to_workspace:
|
||||
root: ./
|
||||
paths:
|
||||
- modules-*
|
||||
|
||||
HOMEBREW_NO_AUTO_UPDATE=1 brew install mono
|
||||
# Compress build files
|
||||
- run:
|
||||
name: Install dotnet
|
||||
command: curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin
|
||||
- run:
|
||||
name: Build & Patch Version
|
||||
command: |
|
||||
TAG=$(if [ "${CIRCLE_TAG}" ]; then echo $CIRCLE_TAG; else echo "0.0.0"; fi;)
|
||||
SEMVER=$(echo "$TAG" | sed -e 's/[a-zA-Z-]*\///')
|
||||
VER=$(echo "$SEMVER" | sed -e 's/-beta//')
|
||||
VERSION=$(echo $VER.$CIRCLE_BUILD_NUM)
|
||||
CHANNEL=$(if [[ "$VERSION" == *"-"* ]]; then echo $(cut -d "-" -f2 \<\<\< $VERSION); else echo latest; fi)
|
||||
mkdir -p speckle-sharp-ci-tools/Installers/<< parameters.slug >>
|
||||
if [ "${CIRCLE_TAG}" ]; then echo "version: $SEMVER" > "speckle-sharp-ci-tools/Installers/<< parameters.slug >>/$CHANNEL.yml"; fi
|
||||
python3 patch_version.py $SEMVER
|
||||
# update python and package dependencies
|
||||
- when:
|
||||
condition:
|
||||
and:
|
||||
# - << parameters.installer >>
|
||||
- equal: [osx-x64, << parameters.runtime >>]
|
||||
steps:
|
||||
- run:
|
||||
name: Install python 3.10
|
||||
command: |
|
||||
brew install python@3.10
|
||||
brew link --overwrite python@3.10
|
||||
- run:
|
||||
name: Package specklepy dependencies for blender 3.1 & 3.2
|
||||
command: |
|
||||
python3 --version
|
||||
python3 -m pip install --target=./modules-intel-3.10 specklepy==$(python3 -m patch_version)
|
||||
zip -r modules-intel-3.10.zip modules-intel-3.10/
|
||||
cp modules-intel-3.10.zip speckle-sharp-ci-tools/Mac/<<parameters.installername>>/.installationFiles
|
||||
- run:
|
||||
name: Zip Connector files
|
||||
command: |
|
||||
zip -r << parameters.slug >>.zip bpy_speckle/
|
||||
- run:
|
||||
name: Copy connector files to installer
|
||||
command: |
|
||||
mkdir -p speckle-sharp-ci-tools/Mac/<<parameters.installername>>/.installationFiles/
|
||||
cp << parameters.slug >>.zip speckle-sharp-ci-tools/Mac/<<parameters.installername>>/.installationFiles
|
||||
python3 patch_version.py > speckle-sharp-ci-tools/Mac/<<parameters.installername>>/.installationFiles/specklepy_ver.yml
|
||||
|
||||
- run:
|
||||
name: Build Mac installer
|
||||
command: ~/.dotnet/dotnet publish speckle-sharp-ci-tools/Mac/<<parameters.installername>>/<<parameters.installername>>.sln -r << parameters.runtime >> -c Release
|
||||
- run:
|
||||
name: Zip installer
|
||||
command: |
|
||||
cd speckle-sharp-ci-tools/Mac/<<parameters.installername>>/bin/Release/net6.0/<< parameters.runtime >>/
|
||||
zip -r << parameters.slug >>.zip ./
|
||||
- store_artifacts:
|
||||
path: speckle-sharp-ci-tools/Mac/<<parameters.installername>>/bin/Release/net6.0/<< parameters.runtime >>/<< parameters.slug >>.zip
|
||||
- run:
|
||||
name: Copy to installer location
|
||||
command: |
|
||||
TAG=$(if [ "${CIRCLE_TAG}" ]; then echo $CIRCLE_TAG; else echo "0.0.0"; fi;)
|
||||
SEMVER=$(echo "$TAG" | sed -e 's/[a-zA-Z-]*\///')
|
||||
cp speckle-sharp-ci-tools/Mac/<<parameters.installername>>/bin/Release/net6.0/<< parameters.runtime >>/<< parameters.slug >>.zip speckle-sharp-ci-tools/Installers/<< parameters.slug >>/<< parameters.slug >>-$SEMVER.zip
|
||||
- when:
|
||||
condition: << parameters.installer >>
|
||||
steps:
|
||||
- persist_to_workspace:
|
||||
root: ./
|
||||
paths:
|
||||
- speckle-sharp-ci-tools/Installers
|
||||
|
||||
get-ci-tools: # Clones our ci tools and persists them to the workspace
|
||||
docker:
|
||||
- image: cimg/base:2021.01
|
||||
@@ -110,34 +202,127 @@ jobs:
|
||||
from: '"speckle-sharp-ci-tools/Installers/"'
|
||||
to: s3://speckle-releases/installers/
|
||||
|
||||
deploy-manager2:
|
||||
docker:
|
||||
- image: mcr.microsoft.com/dotnet/sdk:6.0
|
||||
parameters:
|
||||
slug:
|
||||
type: string
|
||||
os:
|
||||
type: string
|
||||
extension:
|
||||
type: string
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: ./
|
||||
- run:
|
||||
name: Install Manager Feed CLI
|
||||
command: dotnet tool install --global Speckle.Manager.Feed
|
||||
- run:
|
||||
name: Upload new version
|
||||
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 >>
|
||||
|
||||
workflows:
|
||||
main:
|
||||
build: # build the installers, but don't persist to workspace for deployment
|
||||
jobs:
|
||||
- get-ci-tools:
|
||||
filters:
|
||||
branches:
|
||||
only:
|
||||
- main
|
||||
- /ci\/.*/
|
||||
- build-connector-win:
|
||||
name: Windows Build
|
||||
requires:
|
||||
- get-ci-tools
|
||||
filters:
|
||||
branches:
|
||||
only:
|
||||
- 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
|
||||
deploy: # build installers and deploy
|
||||
jobs:
|
||||
- get-ci-tools:
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
branches:
|
||||
only:
|
||||
- main
|
||||
- /ci\/.*/
|
||||
- build-connector:
|
||||
ignore: /.*/
|
||||
|
||||
- build-connector-win:
|
||||
name: Windows Build&Deploy
|
||||
slug: blender
|
||||
installer: true
|
||||
requires:
|
||||
- get-ci-tools
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
only: /([0-9]+)\.([0-9]+)\.([0-9]+)(?:-\w+)?$/
|
||||
branches:
|
||||
only:
|
||||
- main
|
||||
- /ci\/.*/
|
||||
ignore: /.*/
|
||||
|
||||
- build-connector-mac:
|
||||
name: Mac ARM Build&Deploy
|
||||
slug: blender-mac-arm
|
||||
runtime: osx-arm64
|
||||
installer: true
|
||||
requires:
|
||||
- get-ci-tools
|
||||
filters:
|
||||
tags:
|
||||
only: /([0-9]+)\.([0-9]+)\.([0-9]+)(?:-\w+)?$/
|
||||
branches:
|
||||
ignore: /.*/
|
||||
|
||||
- build-connector-mac:
|
||||
name: Mac Intel Build&Deploy
|
||||
slug: blender-mac-intel
|
||||
runtime: osx-x64
|
||||
installer: true
|
||||
requires:
|
||||
- get-ci-tools
|
||||
filters:
|
||||
tags:
|
||||
only: /([0-9]+)\.([0-9]+)\.([0-9]+)(?:-\w+)?$/
|
||||
branches:
|
||||
ignore: /.*/
|
||||
|
||||
- deploy:
|
||||
requires:
|
||||
- get-ci-tools
|
||||
- build-connector
|
||||
- Windows Build&Deploy
|
||||
- Mac ARM Build&Deploy
|
||||
- Mac Intel Build&Deploy
|
||||
filters:
|
||||
tags:
|
||||
only: /[0-9]+(\.[0-9]+)*/
|
||||
only: /([0-9]+)\.([0-9]+)\.([0-9]+)(?:-\w+)?$/
|
||||
branches:
|
||||
ignore: /.*/
|
||||
|
||||
- deploy-manager2:
|
||||
slug: blender
|
||||
os: Win
|
||||
extension: exe
|
||||
requires:
|
||||
- deploy
|
||||
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!
|
||||
|
||||
+2
-38
@@ -1,25 +1,4 @@
|
||||
# MIT License
|
||||
|
||||
# Copyright (c) 2018-2021 Tom Svilans
|
||||
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
import bpy
|
||||
|
||||
bl_info = {
|
||||
"name": "SpeckleBlender 2.0",
|
||||
@@ -33,21 +12,7 @@ bl_info = {
|
||||
"category": "Scene",
|
||||
}
|
||||
|
||||
import bpy
|
||||
|
||||
"""
|
||||
Import PySpeckle and attempt install if not found
|
||||
"""
|
||||
|
||||
try:
|
||||
import specklepy
|
||||
except ModuleNotFoundError as error:
|
||||
print("Speckle not found.")
|
||||
# TODO: Implement automatic installation of speckle and dependencies
|
||||
# to the local Blender module folder
|
||||
|
||||
# from .install_dependencies import install_dependencies
|
||||
# install_dependencies()
|
||||
|
||||
"""
|
||||
Import SpeckleBlender classes
|
||||
@@ -67,7 +32,6 @@ Add load handler to initialize Speckle when
|
||||
loading a Blender file
|
||||
"""
|
||||
|
||||
|
||||
@persistent
|
||||
def load_handler(dummy):
|
||||
bpy.ops.speckle.users_load()
|
||||
@@ -95,7 +59,7 @@ def register():
|
||||
for cls in speckle_classes:
|
||||
register_class(cls)
|
||||
|
||||
metrics.set_host_app("Blender")
|
||||
metrics.set_host_app("blender", f"blender {bpy.app.version_string}")
|
||||
|
||||
"""
|
||||
Register all new properties
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
"""
|
||||
Permanent handle on all user clients
|
||||
"""
|
||||
speckle_clients = []
|
||||
from specklepy.api.client import SpeckleClient
|
||||
|
||||
|
||||
speckle_clients: list[SpeckleClient] = []
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
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 = []
|
||||
for key in attr.keys():
|
||||
keys = attr.keys() if isinstance(attr, dict) else attr.get_dynamic_member_names()
|
||||
for key in keys:
|
||||
if isinstance(attr[key], dict):
|
||||
subtype = attr[key].get("type", None)
|
||||
if subtype:
|
||||
|
||||
@@ -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,11 +26,11 @@ 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(
|
||||
speckle_object, "displayMesh", getattr(speckle_object, "displayValue", None)
|
||||
speckle_object, "displayValue", getattr(speckle_object, "displayMesh", None)
|
||||
):
|
||||
return True
|
||||
|
||||
@@ -37,49 +38,67 @@ 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
|
||||
or getattr(speckle_object, "name", None)
|
||||
or f"{speckle_object.speckle_type} -- {speckle_object.id}"
|
||||
)
|
||||
|
||||
# convert unsupported types with display values
|
||||
if speckle_type not in CAN_CONVERT_TO_NATIVE:
|
||||
elements = getattr(speckle_object, "elements", []) or []
|
||||
display = getattr(
|
||||
speckle_object, "displayMesh", getattr(speckle_object, "displayValue", None)
|
||||
speckle_object, "displayValue", getattr(speckle_object, "displayMesh", None)
|
||||
)
|
||||
if not display:
|
||||
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:
|
||||
elements.append(display)
|
||||
# TODO: depreciate the parent type
|
||||
# add parent type here so we can use it as a blender custom prop
|
||||
# not making it hidden, so it will get added on send as i think it might be helpful? can reconsider
|
||||
if isinstance(display, list):
|
||||
converted = []
|
||||
for item in display:
|
||||
item.parent_speckle_type = speckle_object.speckle_type
|
||||
converted.append(convert_to_native(item))
|
||||
return converted
|
||||
else:
|
||||
display.parent_speckle_type = speckle_object.speckle_type
|
||||
return convert_to_native(display, speckle_name)
|
||||
|
||||
units = getattr(speckle_object, "units", None)
|
||||
if units:
|
||||
scale = get_scale_length(units) / bpy.context.scene.unit_settings.scale_length
|
||||
|
||||
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):
|
||||
converted.extend(blender_object)
|
||||
else:
|
||||
add_custom_properties(speckle_object, blender_object)
|
||||
converted.append(blender_object)
|
||||
return converted
|
||||
|
||||
try:
|
||||
if speckle_type is Mesh:
|
||||
# convert breps
|
||||
if speckle_type is Brep:
|
||||
meshes = getattr(
|
||||
speckle_object, "displayValue", getattr(speckle_object, "displayMesh", iter([]))
|
||||
)
|
||||
if material := getattr(speckle_object, "renderMaterial", getattr(speckle_object, "@renderMaterial", None),):
|
||||
for mesh in meshes:
|
||||
mesh["renderMaterial"] = material
|
||||
|
||||
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 isinstance(speckle_object, Mesh):
|
||||
obj_data = mesh_to_native(speckle_object, name=speckle_name, scale=scale)
|
||||
elif speckle_type is Brep:
|
||||
obj_data = brep_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}")
|
||||
@@ -115,14 +134,7 @@ def convert_to_native(speckle_object, name=None):
|
||||
return blender_object
|
||||
|
||||
|
||||
def brep_to_native(speckle_brep, name, scale=1.0):
|
||||
display = getattr(
|
||||
speckle_brep, "displayMesh", getattr(speckle_brep, "displayValue", None)
|
||||
)
|
||||
return mesh_to_native(display, name, scale) if display else None
|
||||
|
||||
|
||||
def mesh_to_native(speckle_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]
|
||||
@@ -142,8 +154,7 @@ def mesh_to_native(speckle_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)
|
||||
|
||||
@@ -166,12 +177,8 @@ def line_to_native(speckle_curve, blender_curve, scale):
|
||||
return line
|
||||
|
||||
|
||||
def polyline_to_native(scurve, bcurve, scale):
|
||||
|
||||
# value = find_key_case_insensitive(scurve, "value")
|
||||
value = scurve.value
|
||||
|
||||
if value:
|
||||
def polyline_to_native(scurve: Polyline, bcurve: bpy.types.Curve, scale: float) -> bpy.types.Spline | None:
|
||||
if value := scurve.value:
|
||||
N = len(value) // 3
|
||||
|
||||
polyline = bcurve.splines.new("POLY")
|
||||
@@ -194,12 +201,8 @@ def polyline_to_native(scurve, bcurve, scale):
|
||||
return polyline
|
||||
|
||||
|
||||
def nurbs_to_native(scurve, bcurve, scale):
|
||||
|
||||
# points = find_key_case_insensitive(scurve, "points")
|
||||
points = scurve.points
|
||||
|
||||
if points:
|
||||
def nurbs_to_native(scurve: Curve, bcurve: bpy.types.Curve, scale: float) -> bpy.types.Spline | None:
|
||||
if points := scurve.points:
|
||||
N = len(points) // 3
|
||||
|
||||
nurbs = bcurve.splines.new("NURBS")
|
||||
@@ -227,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])
|
||||
|
||||
@@ -289,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
|
||||
"""
|
||||
@@ -308,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)
|
||||
@@ -322,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}")
|
||||
@@ -341,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],
|
||||
@@ -356,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
|
||||
@@ -364,8 +367,7 @@ def block_def_to_native(definition: BlockDefinition, scale=1.0):
|
||||
native_def = bpy.data.collections.new(definition.name)
|
||||
native_def["applicationId"] = definition.applicationId
|
||||
for geo in definition.geometry:
|
||||
b_obj = convert_to_native(geo)
|
||||
if b_obj:
|
||||
if b_obj := convert_to_native(geo):
|
||||
native_def.objects.link(
|
||||
b_obj
|
||||
if isinstance(b_obj, bpy_types.Object)
|
||||
@@ -375,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
|
||||
"""
|
||||
@@ -383,6 +385,8 @@ def block_instance_to_native(instance: BlockInstance, scale=1.0):
|
||||
native_def = block_def_to_native(instance.blockDefinition, scale)
|
||||
|
||||
native_instance = bpy.data.objects.new(name, None)
|
||||
add_custom_properties(instance, native_instance)
|
||||
native_instance["name"] = getattr(instance, 'name', None) or instance.blockDefinition.name
|
||||
# hide the instance axes so they don't clutter the viewport
|
||||
native_instance.empty_display_size = 0
|
||||
native_instance.instance_collection = native_def
|
||||
|
||||
@@ -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,10 +14,12 @@ UNITS = "m"
|
||||
CAN_CONVERT_TO_SPECKLE = ("MESH", "CURVE", "EMPTY")
|
||||
|
||||
|
||||
def convert_to_speckle(blender_object, scale, 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)
|
||||
@@ -52,10 +55,11 @@ def convert_to_speckle(blender_object, scale, 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]
|
||||
@@ -69,7 +73,7 @@ def mesh_to_speckle(blender_object, data, scale=1.0):
|
||||
faces=[],
|
||||
colors=[],
|
||||
textureCoordinates=[],
|
||||
units="m" if unit_system == "METRIC" else "ft",
|
||||
units=UNITS,
|
||||
bbox=Box(area=0.0, volume=0.0),
|
||||
)
|
||||
|
||||
@@ -87,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
|
||||
|
||||
@@ -103,9 +113,13 @@ def bezier_to_speckle(matrix, spline, scale, name=None):
|
||||
points.append(tuple(matrix @ bp.handle_right * scale))
|
||||
|
||||
if closed:
|
||||
points.append(tuple(matrix @ spline.bezier_points[-1].handle_right * scale))
|
||||
points.append(tuple(matrix @ spline.bezier_points[0].handle_left * scale))
|
||||
points.append(tuple(matrix @ spline.bezier_points[0].co * scale))
|
||||
points.extend(
|
||||
(
|
||||
tuple(matrix @ spline.bezier_points[-1].handle_right * scale),
|
||||
tuple(matrix @ spline.bezier_points[0].handle_left * scale),
|
||||
tuple(matrix @ spline.bezier_points[0].co * scale),
|
||||
)
|
||||
)
|
||||
|
||||
num_points = len(points)
|
||||
|
||||
@@ -135,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
|
||||
@@ -161,14 +175,14 @@ 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()
|
||||
domain = Interval(start=0, end=length, totalChildrenCount=0)
|
||||
return Polyline(
|
||||
name=name,
|
||||
closed=spline.use_cyclic_u,
|
||||
closed=bool(spline.use_cyclic_u),
|
||||
value=list(sum(points, ())), # magic (flatten list of tuples)
|
||||
length=length,
|
||||
domain=domain,
|
||||
@@ -178,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":
|
||||
@@ -207,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":
|
||||
@@ -239,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
|
||||
@@ -269,20 +283,19 @@ def material_to_speckle(blender_object) -> RenderMaterial:
|
||||
return speckle_mat
|
||||
|
||||
|
||||
def transform_to_speckle(blender_transform, scale=1.0):
|
||||
units = "m" if bpy.context.scene.unit_settings.system == "METRIC" else "ft"
|
||||
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):
|
||||
value[i] *= scale
|
||||
|
||||
return Transform(value=value, units=units)
|
||||
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))
|
||||
geometry.extend(convert_to_speckle(geo, scale, UNITS))
|
||||
block_def = BlockDefinition(
|
||||
units=UNITS,
|
||||
name=blender_definition.name,
|
||||
@@ -294,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
|
||||
@@ -305,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()
|
||||
|
||||
+78
-55
@@ -1,9 +1,28 @@
|
||||
import base64
|
||||
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",
|
||||
"elements",
|
||||
"displayMesh",
|
||||
"displayValue",
|
||||
"speckle_type",
|
||||
"parameters",
|
||||
"faces",
|
||||
"colors",
|
||||
"vertices",
|
||||
"renderMaterial",
|
||||
"textureCoordinates",
|
||||
"totalChildrenCount"
|
||||
}
|
||||
|
||||
|
||||
def to_rgba(argb_int: int) -> Tuple[float]:
|
||||
@@ -23,8 +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
|
||||
|
||||
@@ -34,15 +52,28 @@ def add_custom_properties(speckle_object, blender_object):
|
||||
app_id = getattr(speckle_object, "applicationId", None)
|
||||
if app_id:
|
||||
blender_object["applicationId"] = speckle_object.applicationId
|
||||
keys = speckle_object.get_dynamic_member_names() if "Geometry" in speckle_object.speckle_type else (set(speckle_object.get_member_names()) - IGNORED_PROPERTY_KEYS)
|
||||
for key in keys:
|
||||
val = getattr(speckle_object, key, None)
|
||||
if val is None:
|
||||
continue
|
||||
|
||||
for key in speckle_object.get_dynamic_member_names():
|
||||
if isinstance(speckle_object[key], (int, str, float)):
|
||||
blender_object[key] = speckle_object[key]
|
||||
elif isinstance(speckle_object[key], (dict, list)):
|
||||
blender_object[key] = serializer.traverse_value(speckle_object[key])
|
||||
if isinstance(val, (int, str, float)):
|
||||
blender_object[key] = val
|
||||
elif key == "properties" and isinstance(val, Base):
|
||||
val["applicationId"] = None
|
||||
add_custom_properties(val, blender_object)
|
||||
elif isinstance(val, list):
|
||||
items = [item for item in val if not isinstance(item, Base)]
|
||||
if items:
|
||||
blender_object[key] = items
|
||||
elif isinstance(val,dict):
|
||||
for (k,v) in val.items():
|
||||
if not isinstance(v, Base):
|
||||
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
|
||||
@@ -80,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:
|
||||
@@ -96,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:
|
||||
@@ -120,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
|
||||
|
||||
@@ -151,74 +182,66 @@ def add_colors(speckle_mesh, blender_mesh):
|
||||
loop[color_layer] = colors[loop.vert.index]
|
||||
|
||||
|
||||
def add_uv_coords(speckle_mesh, blender_mesh):
|
||||
if not hasattr(speckle_mesh, "properties"):
|
||||
def add_uv_coords(speckle_mesh: Mesh, blender_mesh: BMesh):
|
||||
s_uvs = speckle_mesh.textureCoordinates
|
||||
if not s_uvs:
|
||||
return
|
||||
try:
|
||||
uv = []
|
||||
|
||||
sprops = speckle_mesh.properties
|
||||
if sprops:
|
||||
texKey = ""
|
||||
if "texture_coordinates" in sprops.keys():
|
||||
texKey = "texture_coordinates"
|
||||
elif "TextureCoordinates" in sprops.keys():
|
||||
texKey = "TextureCoordinates"
|
||||
if len(s_uvs) // 2 == len(blender_mesh.verts):
|
||||
uv.extend(
|
||||
(float(s_uvs[i]), float(s_uvs[i + 1]))
|
||||
for i in range(0, len(s_uvs), 2)
|
||||
)
|
||||
else:
|
||||
_report(
|
||||
f"Failed to match UV coordinates to vert data. Blender mesh verts: {len(blender_mesh.verts)}, Speckle UVs * 2: {len(s_uvs) * 2}"
|
||||
)
|
||||
return
|
||||
|
||||
if texKey != "":
|
||||
# Make UVs
|
||||
uv_layer = blender_mesh.loops.layers.uv.verify()
|
||||
|
||||
try:
|
||||
decoded = base64.b64decode(sprops[texKey]).decode("utf-8")
|
||||
s_uvs = decoded.split()
|
||||
uv = []
|
||||
|
||||
if len(s_uvs) // 2 == len(blender_mesh.verts):
|
||||
for i in range(0, len(s_uvs), 2):
|
||||
uv.append((float(s_uvs[i]), float(s_uvs[i + 1])))
|
||||
else:
|
||||
_report(
|
||||
f"Failed to match UV coordinates to vert data. Blender mesh verts: {len(blender_mesh.verts)}, Speckle UVs * 2: {len(s_uvs) * 2}"
|
||||
)
|
||||
|
||||
# Make UVs
|
||||
uv_layer = blender_mesh.loops.layers.uv.verify()
|
||||
|
||||
for f in blender_mesh.faces:
|
||||
for l in f.loops:
|
||||
luv = l[uv_layer]
|
||||
luv.uv = uv[l.vert.index]
|
||||
except:
|
||||
_report("Failed to decode texture coordinates.")
|
||||
raise
|
||||
|
||||
del speckle_mesh.properties[texKey]
|
||||
for f in blender_mesh.faces:
|
||||
for l in f.loops:
|
||||
luv = l[uv_layer]
|
||||
luv.uv = uv[l.vert.index]
|
||||
except:
|
||||
_report("Failed to decode texture coordinates.")
|
||||
raise
|
||||
|
||||
|
||||
ignored_keys = (
|
||||
ignored_keys = {
|
||||
"id",
|
||||
"speckle",
|
||||
"speckle_type"
|
||||
"_speckle_type",
|
||||
"_speckle_name",
|
||||
"_speckle_transform",
|
||||
"_RNA_UI",
|
||||
"elements",
|
||||
"transform",
|
||||
"_units",
|
||||
"_chunkable",
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
def get_blender_custom_properties(obj, max_depth=1000):
|
||||
if max_depth < 0:
|
||||
return obj
|
||||
|
||||
if hasattr(obj, "keys"):
|
||||
keys = set(obj.keys()) - ignored_keys
|
||||
return {
|
||||
key: get_blender_custom_properties(obj[key], max_depth - 1)
|
||||
for key in obj.keys()
|
||||
if key not in ignored_keys and not key.startswith("_")
|
||||
for key in keys
|
||||
if not key.startswith("_")
|
||||
}
|
||||
|
||||
elif isinstance(obj, (list, tuple, idprop.types.IDPropertyArray)):
|
||||
if isinstance(obj, (list, tuple, idprop.types.IDPropertyArray)):
|
||||
return [get_blender_custom_properties(o, max_depth - 1) for o in obj]
|
||||
else:
|
||||
return obj
|
||||
|
||||
return obj
|
||||
|
||||
|
||||
"""
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -1,104 +0,0 @@
|
||||
import os, sys, bpy
|
||||
import ctypes, sys
|
||||
|
||||
import os, sys
|
||||
|
||||
|
||||
def modules_path():
|
||||
# set up addons/modules under the user
|
||||
# script path. Here we'll install the
|
||||
# dependencies
|
||||
modulespath = os.path.normpath(
|
||||
os.path.join(bpy.utils.script_path_user(), "addons", "modules")
|
||||
)
|
||||
if not os.path.exists(modulespath):
|
||||
os.makedirs(modulespath)
|
||||
|
||||
# set user modules path at beginning of paths for earlier hit
|
||||
if sys.path[1] != modulespath:
|
||||
sys.path.insert(1, modulespath)
|
||||
|
||||
return modulespath
|
||||
|
||||
|
||||
def install_dependencies():
|
||||
import sys
|
||||
import os
|
||||
|
||||
try:
|
||||
try:
|
||||
import pip
|
||||
except:
|
||||
print("Installing pip... "),
|
||||
from subprocess import run as sprun
|
||||
|
||||
res = sprun([bpy.app.binary_path_python, "-m", "ensurepip"])
|
||||
|
||||
if res.returncode == 0:
|
||||
import pip
|
||||
else:
|
||||
raise Exception("Failed to install pip.")
|
||||
|
||||
modulespath = modules_path()
|
||||
|
||||
if not os.path.exists(modulespath):
|
||||
os.makedirs(modulespath)
|
||||
|
||||
print("Installing speckle to {}... ".format(modulespath)),
|
||||
from subprocess import run as sprun
|
||||
|
||||
res = sprun(
|
||||
[
|
||||
bpy.app.binary_path_python,
|
||||
"-m",
|
||||
"pip",
|
||||
"install",
|
||||
"-q",
|
||||
"-t",
|
||||
"{}".format(modulespath),
|
||||
"--no-deps",
|
||||
"pydantic",
|
||||
]
|
||||
)
|
||||
res = sprun(
|
||||
[
|
||||
bpy.app.binary_path_python,
|
||||
"-m",
|
||||
"pip",
|
||||
"install",
|
||||
"-q",
|
||||
"-t",
|
||||
"{}".format(modulespath),
|
||||
"specklepy",
|
||||
]
|
||||
)
|
||||
|
||||
except:
|
||||
raise Exception(
|
||||
"Failed to install dependencies. Please make sure you have pip installed."
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
import specklepy
|
||||
except:
|
||||
print("Failed to load speckle.")
|
||||
from sys import platform
|
||||
|
||||
if platform == "win32":
|
||||
if ctypes.windll.shell32.IsUserAnAdmin():
|
||||
install_dependencies()
|
||||
import specklepy
|
||||
else:
|
||||
ctypes.windll.shell32.ShellExecuteW(
|
||||
None, "runas", sys.executable, __file__, None, 1
|
||||
)
|
||||
|
||||
else:
|
||||
print(
|
||||
"Platform {} cannot automatically install dependencies.".format(
|
||||
platform
|
||||
)
|
||||
)
|
||||
raise
|
||||
@@ -151,9 +151,10 @@ class UploadNgonsAsPolylines(bpy.types.Operator):
|
||||
client = speckle_clients[int(context.scene.speckle.active_user)]
|
||||
stream = user.streams[user.active_stream]
|
||||
|
||||
scale = context.scene.unit_settings.scale_length / get_scale_length(
|
||||
stream.units
|
||||
)
|
||||
# scale = context.scene.unit_settings.scale_length / get_scale_length(
|
||||
# stream.units
|
||||
# )
|
||||
scale = 1.0
|
||||
|
||||
sp = ngons_to_speckle_polylines(active, scale)
|
||||
|
||||
|
||||
@@ -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):
|
||||
@@ -75,10 +77,10 @@ def get_objects_collections_recursive(base, parent_col=None) -> List:
|
||||
|
||||
for name in base.get_dynamic_member_names():
|
||||
value = base[name]
|
||||
if name == "parameters" and "Revit" in base.speckle_type:
|
||||
continue
|
||||
if isinstance(value, list):
|
||||
for item in value:
|
||||
if isinstance(item, Base):
|
||||
objects.append(item)
|
||||
objects.extend(item for item in value if isinstance(item, Base))
|
||||
if isinstance(value, Base):
|
||||
col = parent_col.children.get(name)
|
||||
if not col:
|
||||
@@ -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,13 +199,12 @@ 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
|
||||
): # Make sure that the injected function returned an object
|
||||
new_obj = new_object
|
||||
_report("Script '{}' returned None.".format(func.__module__))
|
||||
_report(f"Script '{func.__module__}' returned None.")
|
||||
continue
|
||||
|
||||
new_object.speckle.stream_id = stream_id
|
||||
@@ -163,7 +212,7 @@ def base_to_native(context, base, scale, stream_id, col, existing, func=None):
|
||||
|
||||
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"
|
||||
existing[new_object.speckle.object_id].name = f"{name}__deleted"
|
||||
new_object.name = name
|
||||
col.objects.unlink(existing[new_object.speckle.object_id])
|
||||
|
||||
@@ -171,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:
|
||||
@@ -183,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 != ""
|
||||
}
|
||||
@@ -230,7 +279,6 @@ def create_nested_hierarchy(base, hierarchy, objects):
|
||||
|
||||
return base
|
||||
|
||||
|
||||
class ReceiveStreamObjects(bpy.types.Operator):
|
||||
"""
|
||||
Receive stream objects
|
||||
@@ -241,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
|
||||
|
||||
@@ -252,7 +343,7 @@ class ReceiveStreamObjects(bpy.types.Operator):
|
||||
|
||||
client = speckle_clients[int(context.scene.speckle.active_user)]
|
||||
|
||||
stream = client.stream.get(id=bstream.id)
|
||||
stream = client.stream.get(id=bstream.id, branch_limit=20)
|
||||
if stream.branches.totalCount < 1:
|
||||
return {"CANCELLED"}
|
||||
|
||||
@@ -267,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,
|
||||
@@ -278,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
|
||||
"""
|
||||
@@ -289,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)
|
||||
|
||||
@@ -303,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):
|
||||
@@ -375,9 +487,14 @@ class SendStreamObjects(bpy.types.Operator):
|
||||
|
||||
client = speckle_clients[int(context.scene.speckle.active_user)]
|
||||
|
||||
scale = context.scene.unit_settings.scale_length / get_scale_length(
|
||||
stream.units.lower()
|
||||
)
|
||||
# scale = context.scene.unit_settings.scale_length / get_scale_length(
|
||||
# stream.units.lower()
|
||||
# )
|
||||
|
||||
|
||||
scale = 1.0
|
||||
|
||||
units = "m" if bpy.context.scene.unit_settings.system == "METRIC" else "ft"
|
||||
|
||||
"""
|
||||
Get script from text editor for injection
|
||||
@@ -412,18 +529,19 @@ class SendStreamObjects(bpy.types.Operator):
|
||||
|
||||
_report("Converting {}".format(obj.name))
|
||||
|
||||
ngons = obj.get("speckle_ngons_as_polylines", False)
|
||||
# ngons = obj.get("speckle_ngons_as_polylines", False)
|
||||
|
||||
if ngons:
|
||||
converted = ngons_to_speckle_polylines(obj, scale)
|
||||
else:
|
||||
converted = convert_to_speckle(
|
||||
obj,
|
||||
scale,
|
||||
bpy.context.evaluated_depsgraph_get()
|
||||
if self.apply_modifiers
|
||||
else None,
|
||||
)
|
||||
# if ngons:
|
||||
# converted = ngons_to_speckle_polylines(obj, scale)
|
||||
# else:
|
||||
converted = convert_to_speckle(
|
||||
obj,
|
||||
scale,
|
||||
units,
|
||||
bpy.context.evaluated_depsgraph_get()
|
||||
if self.apply_modifiers
|
||||
else None,
|
||||
)
|
||||
|
||||
if not converted:
|
||||
continue
|
||||
@@ -520,7 +638,7 @@ class AddStreamFromURL(bpy.types.Operator):
|
||||
user = speckle.users[user_index]
|
||||
|
||||
client = speckle_clients[user_index]
|
||||
stream = client.stream.get(wrapper.stream_id)
|
||||
stream = client.stream.get(wrapper.stream_id, branch_limit=20)
|
||||
if not isinstance(stream, Stream):
|
||||
raise SpeckleException("Could not get the requested stream")
|
||||
|
||||
|
||||
@@ -5,7 +5,9 @@ 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
|
||||
|
||||
|
||||
class LoadUsers(bpy.types.Operator):
|
||||
@@ -58,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
|
||||
@@ -81,7 +83,7 @@ def add_user_stream(user, stream):
|
||||
commit.message = c.message or ""
|
||||
commit.author_name = c.authorName
|
||||
commit.author_id = c.authorId
|
||||
commit.created_at = c.createdAt
|
||||
commit.created_at = datetime.strftime(c.createdAt, "%Y-%m-%d %H:%M:%S.%f%Z")
|
||||
commit.source_application = str(c.sourceApplication)
|
||||
|
||||
if hasattr(s, "baseProperties"):
|
||||
@@ -110,7 +112,7 @@ class LoadUserStreams(bpy.types.Operator):
|
||||
try:
|
||||
streams = client.stream.list(stream_limit=20)
|
||||
except Exception as e:
|
||||
_report("Failed to retrieve streams: {}".format(e))
|
||||
_report(f"Failed to retrieve streams: {e}")
|
||||
return
|
||||
if not streams:
|
||||
_report("Failed to retrieve streams.")
|
||||
@@ -121,7 +123,7 @@ class LoadUserStreams(bpy.types.Operator):
|
||||
default_units = "Meters"
|
||||
|
||||
for s in streams:
|
||||
sstream = client.stream.get(id=s.id)
|
||||
sstream = client.stream.get(id=s.id, branch_limit=20)
|
||||
add_user_stream(user, sstream)
|
||||
|
||||
bpy.context.view_layer.update()
|
||||
|
||||
@@ -12,7 +12,7 @@ from bpy.props import (
|
||||
EnumProperty,
|
||||
)
|
||||
|
||||
import datetime
|
||||
from datetime import datetime
|
||||
|
||||
"""
|
||||
Compatibility
|
||||
@@ -177,7 +177,7 @@ class VIEW3D_PT_SpeckleActiveStream(bpy.types.Panel):
|
||||
stream = user.streams[user.active_stream]
|
||||
# user.active_stream = min(user.active_stream, len(user.streams) - 1)
|
||||
row = col.row()
|
||||
row.label(text="{} ({})".format(stream.name, stream.id))
|
||||
row.label(text=f"{stream.name} ({stream.id})")
|
||||
row.operator("speckle.stream_copy_id", text="", icon="COPY_ID")
|
||||
col.separator()
|
||||
|
||||
@@ -205,13 +205,11 @@ class VIEW3D_PT_SpeckleActiveStream(bpy.types.Panel):
|
||||
row.label(text=line)
|
||||
area.separator()
|
||||
|
||||
dt = datetime.datetime.strptime(
|
||||
commit.created_at, "%Y-%m-%dT%H:%M:%S.%fZ"
|
||||
)
|
||||
col.label(text="{}".format(dt.ctime()))
|
||||
col.label(
|
||||
text="{} ({})".format(commit.author_name, commit.author_id)
|
||||
dt = datetime.strptime(
|
||||
commit.created_at, "%Y-%m-%d %H:%M:%S.%f%Z"
|
||||
)
|
||||
col.label(text=f"{dt.ctime()}")
|
||||
col.label(text=f"{commit.author_name} ({commit.author_id})")
|
||||
col.label(text=commit.source_application)
|
||||
else:
|
||||
col.label(text="No branches found!")
|
||||
@@ -232,9 +230,6 @@ class VIEW3D_PT_SpeckleActiveStream(bpy.types.Panel):
|
||||
|
||||
row = col.row(align=True)
|
||||
subcol = row.column()
|
||||
subcol.label(text="Units:")
|
||||
subcol = row.column()
|
||||
subcol.label(text=stream.units)
|
||||
|
||||
col.label(text="Description:")
|
||||
area = col.box()
|
||||
|
||||
+4
-3
@@ -28,7 +28,8 @@ def patch_installer(tag):
|
||||
with open(iss_file, "r") as file:
|
||||
lines = file.readlines()
|
||||
lines.insert(11, f'#define SpecklepyVersion "{py_tag}"\n')
|
||||
lines.insert(11, f'#define AppVersion "{tag}"\n')
|
||||
lines.insert(12, f'#define AppVersion "{tag.split("-")[0]}"\n')
|
||||
lines.insert(13, f'#define AppInfoVersion "{tag}"\n')
|
||||
|
||||
with open(iss_file, "w") as file:
|
||||
file.writelines(lines)
|
||||
@@ -54,11 +55,11 @@ def main():
|
||||
return
|
||||
|
||||
tag = sys.argv[1]
|
||||
if not re.match(r"[0-9]+(\.[0-9]+)*$", tag):
|
||||
if not re.match(r"([0-9]+)\.([0-9]+)\.([0-9]+)", tag):
|
||||
raise ValueError(f"Invalid tag provided: {tag}")
|
||||
|
||||
print(f"Patching version: {tag}")
|
||||
patch_connector(tag)
|
||||
patch_connector(tag.split("-")[0])
|
||||
patch_installer(tag)
|
||||
|
||||
|
||||
|
||||
Generated
+456
-897
File diff suppressed because it is too large
Load Diff
+7
-5
@@ -2,21 +2,23 @@
|
||||
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.7,<3.10"
|
||||
specklepy = "^2.6.0"
|
||||
python = ">=3.8, <4.0.0"
|
||||
specklepy = "^2.9.0"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
devtools = "^0.6.1"
|
||||
numpy = "^1.20.2"
|
||||
bpy = "^2.82.1"
|
||||
bpy-build = "^2.1.0"
|
||||
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