Skip to content

Commit

Permalink
Merge pull request #1 from ynput/feature/initial_integration
Browse files Browse the repository at this point in the history
Initial draft for Cinema4D Integration
  • Loading branch information
BigRoy authored Oct 2, 2024
2 parents 9fdc1f8 + ce1217d commit 2f79fc6
Show file tree
Hide file tree
Showing 37 changed files with 3,026 additions and 54 deletions.
61 changes: 19 additions & 42 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,50 +1,27 @@
# Addon template
This is a boilerplate git repository for creating new ayon addons.
# Cinema4D Addon

## Folder structure
All addons must have server code which is located in `server` subfolder. Server side addon definition is entrypoint for each addon. Can define settings, frontend, custom endpoints, etc. Root by default contains `create_package.py` which is a helper script that prepares package structure for server. The script may be modified or expanded by needs of addon (e.g. when frontend needs to be build first). File with `version.py` is kept at the root and is copied to server and client code with script -> The reason is to make sure both parts contain same version.
Cinema4D addon integration for AYON.

### Server content
Default base of server addon is `__init__.py` file in root of repository which define addon for server. Most of addons have settings that's why `settings.py` is by default in the structure. Settings can be changed to folder/module when more than one file is needed.
## Setup

### Server frontend
Addons may have their frontend. By default, server looks into `/frontend/dist` for `index.html` and addon have to have specified scopes where the frontend should be showed (check documentation of `frontend_scopes` on server addon implementation for more information).
### Install Qt library for Cinema4D

### Private server files
Root of addon may contain subfolder `private` where can be added files that are accessible via ayon server. Url schema is `{server url}/addons/{addon name}/{addon_version}/private/*`. By default it is place where client zip file is created (during package creation). The endpoint requires authorized user.
A Qt library must be installed for Cinema4D to ensure the AYON tools can run, either make sure
a `PySide6` or `PySide2` library is available on `PYTHONPATH` matching the Python version of the Cinema4D release.

### Public server files
Public files works the same as private files but does not require authorized user. Subfolder name is `public`. Url schema is `{server url}/addons/{addon name}/{addon_version}/public/*`. Endpoint is helpful for images/icons or other static content.
Alternatively, you can use the Tray launcher to trigger the `Terminal` tool to open a command-line initialized for the Cinema4D application. From there you should be able to run:
```cmd
c4dpy.exe -m pip install --ignore-installed PySide6
```
## Known Issues

### Client content
Addons that have code for desktop client application should create subfolder `client` where a client content is located. It is expected the directory has only one file or folder in it which is named the way how should be imported on a client side (e.g. `ayon_core`).
### High DPI scaling

The Redshift render view may appear oddly scaled on high DPI monitors due to some Qt scaling environment variables that AYON sets by default. To resolve this, launch Cinema4D with the environment variable: `QT_AUTO_SCREEN_SCALE_FACTOR=0`

### Example strucutre
```
├─ server
│ ├─ __init__.py
│ └─ settings.py
├─ frontend
│ └─ dist
│ └─ index.html
├─ public
│ └─ my_icon.png
├─ private
│ └─ kittens.png
├─ client
│ ├─ ayon_core
│ │ ├─ pipeline
│ │ ├─ lib
│ │ └─ ...
│ └─ pyproject.toml
├─ create_package.py
├─ LICENSE
├─ package.py
└─ README.md
```
This can be easily setup in the Application environment settings:
```json
{
"QT_AUTO_SCREEN_SCALE_FACTOR": "0"
}
```
13 changes: 13 additions & 0 deletions client/ayon_cinema4d/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from .version import __version__
from .addon import (
Cinema4DAddon,
CINEMA4D_ADDON_ROOT,
)


__all__ = (
"__version__",

"Cinema4DAddon",
"CINEMA4D_ADDON_ROOT",
)
31 changes: 31 additions & 0 deletions client/ayon_cinema4d/addon.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import os
from ayon_core.addon import AYONAddon, IHostAddon

from .version import __version__

CINEMA4D_ADDON_ROOT = os.path.dirname(os.path.abspath(__file__))


class Cinema4DAddon(AYONAddon, IHostAddon):
name = "cinema4d"
version = __version__
host_name = "cinema4d"

def get_launch_hook_paths(self, app):
if app.host_name != self.host_name:
return []
return [os.path.join(CINEMA4D_ADDON_ROOT, "hooks")]

def add_implementation_envs(self, env, app):
# Set default values if are not already set via settings
defaults = {"AYON_LOG_NO_COLORS": "1"}
for key, value in defaults.items():
if not env.get(key):
env[key] = value

# Register the startup `ayon_cinema4d.pyp`
env["g_additionalModulePath"] = os.path.join(CINEMA4D_ADDON_ROOT,
"startup")

def get_workfile_extensions(self):
return [".c4d"]
21 changes: 21 additions & 0 deletions client/ayon_cinema4d/api/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from .pipeline import (
Cinema4DHost
)

from .lib import (
maintained_selection
)

from .workio import (
save_file,
current_file,
has_unsaved_changes
)

__all__ = [
"Cinema4DHost",
"maintained_selection",
"save_file",
"current_file",
"has_unsaved_changes",
]
66 changes: 66 additions & 0 deletions client/ayon_cinema4d/api/commands.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import logging

from ayon_core.pipeline.context_tools import get_current_task_entity
from ayon_core.pipeline.colorspace import (
get_current_context_imageio_config_preset,
)
from .lib import (
set_resolution_from_entity,
set_frame_range_from_entity
)

import c4d

log = logging.getLogger(__name__)

REDSHIFT_RENDER_ENGINE_ID = 1036219


def reset_frame_range():
task_entity = get_current_task_entity()
set_frame_range_from_entity(task_entity)


def reset_resolution():
task_entity = get_current_task_entity()
set_resolution_from_entity(task_entity)


def reset_colorspace():
ocio_config = get_current_context_imageio_config_preset()
if not ocio_config:
log.info("No ocio config set.")
return

# Set the document colorspace to use OCIO from the OCIO env var
doc = c4d.documents.GetActiveDocument()
doc[c4d.DOCUMENT_COLOR_MANAGEMENT] = c4d.DOCUMENT_COLOR_MANAGEMENT_OCIO
doc[c4d.DOCUMENT_OCIO_CONFIG] = "$OCIO"

# TODO: Get preferred OCIO settings from project settings
# colorspace: str = "colorspace"
# display: str = "display"
# view: str = "view"
#
# doc[c4d.DOCUMENT_OCIO_RENDER_COLORSPACE] = colorspace
# doc[c4d.DOCUMENT_OCIO_DISPLAY_COLORSPACE] = display
# doc[c4d.DOCUMENT_OCIO_VIEW_TRANSFORM] = view
# doc[c4d.DOCUMENT_OCIO_VIEW_TRANSFORM_THUMBNAILS] = view
#
# # Iterate over the video post to find one matching the render engine.
# render_data = c4d.documents.GetActiveDocument().GetActiveRenderData()
# video_post = render_data.GetFirstVideoPost()
# while video_post:
# # Set redshift render colorspace
# if video_post.CheckType(REDSHIFT_RENDER_ENGINE_ID):
# _set_redshift_colorspace(video_post, colorspace, display, view)
# video_post = video_post.GetNext()

c4d.EventAdd()


def _set_redshift_colorspace(video_post, colorspace, display, view):
video_post[c4d.REDSHIFT_RENDERER_COLOR_MANAGEMENT_OCIO_RENDERING_COLORSPACE] = colorspace # noqa: E501
video_post[c4d.REDSHIFT_RENDERER_COLOR_MANAGEMENT_OCIO_DISPLAY] = display
video_post[c4d.REDSHIFT_RENDERER_COLOR_MANAGEMENT_OCIO_VIEW] = view

Loading

0 comments on commit 2f79fc6

Please sign in to comment.