feat: init repo
This commit is contained in:
+117
@@ -0,0 +1,117 @@
|
|||||||
|
.tool-versions
|
||||||
|
.envrc
|
||||||
|
reports/
|
||||||
|
|
||||||
|
deps_windows_py39/
|
||||||
|
|
||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
MANIFEST
|
||||||
|
|
||||||
|
# PyInstaller
|
||||||
|
# Usually these files are written by a python script from a template
|
||||||
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
|
*.manifest
|
||||||
|
*.spec
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
.hypothesis/
|
||||||
|
.pytest_cache/
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
*.pot
|
||||||
|
|
||||||
|
# Jetbrains stuff:
|
||||||
|
.idea
|
||||||
|
|
||||||
|
# Django stuff:
|
||||||
|
*.log
|
||||||
|
local_settings.py
|
||||||
|
db.sqlite3
|
||||||
|
|
||||||
|
# Flask stuff:
|
||||||
|
instance/
|
||||||
|
.webassets-cache
|
||||||
|
|
||||||
|
# Scrapy stuff:
|
||||||
|
.scrapy
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/_build/
|
||||||
|
|
||||||
|
# PyBuilder
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Jupyter Notebook
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
.python-version
|
||||||
|
|
||||||
|
# celery beat schedule file
|
||||||
|
celerybeat-schedule
|
||||||
|
|
||||||
|
# SageMath parsed files
|
||||||
|
*.sage.py
|
||||||
|
|
||||||
|
# Environments
|
||||||
|
.env
|
||||||
|
.venv
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env.bak/
|
||||||
|
venv.bak/
|
||||||
|
|
||||||
|
# Spyder project settings
|
||||||
|
.spyderproject
|
||||||
|
.spyproject
|
||||||
|
|
||||||
|
# Rope project settings
|
||||||
|
.ropeproject
|
||||||
|
|
||||||
|
# mkdocs documentation
|
||||||
|
/site
|
||||||
|
|
||||||
|
# mypy
|
||||||
|
.mypy_cache/
|
||||||
|
|
||||||
|
# other
|
||||||
|
scratch.py
|
||||||
|
settings.json
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
# Cinema 4D 26: One-Click Send to Speckle
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
This script allows you to send a Cinema4D model to Speckle with one click! Simply paste in a URL, execute the script, and away you go 🚀
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
In order to achieve this, the script will (1) export your entire model to an `STL` file, (2) convert that file to Speckle, and (3) send and commit the converted mesh.
|
||||||
|
|
||||||
|
It is recommended that you have [Speckle Manager](https://speckle.guide/user/manager.html) installed before using this script, however you can also use a [token](https://speckle.guide/dev/tokens.html#personal-access-tokens) to authenticate yourself instead.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
### Windows
|
||||||
|
|
||||||
|
1. Extract the dependencies from `deps_windows_py39.zip`
|
||||||
|
2. Copy the contents of the extracted `deps_windows_py39` folder into your `%APPDATA%\Maxon\python\python39\libs`
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
2. Copy the `send_to_speckle.py` script into your `%APPDATA%\Maxon\Maxon Cinema 4D R26_7DC20B77\library\scripts` (or load it in from wherever you like to save your Cinema4D python scripts)
|
||||||
|
|
||||||
|
### Mac
|
||||||
|
|
||||||
|
I was not able to get this on an M1 Pro mac, but feel free to try this manual installation out as you may have better luck.
|
||||||
|
|
||||||
|
1. Using Python 3.9, `pip install specklepy numpy-stl` into `~/Library/Preferences/MAXON/python39/libs` (**NOTE:** it is critical that you use Python 3.9 as the major version needs to match the one bundled with Cinema4D)
|
||||||
|
2. Copy the `send_to_speckle.py` script into your C4D scripts folder
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
1. Load up the `send_to_speckle.py` script in the script tab
|
||||||
|
2. Paste in the url of the stream or branch you want to send to in the `STREAM_URL` field (defaults to `main` branch)
|
||||||
|
3. Optional: if you _don't_ have [Speckle Manager](https://speckle.guide/user/manager.html) installed with a Speckle account added to it, you can provide an authentication token to `TOKEN` field
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Note that C4D may become unresponsive for a minute or two until the operation is complete.
|
||||||
Binary file not shown.
Generated
+1186
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,18 @@
|
|||||||
|
[tool.poetry]
|
||||||
|
name = "oneclick-c4d"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = ""
|
||||||
|
authors = ["izzy lyseggen <izzy.lyseggen@gmail.com>"]
|
||||||
|
|
||||||
|
[tool.poetry.dependencies]
|
||||||
|
python = ">=3.9, <3.10"
|
||||||
|
specklepy = "^2.6.7"
|
||||||
|
numpy-stl = "^2.16.3"
|
||||||
|
|
||||||
|
[tool.poetry.dev-dependencies]
|
||||||
|
black = "^22.3.0"
|
||||||
|
pylint = "^2.13.7"
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["poetry-core>=1.0.0"]
|
||||||
|
build-backend = "poetry.core.masonry.api"
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
import os
|
||||||
|
import c4d
|
||||||
|
import stl
|
||||||
|
from specklepy.api import operations
|
||||||
|
from specklepy.api.wrapper import StreamWrapper
|
||||||
|
from specklepy.objects.geometry import Mesh
|
||||||
|
from specklepy.logging.exceptions import SpeckleException
|
||||||
|
|
||||||
|
# the url to the stream or branch you want to send to (if not a branch url, it will default to the main branch)
|
||||||
|
STREAM_URL = "https://latest.speckle.dev/streams/0c6ad366c4/branches/c4d_tests"
|
||||||
|
|
||||||
|
# the file will get exported to an STL in this folder right next to your c4d file
|
||||||
|
STL_EXPORT_FOLDER_NAME = "stl_export"
|
||||||
|
|
||||||
|
# if you have a account on Speckle Manager, you don't need this
|
||||||
|
TOKEN = ""
|
||||||
|
|
||||||
|
|
||||||
|
def export_stl() -> str:
|
||||||
|
"""Export the current file to an STL in a subfolder next to this file's location"""
|
||||||
|
active_doc = c4d.documents.GetActiveDocument()
|
||||||
|
path = active_doc.GetDocumentPath()
|
||||||
|
|
||||||
|
export_folder = os.path.join(path, STL_EXPORT_FOLDER_NAME)
|
||||||
|
os.makedirs(export_folder, exist_ok=True)
|
||||||
|
|
||||||
|
export_path = os.path.join(
|
||||||
|
export_folder, f"{os.path.splitext(active_doc.GetDocumentName())[0]} EXPORT.stl"
|
||||||
|
)
|
||||||
|
|
||||||
|
saved = c4d.documents.SaveDocument(
|
||||||
|
active_doc,
|
||||||
|
export_path,
|
||||||
|
saveflags=c4d.SAVEDOCUMENTFLAGS_DONTADDTORECENTLIST,
|
||||||
|
format=c4d.FORMAT_STL_EXPORT,
|
||||||
|
)
|
||||||
|
|
||||||
|
if not saved:
|
||||||
|
raise SpeckleException("Failed to export file to STL")
|
||||||
|
|
||||||
|
print(f"Exported STL to {export_path}")
|
||||||
|
|
||||||
|
return export_path
|
||||||
|
|
||||||
|
|
||||||
|
def convert_stl(stl_file_path: str) -> Mesh:
|
||||||
|
"""
|
||||||
|
Convert STL file into a Speckle Mesh
|
||||||
|
(from the [speckle server import service](https://github.com/specklesystems/speckle-server/blob/main/packages/fileimport-service/stl/import_file.py))
|
||||||
|
"""
|
||||||
|
# Parse input
|
||||||
|
stl_mesh = stl.mesh.Mesh.from_file(stl_file_path)
|
||||||
|
print(
|
||||||
|
f"Parsed mesh with {stl_mesh.points.shape[0]} faces ({stl_mesh.points.shape[0] * 3} vertices)"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Construct speckle obj
|
||||||
|
vertices = stl_mesh.points.flatten().tolist()
|
||||||
|
faces = []
|
||||||
|
for i in range(stl_mesh.points.shape[0]):
|
||||||
|
faces.extend([0, 3 * i, 3 * i + 1, 3 * i + 2])
|
||||||
|
|
||||||
|
speckle_mesh = Mesh(
|
||||||
|
vertices=vertices, faces=faces, colors=[], textureCoordinates=[]
|
||||||
|
)
|
||||||
|
print("Constructed Speckle Mesh object")
|
||||||
|
|
||||||
|
return speckle_mesh
|
||||||
|
|
||||||
|
|
||||||
|
def send_to_speckle(
|
||||||
|
wrapper: StreamWrapper, speckle_mesh: Mesh, commit_msg: str = None
|
||||||
|
) -> str:
|
||||||
|
"""Send the mesh to speckle and create a commit"""
|
||||||
|
client = wrapper.get_client(TOKEN or None)
|
||||||
|
|
||||||
|
if wrapper.branch_name and not client.branch.get(
|
||||||
|
wrapper.stream_id, wrapper.branch_name
|
||||||
|
):
|
||||||
|
client.branch.create(
|
||||||
|
wrapper.stream_id,
|
||||||
|
wrapper.branch_name,
|
||||||
|
"File upload branch" if wrapper.branch_name == "uploads" else "",
|
||||||
|
)
|
||||||
|
|
||||||
|
obj_id = operations.send(base=speckle_mesh, transports=[wrapper.get_transport()])
|
||||||
|
|
||||||
|
return client.commit.create(
|
||||||
|
wrapper.stream_id,
|
||||||
|
obj_id,
|
||||||
|
wrapper.branch_name or "main",
|
||||||
|
commit_msg or "STL file upload",
|
||||||
|
source_application="cinema4D",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
w = StreamWrapper(STREAM_URL)
|
||||||
|
acct = w.get_account()
|
||||||
|
if not acct.token or TOKEN:
|
||||||
|
raise SpeckleException(
|
||||||
|
"No token available. Please either add a Speckle Account to Speckle Manager or provide a token in `TOKEN`"
|
||||||
|
)
|
||||||
|
|
||||||
|
stl_path = export_stl()
|
||||||
|
|
||||||
|
speckle_mesh = convert_stl(stl_path)
|
||||||
|
|
||||||
|
commit_id = send_to_speckle(
|
||||||
|
w, speckle_mesh, f"File upload: {os.path.basename(stl_path)}"
|
||||||
|
)
|
||||||
|
|
||||||
|
print(
|
||||||
|
f"File uploaded to\n{w.server_url}/streams/{w.stream_id}/commits/{commit_id}\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user