Skip to content

Commit ab20a77

Browse files
committed
fix: Fix the color tone for tile rendering.
Signed-off-by: Karthik Bekal Pattathana <133984042+karthikbekalp@users.noreply.github.com>
1 parent 0364064 commit ab20a77

49 files changed

Lines changed: 862 additions & 41 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/deadline/cinema4d_adaptor/Cinema4DClient/tile_rendering.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -228,13 +228,16 @@ def finalize_tile_render(
228228
render_data: The render data object (to restore output paths).
229229
frame: The current frame number.
230230
"""
231-
# Restore the OCIO bake flag to its original value
232-
if ctx.orig_bake_flag is not None:
233-
rd[c4d.RDATA_BAKE_OCIO_VIEW_TRANSFORM_RENDER] = ctx.orig_bake_flag
234-
235-
if ctx.requires_baking:
236-
baked = c4d.documents.BakeOcioViewToBitmap(bm, rd, c4d.SAVEBIT_NONE)
237-
bm = baked or bm
231+
try:
232+
if ctx.requires_baking:
233+
# setup_tile_render disabled the render-time bake. It must remain
234+
# disabled here or BakeOcioViewToBitmap assumes the bitmap was
235+
# already baked and returns None.
236+
baked = c4d.documents.BakeOcioViewToBitmap(bm, rd, c4d.SAVEBIT_NONE)
237+
bm = baked or bm
238+
finally:
239+
if ctx.orig_bake_flag is not None:
240+
rd[c4d.RDATA_BAKE_OCIO_VIEW_TRANSFORM_RENDER] = ctx.orig_bake_flag
238241

239242
if c4d.GetC4DVersion() >= C4D_VERSION_2025_2:
240243
bm.SetColorProfile(c4d.bitmaps.ColorProfile(), c4d.COLORPROFILE_INDEX_DISPLAYSPACE)

test/integ/fixtures/auto_open_submitter/AutoOpenSubmitter.pyp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ Where the submitter's AWS calls go depends on the test mode:
3232
3333
The test then drives the resulting Qt dialog with xa11y.
3434
"""
35+
3536
import os
3637
import traceback
3738

@@ -201,6 +202,11 @@ def _load_active_scene(scene_path):
201202
c4d.documents.InsertBaseDocument(doc)
202203
c4d.documents.SetActiveDocument(doc)
203204
c4d.EventAdd()
205+
if doc.GetChanged():
206+
# The fixture was just generated and saved. Older C4D versions
207+
# can mark Redshift node materials changed while loading them.
208+
doc[c4d.DOCUMENT_USERCHANGE] = False
209+
_diag("cleared generated scene changed state after load")
204210
_diag("scene loaded and set as active")
205211
else:
206212
_diag(f"LoadDocument returned None for {scene_path}")

test/integ/ocio_scene.py

Lines changed: 335 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,335 @@
1+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
"""Shared Redshift OCIO studio scene for Cinema 4D integration tests."""
3+
4+
from __future__ import annotations
5+
6+
import math
7+
import os
8+
from typing import Any
9+
10+
import c4d
11+
import maxon
12+
13+
WIDTH = 384
14+
HEIGHT = 216
15+
ACES_DEFAULT_VIEW_TRANSFORM = 0
16+
ACES_UNTONE_MAPPED_VIEW_TRANSFORM = 3
17+
REDSHIFT_RENDERER_ID = 1036219
18+
19+
_REDSHIFT_NODE_SPACE = maxon.Id("com.redshift3d.redshift4c4d.class.nodespace")
20+
_STANDARD_MATERIAL_PORT = "com.redshift3d.redshift4c4d.nodes.core.standardmaterial"
21+
22+
23+
def _redshift_material(
24+
doc: Any,
25+
name: str,
26+
color: tuple[float, float, float],
27+
*,
28+
roughness: float = 0.35,
29+
metalness: float = 0.0,
30+
emission: tuple[float, float, float] | None = None,
31+
) -> Any:
32+
material = c4d.BaseMaterial(c4d.Mmaterial)
33+
material.SetName(name)
34+
doc.InsertMaterial(material)
35+
node_material = material.GetNodeMaterialReference()
36+
node_material.CreateDefaultGraph(_REDSHIFT_NODE_SPACE)
37+
graph = node_material.GetGraph(_REDSHIFT_NODE_SPACE)
38+
39+
standard_node = None
40+
base_color_port = f"{_STANDARD_MATERIAL_PORT}.base_color"
41+
get_view_root = getattr(graph, "GetViewRoot", None)
42+
root = get_view_root() if get_view_root is not None else graph.GetRoot()
43+
nodes = root.GetChildren()
44+
for node in nodes:
45+
if node.GetId().ToString().split("@", 1)[0] == "standardmaterial":
46+
standard_node = node
47+
break
48+
if standard_node is None:
49+
raise RuntimeError("Redshift Standard Material node was not created")
50+
51+
with graph.BeginTransaction() as transaction:
52+
inputs = standard_node.GetInputs()
53+
inputs.FindChild(base_color_port).SetPortValue(maxon.Color(*color))
54+
inputs.FindChild(f"{_STANDARD_MATERIAL_PORT}.refl_roughness").SetPortValue(roughness)
55+
inputs.FindChild(f"{_STANDARD_MATERIAL_PORT}.metalness").SetPortValue(metalness)
56+
if emission is not None:
57+
inputs.FindChild(f"{_STANDARD_MATERIAL_PORT}.base_color_weight").SetPortValue(0.0)
58+
inputs.FindChild(f"{_STANDARD_MATERIAL_PORT}.emission_color").SetPortValue(
59+
maxon.Color(*emission)
60+
)
61+
inputs.FindChild(f"{_STANDARD_MATERIAL_PORT}.emission_weight").SetPortValue(1.0)
62+
transaction.Commit()
63+
64+
return material
65+
66+
67+
def _attach_material(obj: Any, material: Any) -> None:
68+
tag = c4d.TextureTag()
69+
tag.SetMaterial(material)
70+
obj.InsertTag(tag)
71+
72+
73+
def _add_cube(
74+
doc: Any,
75+
name: str,
76+
size: tuple[float, float, float],
77+
position: tuple[float, float, float],
78+
material: Any,
79+
*,
80+
rotation: tuple[float, float, float] = (0.0, 0.0, 0.0),
81+
fillet: float = 0.0,
82+
) -> Any:
83+
cube = c4d.BaseObject(c4d.Ocube)
84+
cube.SetName(name)
85+
cube[c4d.PRIM_CUBE_LEN] = c4d.Vector(*size)
86+
if fillet > 0.0:
87+
cube[c4d.PRIM_CUBE_DOFILLET] = True
88+
cube[c4d.PRIM_CUBE_FRAD] = fillet
89+
cube[c4d.PRIM_CUBE_SUBF] = 4
90+
cube.SetAbsPos(c4d.Vector(*position))
91+
cube.SetAbsRot(c4d.Vector(*(math.radians(value) for value in rotation)))
92+
_attach_material(cube, material)
93+
doc.InsertObject(cube)
94+
return cube
95+
96+
97+
def _point_at(obj: Any, target: tuple[float, float, float]) -> None:
98+
direction = c4d.Vector(*target) - obj.GetAbsPos()
99+
obj.SetAbsRot(c4d.utils.VectorToHPB(direction))
100+
101+
102+
def _add_area_light(
103+
doc: Any,
104+
name: str,
105+
position: tuple[float, float, float],
106+
target: tuple[float, float, float],
107+
color: tuple[float, float, float],
108+
exposure: float,
109+
size: tuple[float, float],
110+
) -> None:
111+
light = c4d.BaseObject(c4d.Orslight)
112+
light.SetName(name)
113+
light[c4d.REDSHIFT_LIGHT_TYPE] = c4d.REDSHIFT_LIGHT_TYPE_PHYSICAL_AREA
114+
light[c4d.REDSHIFT_LIGHT_PHYSICAL_COLORMODE] = c4d.REDSHIFT_LIGHT_COLORMODE_COLOR
115+
light[c4d.REDSHIFT_LIGHT_PHYSICAL_COLOR] = c4d.Vector(*color)
116+
light[c4d.REDSHIFT_LIGHT_PHYSICAL_INTENSITY] = 1.0
117+
light[c4d.REDSHIFT_LIGHT_PHYSICAL_EXPOSURE] = exposure
118+
light[c4d.REDSHIFT_LIGHT_PHYSICAL_AREA_GEOMETRY] = c4d.REDSHIFT_LIGHT_AREA_GEOMETRY_RECTANGLE
119+
light[c4d.REDSHIFT_LIGHT_PHYSICAL_AREA_SIZEX] = size[0]
120+
light[c4d.REDSHIFT_LIGHT_PHYSICAL_AREA_SIZEY] = size[1]
121+
light[c4d.REDSHIFT_LIGHT_PHYSICAL_AREA_VISIBLE_IN_RENDER] = False
122+
light.SetAbsPos(c4d.Vector(*position))
123+
_point_at(light, target)
124+
doc.InsertObject(light)
125+
126+
127+
def _build_geometry(doc: Any) -> None:
128+
backdrop = _redshift_material(
129+
doc,
130+
"Charcoal backdrop",
131+
(0.028, 0.035, 0.05),
132+
roughness=0.62,
133+
)
134+
floor = _redshift_material(
135+
doc,
136+
"Neutral floor",
137+
(0.12, 0.13, 0.15),
138+
roughness=0.28,
139+
metalness=0.15,
140+
)
141+
orange = _redshift_material(
142+
doc,
143+
"Burnished orange",
144+
(0.95, 0.09, 0.015),
145+
roughness=0.2,
146+
metalness=0.25,
147+
)
148+
cyan = _redshift_material(
149+
doc,
150+
"Glossy cyan",
151+
(0.015, 0.42, 0.72),
152+
roughness=0.14,
153+
metalness=0.05,
154+
)
155+
magenta = _redshift_material(
156+
doc,
157+
"Matte magenta",
158+
(0.72, 0.025, 0.2),
159+
roughness=0.48,
160+
)
161+
green = _redshift_material(
162+
doc,
163+
"Metallic green",
164+
(0.025, 0.52, 0.13),
165+
roughness=0.24,
166+
metalness=0.7,
167+
)
168+
blue_emission = _redshift_material(
169+
doc,
170+
"HDR blue strip",
171+
(0.0, 0.0, 0.0),
172+
emission=(0.02, 0.65, 6.0),
173+
)
174+
175+
_add_cube(
176+
doc,
177+
"Backdrop",
178+
(1300, 760, 30),
179+
(0, 90, 430),
180+
backdrop,
181+
)
182+
_add_cube(
183+
doc,
184+
"Floor",
185+
(1300, 30, 1050),
186+
(0, -210, 50),
187+
floor,
188+
fillet=8,
189+
)
190+
_add_cube(
191+
doc,
192+
"Diagonal HDR strip",
193+
(980, 24, 18),
194+
(0, 115, 395),
195+
blue_emission,
196+
rotation=(0, 0, -8),
197+
fillet=6,
198+
)
199+
200+
sphere = c4d.BaseObject(c4d.Osphere)
201+
sphere.SetName("Orange sphere")
202+
sphere[c4d.PRIM_SPHERE_RAD] = 150
203+
sphere[c4d.PRIM_SPHERE_SUB] = 48
204+
sphere.SetAbsPos(c4d.Vector(-285, -55, 45))
205+
_attach_material(sphere, orange)
206+
doc.InsertObject(sphere)
207+
208+
_add_cube(
209+
doc,
210+
"Cyan beveled cube",
211+
(225, 225, 225),
212+
(-35, -50, 60),
213+
cyan,
214+
rotation=(12, -24, 8),
215+
fillet=24,
216+
)
217+
218+
torus = c4d.BaseObject(c4d.Otorus)
219+
torus.SetName("Green torus")
220+
torus[c4d.PRIM_TORUS_OUTERRAD] = 150
221+
torus[c4d.PRIM_TORUS_INNERRAD] = 48
222+
torus[c4d.PRIM_TORUS_SEG] = 64
223+
torus[c4d.PRIM_TORUS_CSUB] = 24
224+
torus.SetAbsPos(c4d.Vector(285, -45, 75))
225+
torus.SetAbsRot(c4d.Vector(math.radians(78), math.radians(-10), math.radians(16)))
226+
_attach_material(torus, green)
227+
doc.InsertObject(torus)
228+
229+
pyramid = c4d.BaseObject(c4d.Opyramid)
230+
pyramid.SetName("Magenta pyramid")
231+
pyramid[c4d.PRIM_PYRAMID_LEN] = c4d.Vector(230, 285, 230)
232+
pyramid.SetAbsPos(c4d.Vector(115, 85, 205))
233+
pyramid.SetAbsRot(c4d.Vector(0, math.radians(24), math.radians(-5)))
234+
_attach_material(pyramid, magenta)
235+
doc.InsertObject(pyramid)
236+
237+
exposure_values = (0.18, 1.0, 4.0, 16.0)
238+
exposure_positions = (-300, -100, 100, 300)
239+
for value, x_position in zip(exposure_values, exposure_positions):
240+
swatch_material = _redshift_material(
241+
doc,
242+
f"Exposure {value:g}",
243+
(0.0, 0.0, 0.0),
244+
emission=(value, value, value),
245+
)
246+
_add_cube(
247+
doc,
248+
f"Exposure swatch {value:g}",
249+
(118, 42, 18),
250+
(x_position, 285, 395),
251+
swatch_material,
252+
fillet=5,
253+
)
254+
255+
target = (0, 10, 80)
256+
_add_area_light(
257+
doc,
258+
"Warm key",
259+
(-430, 430, -360),
260+
target,
261+
(1.0, 0.68, 0.48),
262+
6.0,
263+
(430, 430),
264+
)
265+
_add_area_light(
266+
doc,
267+
"Cool fill",
268+
(470, 160, -220),
269+
target,
270+
(0.3, 0.62, 1.0),
271+
4.5,
272+
(340, 340),
273+
)
274+
_add_area_light(
275+
doc,
276+
"Magenta rim",
277+
(60, 440, 360),
278+
target,
279+
(1.0, 0.18, 0.45),
280+
4.0,
281+
(280, 280),
282+
)
283+
284+
camera = c4d.BaseObject(c4d.Ocamera)
285+
camera.SetName("OCIO studio camera")
286+
camera.SetAbsPos(c4d.Vector(0, 70, -1320))
287+
_point_at(camera, (0, 35, 90))
288+
camera[c4d.CAMERA_FOCUS] = 52.0
289+
doc.InsertObject(camera)
290+
doc.GetRenderBaseDraw().SetSceneCamera(camera)
291+
292+
293+
def build_ocio_scene(
294+
output_dir: str,
295+
scene_name: str,
296+
view_transform: int,
297+
) -> None:
298+
"""Build and save the shared Redshift OCIO scene."""
299+
output_dir = os.path.abspath(output_dir)
300+
os.makedirs(output_dir, exist_ok=True)
301+
302+
doc = c4d.documents.GetActiveDocument()
303+
doc.Flush()
304+
_build_geometry(doc)
305+
306+
doc[c4d.DOCUMENT_COLOR_MANAGEMENT] = c4d.DOCUMENT_COLOR_MANAGEMENT_OCIO
307+
doc[c4d.DOCUMENT_OCIO_PRESET] = c4d.DOCUMENT_OCIO_PRESET_ACES
308+
doc[c4d.DOCUMENT_OCIO_VIEW_TRANSFORM] = view_transform
309+
310+
render_data = doc.GetActiveRenderData()
311+
render_data[c4d.RDATA_RENDERENGINE] = REDSHIFT_RENDERER_ID
312+
render_data[c4d.RDATA_XRES] = WIDTH
313+
render_data[c4d.RDATA_YRES] = HEIGHT
314+
render_data[c4d.RDATA_FRAMEFROM] = c4d.BaseTime(1, doc.GetFps())
315+
render_data[c4d.RDATA_FRAMETO] = c4d.BaseTime(1, doc.GetFps())
316+
render_data[c4d.RDATA_FORMAT] = c4d.FILTER_PNG
317+
render_data[c4d.RDATA_FORMATDEPTH] = c4d.RDATA_FORMATDEPTH_8
318+
render_data[c4d.RDATA_ALPHACHANNEL] = False
319+
render_data[c4d.RDATA_MULTIPASS_SAVEIMAGE] = False
320+
if hasattr(c4d, "RDATA_BAKE_OCIO_VIEW_TRANSFORM_RENDER"):
321+
render_data.GetDataInstance()[c4d.RDATA_BAKE_OCIO_VIEW_TRANSFORM_RENDER] = True
322+
323+
render_data[c4d.RDATA_PATH] = "renders/$prj"
324+
325+
scene_path = os.path.join(output_dir, scene_name)
326+
doc.SetDocumentPath(output_dir)
327+
doc.SetDocumentName(scene_name)
328+
if not c4d.documents.SaveDocument(
329+
doc,
330+
scene_path,
331+
c4d.SAVEDOCUMENTFLAGS_0,
332+
c4d.FORMAT_C4DEXPORT,
333+
):
334+
raise RuntimeError(f"Failed to save integration scene: {scene_path}")
335+
c4d.EventAdd()
86 KB
Loading
9.49 KB
Loading
10.7 KB
Loading
10.3 KB
Loading
8.63 KB
Loading
6.91 KB
Loading
9.23 KB
Loading

0 commit comments

Comments
 (0)