Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
assetReferences:
inputs:
directories: []
filenames:
- C:\normalized\job\bundle\dir\redshift_textured-_\u20BF_\u0119_\xF1_\u03B2_\u0411\
_\u062A.c4d
- C:\normalized\job\bundle\dir\tex\checkerboard-_\u20BF_\u0119_\xF1_\u03B2_\u0411\
_\u062A.bmp
outputs:
directories:
- C:\normalized\job\bundle\dir\renders
referencedPaths: []
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
parameterValues:
- name: Cinema4DFile
value: C:\normalized\job\bundle\dir\redshift_textured-_\u20BF_\u0119_\xF1_\u03B2_\u0411\
_\u062A.c4d
- name: OutputPath
value: C:\normalized\job\bundle\dir\renders\redshift_textured
- name: MultiPassPath
value: ''
- name: Frames
value: '1'
- name: deadline:targetTaskRunStatus
value: READY
- name: deadline:maxFailedTasksCount
value: 20
- name: deadline:maxRetriesPerTask
value: 5
- name: deadline:priority
value: 50
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
specificationVersion: jobtemplate-2023-09
name: physical-_₿_ę_ñ_β_Б_ت.c4d
parameterDefinitions:
- name: Cinema4DFile
type: PATH
objectType: FILE
dataFlow: IN
userInterface:
control: CHOOSE_INPUT_FILE
label: Cinema4D Document File
groupLabel: Cinema4D Settings
fileFilters:
- label: Cinema4D document files
patterns:
- '*.c4d'
- label: All Files
patterns:
- '*'
description: The Cinema4D document file to render.
- name: Frames
type: STRING
userInterface:
control: LINE_EDIT
label: Frames
groupLabel: Cinema4D Settings
description: The frames to render. E.g. 1-3,8,11-15
minLength: 1
- name: OutputPath
type: STRING
userInterface:
control: LINE_EDIT
label: Default image output
groupLabel: Cinema4D Settings
description: Image output path
- name: MultiPassPath
type: STRING
userInterface:
control: LINE_EDIT
label: Multi-pass output path
groupLabel: Cinema4D Settings
description: Multi-pass image output
steps:
- name: Main
parameterSpace:
taskParameterDefinitions:
- name: Frame
type: INT
range: '{{Param.Frames}}'
stepEnvironments:
- name: Cinema4D
description: Runs Cinema4D in the background.
script:
embeddedFiles:
- name: initData
filename: init-data.yaml
type: TEXT
data: |-
scene_file: '{{Param.Cinema4DFile}}'
take: 'Main'
output_path: '{{Param.OutputPath}}'
multi_pass_path: '{{Param.MultiPassPath}}'
actions:
onEnter:
command: cinema4d-openjd
args:
- daemon
- start
- --path-mapping-rules
- file://{{Session.PathMappingRulesFile}}
- --connection-file
- '{{Session.WorkingDirectory}}/connection.json'
- --init-data
- file://{{Env.File.initData}}
cancelation:
mode: NOTIFY_THEN_TERMINATE
onExit:
command: cinema4d-openjd
args:
- daemon
- stop
- --connection-file
- '{{ Session.WorkingDirectory }}/connection.json'
cancelation:
mode: NOTIFY_THEN_TERMINATE
script:
embeddedFiles:
- name: runData
filename: run-data.yaml
type: TEXT
data: |
frame: {{Task.Param.Frame}}
actions:
onRun:
command: cinema4d-openjd
args:
- daemon
- run
- --connection-file
- '{{ Session.WorkingDirectory }}/connection.json'
- --run-data
- file://{{ Task.File.runData }}
cancelation:
mode: NOTIFY_THEN_TERMINATE
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import os
import struct
import c4d


def _checkerboard_bmp(filename):
width = 128
height = 128
color1 = (255, 255, 255)
color2 = (0, 0, 0)
# BMP file header
file_size = 14 + 40 + (width * height * 3)
file_header = struct.pack("<2sIHHI", b"BM", file_size, 0, 0, 54)
# DIB header
dib_header = struct.pack(
"<IiiHHIIiiII", 40, width, height, 1, 24, 0, width * height * 3, 2835, 2835, 0, 0
)
pixel_data = []
for y in range(height):
row_data = []
for x in range(width):
if (x // 8 + y // 8) % 2 == 0:
row_data.extend(color1)
else:
row_data.extend(color2)
padding = (4 - (width * 3) % 4) % 4
row_data.extend([0] * padding)
pixel_data.extend(row_data)
with open(filename, "wb") as bmp_file:
bmp_file.write(file_header)
bmp_file.write(dib_header)
bmp_file.write(bytearray(pixel_data))


def main():
doc = c4d.documents.GetActiveDocument()
doc.Flush()
cube = c4d.BaseObject(c4d.Ocube)
cube[c4d.PRIM_CUBE_LEN] = c4d.Vector(400, 400, 400)
cube.SetAbsPos(c4d.Vector(0, 50, -50))
doc.InsertObject(cube)
mat = c4d.BaseList2D(c4d.Mmaterial)
doc.InsertMaterial(mat)
mat[c4d.MATERIAL_USE_REFLECTION] = False
bitmap_shader = c4d.BaseShader(c4d.Xbitmap)
tex_dir = os.path.join(os.path.dirname(__file__), "tex")
os.makedirs(tex_dir, exist_ok=True)
_checkerboard_bmp(os.path.join(tex_dir, "checkerboard-_₿_ę_ñ_β_Б_ت.bmp"))
bitmap_shader[c4d.BITMAPSHADER_FILENAME] = "tex/checkerboard-_₿_ę_ñ_β_Б_ت.bmp"
mat[c4d.MATERIAL_COLOR_SHADER] = bitmap_shader
mat.InsertShader(bitmap_shader)
texture_tag = c4d.TextureTag()
texture_tag.SetMaterial(mat)
cube.InsertTag(texture_tag)
render_data = doc.GetActiveRenderData()
render_data[c4d.RDATA_PATH] = "renders/$prj"
frame_start = c4d.BaseTime(1, doc.GetFps())
frame_end = c4d.BaseTime(1, doc.GetFps())
render_data[c4d.RDATA_FRAMEFROM] = frame_start
render_data[c4d.RDATA_FRAMETO] = frame_end
render_data[c4d.RDATA_RENDERENGINE] = 1036219 # redshift

save_dir = os.path.dirname(__file__)
save_name = "redshift_textured-_₿_ę_ñ_β_Б_ت.c4d"
save_file = os.path.join(save_dir, save_name)
doc.SetDocumentPath(save_dir)
doc.SetDocumentName(save_name)
c4d.documents.SaveDocument(doc, save_file, c4d.SAVEDOCUMENTFLAGS_0, c4d.FORMAT_C4DEXPORT)

c4d.documents.InsertBaseDocument(doc)
c4d.EventAdd()


if __name__ == "__main__":
main()
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import os
from typing import Any, Callable, Dict
import locale
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can you run hatch fmt on this? I would expect the imports to be in a different order.


try:
import c4d # type: ignore
Expand Down Expand Up @@ -38,6 +39,7 @@ def __init__(self, map_path: Callable[[str], str]) -> None:
"""
Constructor for the c4dpy handler. Initializes action_dict and render variables
"""

self.action_dict = {
"scene_file": self.set_scene_file,
"take": self.set_take,
Expand Down Expand Up @@ -164,6 +166,16 @@ def set_frame(self, data: dict) -> None:
"""
self.render_kwargs["frame"] = int(data.get("frame", ""))

def _convert_filename_to_utf8_encoding(self, scene_file: str) -> str:
try:
return scene_file.encode(locale.getpreferredencoding()).decode("utf-8")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't look right to me, because the Python 3 str type is already unicode. If this fixes something, it means that there was an earlier processing step that was wrong. We should track down that earlier step instead of patching it after it's already wrong.

Copy link
Contributor Author

@karthikbekalp karthikbekalp Dec 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because the Python 3 str type is already unicode

Yup, that tripped me as well when I was trying to root cause this issue. Turns out python uses Unicode but it does not use UTF-8 encoding by default. The default encoding is platform dependent and in Windows it is CP-1252. This is what I found out while investigating this issue in the official docs. (Its for the open() library function but I think it uses the same philosophy here)

We should track down that earlier step instead of patching it after it's already wrong.

I traced the issue back to adaptor.py while investigating, which showed the problem even before Cinema 4D started running.

...
2024/12/18 09:52:41-06:00 ADAPTOR_OUTPUT: INFO: Loading 'init_data' schema from C:\Program Files\Python312\Lib\site-packages\deadline\cinema4d_adaptor\Cinema4DAdaptor\schemas\init_data.schema.json
2024/12/18 09:52:41-06:00 ADAPTOR_OUTPUT: INFO: Loading 'run_data' schema from C:\Program Files\Python312\Lib\site-packages\deadline\cinema4d_adaptor\Cinema4DAdaptor\schemas\run_data.schema.json
2024/12/18 09:52:42-06:00 ADAPTOR_OUTPUT: INFO: In Adaptor: Scene file is C:\ProgramData\Amazon\OpenJD\session-xxx\assetroot-yyyy\scene\redshift_textured_β.c4d
2024/12/18 09:52:42-06:00 ADAPTOR_OUTPUT: INFO: In adaptor: Scene file is file? False
...

Patching it in the adaptor unfortunately causes the adaptor to freeze while running. It does not happen often but only at times and I tried root causing the issue but couldn't find it. I patched it in handler as it was the most straightforward fix and quick fix to unblock customers.

2024/12/18 10:14:37-06:00 ADAPTOR_OUTPUT: INFO: In Adaptor: Scene file is C:\ProgramData\Amazon\OpenJD\session-xxx\assetroot-yyyy\scene\redshift_textured-_₿_ę_ñ_β_Б_ت.c4d
2024/12/18 10:14:37-06:00 ADAPTOR_OUTPUT: INFO: In adaptor: Scene file is file? False

...
It was stuck and I had to fail the job manually. 
...
2024/12/18 10:18:44-06:00 Canceling subprocess 3868 via notify then terminate method at 2024-12-18T16:15:44Z.

Initially, this seemed to be a Cinema 4D-specific issue, not occurring in other DCCs like Unreal and Keyshot on Windows. Hence, I ruled out checking the dependencies of Cinema 4D.
However, while reviewing logs yesterday evening, I discovered that Keyshot jobs, despite being successful, had a similar error in the adaptor logs. This suggests the issue could affect all DCCs that run in Windows.

2024/12/17 12:49:39-06:00 ADAPTOR_OUTPUT: STDOUT:     self._perform_action(action)
2024/12/17 12:49:39-06:00 ADAPTOR_OUTPUT: STDOUT:   File "C:\Users\job-user\.conda\envs\Lib\openjd\adaptor_runtime_client\base_client_interface.py", line 233, in _perform_action
2024/12/17 12:49:39-06:00 ADAPTOR_OUTPUT: STDOUT:     action_func(a.args)
2024/12/17 12:49:39-06:00 ADAPTOR_OUTPUT: STDOUT:   File "C:\Users\job-user\.conda\envs\hashname_xxx\Lib\deadline\keyshot_adaptor\KeyShotClient\keyshot_handler.py", line 110, in set_scene_file
2024/12/17 12:49:39-06:00 ADAPTOR_OUTPUT: STDOUT:     raise FileNotFoundError(f"The scene file '{scene_file}' does not exist")
2024/12/17 12:49:39-06:00 ADAPTOR_OUTPUT: STDOUT: FileNotFoundError: The scene file 'C:\ProgramData\Amazon\OpenJD\session-xxx\assetroot-yyy\AppData\Local\Temp\tmpd3n517x9\unpack\Keyframe-Robot-Scene Sharing Version  ñot Angry.bip' does not exist
...

Since Cinema 4D customers were currently impacted and are blocked for working, I prioritized fixing it for Cinema 4D first. After that, I plan to investigate the issue further.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you looked into creating a minimal reproducer example as a small job template? That could help isolate the issue from the environment and provide faster iteration to debug it.

except UnicodeError as ue:
print(f"Error: Encoding to UTF-8 failed due to unicode error. Exception: {ue}")
except Exception as e:
print(f"Error: Encoding to UTF-8 failed due to unexpected error. Exception: {e}")
print("Returning scene file string as is as encoding/decoding failed.")
return scene_file

def set_scene_file(self, data: dict) -> None:
"""
Opens the scene file in Cinema4D.
Expand All @@ -175,6 +187,9 @@ def set_scene_file(self, data: dict) -> None:
FileNotFoundError: If path to the scene file does not yield a file
"""
scene_file = data.get("scene_file", "")

scene_file = self._convert_filename_to_utf8_encoding(scene_file)

if not os.path.isfile(scene_file):
raise FileNotFoundError(f"The scene file '{scene_file}' does not exist")
doc = c4d.documents.LoadDocument(
Expand Down
Loading