Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam Kottler <dev@samkottler.net>2020-08-12 19:59:35 +0300
committerSam Kottler <dev@samkottler.net>2020-08-12 19:59:35 +0300
commitac0bab2d432fe8834d21b3549721ed7feecd863a (patch)
tree39d3cb6b955bc7f61cd538e4469bf934af0ca306 /intern/cycles/blender
parentdf8bbe98c46b8999463767b73cb0764b7500a195 (diff)
parent0e280b96ca82eef0d9039ca06b150cfa05a33a65 (diff)
Merge branch 'blender-v2.90-release' into soc-2020-production-ready-light-tree-2
Diffstat (limited to 'intern/cycles/blender')
-rw-r--r--intern/cycles/blender/CMakeLists.txt38
-rw-r--r--intern/cycles/blender/addon/__init__.py21
-rw-r--r--intern/cycles/blender/addon/engine.py156
-rw-r--r--intern/cycles/blender/addon/operators.py41
-rw-r--r--intern/cycles/blender/addon/osl.py1
-rw-r--r--intern/cycles/blender/addon/properties.py496
-rw-r--r--intern/cycles/blender/addon/ui.py778
-rw-r--r--intern/cycles/blender/addon/version_update.py206
-rw-r--r--intern/cycles/blender/blender_camera.cpp78
-rw-r--r--intern/cycles/blender/blender_curves.cpp1018
-rw-r--r--intern/cycles/blender/blender_device.cpp28
-rw-r--r--intern/cycles/blender/blender_device.h2
-rw-r--r--intern/cycles/blender/blender_geometry.cpp178
-rw-r--r--intern/cycles/blender/blender_id_map.h299
-rw-r--r--intern/cycles/blender/blender_image.cpp220
-rw-r--r--intern/cycles/blender/blender_image.h61
-rw-r--r--intern/cycles/blender/blender_light.cpp212
-rw-r--r--intern/cycles/blender/blender_mesh.cpp425
-rw-r--r--intern/cycles/blender/blender_object.cpp333
-rw-r--r--intern/cycles/blender/blender_object_cull.cpp1
-rw-r--r--intern/cycles/blender/blender_particles.cpp6
-rw-r--r--intern/cycles/blender/blender_python.cpp101
-rw-r--r--intern/cycles/blender/blender_session.cpp676
-rw-r--r--intern/cycles/blender/blender_session.h42
-rw-r--r--intern/cycles/blender/blender_shader.cpp353
-rw-r--r--intern/cycles/blender/blender_sync.cpp405
-rw-r--r--intern/cycles/blender/blender_sync.h118
-rw-r--r--intern/cycles/blender/blender_texture.h2
-rw-r--r--intern/cycles/blender/blender_util.h318
-rw-r--r--intern/cycles/blender/blender_viewport.cpp88
-rw-r--r--intern/cycles/blender/blender_viewport.h56
-rw-r--r--intern/cycles/blender/blender_volume.cpp387
32 files changed, 4065 insertions, 3079 deletions
diff --git a/intern/cycles/blender/CMakeLists.txt b/intern/cycles/blender/CMakeLists.txt
index 7354b1e615e..2316800e21e 100644
--- a/intern/cycles/blender/CMakeLists.txt
+++ b/intern/cycles/blender/CMakeLists.txt
@@ -18,6 +18,9 @@ set(INC_SYS
set(SRC
blender_camera.cpp
blender_device.cpp
+ blender_image.cpp
+ blender_geometry.cpp
+ blender_light.cpp
blender_mesh.cpp
blender_object.cpp
blender_object_cull.cpp
@@ -29,13 +32,19 @@ set(SRC
blender_shader.cpp
blender_sync.cpp
blender_texture.cpp
+ blender_viewport.cpp
+ blender_volume.cpp
CCL_api.h
+ blender_device.h
+ blender_id_map.h
+ blender_image.h
blender_object_cull.h
blender_sync.h
blender_session.h
blender_texture.h
blender_util.h
+ blender_viewport.h
)
set(LIB
@@ -46,11 +55,15 @@ set(LIB
cycles_render
cycles_subd
cycles_util
+
+ ${PYTHON_LINKFLAGS}
+ ${PYTHON_LIBRARIES}
)
if(WITH_CYCLES_LOGGING)
list(APPEND LIB
- extern_glog
+ ${GLOG_LIBRARIES}
+ ${GFLAGS_LIBRARIES}
)
endif()
@@ -68,13 +81,34 @@ set(ADDON_FILES
add_definitions(${GL_DEFINITIONS})
if(WITH_CYCLES_DEVICE_OPENCL)
- add_definitions(-DWITH_OPENCL)
+ add_definitions(-DWITH_OPENCL)
endif()
if(WITH_CYCLES_NETWORK)
add_definitions(-DWITH_NETWORK)
endif()
+if(WITH_MOD_FLUID)
+ add_definitions(-DWITH_FLUID)
+endif()
+
+if(WITH_OPENVDB)
+ add_definitions(-DWITH_OPENVDB ${OPENVDB_DEFINITIONS})
+ list(APPEND INC_SYS
+ ${OPENVDB_INCLUDE_DIRS}
+ )
+ list(APPEND LIB
+ ${OPENVDB_LIBRARIES}
+ )
+endif()
+
+if(WITH_OPENIMAGEDENOISE)
+ add_definitions(-DWITH_OPENIMAGEDENOISE)
+ list(APPEND INC_SYS
+ ${OPENIMAGEDENOISE_INCLUDE_DIRS}
+ )
+endif()
+
blender_add_lib(bf_intern_cycles "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
# avoid link failure with clang 3.4 debug
diff --git a/intern/cycles/blender/addon/__init__.py b/intern/cycles/blender/addon/__init__.py
index 93a1271b4b4..3ab352e52a2 100644
--- a/intern/cycles/blender/addon/__init__.py
+++ b/intern/cycles/blender/addon/__init__.py
@@ -20,10 +20,9 @@ bl_info = {
"name": "Cycles Render Engine",
"author": "",
"blender": (2, 80, 0),
- "location": "Info header, render engine menu",
- "description": "Cycles Render Engine integration",
+ "description": "Cycles renderer integration",
"warning": "",
- "wiki_url": "https://docs.blender.org/manual/en/dev/render/cycles/",
+ "doc_url": "https://docs.blender.org/manual/en/latest/render/cycles/",
"tracker_url": "",
"support": 'OFFICIAL',
"category": "Render"}
@@ -55,7 +54,7 @@ from . import (
class CyclesRender(bpy.types.RenderEngine):
bl_idname = 'CYCLES'
bl_label = "Cycles"
- bl_use_shading_nodes = True
+ bl_use_eevee_viewport = True
bl_use_preview = True
bl_use_exclude_layers = True
bl_use_save_buffers = True
@@ -83,20 +82,20 @@ class CyclesRender(bpy.types.RenderEngine):
def render(self, depsgraph):
engine.render(self, depsgraph)
- def bake(self, depsgraph, obj, pass_type, pass_filter, object_id, pixel_array, num_pixels, depth, result):
- engine.bake(self, depsgraph, obj, pass_type, pass_filter, object_id, pixel_array, num_pixels, depth, result)
+ def bake(self, depsgraph, obj, pass_type, pass_filter, width, height):
+ engine.bake(self, depsgraph, obj, pass_type, pass_filter, width, height)
# viewport render
- def view_update(self, context):
+ def view_update(self, context, depsgraph):
if not self.session:
engine.create(self, context.blend_data,
context.region, context.space_data, context.region_data)
- engine.reset(self, context.blend_data, context.depsgraph)
- engine.sync(self, context.depsgraph, context.blend_data)
+ engine.reset(self, context.blend_data, depsgraph)
+ engine.sync(self, depsgraph, context.blend_data)
- def view_draw(self, context):
- engine.draw(self, context.depsgraph, context.region, context.space_data, context.region_data)
+ def view_draw(self, context, depsgraph):
+ engine.draw(self, depsgraph, context.region, context.space_data, context.region_data)
def update_script_node(self, node):
if engine.with_osl():
diff --git a/intern/cycles/blender/addon/engine.py b/intern/cycles/blender/addon/engine.py
index b8bc74f9e35..67e448db859 100644
--- a/intern/cycles/blender/addon/engine.py
+++ b/intern/cycles/blender/addon/engine.py
@@ -33,7 +33,7 @@ def _is_using_buggy_driver():
# in the version string, but those cards do not quite work and
# causing crashes.
return True
- regex = re.compile(".*Compatibility Profile Context ([0-9]+(\.[0-9]+)+)$")
+ regex = re.compile(".*Compatibility Profile Context ([0-9]+(\\.[0-9]+)+)$")
if not regex.match(version):
# Skip cards like FireGL
return False
@@ -139,15 +139,19 @@ def create(engine, data, region=None, v3d=None, rv3d=None, preview_osl=False):
data = data.as_pointer()
prefs = bpy.context.preferences.as_pointer()
+ screen = 0
if region:
+ screen = region.id_data.as_pointer()
region = region.as_pointer()
if v3d:
+ screen = screen or v3d.id_data.as_pointer()
v3d = v3d.as_pointer()
if rv3d:
+ screen = screen or rv3d.id_data.as_pointer()
rv3d = rv3d.as_pointer()
engine.session = _cycles.create(
- engine.as_pointer(), prefs, data, region, v3d, rv3d, preview_osl)
+ engine.as_pointer(), prefs, data, screen, region, v3d, rv3d, preview_osl)
def free(engine):
@@ -164,18 +168,19 @@ def render(engine, depsgraph):
_cycles.render(engine.session, depsgraph.as_pointer())
-def bake(engine, depsgraph, obj, pass_type, pass_filter, object_id, pixel_array, num_pixels, depth, result):
+def bake(engine, depsgraph, obj, pass_type, pass_filter, width, height):
import _cycles
session = getattr(engine, "session", None)
if session is not None:
- _cycles.bake(engine.session, depsgraph.as_pointer(), obj.as_pointer(), pass_type, pass_filter, object_id, pixel_array.as_pointer(), num_pixels, depth, result.as_pointer())
+ _cycles.bake(engine.session, depsgraph.as_pointer(), obj.as_pointer(), pass_type, pass_filter, width, height)
def reset(engine, data, depsgraph):
import _cycles
import bpy
- if bpy.app.debug_value == 256:
+ prefs = bpy.context.preferences
+ if prefs.experimental.use_cycles_debug and prefs.view.show_developer_ui:
_cycles.debug_flags_update(depsgraph.scene.as_pointer())
else:
_cycles.debug_flags_reset()
@@ -219,65 +224,96 @@ def system_info():
import _cycles
return _cycles.system_info()
-
-def register_passes(engine, scene, srl):
- engine.register_pass(scene, srl, "Combined", 4, "RGBA", 'COLOR')
-
- if srl.use_pass_z: engine.register_pass(scene, srl, "Depth", 1, "Z", 'VALUE')
- if srl.use_pass_mist: engine.register_pass(scene, srl, "Mist", 1, "Z", 'VALUE')
- if srl.use_pass_normal: engine.register_pass(scene, srl, "Normal", 3, "XYZ", 'VECTOR')
- if srl.use_pass_vector: engine.register_pass(scene, srl, "Vector", 4, "XYZW", 'VECTOR')
- if srl.use_pass_uv: engine.register_pass(scene, srl, "UV", 3, "UVA", 'VECTOR')
- if srl.use_pass_object_index: engine.register_pass(scene, srl, "IndexOB", 1, "X", 'VALUE')
- if srl.use_pass_material_index: engine.register_pass(scene, srl, "IndexMA", 1, "X", 'VALUE')
- if srl.use_pass_shadow: engine.register_pass(scene, srl, "Shadow", 3, "RGB", 'COLOR')
- if srl.use_pass_ambient_occlusion: engine.register_pass(scene, srl, "AO", 3, "RGB", 'COLOR')
- if srl.use_pass_diffuse_direct: engine.register_pass(scene, srl, "DiffDir", 3, "RGB", 'COLOR')
- if srl.use_pass_diffuse_indirect: engine.register_pass(scene, srl, "DiffInd", 3, "RGB", 'COLOR')
- if srl.use_pass_diffuse_color: engine.register_pass(scene, srl, "DiffCol", 3, "RGB", 'COLOR')
- if srl.use_pass_glossy_direct: engine.register_pass(scene, srl, "GlossDir", 3, "RGB", 'COLOR')
- if srl.use_pass_glossy_indirect: engine.register_pass(scene, srl, "GlossInd", 3, "RGB", 'COLOR')
- if srl.use_pass_glossy_color: engine.register_pass(scene, srl, "GlossCol", 3, "RGB", 'COLOR')
- if srl.use_pass_transmission_direct: engine.register_pass(scene, srl, "TransDir", 3, "RGB", 'COLOR')
- if srl.use_pass_transmission_indirect: engine.register_pass(scene, srl, "TransInd", 3, "RGB", 'COLOR')
- if srl.use_pass_transmission_color: engine.register_pass(scene, srl, "TransCol", 3, "RGB", 'COLOR')
- if srl.use_pass_subsurface_direct: engine.register_pass(scene, srl, "SubsurfaceDir", 3, "RGB", 'COLOR')
- if srl.use_pass_subsurface_indirect: engine.register_pass(scene, srl, "SubsurfaceInd", 3, "RGB", 'COLOR')
- if srl.use_pass_subsurface_color: engine.register_pass(scene, srl, "SubsurfaceCol", 3, "RGB", 'COLOR')
- if srl.use_pass_emit: engine.register_pass(scene, srl, "Emit", 3, "RGB", 'COLOR')
- if srl.use_pass_environment: engine.register_pass(scene, srl, "Env", 3, "RGB", 'COLOR')
-
+def list_render_passes(scene, srl):
+ # Builtin Blender passes.
+ yield ("Combined", "RGBA", 'COLOR')
+
+ if srl.use_pass_z: yield ("Depth", "Z", 'VALUE')
+ if srl.use_pass_mist: yield ("Mist", "Z", 'VALUE')
+ if srl.use_pass_normal: yield ("Normal", "XYZ", 'VECTOR')
+ if srl.use_pass_vector: yield ("Vector", "XYZW", 'VECTOR')
+ if srl.use_pass_uv: yield ("UV", "UVA", 'VECTOR')
+ if srl.use_pass_object_index: yield ("IndexOB", "X", 'VALUE')
+ if srl.use_pass_material_index: yield ("IndexMA", "X", 'VALUE')
+ if srl.use_pass_shadow: yield ("Shadow", "RGB", 'COLOR')
+ if srl.use_pass_ambient_occlusion: yield ("AO", "RGB", 'COLOR')
+ if srl.use_pass_diffuse_direct: yield ("DiffDir", "RGB", 'COLOR')
+ if srl.use_pass_diffuse_indirect: yield ("DiffInd", "RGB", 'COLOR')
+ if srl.use_pass_diffuse_color: yield ("DiffCol", "RGB", 'COLOR')
+ if srl.use_pass_glossy_direct: yield ("GlossDir", "RGB", 'COLOR')
+ if srl.use_pass_glossy_indirect: yield ("GlossInd", "RGB", 'COLOR')
+ if srl.use_pass_glossy_color: yield ("GlossCol", "RGB", 'COLOR')
+ if srl.use_pass_transmission_direct: yield ("TransDir", "RGB", 'COLOR')
+ if srl.use_pass_transmission_indirect: yield ("TransInd", "RGB", 'COLOR')
+ if srl.use_pass_transmission_color: yield ("TransCol", "RGB", 'COLOR')
+ if srl.use_pass_emit: yield ("Emit", "RGB", 'COLOR')
+ if srl.use_pass_environment: yield ("Env", "RGB", 'COLOR')
+
+ # Cycles specific passes.
crl = srl.cycles
- if crl.pass_debug_render_time: engine.register_pass(scene, srl, "Debug Render Time", 1, "X", 'VALUE')
- if crl.pass_debug_bvh_traversed_nodes: engine.register_pass(scene, srl, "Debug BVH Traversed Nodes", 1, "X", 'VALUE')
- if crl.pass_debug_bvh_traversed_instances: engine.register_pass(scene, srl, "Debug BVH Traversed Instances", 1, "X", 'VALUE')
- if crl.pass_debug_bvh_intersections: engine.register_pass(scene, srl, "Debug BVH Intersections", 1, "X", 'VALUE')
- if crl.pass_debug_ray_bounces: engine.register_pass(scene, srl, "Debug Ray Bounces", 1, "X", 'VALUE')
- if crl.use_pass_volume_direct: engine.register_pass(scene, srl, "VolumeDir", 3, "RGB", 'COLOR')
- if crl.use_pass_volume_indirect: engine.register_pass(scene, srl, "VolumeInd", 3, "RGB", 'COLOR')
-
+ if crl.pass_debug_render_time: yield ("Debug Render Time", "X", 'VALUE')
+ if crl.pass_debug_bvh_traversed_nodes: yield ("Debug BVH Traversed Nodes", "X", 'VALUE')
+ if crl.pass_debug_bvh_traversed_instances: yield ("Debug BVH Traversed Instances", "X", 'VALUE')
+ if crl.pass_debug_bvh_intersections: yield ("Debug BVH Intersections", "X", 'VALUE')
+ if crl.pass_debug_ray_bounces: yield ("Debug Ray Bounces", "X", 'VALUE')
+ if crl.pass_debug_sample_count: yield ("Debug Sample Count", "X", 'VALUE')
+ if crl.use_pass_volume_direct: yield ("VolumeDir", "RGB", 'COLOR')
+ if crl.use_pass_volume_indirect: yield ("VolumeInd", "RGB", 'COLOR')
+
+ # Cryptomatte passes.
+ crypto_depth = (crl.pass_crypto_depth + 1) // 2
if crl.use_pass_crypto_object:
- for i in range(0, crl.pass_crypto_depth, 2):
- engine.register_pass(scene, srl, "CryptoObject" + '{:02d}'.format(i), 4, "RGBA", 'COLOR')
+ for i in range(0, crypto_depth):
+ yield ("CryptoObject" + '{:02d}'.format(i), "RGBA", 'COLOR')
if crl.use_pass_crypto_material:
- for i in range(0, crl.pass_crypto_depth, 2):
- engine.register_pass(scene, srl, "CryptoMaterial" + '{:02d}'.format(i), 4, "RGBA", 'COLOR')
+ for i in range(0, crypto_depth):
+ yield ("CryptoMaterial" + '{:02d}'.format(i), "RGBA", 'COLOR')
if srl.cycles.use_pass_crypto_asset:
- for i in range(0, srl.cycles.pass_crypto_depth, 2):
- engine.register_pass(scene, srl, "CryptoAsset" + '{:02d}'.format(i), 4, "RGBA", 'COLOR')
+ for i in range(0, crypto_depth):
+ yield ("CryptoAsset" + '{:02d}'.format(i), "RGBA", 'COLOR')
+ # Denoising passes.
if crl.use_denoising or crl.denoising_store_passes:
- engine.register_pass(scene, srl, "Noisy Image", 4, "RGBA", 'COLOR')
+ yield ("Noisy Image", "RGBA", 'COLOR')
if crl.denoising_store_passes:
- engine.register_pass(scene, srl, "Denoising Normal", 3, "XYZ", 'VECTOR')
- engine.register_pass(scene, srl, "Denoising Albedo", 3, "RGB", 'COLOR')
- engine.register_pass(scene, srl, "Denoising Depth", 1, "Z", 'VALUE')
- engine.register_pass(scene, srl, "Denoising Shadowing", 1, "X", 'VALUE')
- engine.register_pass(scene, srl, "Denoising Variance", 3, "RGB", 'COLOR')
- engine.register_pass(scene, srl, "Denoising Intensity", 1, "X", 'VALUE')
- clean_options = ("denoising_diffuse_direct", "denoising_diffuse_indirect",
- "denoising_glossy_direct", "denoising_glossy_indirect",
- "denoising_transmission_direct", "denoising_transmission_indirect",
- "denoising_subsurface_direct", "denoising_subsurface_indirect")
- if any(getattr(crl, option) for option in clean_options):
- engine.register_pass(scene, srl, "Denoising Clean", 3, "RGB", 'COLOR')
+ yield ("Denoising Normal", "XYZ", 'VECTOR')
+ yield ("Denoising Albedo", "RGB", 'COLOR')
+ yield ("Denoising Depth", "Z", 'VALUE')
+
+ if scene.cycles.denoiser == 'NLM':
+ yield ("Denoising Shadowing", "X", 'VALUE')
+ yield ("Denoising Variance", "RGB", 'COLOR')
+ yield ("Denoising Intensity", "X", 'VALUE')
+
+ clean_options = ("denoising_diffuse_direct", "denoising_diffuse_indirect",
+ "denoising_glossy_direct", "denoising_glossy_indirect",
+ "denoising_transmission_direct", "denoising_transmission_indirect")
+ if any(getattr(crl, option) for option in clean_options):
+ yield ("Denoising Clean", "RGB", 'COLOR')
+
+ # Custom AOV passes.
+ for aov in crl.aovs:
+ if aov.type == 'VALUE':
+ yield (aov.name, "X", 'VALUE')
+ else:
+ yield (aov.name, "RGBA", 'COLOR')
+
+def register_passes(engine, scene, view_layer):
+ # Detect duplicate render pass names, first one wins.
+ listed = set()
+ for name, channelids, channeltype in list_render_passes(scene, view_layer):
+ if name not in listed:
+ engine.register_pass(scene, view_layer, name, len(channelids), channelids, channeltype)
+ listed.add(name)
+
+def detect_conflicting_passes(scene, view_layer):
+ # Detect conflicting render pass names for UI.
+ counter = {}
+ for name, _, _ in list_render_passes(scene, view_layer):
+ counter[name] = counter.get(name, 0) + 1
+
+ for aov in view_layer.cycles.aovs:
+ if counter[aov.name] > 1:
+ aov.conflict = "Conflicts with another render pass with the same name"
+ else:
+ aov.conflict = ""
diff --git a/intern/cycles/blender/addon/operators.py b/intern/cycles/blender/addon/operators.py
index 63c61c4799e..3c8e79eaba5 100644
--- a/intern/cycles/blender/addon/operators.py
+++ b/intern/cycles/blender/addon/operators.py
@@ -20,6 +20,8 @@ import bpy
from bpy.types import Operator
from bpy.props import StringProperty
+from bpy.app.translations import pgettext_tip as tip_
+
class CYCLES_OT_use_shading_nodes(Operator):
"""Enable nodes on a material, world or light"""
@@ -42,6 +44,36 @@ class CYCLES_OT_use_shading_nodes(Operator):
return {'FINISHED'}
+class CYCLES_OT_add_aov(bpy.types.Operator):
+ """Add an AOV pass"""
+ bl_idname="cycles.add_aov"
+ bl_label="Add AOV"
+
+ def execute(self, context):
+ view_layer = context.view_layer
+ cycles_view_layer = view_layer.cycles
+
+ cycles_view_layer.aovs.add()
+
+ view_layer.update_render_passes()
+ return {'FINISHED'}
+
+
+class CYCLES_OT_remove_aov(bpy.types.Operator):
+ """Remove an AOV pass"""
+ bl_idname="cycles.remove_aov"
+ bl_label="Remove AOV"
+
+ def execute(self, context):
+ view_layer = context.view_layer
+ cycles_view_layer = view_layer.cycles
+
+ cycles_view_layer.aovs.remove(cycles_view_layer.active_aov)
+
+ view_layer.update_render_passes()
+ return {'FINISHED'}
+
+
class CYCLES_OT_denoise_animation(Operator):
"Denoise rendered animation sequence using current scene and view " \
"layer settings. Requires denoising data passes and output to " \
@@ -98,7 +130,8 @@ class CYCLES_OT_denoise_animation(Operator):
if not os.path.isfile(filepath):
scene.render.filepath = original_filepath
- self.report({'ERROR'}, f"Frame '{filepath}' not found, animation must be complete.")
+ err_msg = tip_("Frame '%s' not found, animation must be complete") % filepath
+ self.report({'ERROR'}, err_msg)
return {'CANCELLED'}
scene.render.filepath = out_filepath
@@ -120,12 +153,12 @@ class CYCLES_OT_denoise_animation(Operator):
self.report({'ERROR'}, str(e))
return {'FINISHED'}
- self.report({'INFO'}, "Denoising completed.")
+ self.report({'INFO'}, "Denoising completed")
return {'FINISHED'}
class CYCLES_OT_merge_images(Operator):
- "Combine OpenEXR multilayer images rendered with different sample" \
+ "Combine OpenEXR multilayer images rendered with different sample " \
"ranges into one image with reduced noise"
bl_idname = "cycles.merge_images"
bl_label = "Merge Images"
@@ -164,6 +197,8 @@ class CYCLES_OT_merge_images(Operator):
classes = (
CYCLES_OT_use_shading_nodes,
+ CYCLES_OT_add_aov,
+ CYCLES_OT_remove_aov,
CYCLES_OT_denoise_animation,
CYCLES_OT_merge_images
)
diff --git a/intern/cycles/blender/addon/osl.py b/intern/cycles/blender/addon/osl.py
index dd92ce642d4..4c6e7952491 100644
--- a/intern/cycles/blender/addon/osl.py
+++ b/intern/cycles/blender/addon/osl.py
@@ -85,6 +85,7 @@ def update_script_node(node, report):
# write text datablock contents to temporary file
osl_file = tempfile.NamedTemporaryFile(mode='w', suffix=".osl", delete=False)
osl_file.write(script.as_string())
+ osl_file.write("\n")
osl_file.close()
ok, oso_path = osl_compile(osl_file.name, report)
diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py
index db9e8bb46b3..d764f469eb7 100644
--- a/intern/cycles/blender/addon/properties.py
+++ b/intern/cycles/blender/addon/properties.py
@@ -19,6 +19,7 @@
import bpy
from bpy.props import (
BoolProperty,
+ CollectionProperty,
EnumProperty,
FloatProperty,
IntProperty,
@@ -31,6 +32,7 @@ from math import pi
# enums
import _cycles
+from . import engine
enum_devices = (
('CPU', "CPU", "Use CPU for rendering"),
@@ -53,8 +55,7 @@ enum_displacement_methods = (
enum_bvh_layouts = (
('BVH2', "BVH2", "", 1),
- ('BVH4', "BVH4", "", 2),
- ('BVH8', "BVH8", "", 4),
+ ('EMBREE', "Embree", "", 4),
)
enum_bvh_types = (
@@ -68,11 +69,6 @@ enum_filter_types = (
('BLACKMAN_HARRIS', "Blackman-Harris", "Blackman-Harris filter"),
)
-enum_aperture_types = (
- ('RADIUS', "Radius", "Directly change the size of the aperture"),
- ('FSTOP', "F-stop", "Change the size of the aperture by f-stop"),
-)
-
enum_panorama_types = (
('EQUIRECTANGULAR', "Equirectangular", "Render the scene with a spherical camera, also known as Lat Long panorama"),
('FISHEYE_EQUIDISTANT', "Fisheye Equidistant", "Ideal for fulldomes, ignore the sensor dimensions"),
@@ -81,20 +77,9 @@ enum_panorama_types = (
('MIRRORBALL', "Mirror Ball", "Uses the mirror ball mapping"),
)
-enum_curve_primitives = (
- ('TRIANGLES', "Triangles", "Create triangle geometry around strands"),
- ('LINE_SEGMENTS', "Line Segments", "Use line segment primitives"),
- ('CURVE_SEGMENTS', "Curve Segments", "Use segmented cardinal curve primitives"),
-)
-
-enum_triangle_curves = (
- ('CAMERA_TRIANGLES', "Planes", "Create individual triangles forming planes that face camera"),
- ('TESSELLATED_TRIANGLES', "Tessellated", "Create mesh surrounding each strand"),
-)
-
enum_curve_shape = (
- ('RIBBONS', "Ribbons", "Ignore thickness of each strand"),
- ('THICK', "Thick", "Use thickness of strand when rendering"),
+ ('RIBBONS', "Rounded Ribbons", "Render hair as flat ribbon with rounded normals, for fast rendering"),
+ ('THICK', "3D Curves", "Render hair as 3D curve, for accurate results when viewing hair close up"),
)
enum_tile_order = (
@@ -115,6 +100,7 @@ enum_use_layer_samples = (
enum_sampling_pattern = (
('SOBOL', "Sobol", "Use Sobol random sampling pattern"),
('CORRELATED_MUTI_JITTER', "Correlated Multi-Jitter", "Use Correlated Multi-Jitter random sampling pattern"),
+ ('PROGRESSIVE_MUTI_JITTER', "Progressive Multi-Jitter", "Use Progressive Multi-Jitter random sampling pattern"),
)
enum_integrator = (
@@ -142,6 +128,7 @@ enum_world_mis = (
enum_device_type = (
('CPU', "CPU", "CPU", 0),
('CUDA', "CUDA", "CUDA", 1),
+ ('OPTIX', "OptiX", "OptiX", 3),
('OPENCL', "OpenCL", "OpenCL", 2)
)
@@ -156,6 +143,88 @@ enum_texture_limit = (
('8192', "8192", "Limit texture size to 8192 pixels", 7),
)
+enum_view3d_shading_render_pass= (
+ ('', "General", ""),
+
+ ('COMBINED', "Combined", "Show the Combined Render pass", 1),
+ ('EMISSION', "Emission", "Show the Emission render pass", 33),
+ ('BACKGROUND', "Background", "Show the Background render pass", 34),
+ ('AO', "Ambient Occlusion", "Show the Ambient Occlusion render pass", 35),
+
+ ('', "Light", ""),
+
+ ('DIFFUSE_DIRECT', "Diffuse Direct", "Show the Diffuse Direct render pass", 38),
+ ('DIFFUSE_INDIRECT', "Diffuse Indirect", "Show the Diffuse Indirect render pass", 39),
+ ('DIFFUSE_COLOR', "Diffuse Color", "Show the Diffuse Color render pass", 40),
+
+ ('GLOSSY_DIRECT', "Glossy Direct", "Show the Glossy Direct render pass", 41),
+ ('GLOSSY_INDIRECT', "Glossy Indirect", "Show the Glossy Indirect render pass", 42),
+ ('GLOSSY_COLOR', "Glossy Color", "Show the Glossy Color render pass", 43),
+
+ ('', "", ""),
+
+ ('TRANSMISSION_DIRECT', "Transmission Direct", "Show the Transmission Direct render pass", 44),
+ ('TRANSMISSION_INDIRECT', "Transmission Indirect", "Show the Transmission Indirect render pass", 45),
+ ('TRANSMISSION_COLOR', "Transmission Color", "Show the Transmission Color render pass", 46),
+
+ ('VOLUME_DIRECT', "Volume Direct", "Show the Volume Direct render pass", 50),
+ ('VOLUME_INDIRECT', "Volume Indirect", "Show the Volume Indirect render pass", 51),
+
+ ('', "Data", ""),
+
+ ('NORMAL', "Normal", "Show the Normal render pass", 3),
+ ('UV', "UV", "Show the UV render pass", 4),
+ ('MIST', "Mist", "Show the Mist render pass", 32),
+)
+
+enum_aov_types = (
+ ('VALUE', "Value", "Write a Value pass", 0),
+ ('COLOR', "Color", "Write a Color pass", 1),
+)
+
+
+def enum_openimagedenoise_denoiser(self, context):
+ if _cycles.with_openimagedenoise:
+ return [('OPENIMAGEDENOISE', "OpenImageDenoise", "Use Intel OpenImageDenoise AI denoiser running on the CPU", 4)]
+ return []
+
+def enum_optix_denoiser(self, context):
+ if not context or bool(context.preferences.addons[__package__].preferences.get_devices_for_type('OPTIX')):
+ return [('OPTIX', "OptiX", "Use the OptiX AI denoiser with GPU acceleration, only available on NVIDIA GPUs", 2)]
+ return []
+
+def enum_preview_denoiser(self, context):
+ optix_items = enum_optix_denoiser(self, context)
+ oidn_items = enum_openimagedenoise_denoiser(self, context)
+
+ if len(optix_items) or len(oidn_items):
+ items = [('AUTO', "Automatic", "Use the fastest available denoiser for viewport rendering (OptiX if available, OpenImageDenoise otherwise)", 0)]
+ else:
+ items = [('AUTO', "None", "Blender was compiled without a viewport denoiser", 0)]
+
+ items += optix_items
+ items += oidn_items
+ return items
+
+def enum_denoiser(self, context):
+ items = [('NLM', "NLM", "Cycles native non-local means denoiser, running on any compute device", 1)]
+ items += enum_optix_denoiser(self, context)
+ items += enum_openimagedenoise_denoiser(self, context)
+ return items
+
+enum_denoising_input_passes = (
+ ('RGB', "Color", "Use only color as input", 1),
+ ('RGB_ALBEDO', "Color + Albedo", "Use color and albedo data as input", 2),
+ ('RGB_ALBEDO_NORMAL', "Color + Albedo + Normal", "Use color, albedo and normal data as input", 3),
+)
+
+
+def update_render_passes(self, context):
+ scene = context.scene
+ view_layer = context.view_layer
+ view_layer.update_render_passes()
+ engine.detect_conflicting_passes(scene, view_layer)
+
class CyclesRenderSettings(bpy.types.PropertyGroup):
@@ -183,6 +252,39 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
default='PATH',
)
+ preview_pause: BoolProperty(
+ name="Pause Preview",
+ description="Pause all viewport preview renders",
+ default=False,
+ )
+
+ use_denoising: BoolProperty(
+ name="Use Denoising",
+ description="Denoise the rendered image",
+ default=False,
+ )
+ use_preview_denoising: BoolProperty(
+ name="Use Viewport Denoising",
+ description="Denoise the image in the 3D viewport",
+ default=False,
+ )
+
+ denoiser: EnumProperty(
+ name="Denoiser",
+ description="Denoise the image with the selected denoiser. "
+ "For denoising the image after rendering, denoising data render passes "
+ "also adapt to the selected denoiser",
+ items=enum_denoiser,
+ default=1,
+ update=update_render_passes,
+ )
+ preview_denoiser: EnumProperty(
+ name="Viewport Denoiser",
+ description="Denoise the image after each preview update with the selected denoiser",
+ items=enum_preview_denoiser,
+ default=0,
+ )
+
use_square_samples: BoolProperty(
name="Square Samples",
description="Square sampling values for easier artist control",
@@ -209,16 +311,11 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
default=128,
)
preview_samples: IntProperty(
- name="Preview Samples",
+ name="Viewport Samples",
description="Number of samples to render in the viewport, unlimited if 0",
min=0, max=(1 << 24),
default=32,
)
- preview_pause: BoolProperty(
- name="Pause Preview",
- description="Pause all viewport preview renders",
- default=False,
- )
aa_samples: IntProperty(
name="AA Samples",
description="Number of antialiasing samples to render for each pixel",
@@ -231,6 +328,7 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
min=0, max=2097151,
default=32,
)
+
diffuse_samples: IntProperty(
name="Diffuse Samples",
description="Number of diffuse bounce samples to render for each AA sample",
@@ -261,14 +359,12 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
min=1, max=1024,
default=1,
)
-
subsurface_samples: IntProperty(
name="Subsurface Samples",
description="Number of subsurface scattering samples to render for each AA sample",
min=1, max=1024,
default=1,
)
-
volume_samples: IntProperty(
name="Volume Samples",
description="Number of volume scattering samples to render for each AA sample",
@@ -309,6 +405,41 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
default=0.01,
)
+ use_adaptive_sampling: BoolProperty(
+ name="Use Adaptive Sampling",
+ description="Automatically reduce the number of samples per pixel based on estimated noise level",
+ default=False,
+ )
+
+ adaptive_threshold: FloatProperty(
+ name="Adaptive Sampling Threshold",
+ description="Noise level step to stop sampling at, lower values reduce noise the cost of render time. Zero for automatic setting based on number of AA samples",
+ min=0.0, max=1.0,
+ default=0.0,
+ precision=4,
+ )
+ adaptive_min_samples: IntProperty(
+ name="Adaptive Min Samples",
+ description="Minimum AA samples for adaptive sampling, to discover noisy features before stopping sampling. Zero for automatic setting based on number of AA samples",
+ min=0, max=4096,
+ default=0,
+ )
+
+ min_light_bounces: IntProperty(
+ name="Min Light Bounces",
+ description="Minimum number of light bounces. Setting this higher reduces noise in the first bounces, "
+ "but can also be less efficient for more complex geometry like hair and volumes",
+ min=0, max=1024,
+ default=0,
+ )
+ min_transparent_bounces: IntProperty(
+ name="Min Transparent Bounces",
+ description="Minimum number of transparent bounces. Setting this higher reduces noise in the first bounces, "
+ "but can also be less efficient for more complex geometry like hair and volumes",
+ min=0, max=1024,
+ default=0,
+ )
+
caustics_reflective: BoolProperty(
name="Reflective Caustics",
description="Use reflective caustics, resulting in a brighter image (more noise but added realism)",
@@ -368,13 +499,20 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
default=8,
)
- volume_step_size: FloatProperty(
- name="Step Size",
- description="Distance between volume shader samples when rendering the volume "
- "(lower values give more accurate and detailed results, but also increased render time)",
- default=0.1,
- min=0.0000001, max=100000.0, soft_min=0.01, soft_max=1.0, precision=4,
- unit='LENGTH'
+ volume_step_rate: FloatProperty(
+ name="Step Rate",
+ description="Globally adjust detail for volume rendering, on top of automatically estimated step size. "
+ "Higher values reduce render time, lower values render with more detail",
+ default=1.0,
+ min=0.01, max=100.0, soft_min=0.1, soft_max=10.0, precision=2
+ )
+
+ volume_preview_step_rate: FloatProperty(
+ name="Step Rate",
+ description="Globally adjust detail for volume rendering, on top of automatically estimated step size. "
+ "Higher values reduce render time, lower values render with more detail",
+ default=1.0,
+ min=0.01, max=100.0, soft_min=0.1, soft_max=10.0, precision=2
)
volume_max_steps: IntProperty(
@@ -393,7 +531,7 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
subtype='PIXEL'
)
preview_dicing_rate: FloatProperty(
- name="Preview Dicing Rate",
+ name="Viewport Dicing Rate",
description="Size of a micropolygon in pixels during preview render",
min=0.1, max=1000.0, soft_min=0.5,
default=8.0,
@@ -430,11 +568,6 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
min=0.0, max=10.0,
default=1.0,
)
- film_transparent: BoolProperty(
- name="Transparent",
- description="World background is transparent, for compositing the render over another background",
- default=False,
- )
film_transparent_glass: BoolProperty(
name="Transparent Glass",
description="Render transmissive surfaces as transparent, for compositing glass over another background",
@@ -519,6 +652,12 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
default=64,
subtype='PIXEL'
)
+ preview_denoising_start_sample: IntProperty(
+ name="Start Denoising",
+ description="Sample to start denoising the preview at",
+ min=0, max=(1 << 24),
+ default=1,
+ )
debug_reset_timeout: FloatProperty(
name="Reset timeout",
@@ -545,11 +684,6 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
items=enum_bvh_types,
default='DYNAMIC_BVH',
)
- use_bvh_embree: BoolProperty(
- name="Use Embree",
- description="Use Embree as ray accelerator",
- default=False,
- )
debug_use_spatial_splits: BoolProperty(
name="Use Spatial Splits",
description="Use BVH spatial splits: longer builder time, faster render",
@@ -598,7 +732,6 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
('DIFFUSE', "Diffuse", ""),
('GLOSSY', "Glossy", ""),
('TRANSMISSION', "Transmission", ""),
- ('SUBSURFACE', "Subsurface", ""),
),
)
@@ -703,13 +836,16 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
debug_bvh_layout: EnumProperty(
name="BVH Layout",
items=enum_bvh_layouts,
- default='BVH8',
+ default='EMBREE',
)
debug_use_cpu_split_kernel: BoolProperty(name="Split Kernel", default=False)
debug_use_cuda_adaptive_compile: BoolProperty(name="Adaptive Compile", default=False)
debug_use_cuda_split_kernel: BoolProperty(name="Split Kernel", default=False)
+ debug_optix_cuda_streams: IntProperty(name="CUDA Streams", default=1, min=1)
+ debug_optix_curves_api: BoolProperty(name="Native OptiX Curve Primitive", default=False)
+
debug_opencl_kernel_type: EnumProperty(
name="OpenCL Kernel Type",
default='DEFAULT',
@@ -760,49 +896,6 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
class CyclesCameraSettings(bpy.types.PropertyGroup):
- aperture_type: EnumProperty(
- name="Aperture Type",
- description="Use f-stop number or aperture radius",
- items=enum_aperture_types,
- default='RADIUS',
- )
- aperture_fstop: FloatProperty(
- name="Aperture f-stop",
- description="F-stop ratio (lower numbers give more defocus, higher numbers give a sharper image)",
- min=0.0, soft_min=0.1, soft_max=64.0,
- default=5.6,
- step=10,
- precision=1,
- )
- aperture_size: FloatProperty(
- name="Aperture Size",
- description="Radius of the aperture for depth of field (higher values give more defocus)",
- min=0.0, soft_max=10.0,
- default=0.0,
- step=1,
- precision=4,
- subtype='DISTANCE',
- )
- aperture_blades: IntProperty(
- name="Aperture Blades",
- description="Number of blades in aperture for polygonal bokeh (at least 3)",
- min=0, max=100,
- default=0,
- )
- aperture_rotation: FloatProperty(
- name="Aperture Rotation",
- description="Rotation of blades in aperture",
- soft_min=-pi, soft_max=pi,
- subtype='ANGLE',
- default=0,
- )
- aperture_ratio: FloatProperty(
- name="Aperture Ratio",
- description="Distortion to simulate anamorphic lens bokeh",
- min=0.01, soft_min=1.0, soft_max=2.0,
- default=1.0,
- precision=4,
- )
panorama_type: EnumProperty(
name="Panorama Type",
description="Distortion to use for the calculation",
@@ -899,6 +992,14 @@ class CyclesMaterialSettings(bpy.types.PropertyGroup):
default='LINEAR',
)
+ volume_step_rate: FloatProperty(
+ name="Step Rate",
+ description="Scale the distance between volume shader samples when rendering the volume "
+ "(lower values give more accurate and detailed results, but also increased render time)",
+ default=1.0,
+ min=0.001, max=1000.0, soft_min=0.1, soft_max=10.0, precision=4
+ )
+
displacement_method: EnumProperty(
name="Displacement Method",
description="Method to use for the displacement",
@@ -967,7 +1068,7 @@ class CyclesLightSettings(bpy.types.PropertyGroup):
class CyclesWorldSettings(bpy.types.PropertyGroup):
sampling_method: EnumProperty(
- name="Sampling method",
+ name="Sampling Method",
description="How to sample the background light",
items=enum_world_mis,
default='AUTOMATIC',
@@ -1009,6 +1110,13 @@ class CyclesWorldSettings(bpy.types.PropertyGroup):
items=enum_volume_interpolation,
default='LINEAR',
)
+ volume_step_size: FloatProperty(
+ name="Step Size",
+ description="Distance between volume shader samples when rendering the volume "
+ "(lower values give more accurate and detailed results, but also increased render time)",
+ default=1.0,
+ min=0.0000001, max=100000.0, soft_min=0.1, soft_max=100.0, precision=4
+ )
@classmethod
def register(cls):
@@ -1119,7 +1227,7 @@ class CyclesObjectSettings(bpy.types.PropertyGroup):
motion_steps: IntProperty(
name="Motion Steps",
description="Control accuracy of motion blur, more steps gives more memory usage (actual number of steps is 2^(steps - 1))",
- min=1, soft_max=8,
+ min=1, max=7,
default=1,
)
@@ -1148,6 +1256,13 @@ class CyclesObjectSettings(bpy.types.PropertyGroup):
default=1.0,
)
+ shadow_terminator_offset: FloatProperty(
+ name="Shadow Terminator Offset",
+ description="Push the shadow terminator towards the light to hide artifacts on low poly geometry",
+ min=0.0, max=1.0,
+ default=0.0,
+ )
+
is_shadow_catcher: BoolProperty(
name="Shadow Catcher",
description="Only render shadows on this object, for compositing renders into real footage",
@@ -1177,53 +1292,17 @@ class CyclesObjectSettings(bpy.types.PropertyGroup):
class CyclesCurveRenderSettings(bpy.types.PropertyGroup):
- primitive: EnumProperty(
- name="Primitive",
- description="Type of primitive used for hair rendering",
- items=enum_curve_primitives,
- default='LINE_SEGMENTS',
- )
shape: EnumProperty(
name="Shape",
description="Form of hair",
items=enum_curve_shape,
- default='THICK',
- )
- cull_backfacing: BoolProperty(
- name="Cull Back-faces",
- description="Do not test the back-face of each strand",
- default=True,
- )
- use_curves: BoolProperty(
- name="Use Cycles Hair Rendering",
- description="Activate Cycles hair rendering for particle system",
- default=True,
- )
- resolution: IntProperty(
- name="Resolution",
- description="Resolution of generated mesh",
- min=3, max=64,
- default=3,
- )
- minimum_width: FloatProperty(
- name="Minimal width",
- description="Minimal pixel width for strands (0 - deactivated)",
- min=0.0, max=100.0,
- default=0.0,
- subtype='PIXEL'
- )
- maximum_width: FloatProperty(
- name="Maximal width",
- description="Maximum extension that strand radius can be increased by",
- min=0.0, max=100.0,
- default=0.1,
- subtype='PIXEL'
+ default='RIBBONS',
)
subdivisions: IntProperty(
name="Subdivisions",
description="Number of subdivisions used in Cardinal curve intersection (power of 2)",
min=0, max=24,
- default=4,
+ default=2,
)
@classmethod
@@ -1239,10 +1318,25 @@ class CyclesCurveRenderSettings(bpy.types.PropertyGroup):
del bpy.types.Scene.cycles_curves
-def update_render_passes(self, context):
- view_layer = context.view_layer
- view_layer.update_render_passes()
-
+class CyclesAOVPass(bpy.types.PropertyGroup):
+ name: StringProperty(
+ name="Name",
+ description="Name of the pass, to use in the AOV Output shader node",
+ update=update_render_passes,
+ default="AOV"
+ )
+ type: EnumProperty(
+ name="Type",
+ description="Pass data type",
+ update=update_render_passes,
+ items=enum_aov_types,
+ default='COLOR'
+ )
+ conflict: StringProperty(
+ name="Conflict",
+ description="If there is a conflict with another render passes, message explaining why",
+ default=""
+ )
class CyclesRenderLayerSettings(bpy.types.PropertyGroup):
@@ -1276,6 +1370,12 @@ class CyclesRenderLayerSettings(bpy.types.PropertyGroup):
default=False,
update=update_render_passes,
)
+ pass_debug_sample_count: BoolProperty(
+ name="Debug Sample Count",
+ description="Number of samples/camera rays per pixel",
+ default=False,
+ update=update_render_passes,
+ )
use_pass_volume_direct: BoolProperty(
name="Volume Direct",
description="Deliver direct volumetric scattering pass",
@@ -1292,7 +1392,7 @@ class CyclesRenderLayerSettings(bpy.types.PropertyGroup):
use_denoising: BoolProperty(
name="Use Denoising",
description="Denoise the rendered image",
- default=False,
+ default=True,
update=update_render_passes,
)
denoising_diffuse_direct: BoolProperty(
@@ -1325,16 +1425,6 @@ class CyclesRenderLayerSettings(bpy.types.PropertyGroup):
description="Denoise the indirect transmission lighting",
default=True,
)
- denoising_subsurface_direct: BoolProperty(
- name="Subsurface Direct",
- description="Denoise the direct subsurface lighting",
- default=True,
- )
- denoising_subsurface_indirect: BoolProperty(
- name="Subsurface Indirect",
- description="Denoise the indirect subsurface lighting",
- default=True,
- )
denoising_strength: FloatProperty(
name="Denoising Strength",
description="Controls neighbor pixel weighting for the denoising filter (lower values preserve more detail, but aren't as smooth)",
@@ -1355,13 +1445,13 @@ class CyclesRenderLayerSettings(bpy.types.PropertyGroup):
subtype="PIXEL",
)
denoising_relative_pca: BoolProperty(
- name="Relative filter",
+ name="Relative Filter",
description="When removing pixels that don't carry information, use a relative threshold instead of an absolute one (can help to reduce artifacts, but might cause detail loss around edges)",
default=False,
)
denoising_store_passes: BoolProperty(
- name="Store denoising passes",
- description="Store the denoising feature passes and the noisy image",
+ name="Store Denoising Passes",
+ description="Store the denoising feature passes and the noisy image. The passes adapt to the denoiser selected for rendering",
default=False,
update=update_render_passes,
)
@@ -1371,6 +1461,21 @@ class CyclesRenderLayerSettings(bpy.types.PropertyGroup):
min=0, max=7,
default=0,
)
+
+ denoising_optix_input_passes: EnumProperty(
+ name="Input Passes",
+ description="Passes used by the denoiser to distinguish noise from shader and geometry detail",
+ items=enum_denoising_input_passes,
+ default='RGB_ALBEDO',
+ )
+
+ denoising_openimagedenoise_input_passes: EnumProperty(
+ name="Input Passes",
+ description="Passes used by the denoiser to distinguish noise from shader and geometry detail",
+ items=enum_denoising_input_passes,
+ default='RGB_ALBEDO_NORMAL',
+ )
+
use_pass_crypto_object: BoolProperty(
name="Cryptomatte Object",
description="Render cryptomatte object pass, for isolating objects in compositing",
@@ -1402,6 +1507,15 @@ class CyclesRenderLayerSettings(bpy.types.PropertyGroup):
update=update_render_passes,
)
+ aovs: CollectionProperty(
+ type=CyclesAOVPass,
+ description="Custom render passes that can be output by shader nodes",
+ )
+ active_aov: IntProperty(
+ default=0,
+ min=0
+ )
+
@classmethod
def register(cls):
bpy.types.ViewLayer.cycles = PointerProperty(
@@ -1427,10 +1541,12 @@ class CyclesPreferences(bpy.types.AddonPreferences):
def get_device_types(self, context):
import _cycles
- has_cuda, has_opencl = _cycles.get_device_types()
+ has_cuda, has_optix, has_opencl = _cycles.get_device_types()
list = [('NONE', "None", "Don't use compute device", 0)]
if has_cuda:
list.append(('CUDA', "CUDA", "Use CUDA for GPU acceleration", 1))
+ if has_optix:
+ list.append(('OPTIX', "OptiX", "Use OptiX for GPU acceleration", 3))
if has_opencl:
list.append(('OPENCL', "OpenCL", "Use OpenCL for GPU acceleration", 2))
return list
@@ -1443,6 +1559,12 @@ class CyclesPreferences(bpy.types.AddonPreferences):
devices: bpy.props.CollectionProperty(type=CyclesDeviceSettings)
+ peer_memory: BoolProperty(
+ name="Distribute memory across devices",
+ description="Make more room for large scenes to fit by distributing memory across interconnected devices (e.g. via NVLink) rather than duplicating it",
+ default=False,
+ )
+
def find_existing_device_entry(self, device):
for device_entry in self.devices:
if device_entry.id == device[2] and device_entry.type == device[1]:
@@ -1451,7 +1573,7 @@ class CyclesPreferences(bpy.types.AddonPreferences):
def update_device_entries(self, device_list):
for device in device_list:
- if not device[1] in {'CUDA', 'OPENCL', 'CPU'}:
+ if not device[1] in {'CUDA', 'OPTIX', 'OPENCL', 'CPU'}:
continue
# Try to find existing Device entry
entry = self.find_existing_device_entry(device)
@@ -1466,8 +1588,8 @@ class CyclesPreferences(bpy.types.AddonPreferences):
# Update name in case it changed
entry.name = device[0]
- # Gets all devices types by default.
- def get_devices(self, compute_device_type=''):
+ # Gets all devices types for a compute device type.
+ def get_devices_for_type(self, compute_device_type):
import _cycles
# Layout of the device tuples: (Name, Type, Persistent ID)
device_list = _cycles.available_devices(compute_device_type)
@@ -1476,20 +1598,25 @@ class CyclesPreferences(bpy.types.AddonPreferences):
# hold pointers to a resized array.
self.update_device_entries(device_list)
# Sort entries into lists
- cuda_devices = []
- opencl_devices = []
+ devices = []
cpu_devices = []
for device in device_list:
entry = self.find_existing_device_entry(device)
- if entry.type == 'CUDA':
- cuda_devices.append(entry)
- elif entry.type == 'OPENCL':
- opencl_devices.append(entry)
+ if entry.type == compute_device_type:
+ devices.append(entry)
elif entry.type == 'CPU':
cpu_devices.append(entry)
# Extend all GPU devices with CPU.
- cuda_devices.extend(cpu_devices)
- opencl_devices.extend(cpu_devices)
+ if compute_device_type in ('CUDA', 'OPENCL'):
+ devices.extend(cpu_devices)
+ return devices
+
+ # For backwards compatibility, only returns CUDA and OpenCL but still
+ # refreshes all devices.
+ def get_devices(self, compute_device_type=''):
+ cuda_devices = self.get_devices_for_type('CUDA')
+ self.get_devices_for_type('OPTIX')
+ opencl_devices = self.get_devices_for_type('OPENCL')
return cuda_devices, opencl_devices
def get_num_gpu_devices(self):
@@ -1517,27 +1644,53 @@ class CyclesPreferences(bpy.types.AddonPreferences):
break
if not found_device:
- box.label(text="No compatible GPUs found", icon='INFO')
+ col = box.column(align=True)
+ col.label(text="No compatible GPUs found for path tracing", icon='INFO')
+ col.label(text="Cycles will render on the CPU", icon='BLANK1')
return
for device in devices:
box.prop(device, "use", text=device.name)
+ if device_type == 'OPTIX':
+ col = box.column(align=True)
+ col.label(text="OptiX support is experimental", icon='INFO')
+ col.label(text="Not all Cycles features are supported yet", icon='BLANK1')
+
+
def draw_impl(self, layout, context):
row = layout.row()
row.prop(self, "compute_device_type", expand=True)
- cuda_devices, opencl_devices = self.get_devices(self.compute_device_type)
+ if self.compute_device_type == 'NONE':
+ return
row = layout.row()
- if self.compute_device_type == 'CUDA':
- self._draw_devices(row, 'CUDA', cuda_devices)
- elif self.compute_device_type == 'OPENCL':
- self._draw_devices(row, 'OPENCL', opencl_devices)
+ devices = self.get_devices_for_type(self.compute_device_type)
+ self._draw_devices(row, self.compute_device_type, devices)
+
+ import _cycles
+ has_peer_memory = 0
+ for device in _cycles.available_devices(self.compute_device_type):
+ if device[3] and self.find_existing_device_entry(device).use:
+ has_peer_memory += 1
+ if has_peer_memory > 1:
+ row = layout.row()
+ row.use_property_split = True
+ row.prop(self, "peer_memory")
def draw(self, context):
self.draw_impl(self.layout, context)
+class CyclesView3DShadingSettings(bpy.types.PropertyGroup):
+ render_pass: EnumProperty(
+ name="Render Pass",
+ description="Render pass to show in the 3D Viewport",
+ items=enum_view3d_shading_render_pass,
+ default='COMBINED',
+ )
+
+
def register():
bpy.utils.register_class(CyclesRenderSettings)
bpy.utils.register_class(CyclesCameraSettings)
@@ -1550,7 +1703,14 @@ def register():
bpy.utils.register_class(CyclesCurveRenderSettings)
bpy.utils.register_class(CyclesDeviceSettings)
bpy.utils.register_class(CyclesPreferences)
+ bpy.utils.register_class(CyclesAOVPass)
bpy.utils.register_class(CyclesRenderLayerSettings)
+ bpy.utils.register_class(CyclesView3DShadingSettings)
+
+ bpy.types.View3DShading.cycles = bpy.props.PointerProperty(
+ name="Cycles Settings",
+ type=CyclesView3DShadingSettings,
+ )
def unregister():
@@ -1565,4 +1725,6 @@ def unregister():
bpy.utils.unregister_class(CyclesCurveRenderSettings)
bpy.utils.unregister_class(CyclesDeviceSettings)
bpy.utils.unregister_class(CyclesPreferences)
+ bpy.utils.unregister_class(CyclesAOVPass)
bpy.utils.unregister_class(CyclesRenderLayerSettings)
+ bpy.utils.unregister_class(CyclesView3DShadingSettings)
diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py
index 303f1ddf1b2..39cb1477c33 100644
--- a/intern/cycles/blender/addon/ui.py
+++ b/intern/cycles/blender/addon/ui.py
@@ -22,6 +22,8 @@ from bl_ui.utils import PresetPanel
from bpy.types import Panel
+from bl_ui.properties_grease_pencil_common import GreasePencilSimplifyPanel
+
class CYCLES_PT_sampling_presets(PresetPanel, Panel):
bl_label = "Sampling Presets"
@@ -57,7 +59,7 @@ def node_panel(cls):
node_cls.bl_space_type = 'NODE_EDITOR'
node_cls.bl_region_type = 'UI'
- node_cls.bl_category = "Node"
+ node_cls.bl_category = "Options"
if hasattr(node_cls, 'bl_parent_id'):
node_cls.bl_parent_id = 'NODE_' + node_cls.bl_parent_id
@@ -86,10 +88,16 @@ def use_cuda(context):
return (get_device_type(context) == 'CUDA' and cscene.device == 'GPU')
+def use_optix(context):
+ cscene = context.scene.cycles
+
+ return (get_device_type(context) == 'OPTIX' and cscene.device == 'GPU')
+
+
def use_branched_path(context):
cscene = context.scene.cycles
- return (cscene.progressive == 'BRANCHED_PATH')
+ return (cscene.progressive == 'BRANCHED_PATH' and not use_optix(context))
def use_sample_all_lights(context):
@@ -166,29 +174,29 @@ class CYCLES_RENDER_PT_sampling(CyclesButtonsPanel, Panel):
layout.use_property_split = True
layout.use_property_decorate = False
- layout.prop(cscene, "progressive")
+ if not use_optix(context):
+ layout.prop(cscene, "progressive")
- if cscene.progressive == 'PATH' or use_branched_path(context) is False:
+ if not use_branched_path(context):
col = layout.column(align=True)
col.prop(cscene, "samples", text="Render")
col.prop(cscene, "preview_samples", text="Viewport")
-
- draw_samples_info(layout, context)
else:
col = layout.column(align=True)
col.prop(cscene, "aa_samples", text="Render")
col.prop(cscene, "preview_aa_samples", text="Viewport")
+ if not use_branched_path(context):
+ draw_samples_info(layout, context)
+
class CYCLES_RENDER_PT_sampling_sub_samples(CyclesButtonsPanel, Panel):
bl_label = "Sub Samples"
bl_parent_id = "CYCLES_RENDER_PT_sampling"
@classmethod
- def poll(self, context):
- scene = context.scene
- cscene = scene.cycles
- return cscene.progressive != 'PATH' and use_branched_path(context)
+ def poll(cls, context):
+ return use_branched_path(context)
def draw(self, context):
layout = self.layout
@@ -213,6 +221,70 @@ class CYCLES_RENDER_PT_sampling_sub_samples(CyclesButtonsPanel, Panel):
draw_samples_info(layout, context)
+class CYCLES_RENDER_PT_sampling_adaptive(CyclesButtonsPanel, Panel):
+ bl_label = "Adaptive Sampling"
+ bl_parent_id = "CYCLES_RENDER_PT_sampling"
+ bl_options = {'DEFAULT_CLOSED'}
+
+ def draw_header(self, context):
+ layout = self.layout
+ scene = context.scene
+ cscene = scene.cycles
+
+ layout.prop(cscene, "use_adaptive_sampling", text="")
+
+ def draw(self, context):
+ layout = self.layout
+ layout.use_property_split = True
+ layout.use_property_decorate = False
+
+ scene = context.scene
+ cscene = scene.cycles
+
+ layout.active = cscene.use_adaptive_sampling
+
+ col = layout.column(align=True)
+ col.prop(cscene, "adaptive_threshold", text="Noise Threshold")
+ col.prop(cscene, "adaptive_min_samples", text="Min Samples")
+
+
+class CYCLES_RENDER_PT_sampling_denoising(CyclesButtonsPanel, Panel):
+ bl_label = "Denoising"
+ bl_parent_id = "CYCLES_RENDER_PT_sampling"
+ bl_options = {'DEFAULT_CLOSED'}
+
+ def draw(self, context):
+ layout = self.layout
+ layout.use_property_split = True
+ layout.use_property_decorate = False
+
+ scene = context.scene
+ cscene = scene.cycles
+
+ heading = layout.column(align=True, heading="Render")
+ row = heading.row(align=True)
+ row.prop(cscene, "use_denoising", text="")
+ sub = row.row()
+
+ sub.active = cscene.use_denoising
+ for view_layer in scene.view_layers:
+ if view_layer.cycles.denoising_store_passes:
+ sub.active = True
+
+ sub.prop(cscene, "denoiser", text="")
+
+ heading = layout.column(align=False, heading="Viewport")
+ row = heading.row(align=True)
+ row.prop(cscene, "use_preview_denoising", text="")
+ sub = row.row()
+ sub.active = cscene.use_preview_denoising
+ sub.prop(cscene, "preview_denoiser", text="")
+
+ sub = heading.row(align=True)
+ sub.active = cscene.use_preview_denoising
+ sub.prop(cscene, "preview_denoising_start_sample", text="Start Sample")
+
+
class CYCLES_RENDER_PT_sampling_advanced(CyclesButtonsPanel, Panel):
bl_label = "Advanced"
bl_parent_id = "CYCLES_RENDER_PT_sampling"
@@ -230,13 +302,17 @@ class CYCLES_RENDER_PT_sampling_advanced(CyclesButtonsPanel, Panel):
row.prop(cscene, "seed")
row.prop(cscene, "use_animated_seed", text="", icon='TIME')
- layout.prop(cscene, "sampling_pattern", text="Pattern")
+ col = layout.column(align=True)
+ col.active = not(cscene.use_adaptive_sampling)
+ col.prop(cscene, "sampling_pattern", text="Pattern")
layout.prop(cscene, "use_square_samples")
layout.separator()
col = layout.column(align=True)
+ col.prop(cscene, "min_light_bounces")
+ col.prop(cscene, "min_transparent_bounces")
col.prop(cscene, "light_sampling_threshold", text="Light Threshold")
if cscene.progressive != 'PATH' and use_branched_path(context):
@@ -264,7 +340,7 @@ class CYCLES_RENDER_PT_sampling_total(CyclesButtonsPanel, Panel):
bl_parent_id = "CYCLES_RENDER_PT_sampling"
@classmethod
- def poll(self, context):
+ def poll(cls, context):
scene = context.scene
cscene = scene.cycles
@@ -320,7 +396,7 @@ class CYCLES_RENDER_PT_subdivision(CyclesButtonsPanel, Panel):
bl_options = {'DEFAULT_CLOSED'}
@classmethod
- def poll(self, context):
+ def poll(cls, context):
return (context.scene.render.engine == 'CYCLES') and (context.scene.cycles.feature_set == 'EXPERIMENTAL')
def draw(self, context):
@@ -334,7 +410,7 @@ class CYCLES_RENDER_PT_subdivision(CyclesButtonsPanel, Panel):
col = layout.column()
sub = col.column(align=True)
sub.prop(cscene, "dicing_rate", text="Dicing Rate Render")
- sub.prop(cscene, "preview_dicing_rate", text="Preview")
+ sub.prop(cscene, "preview_dicing_rate", text="Viewport")
col.separator()
@@ -348,13 +424,6 @@ class CYCLES_RENDER_PT_hair(CyclesButtonsPanel, Panel):
bl_label = "Hair"
bl_options = {'DEFAULT_CLOSED'}
- def draw_header(self, context):
- layout = self.layout
- scene = context.scene
- ccscene = scene.cycles_curves
-
- layout.prop(ccscene, "use_curves", text="")
-
def draw(self, context):
layout = self.layout
layout.use_property_split = True
@@ -363,20 +432,10 @@ class CYCLES_RENDER_PT_hair(CyclesButtonsPanel, Panel):
scene = context.scene
ccscene = scene.cycles_curves
- layout.active = ccscene.use_curves
-
col = layout.column()
- col.prop(ccscene, "minimum_width", text="Min Pixels")
- col.prop(ccscene, "maximum_width", text="Max Extension")
col.prop(ccscene, "shape", text="Shape")
- if not (ccscene.primitive in {'CURVE_SEGMENTS', 'LINE_SEGMENTS'} and ccscene.shape == 'RIBBONS'):
- col.prop(ccscene, "cull_backfacing", text="Cull back-faces")
- col.prop(ccscene, "primitive", text="Primitive")
-
- if ccscene.primitive == 'TRIANGLES' and ccscene.shape == 'THICK':
- col.prop(ccscene, "resolution", text="Resolution")
- elif ccscene.primitive == 'CURVE_SEGMENTS':
- col.prop(ccscene, "subdivisions", text="Curve subdivisions")
+ if ccscene.shape == 'RIBBONS':
+ col.prop(ccscene, "subdivisions", text="Curve Subdivisions")
class CYCLES_RENDER_PT_volumes(CyclesButtonsPanel, Panel):
@@ -391,9 +450,11 @@ class CYCLES_RENDER_PT_volumes(CyclesButtonsPanel, Panel):
scene = context.scene
cscene = scene.cycles
- col = layout.column()
- col.prop(cscene, "volume_step_size", text="Step Size")
- col.prop(cscene, "volume_max_steps", text="Max Steps")
+ col = layout.column(align=True)
+ col.prop(cscene, "volume_step_rate", text="Step Rate Render")
+ col.prop(cscene, "volume_preview_step_rate", text="Viewport")
+
+ layout.prop(cscene, "volume_max_steps", text="Max Steps")
class CYCLES_RENDER_PT_light_paths(CyclesButtonsPanel, Panel):
@@ -461,8 +522,9 @@ class CYCLES_RENDER_PT_light_paths_caustics(CyclesButtonsPanel, Panel):
col = layout.column()
col.prop(cscene, "blur_glossy")
- col.prop(cscene, "caustics_reflective")
- col.prop(cscene, "caustics_refractive")
+ col = layout.column(heading="Caustics", align=True)
+ col.prop(cscene, "caustics_reflective", text="Reflective")
+ col.prop(cscene, "caustics_refractive", text="Refractive")
class CYCLES_RENDER_PT_motion_blur(CyclesButtonsPanel, Panel):
@@ -538,31 +600,32 @@ class CYCLES_RENDER_PT_film(CyclesButtonsPanel, Panel):
class CYCLES_RENDER_PT_film_transparency(CyclesButtonsPanel, Panel):
- bl_label = "Transparency"
+ bl_label = "Transparent"
bl_parent_id = "CYCLES_RENDER_PT_film"
def draw_header(self, context):
layout = self.layout
scene = context.scene
- cscene = scene.cycles
+ rd = scene.render
- layout.prop(cscene, "film_transparent", text="")
+ layout.prop(rd, "film_transparent", text="")
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
scene = context.scene
+ rd = scene.render
cscene = scene.cycles
- layout.active = cscene.film_transparent
+ layout.active = rd.film_transparent
col = layout.column()
col.prop(cscene, "film_transparent_glass", text="Transparent Glass")
sub = col.column()
- sub.active = cscene.film_transparent and cscene.film_transparent_glass
+ sub.active = rd.film_transparent and cscene.film_transparent_glass
sub.prop(cscene, "film_transparent_roughness", text="Roughness Threshold")
@@ -633,9 +696,6 @@ class CYCLES_RENDER_PT_performance_tiles(CyclesButtonsPanel, Panel):
sub = col.column()
sub.active = not rd.use_save_buffers
- for view_layer in scene.view_layers:
- if view_layer.cycles.use_denoising:
- sub.active = False
sub.prop(cscene, "use_progressive_refine")
@@ -655,16 +715,20 @@ class CYCLES_RENDER_PT_performance_acceleration_structure(CyclesButtonsPanel, Pa
col = layout.column()
- if _cycles.with_embree:
- row = col.row()
- row.active = use_cpu(context)
- row.prop(cscene, "use_bvh_embree")
+ use_embree = False
+ if use_cpu(context):
+ use_embree = _cycles.with_embree
+ if not use_embree:
+ sub = col.column(align=True)
+ sub.label(text="Cycles built without Embree support")
+ sub.label(text="CPU raytracing performance will be poor")
+
col.prop(cscene, "debug_use_spatial_splits")
sub = col.column()
- sub.active = not cscene.use_bvh_embree or not _cycles.with_embree
+ sub.active = not use_embree
sub.prop(cscene, "debug_use_hair_bvh")
sub = col.column()
- sub.active = not cscene.debug_use_spatial_splits and not cscene.use_bvh_embree
+ sub.active = not cscene.debug_use_spatial_splits and not use_embree
sub.prop(cscene, "debug_bvh_time_steps")
@@ -720,20 +784,12 @@ class CYCLES_RENDER_PT_filter(CyclesButtonsPanel, Panel):
rd = scene.render
view_layer = context.view_layer
- flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
-
- col = flow.column()
+ col = layout.column(heading="Include")
col.prop(view_layer, "use_sky", text="Environment")
- col = flow.column()
col.prop(view_layer, "use_ao", text="Ambient Occlusion")
- col = flow.column()
col.prop(view_layer, "use_solid", text="Surfaces")
- col = flow.column()
col.prop(view_layer, "use_strand", text="Hair")
- if with_freestyle:
- col = flow.column()
- col.prop(view_layer, "use_freestyle", text="Freestyle")
- col.active = rd.use_freestyle
+ col.prop(view_layer, "use_volumes", text="Volumes")
class CYCLES_RENDER_PT_override(CyclesButtonsPanel, Panel):
@@ -755,7 +811,6 @@ class CYCLES_RENDER_PT_override(CyclesButtonsPanel, Panel):
class CYCLES_RENDER_PT_passes(CyclesButtonsPanel, Panel):
bl_label = "Passes"
bl_context = "view_layer"
- bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
pass
@@ -776,34 +831,27 @@ class CYCLES_RENDER_PT_passes_data(CyclesButtonsPanel, Panel):
view_layer = context.view_layer
cycles_view_layer = view_layer.cycles
- flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
- col = flow.column()
+ col = layout.column(heading="Include", align=True)
col.prop(view_layer, "use_pass_combined")
- col = flow.column()
col.prop(view_layer, "use_pass_z")
- col = flow.column()
col.prop(view_layer, "use_pass_mist")
- col = flow.column()
col.prop(view_layer, "use_pass_normal")
- col = flow.column()
- col.prop(view_layer, "use_pass_vector")
- col.active = not rd.use_motion_blur
- col = flow.column()
+ sub = col.column()
+ sub.active = not rd.use_motion_blur
+ sub.prop(view_layer, "use_pass_vector")
col.prop(view_layer, "use_pass_uv")
- col = flow.column()
+
+ col.prop(cycles_view_layer, "denoising_store_passes", text="Denoising Data")
+
+ col = layout.column(heading="Indexes", align=True)
col.prop(view_layer, "use_pass_object_index")
- col = flow.column()
col.prop(view_layer, "use_pass_material_index")
- layout.separator()
-
- flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
- col = flow.column()
- col.prop(cycles_view_layer, "denoising_store_passes", text="Denoising Data")
- col = flow.column()
+ col = layout.column(heading="Debug", align=True)
col.prop(cycles_view_layer, "pass_debug_render_time", text="Render Time")
+ col.prop(cycles_view_layer, "pass_debug_sample_count", text="Sample Count")
+
- layout.separator()
layout.prop(view_layer, "pass_alpha_threshold")
@@ -821,46 +869,26 @@ class CYCLES_RENDER_PT_passes_light(CyclesButtonsPanel, Panel):
view_layer = context.view_layer
cycles_view_layer = view_layer.cycles
- split = layout.split(factor=0.35)
- split.use_property_split = False
- split.label(text="Diffuse")
- row = split.row(align=True)
- row.prop(view_layer, "use_pass_diffuse_direct", text="Direct", toggle=True)
- row.prop(view_layer, "use_pass_diffuse_indirect", text="Indirect", toggle=True)
- row.prop(view_layer, "use_pass_diffuse_color", text="Color", toggle=True)
-
- split = layout.split(factor=0.35)
- split.use_property_split = False
- split.label(text="Glossy")
- row = split.row(align=True)
- row.prop(view_layer, "use_pass_glossy_direct", text="Direct", toggle=True)
- row.prop(view_layer, "use_pass_glossy_indirect", text="Indirect", toggle=True)
- row.prop(view_layer, "use_pass_glossy_color", text="Color", toggle=True)
-
- split = layout.split(factor=0.35)
- split.use_property_split = False
- split.label(text="Transmission")
- row = split.row(align=True)
- row.prop(view_layer, "use_pass_transmission_direct", text="Direct", toggle=True)
- row.prop(view_layer, "use_pass_transmission_indirect", text="Indirect", toggle=True)
- row.prop(view_layer, "use_pass_transmission_color", text="Color", toggle=True)
-
- split = layout.split(factor=0.35)
- split.use_property_split = False
- split.label(text="Subsurface")
- row = split.row(align=True)
- row.prop(view_layer, "use_pass_subsurface_direct", text="Direct", toggle=True)
- row.prop(view_layer, "use_pass_subsurface_indirect", text="Indirect", toggle=True)
- row.prop(view_layer, "use_pass_subsurface_color", text="Color", toggle=True)
-
- split = layout.split(factor=0.35)
- split.use_property_split = False
- split.label(text="Volume")
- row = split.row(align=True)
- row.prop(cycles_view_layer, "use_pass_volume_direct", text="Direct", toggle=True)
- row.prop(cycles_view_layer, "use_pass_volume_indirect", text="Indirect", toggle=True)
+ col = layout.column(heading="Diffuse", align=True)
+ col.prop(view_layer, "use_pass_diffuse_direct", text="Direct")
+ col.prop(view_layer, "use_pass_diffuse_indirect", text="Indirect")
+ col.prop(view_layer, "use_pass_diffuse_color", text="Color")
- col = layout.column(align=True)
+ col = layout.column(heading="Glossy", align=True)
+ col.prop(view_layer, "use_pass_glossy_direct", text="Direct")
+ col.prop(view_layer, "use_pass_glossy_indirect", text="Indirect")
+ col.prop(view_layer, "use_pass_glossy_color", text="Color")
+
+ col = layout.column(heading="Transmission", align=True)
+ col.prop(view_layer, "use_pass_transmission_direct", text="Direct")
+ col.prop(view_layer, "use_pass_transmission_indirect", text="Indirect")
+ col.prop(view_layer, "use_pass_transmission_color", text="Color")
+
+ col = layout.column(heading="Volume", align=True)
+ col.prop(cycles_view_layer, "use_pass_volume_direct", text="Direct")
+ col.prop(cycles_view_layer, "use_pass_volume_indirect", text="Indirect")
+
+ col = layout.column(heading="Other", align=True)
col.prop(view_layer, "use_pass_emit", text="Emission")
col.prop(view_layer, "use_pass_environment")
col.prop(view_layer, "use_pass_shadow")
@@ -881,11 +909,10 @@ class CYCLES_RENDER_PT_passes_crypto(CyclesButtonsPanel, Panel):
cycles_view_layer = context.view_layer.cycles
- row = layout.row(align=True)
- row.use_property_split = False
- row.prop(cycles_view_layer, "use_pass_crypto_object", text="Object", toggle=True)
- row.prop(cycles_view_layer, "use_pass_crypto_material", text="Material", toggle=True)
- row.prop(cycles_view_layer, "use_pass_crypto_asset", text="Asset", toggle=True)
+ col = layout.column(heading="Include", align=True)
+ col.prop(cycles_view_layer, "use_pass_crypto_object", text="Object")
+ col.prop(cycles_view_layer, "use_pass_crypto_material", text="Material")
+ col.prop(cycles_view_layer, "use_pass_crypto_asset", text="Asset")
layout.prop(cycles_view_layer, "pass_crypto_depth", text="Levels")
@@ -917,17 +944,58 @@ class CYCLES_RENDER_PT_passes_debug(CyclesButtonsPanel, Panel):
layout.prop(cycles_view_layer, "pass_debug_ray_bounces")
+class CYCLES_RENDER_UL_aov(bpy.types.UIList):
+ def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
+ row = layout.row()
+ split = row.split(factor=0.65)
+ icon = 'ERROR' if item.conflict else 'NONE'
+ split.row().prop(item, "name", text="", icon=icon, emboss=False)
+ split.row().prop(item, "type", text="", emboss=False)
+
+
+class CYCLES_RENDER_PT_passes_aov(CyclesButtonsPanel, Panel):
+ bl_label = "Shader AOV"
+ bl_context = "view_layer"
+ bl_parent_id = "CYCLES_RENDER_PT_passes"
+
+ def draw(self, context):
+ layout = self.layout
+ layout.use_property_split = True
+ layout.use_property_decorate = False
+
+ cycles_view_layer = context.view_layer.cycles
+
+ row = layout.row()
+ col = row.column()
+ col.template_list("CYCLES_RENDER_UL_aov", "aovs", cycles_view_layer, "aovs", cycles_view_layer, "active_aov", rows=2)
+
+ col = row.column()
+ sub = col.column(align=True)
+ sub.operator("cycles.add_aov", icon='ADD', text="")
+ sub.operator("cycles.remove_aov", icon='REMOVE', text="")
+
+ if cycles_view_layer.active_aov < len(cycles_view_layer.aovs):
+ active_aov = cycles_view_layer.aovs[cycles_view_layer.active_aov]
+ if active_aov.conflict:
+ layout.label(text=active_aov.conflict, icon='ERROR')
+
+
class CYCLES_RENDER_PT_denoising(CyclesButtonsPanel, Panel):
bl_label = "Denoising"
bl_context = "view_layer"
bl_options = {'DEFAULT_CLOSED'}
+ @classmethod
+ def poll(cls, context):
+ cscene = context.scene.cycles
+ return CyclesButtonsPanel.poll(context) and cscene.use_denoising
+
def draw_header(self, context):
scene = context.scene
view_layer = context.view_layer
cycles_view_layer = view_layer.cycles
- layout = self.layout
+ layout = self.layout
layout.prop(cycles_view_layer, "use_denoising", text="")
def draw(self, context):
@@ -938,66 +1006,43 @@ class CYCLES_RENDER_PT_denoising(CyclesButtonsPanel, Panel):
scene = context.scene
view_layer = context.view_layer
cycles_view_layer = view_layer.cycles
+ denoiser = scene.cycles.denoiser
- split = layout.split()
- split.active = cycles_view_layer.use_denoising
+ layout.active = denoiser != 'NONE' and cycles_view_layer.use_denoising
- layout = layout.column(align=True)
- layout.prop(cycles_view_layer, "denoising_radius", text="Radius")
- layout.prop(cycles_view_layer, "denoising_strength", slider=True, text="Strength")
- layout.prop(cycles_view_layer, "denoising_feature_strength", slider=True, text="Feature Strength")
- layout.prop(cycles_view_layer, "denoising_relative_pca")
+ col = layout.column()
- layout.separator()
+ if denoiser == 'OPTIX':
+ col.prop(cycles_view_layer, "denoising_optix_input_passes")
+ return
+ elif denoiser == 'OPENIMAGEDENOISE':
+ col.prop(cycles_view_layer, "denoising_openimagedenoise_input_passes")
+ return
- split = layout.split(factor=0.5)
- split.active = cycles_view_layer.use_denoising or cycles_view_layer.denoising_store_passes
+ col.prop(cycles_view_layer, "denoising_radius", text="Radius")
- col = split.column()
- col.alignment = 'RIGHT'
- col.label(text="Diffuse")
+ col = layout.column()
+ col.prop(cycles_view_layer, "denoising_strength", slider=True, text="Strength")
+ col.prop(cycles_view_layer, "denoising_feature_strength", slider=True, text="Feature Strength")
+ col.prop(cycles_view_layer, "denoising_relative_pca")
- row = split.row(align=True)
- row.use_property_split = False
- row.prop(cycles_view_layer, "denoising_diffuse_direct", text="Direct", toggle=True)
- row.prop(cycles_view_layer, "denoising_diffuse_indirect", text="Indirect", toggle=True)
+ layout.separator()
- split = layout.split(factor=0.5)
- split.active = cycles_view_layer.use_denoising or cycles_view_layer.denoising_store_passes
+ col = layout.column()
+ col.active = cycles_view_layer.use_denoising or cycles_view_layer.denoising_store_passes
- col = split.column()
- col.alignment = 'RIGHT'
- col.label(text="Glossy")
+ row = col.row(heading="Diffuse", align=True)
+ row.prop(cycles_view_layer, "denoising_diffuse_direct", text="Direct", toggle=True)
+ row.prop(cycles_view_layer, "denoising_diffuse_indirect", text="Indirect", toggle=True)
- row = split.row(align=True)
- row.use_property_split = False
+ row = col.row(heading="Glossy", align=True)
row.prop(cycles_view_layer, "denoising_glossy_direct", text="Direct", toggle=True)
row.prop(cycles_view_layer, "denoising_glossy_indirect", text="Indirect", toggle=True)
- split = layout.split(factor=0.5)
- split.active = cycles_view_layer.use_denoising or cycles_view_layer.denoising_store_passes
-
- col = split.column()
- col.alignment = 'RIGHT'
- col.label(text="Transmission")
-
- row = split.row(align=True)
- row.use_property_split = False
+ row = col.row(heading="Transmission", align=True)
row.prop(cycles_view_layer, "denoising_transmission_direct", text="Direct", toggle=True)
row.prop(cycles_view_layer, "denoising_transmission_indirect", text="Indirect", toggle=True)
- split = layout.split(factor=0.5)
- split.active = cycles_view_layer.use_denoising or cycles_view_layer.denoising_store_passes
-
- col = split.column()
- col.alignment = 'RIGHT'
- col.label(text="Subsurface")
-
- row = split.row(align=True)
- row.use_property_split = False
- row.prop(cycles_view_layer, "denoising_subsurface_direct", text="Direct", toggle=True)
- row.prop(cycles_view_layer, "denoising_subsurface_indirect", text="Indirect", toggle=True)
-
class CYCLES_PT_post_processing(CyclesButtonsPanel, Panel):
bl_label = "Post Processing"
@@ -1011,7 +1056,7 @@ class CYCLES_PT_post_processing(CyclesButtonsPanel, Panel):
rd = context.scene.render
- col = layout.column(align=True)
+ col = layout.column(align=True, heading="Pipeline")
col.prop(rd, "use_compositing")
col.prop(rd, "use_sequencer")
@@ -1026,20 +1071,27 @@ class CYCLES_CAMERA_PT_dof(CyclesButtonsPanel, Panel):
def poll(cls, context):
return context.camera and CyclesButtonsPanel.poll(context)
+ def draw_header(self, context):
+ cam = context.camera
+ dof = cam.dof
+ self.layout.prop(dof, "use_dof", text="")
+
def draw(self, context):
layout = self.layout
layout.use_property_split = True
cam = context.camera
+ dof = cam.dof
+ layout.active = dof.use_dof
split = layout.split()
col = split.column()
- col.prop(cam, "dof_object", text="Focus Object")
+ col.prop(dof, "focus_object", text="Focus Object")
sub = col.row()
- sub.active = cam.dof_object is None
- sub.prop(cam, "dof_distance", text="Distance")
+ sub.active = dof.focus_object is None
+ sub.prop(dof, "focus_distance", text="Distance")
class CYCLES_CAMERA_PT_dof_aperture(CyclesButtonsPanel, Panel):
@@ -1053,44 +1105,17 @@ class CYCLES_CAMERA_PT_dof_aperture(CyclesButtonsPanel, Panel):
def draw(self, context):
layout = self.layout
layout.use_property_split = True
- flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
cam = context.camera
- ccam = cam.cycles
-
- col = flow.column()
- col.prop(ccam, "aperture_type")
- if ccam.aperture_type == 'RADIUS':
- col.prop(ccam, "aperture_size", text="Size")
- elif ccam.aperture_type == 'FSTOP':
- col.prop(ccam, "aperture_fstop", text="Number")
- col.separator()
-
- col = flow.column()
- col.prop(ccam, "aperture_blades", text="Blades")
- col.prop(ccam, "aperture_rotation", text="Rotation")
- col.prop(ccam, "aperture_ratio", text="Ratio")
-
-
-class CYCLES_CAMERA_PT_dof_viewport(CyclesButtonsPanel, Panel):
- bl_label = "Viewport"
- bl_parent_id = "CYCLES_CAMERA_PT_dof"
-
- @classmethod
- def poll(cls, context):
- return context.camera and CyclesButtonsPanel.poll(context)
-
- def draw(self, context):
- layout = self.layout
- layout.use_property_split = True
+ dof = cam.dof
+ layout.active = dof.use_dof
flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
- cam = context.camera
- dof_options = cam.gpu_dof
-
- sub = flow.column(align=True)
- sub.prop(dof_options, "fstop")
- sub.prop(dof_options, "blades")
+ col = flow.column()
+ col.prop(dof, "aperture_fstop")
+ col.prop(dof, "aperture_blades")
+ col.prop(dof, "aperture_rotation")
+ col.prop(dof, "aperture_ratio")
class CYCLES_PT_context_material(CyclesButtonsPanel, Panel):
@@ -1187,6 +1212,7 @@ class CYCLES_OBJECT_PT_motion_blur(CyclesButtonsPanel, Panel):
def draw(self, context):
layout = self.layout
+ layout.use_property_split = True
rd = context.scene.render
# scene = context.scene
@@ -1196,33 +1222,78 @@ class CYCLES_OBJECT_PT_motion_blur(CyclesButtonsPanel, Panel):
layout.active = (rd.use_motion_blur and cob.use_motion_blur)
- row = layout.row()
+ col = layout.column()
+ col.prop(cob, "motion_steps", text="Steps")
if ob.type != 'CAMERA':
- row.prop(cob, "use_deform_motion", text="Deformation")
- row.prop(cob, "motion_steps", text="Steps")
+ col.prop(cob, "use_deform_motion", text="Deformation")
+
+
+def has_geometry_visibility(ob):
+ return ob and ((ob.type in {'MESH', 'CURVE', 'SURFACE', 'FONT', 'META', 'LIGHT'}) or
+ (ob.instance_type == 'COLLECTION' and ob.instance_collection))
-class CYCLES_OBJECT_PT_cycles_settings(CyclesButtonsPanel, Panel):
- bl_label = "Cycles Settings"
+class CYCLES_OBJECT_PT_shading(CyclesButtonsPanel, Panel):
+ bl_label = "Shading"
bl_context = "object"
bl_options = {'DEFAULT_CLOSED'}
@classmethod
def poll(cls, context):
+ return CyclesButtonsPanel.poll(context) and (context.object)
+
+ def draw(self, context):
+ layout = self.layout
+ layout.use_property_split = True
+
+ flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
+ layout = self.layout
ob = context.object
- return (CyclesButtonsPanel.poll(context) and
- ob and ((ob.type in {'MESH', 'CURVE', 'SURFACE', 'FONT', 'META', 'LIGHT'}) or
- (ob.instance_type == 'COLLECTION' and ob.instance_collection)))
+ cob = ob.cycles
+
+ if has_geometry_visibility(ob):
+ col = flow.column()
+ col.prop(cob, "shadow_terminator_offset")
+
+
+class CYCLES_OBJECT_PT_visibility(CyclesButtonsPanel, Panel):
+ bl_label = "Visibility"
+ bl_context = "object"
+ bl_options = {'DEFAULT_CLOSED'}
+
+ @classmethod
+ def poll(cls, context):
+ return CyclesButtonsPanel.poll(context) and (context.object)
def draw(self, context):
- pass
+ layout = self.layout
+ layout.use_property_split = True
+
+ ob = context.object
+ layout.prop(ob, "hide_select", text="Selectable", invert_checkbox=True, toggle=False)
-class CYCLES_OBJECT_PT_cycles_settings_ray_visibility(CyclesButtonsPanel, Panel):
+ col = layout.column(heading="Show in")
+ col.prop(ob, "hide_viewport", text="Viewports", invert_checkbox=True, toggle=False)
+ col.prop(ob, "hide_render", text="Renders", invert_checkbox=True, toggle=False)
+
+ if has_geometry_visibility(ob):
+ cob = ob.cycles
+ col = layout.column(heading="Mask")
+ col.prop(cob, "is_shadow_catcher")
+ col.prop(cob, "is_holdout")
+
+
+class CYCLES_OBJECT_PT_visibility_ray_visibility(CyclesButtonsPanel, Panel):
bl_label = "Ray Visibility"
- bl_parent_id = "CYCLES_OBJECT_PT_cycles_settings"
+ bl_parent_id = "CYCLES_OBJECT_PT_visibility"
bl_context = "object"
+ @classmethod
+ def poll(cls, context):
+ ob = context.object
+ return CyclesButtonsPanel.poll(context) and has_geometry_visibility(ob)
+
def draw(self, context):
layout = self.layout
layout.use_property_split = True
@@ -1233,38 +1304,27 @@ class CYCLES_OBJECT_PT_cycles_settings_ray_visibility(CyclesButtonsPanel, Panel)
cob = ob.cycles
visibility = ob.cycles_visibility
- flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
-
- col = flow.column()
+ col = layout.column()
col.prop(visibility, "camera")
- col = flow.column()
col.prop(visibility, "diffuse")
- col = flow.column()
col.prop(visibility, "glossy")
- col = flow.column()
col.prop(visibility, "transmission")
- col = flow.column()
col.prop(visibility, "scatter")
if ob.type != 'LIGHT':
- col = flow.column()
- col.prop(visibility, "shadow")
-
- layout.separator()
-
- flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
-
- col = flow.column()
- col.prop(cob, "is_shadow_catcher")
- col = flow.column()
- col.prop(cob, "is_holdout")
+ sub = col.column()
+ sub.prop(visibility, "shadow")
-class CYCLES_OBJECT_PT_cycles_settings_performance(CyclesButtonsPanel, Panel):
- bl_label = "Performance"
- bl_parent_id = "CYCLES_OBJECT_PT_cycles_settings"
+class CYCLES_OBJECT_PT_visibility_culling(CyclesButtonsPanel, Panel):
+ bl_label = "Culling"
+ bl_parent_id = "CYCLES_OBJECT_PT_visibility"
bl_context = "object"
+ @classmethod
+ def poll(cls, context):
+ ob = context.object
+ return CyclesButtonsPanel.poll(context) and has_geometry_visibility(ob)
def draw(self, context):
layout = self.layout
@@ -1276,15 +1336,13 @@ class CYCLES_OBJECT_PT_cycles_settings_performance(CyclesButtonsPanel, Panel):
ob = context.object
cob = ob.cycles
- flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
-
- col = flow.column()
- col.active = scene.render.use_simplify and cscene.use_camera_cull
- col.prop(cob, "use_camera_cull")
+ row = layout.row()
+ row.active = scene.render.use_simplify and cscene.use_camera_cull
+ row.prop(cob, "use_camera_cull")
- col = flow.column()
- col.active = scene.render.use_simplify and cscene.use_distance_cull
- col.prop(cob, "use_distance_cull")
+ row = layout.row()
+ row.active = scene.render.use_simplify and cscene.use_distance_cull
+ row.prop(cob, "use_distance_cull")
def panel_node_draw(layout, id_data, output_type, input_name):
@@ -1341,8 +1399,6 @@ class CYCLES_LIGHT_PT_light(CyclesButtonsPanel, Panel):
light = context.light
clamp = light.cycles
- layout.use_property_decorate = False
-
if self.bl_space_type == 'PROPERTIES':
layout.row().prop(light, "type", expand=True)
layout.use_property_split = True
@@ -1352,8 +1408,14 @@ class CYCLES_LIGHT_PT_light(CyclesButtonsPanel, Panel):
col = layout.column()
- if light.type in {'POINT', 'SUN', 'SPOT'}:
- col.prop(light, "shadow_soft_size", text="Size")
+ col.prop(light, "color")
+ col.prop(light, "energy")
+ col.separator()
+
+ if light.type in {'POINT', 'SPOT'}:
+ col.prop(light, "shadow_soft_size", text="Radius")
+ elif light.type == 'SUN':
+ col.prop(light, "angle")
elif light.type == 'AREA':
col.prop(light, "shape", text="Shape")
sub = col.column(align=True)
@@ -1394,9 +1456,10 @@ class CYCLES_LIGHT_PT_nodes(CyclesButtonsPanel, Panel):
def draw(self, context):
layout = self.layout
+ layout.use_property_split = True
+
light = context.light
- if not panel_node_draw(layout, light, 'OUTPUT_LIGHT', 'Surface'):
- layout.prop(light, "color")
+ panel_node_draw(layout, light, 'OUTPUT_LIGHT', 'Surface')
class CYCLES_LIGHT_PT_spot(CyclesButtonsPanel, Panel):
@@ -1444,6 +1507,8 @@ class CYCLES_WORLD_PT_surface(CyclesButtonsPanel, Panel):
def draw(self, context):
layout = self.layout
+ layout.use_property_split = True
+
world = context.world
if not panel_node_draw(layout, world, 'OUTPUT_WORLD', 'Surface'):
@@ -1463,6 +1528,8 @@ class CYCLES_WORLD_PT_volume(CyclesButtonsPanel, Panel):
def draw(self, context):
layout = self.layout
+ layout.use_property_split = True
+
world = context.world
panel_node_draw(layout, world, 'OUTPUT_WORLD', 'Volume')
@@ -1533,17 +1600,18 @@ class CYCLES_WORLD_PT_ray_visibility(CyclesButtonsPanel, Panel):
def draw(self, context):
layout = self.layout
+ layout.use_property_split = True
+ layout.use_property_decorate = False
world = context.world
visibility = world.cycles_visibility
- flow = layout.column_flow()
-
- flow.prop(visibility, "camera")
- flow.prop(visibility, "diffuse")
- flow.prop(visibility, "glossy")
- flow.prop(visibility, "transmission")
- flow.prop(visibility, "scatter")
+ col = layout.column()
+ col.prop(visibility, "camera")
+ col.prop(visibility, "diffuse")
+ col.prop(visibility, "glossy")
+ col.prop(visibility, "transmission")
+ col.prop(visibility, "scatter")
class CYCLES_WORLD_PT_settings(CyclesButtonsPanel, Panel):
@@ -1619,6 +1687,9 @@ class CYCLES_WORLD_PT_settings_volume(CyclesButtonsPanel, Panel):
sub.prop(cworld, "volume_sampling", text="Sampling")
col.prop(cworld, "volume_interpolation", text="Interpolation")
col.prop(cworld, "homogeneous_volume", text="Homogeneous")
+ sub = col.column()
+ sub.active = not cworld.homogeneous_volume
+ sub.prop(cworld, "volume_step_size")
class CYCLES_MATERIAL_PT_preview(CyclesButtonsPanel, Panel):
@@ -1647,6 +1718,8 @@ class CYCLES_MATERIAL_PT_surface(CyclesButtonsPanel, Panel):
def draw(self, context):
layout = self.layout
+ layout.use_property_split = True
+
mat = context.material
if not panel_node_draw(layout, mat, 'OUTPUT_MATERIAL', 'Surface'):
layout.prop(mat, "diffuse_color")
@@ -1665,6 +1738,8 @@ class CYCLES_MATERIAL_PT_volume(CyclesButtonsPanel, Panel):
def draw(self, context):
layout = self.layout
+ layout.use_property_split = True
+
mat = context.material
# cmat = mat.cycles
@@ -1683,6 +1758,8 @@ class CYCLES_MATERIAL_PT_displacement(CyclesButtonsPanel, Panel):
def draw(self, context):
layout = self.layout
+ layout.use_property_split = True
+
mat = context.material
panel_node_draw(layout, mat, 'OUTPUT_MATERIAL', 'Displacement')
@@ -1725,7 +1802,7 @@ class CYCLES_MATERIAL_PT_settings_surface(CyclesButtonsPanel, Panel):
col = layout.column()
col.prop(cmat, "sample_as_light", text="Multiple Importance")
col.prop(cmat, "use_transparent_shadow")
- col.prop(cmat, "displacement_method", text="Displacement Method")
+ col.prop(cmat, "displacement_method", text="Displacement")
def draw(self, context):
self.draw_shared(self, context.material)
@@ -1750,6 +1827,9 @@ class CYCLES_MATERIAL_PT_settings_volume(CyclesButtonsPanel, Panel):
sub.prop(cmat, "volume_sampling", text="Sampling")
col.prop(cmat, "volume_interpolation", text="Interpolation")
col.prop(cmat, "homogeneous_volume", text="Homogeneous")
+ sub = col.column()
+ sub.active = not cmat.homogeneous_volume
+ sub.prop(cmat, "volume_step_rate")
def draw(self, context):
self.draw_shared(self, context, context.material)
@@ -1761,6 +1841,10 @@ class CYCLES_RENDER_PT_bake(CyclesButtonsPanel, Panel):
bl_options = {'DEFAULT_CLOSED'}
COMPAT_ENGINES = {'CYCLES'}
+ @classmethod
+ def poll(cls, context):
+ return CyclesButtonsPanel.poll(context) and not use_optix(context)
+
def draw(self, context):
layout = self.layout
layout.use_property_split = True
@@ -1793,7 +1877,7 @@ class CYCLES_RENDER_PT_bake_influence(CyclesButtonsPanel, Panel):
cscene = scene.cycles
rd = scene.render
if rd.use_bake_multires == False and cscene.bake_type in {
- 'NORMAL', 'COMBINED', 'DIFFUSE', 'GLOSSY', 'TRANSMISSION', 'SUBSURFACE'}:
+ 'NORMAL', 'COMBINED', 'DIFFUSE', 'GLOSSY', 'TRANSMISSION'}:
return True
def draw(self, context):
@@ -1817,27 +1901,24 @@ class CYCLES_RENDER_PT_bake_influence(CyclesButtonsPanel, Panel):
sub.prop(cbk, "normal_b", text="B")
elif cscene.bake_type == 'COMBINED':
- row = col.row(align=True)
- row.use_property_split = False
- row.prop(cbk, "use_pass_direct", toggle=True)
- row.prop(cbk, "use_pass_indirect", toggle=True)
- flow = col.grid_flow(row_major=False, columns=0, even_columns=False, even_rows=False, align=True)
+ col = layout.column(heading="Lighting", align=True)
+ col.prop(cbk, "use_pass_direct")
+ col.prop(cbk, "use_pass_indirect")
- flow.active = cbk.use_pass_direct or cbk.use_pass_indirect
- flow.prop(cbk, "use_pass_diffuse")
- flow.prop(cbk, "use_pass_glossy")
- flow.prop(cbk, "use_pass_transmission")
- flow.prop(cbk, "use_pass_subsurface")
- flow.prop(cbk, "use_pass_ambient_occlusion")
- flow.prop(cbk, "use_pass_emit")
+ col = layout.column(heading="Contributions", align=True)
+ col.active = cbk.use_pass_direct or cbk.use_pass_indirect
+ col.prop(cbk, "use_pass_diffuse")
+ col.prop(cbk, "use_pass_glossy")
+ col.prop(cbk, "use_pass_transmission")
+ col.prop(cbk, "use_pass_ambient_occlusion")
+ col.prop(cbk, "use_pass_emit")
- elif cscene.bake_type in {'DIFFUSE', 'GLOSSY', 'TRANSMISSION', 'SUBSURFACE'}:
- row = col.row(align=True)
- row.use_property_split = False
- row.prop(cbk, "use_pass_direct", toggle=True)
- row.prop(cbk, "use_pass_indirect", toggle=True)
- row.prop(cbk, "use_pass_color", toggle=True)
+ elif cscene.bake_type in {'DIFFUSE', 'GLOSSY', 'TRANSMISSION'}:
+ col = layout.column(heading="Contributions", align=True)
+ col.prop(cbk, "use_pass_direct")
+ col.prop(cbk, "use_pass_indirect")
+ col.prop(cbk, "use_pass_color")
class CYCLES_RENDER_PT_bake_selected_to_active(CyclesButtonsPanel, Panel):
@@ -1873,10 +1954,15 @@ class CYCLES_RENDER_PT_bake_selected_to_active(CyclesButtonsPanel, Panel):
col.prop(cbk, "use_cage", text="Cage")
if cbk.use_cage:
- col.prop(cbk, "cage_extrusion", text="Extrusion")
- col.prop(cbk, "cage_object", text="Cage Object")
+ col.prop(cbk, "cage_object")
+ col = layout.column()
+ col.prop(cbk, "cage_extrusion")
+ col.active = cbk.cage_object is None
else:
- col.prop(cbk, "cage_extrusion", text="Ray Distance")
+ col.prop(cbk, "cage_extrusion", text="Extrusion")
+
+ col = layout.column()
+ col.prop(cbk, "max_ray_distance")
class CYCLES_RENDER_PT_bake_output(CyclesButtonsPanel, Panel):
@@ -1900,7 +1986,7 @@ class CYCLES_RENDER_PT_bake_output(CyclesButtonsPanel, Panel):
layout.prop(rd, "use_bake_clear", text="Clear Image")
if rd.bake_type == 'DISPLACEMENT':
- col.prop(rd, "use_bake_lores_mesh")
+ layout.prop(rd, "use_bake_lores_mesh")
else:
layout.prop(cbk, "margin")
@@ -1915,7 +2001,10 @@ class CYCLES_RENDER_PT_debug(CyclesButtonsPanel, Panel):
@classmethod
def poll(cls, context):
- return CyclesButtonsPanel.poll(context) and bpy.app.debug_value == 256
+ prefs = bpy.context.preferences
+ return (CyclesButtonsPanel.poll(context)
+ and prefs.experimental.use_cycles_debug
+ and prefs.view.show_developer_ui)
def draw(self, context):
layout = self.layout
@@ -1945,7 +2034,14 @@ class CYCLES_RENDER_PT_debug(CyclesButtonsPanel, Panel):
col.separator()
col = layout.column()
- col.label(text='OpenCL Flags:')
+ col.label(text="OptiX Flags:")
+ col.prop(cscene, "debug_optix_cuda_streams")
+ col.prop(cscene, "debug_optix_curves_api")
+
+ col.separator()
+
+ col = layout.column()
+ col.label(text="OpenCL Flags:")
col.prop(cscene, "debug_opencl_device_type", text="Device")
col.prop(cscene, "debug_use_opencl_debug", text="Debug")
col.prop(cscene, "debug_opencl_mem_limit")
@@ -1992,7 +2088,7 @@ class CYCLES_RENDER_PT_simplify_viewport(CyclesButtonsPanel, Panel):
col.prop(rd, "simplify_child_particles", text="Child Particles")
col.prop(cscene, "texture_limit", text="Texture Limit")
col.prop(cscene, "ao_bounces", text="AO Bounces")
- col.prop(rd, "use_simplify_smoke_highres")
+
class CYCLES_RENDER_PT_simplify_render(CyclesButtonsPanel, Panel):
bl_label = "Render"
@@ -2037,19 +2133,83 @@ class CYCLES_RENDER_PT_simplify_culling(CyclesButtonsPanel, Panel):
layout.active = rd.use_simplify
- col = layout.column()
- col.prop(cscene, "use_camera_cull")
- sub = col.column()
+ row = layout.row(heading="Camera Culling")
+ row.prop(cscene, "use_camera_cull", text="")
+ sub = row.column()
sub.active = cscene.use_camera_cull
- sub.prop(cscene, "camera_cull_margin")
+ sub.prop(cscene, "camera_cull_margin", text="")
- col = layout.column()
- col.prop(cscene, "use_distance_cull")
- sub = col.column()
+ row = layout.row(heading="Distance Culling")
+ row.prop(cscene, "use_distance_cull", text="")
+ sub = row.column()
sub.active = cscene.use_distance_cull
- sub.prop(cscene, "distance_cull_margin", text="Distance")
+ sub.prop(cscene, "distance_cull_margin", text="")
+
+
+class CYCLES_VIEW3D_PT_shading_render_pass(Panel):
+ bl_space_type = 'VIEW_3D'
+ bl_region_type = 'HEADER'
+ bl_label = "Render Pass"
+ bl_parent_id = 'VIEW3D_PT_shading'
+ COMPAT_ENGINES = {'CYCLES'}
+
+ @classmethod
+ def poll(cls, context):
+ return (context.engine in cls.COMPAT_ENGINES
+ and context.space_data.shading.type == 'RENDERED')
+
+ def draw(self, context):
+ shading = context.space_data.shading
+
+ layout = self.layout
+ layout.prop(shading.cycles, "render_pass", text="")
+class CYCLES_VIEW3D_PT_shading_lighting(Panel):
+ bl_space_type = 'VIEW_3D'
+ bl_region_type = 'HEADER'
+ bl_label = "Lighting"
+ bl_parent_id = 'VIEW3D_PT_shading'
+ COMPAT_ENGINES = {'CYCLES'}
+
+ @classmethod
+ def poll(cls, context):
+ return (context.engine in cls.COMPAT_ENGINES
+ and context.space_data.shading.type == 'RENDERED')
+
+ def draw(self, context):
+ layout = self.layout
+ col = layout.column()
+ split = col.split(factor=0.9)
+
+ shading = context.space_data.shading
+ col.prop(shading, "use_scene_lights_render")
+ col.prop(shading, "use_scene_world_render")
+
+ if not shading.use_scene_world_render:
+ col = layout.column()
+ split = col.split(factor=0.9)
+
+ col = split.column()
+ sub = col.row()
+ sub.scale_y = 0.6
+ sub.template_icon_view(shading, "studio_light", scale_popup=3)
+
+ col = split.column()
+ col.operator("preferences.studiolight_show", emboss=False, text="", icon='PREFERENCES')
+
+ split = layout.split(factor=0.9)
+ col = split.column()
+ col.prop(shading, "studiolight_rotate_z", text="Rotation")
+ col.prop(shading, "studiolight_intensity")
+ col.prop(shading, "studiolight_background_alpha")
+
+class CYCLES_VIEW3D_PT_simplify_greasepencil(CyclesButtonsPanel, Panel, GreasePencilSimplifyPanel):
+ bl_label = "Grease Pencil"
+ bl_parent_id = "CYCLES_RENDER_PT_simplify"
+ COMPAT_ENGINES = {'CYCLES'}
+ bl_options = {'DEFAULT_CLOSED'}
+
def draw_device(self, context):
scene = context.scene
layout = self.layout
@@ -2063,8 +2223,6 @@ def draw_device(self, context):
col = layout.column()
col.prop(cscene, "feature_set")
- scene = context.scene
-
col = layout.column()
col.active = show_device_active(context)
col.prop(cscene, "device")
@@ -2083,7 +2241,7 @@ def draw_pause(self, context):
if view.shading.type == 'RENDERED':
cscene = scene.cycles
- layout.prop(cscene, "preview_pause", icon='PAUSE', text="")
+ layout.prop(cscene, "preview_pause", icon='PLAY' if cscene.preview_pause else 'PAUSE', text="")
def get_panels():
@@ -2098,6 +2256,7 @@ def get_panels():
'MATERIAL_PT_preview',
'NODE_DATA_PT_light',
'NODE_DATA_PT_spot',
+ 'OBJECT_PT_visibility',
'VIEWLAYER_PT_filter',
'VIEWLAYER_PT_layer_passes',
'RENDER_PT_post_processing',
@@ -2118,6 +2277,8 @@ classes = (
CYCLES_PT_integrator_presets,
CYCLES_RENDER_PT_sampling,
CYCLES_RENDER_PT_sampling_sub_samples,
+ CYCLES_RENDER_PT_sampling_adaptive,
+ CYCLES_RENDER_PT_sampling_denoising,
CYCLES_RENDER_PT_sampling_advanced,
CYCLES_RENDER_PT_light_paths,
CYCLES_RENDER_PT_light_paths_max_bounces,
@@ -2130,6 +2291,9 @@ classes = (
CYCLES_RENDER_PT_simplify_viewport,
CYCLES_RENDER_PT_simplify_render,
CYCLES_RENDER_PT_simplify_culling,
+ CYCLES_VIEW3D_PT_simplify_greasepencil,
+ CYCLES_VIEW3D_PT_shading_lighting,
+ CYCLES_VIEW3D_PT_shading_render_pass,
CYCLES_RENDER_PT_motion_blur,
CYCLES_RENDER_PT_motion_blur_curve,
CYCLES_RENDER_PT_film,
@@ -2141,23 +2305,25 @@ classes = (
CYCLES_RENDER_PT_performance_acceleration_structure,
CYCLES_RENDER_PT_performance_final_render,
CYCLES_RENDER_PT_performance_viewport,
- CYCLES_RENDER_PT_filter,
- CYCLES_RENDER_PT_override,
CYCLES_RENDER_PT_passes,
CYCLES_RENDER_PT_passes_data,
CYCLES_RENDER_PT_passes_light,
CYCLES_RENDER_PT_passes_crypto,
CYCLES_RENDER_PT_passes_debug,
+ CYCLES_RENDER_UL_aov,
+ CYCLES_RENDER_PT_passes_aov,
+ CYCLES_RENDER_PT_filter,
+ CYCLES_RENDER_PT_override,
CYCLES_RENDER_PT_denoising,
CYCLES_PT_post_processing,
CYCLES_CAMERA_PT_dof,
CYCLES_CAMERA_PT_dof_aperture,
- CYCLES_CAMERA_PT_dof_viewport,
CYCLES_PT_context_material,
CYCLES_OBJECT_PT_motion_blur,
- CYCLES_OBJECT_PT_cycles_settings,
- CYCLES_OBJECT_PT_cycles_settings_ray_visibility,
- CYCLES_OBJECT_PT_cycles_settings_performance,
+ CYCLES_OBJECT_PT_shading,
+ CYCLES_OBJECT_PT_visibility,
+ CYCLES_OBJECT_PT_visibility_ray_visibility,
+ CYCLES_OBJECT_PT_visibility_culling,
CYCLES_LIGHT_PT_preview,
CYCLES_LIGHT_PT_light,
CYCLES_LIGHT_PT_nodes,
diff --git a/intern/cycles/blender/addon/version_update.py b/intern/cycles/blender/addon/version_update.py
index 6f005727b95..49f23f4ba30 100644
--- a/intern/cycles/blender/addon/version_update.py
+++ b/intern/cycles/blender/addon/version_update.py
@@ -22,140 +22,6 @@ import math
from bpy.app.handlers import persistent
-def foreach_cycles_nodetree_group(nodetree, traversed):
- for node in nodetree.nodes:
- if node.bl_idname == 'ShaderNodeGroup':
- group = node.node_tree
- if group and group not in traversed:
- traversed.add(group)
- yield group, group.library
- yield from foreach_cycles_nodetree_group(group, traversed)
-
-
-def foreach_cycles_nodetree():
- traversed = set()
-
- for material in bpy.data.materials:
- nodetree = material.node_tree
- if nodetree:
- yield nodetree, material.library
- yield from foreach_cycles_nodetree_group(nodetree, traversed)
-
- for world in bpy.data.worlds:
- nodetree = world.node_tree
- if nodetree:
- yield nodetree, world.library
- foreach_cycles_nodetree_group(nodetree, traversed)
-
- for light in bpy.data.lights:
- nodetree = light.node_tree
- if nodetree:
- yield nodetree, light.library
- foreach_cycles_nodetree_group(nodetree, traversed)
-
-
-def displacement_node_insert(nodetree):
- # Gather links to replace
- displacement_links = []
- for link in nodetree.links:
- if (
- link.to_node.bl_idname == 'ShaderNodeOutputMaterial' and
- link.from_node.bl_idname != 'ShaderNodeDisplacement' and
- link.to_socket.identifier == 'Displacement'
- ):
- displacement_links.append(link)
-
- # Replace links with displacement node
- for link in displacement_links:
- from_node = link.from_node
- from_socket = link.from_socket
- to_node = link.to_node
- to_socket = link.to_socket
-
- nodetree.links.remove(link)
-
- node = nodetree.nodes.new(type='ShaderNodeDisplacement')
- node.location[0] = 0.5 * (from_node.location[0] + to_node.location[0])
- node.location[1] = 0.5 * (from_node.location[1] + to_node.location[1])
- node.inputs['Scale'].default_value = 0.1
- node.inputs['Midlevel'].default_value = 0.0
-
- nodetree.links.new(from_socket, node.inputs['Height'])
- nodetree.links.new(node.outputs['Displacement'], to_socket)
-
-
-def displacement_principled_nodes(node):
- if node.bl_idname == 'ShaderNodeDisplacement':
- if node.space != 'WORLD':
- node.space = 'OBJECT'
- if node.bl_idname == 'ShaderNodeBsdfPrincipled':
- if node.subsurface_method != 'RANDOM_WALK':
- node.subsurface_method = 'BURLEY'
-
-
-def square_roughness_node_insert(nodetree):
- roughness_node_types = {
- 'ShaderNodeBsdfAnisotropic',
- 'ShaderNodeBsdfGlass',
- 'ShaderNodeBsdfGlossy',
- 'ShaderNodeBsdfRefraction'}
-
- # Update default values
- for node in nodetree.nodes:
- if node.bl_idname in roughness_node_types:
- roughness_input = node.inputs['Roughness']
- roughness_input.default_value = math.sqrt(max(roughness_input.default_value, 0.0))
-
- # Gather roughness links to replace
- roughness_links = []
- for link in nodetree.links:
- if link.to_node.bl_idname in roughness_node_types and \
- link.to_socket.identifier == 'Roughness':
- roughness_links.append(link)
-
- # Replace links with sqrt node
- for link in roughness_links:
- from_node = link.from_node
- from_socket = link.from_socket
- to_node = link.to_node
- to_socket = link.to_socket
-
- nodetree.links.remove(link)
-
- node = nodetree.nodes.new(type='ShaderNodeMath')
- node.operation = 'POWER'
- node.location[0] = 0.5 * (from_node.location[0] + to_node.location[0])
- node.location[1] = 0.5 * (from_node.location[1] + to_node.location[1])
-
- nodetree.links.new(from_socket, node.inputs[0])
- node.inputs[1].default_value = 0.5
- nodetree.links.new(node.outputs['Value'], to_socket)
-
-
-def mapping_node_order_flip(node):
- """
- Flip euler order of mapping shader node
- """
- if node.bl_idname == 'ShaderNodeMapping':
- rot = node.rotation.copy()
- rot.order = 'ZYX'
- quat = rot.to_quaternion()
- node.rotation = quat.to_euler('XYZ')
-
-
-def vector_curve_node_remap(node):
- """
- Remap values of vector curve node from normalized to absolute values
- """
- if node.bl_idname == 'ShaderNodeVectorCurve':
- node.mapping.use_clip = False
- for curve in node.mapping.curves:
- for point in curve.points:
- point.location.x = (point.location.x * 2.0) - 1.0
- point.location.y = (point.location.y - 0.5) * 2.0
- node.mapping.update()
-
-
def custom_bake_remap(scene):
"""
Remap bake types into the new types and set the flags accordingly
@@ -176,10 +42,7 @@ def custom_bake_remap(scene):
'GLOSSY_COLOR',
'TRANSMISSION_DIRECT',
'TRANSMISSION_INDIRECT',
- 'TRANSMISSION_COLOR',
- 'SUBSURFACE_DIRECT',
- 'SUBSURFACE_INDIRECT',
- 'SUBSURFACE_COLOR')
+ 'TRANSMISSION_COLOR')
diffuse_direct_idx = bake_lookup.index('DIFFUSE_DIRECT')
@@ -213,28 +76,6 @@ def custom_bake_remap(scene):
scene.render.bake.use_pass_indirect = False
-def ambient_occlusion_node_relink(nodetree):
- for node in nodetree.nodes:
- if node.bl_idname == 'ShaderNodeAmbientOcclusion':
- node.samples = 1
- node.only_local = False
- node.inputs['Distance'].default_value = 0.0
-
- # Gather links to replace
- ao_links = []
- for link in nodetree.links:
- if link.from_node.bl_idname == 'ShaderNodeAmbientOcclusion':
- ao_links.append(link)
-
- # Replace links
- for link in ao_links:
- from_node = link.from_node
- to_socket = link.to_socket
-
- nodetree.links.remove(link)
- nodetree.links.new(from_node.outputs['Color'], to_socket)
-
-
@persistent
def do_versions(self):
if bpy.context.preferences.version <= (2, 78, 1):
@@ -411,48 +252,3 @@ def do_versions(self):
cmat = mat.cycles
if not cmat.is_property_set("displacement_method"):
cmat.displacement_method = 'DISPLACEMENT'
-
- # Nodes
- for nodetree, library in foreach_cycles_nodetree():
- if library not in libraries:
- continue
-
- # Euler order was ZYX in previous versions.
- if version <= (2, 73, 4):
- for node in nodetree.nodes:
- mapping_node_order_flip(node)
-
- if version <= (2, 76, 5):
- for node in nodetree.nodes:
- vector_curve_node_remap(node)
-
- if version <= (2, 79, 1) or \
- (version >= (2, 80, 0) and version <= (2, 80, 3)):
- displacement_node_insert(nodetree)
-
- if version <= (2, 79, 2):
- for node in nodetree.nodes:
- displacement_principled_nodes(node)
-
- if version <= (2, 79, 3) or \
- (version >= (2, 80, 0) and version <= (2, 80, 4)):
- # Switch to squared roughness convention
- square_roughness_node_insert(nodetree)
-
- if version <= (2, 79, 4):
- ambient_occlusion_node_relink(nodetree)
-
- # Particles
- for part in bpy.data.particles:
- if part.library not in libraries:
- continue
-
- # Copy cycles hair settings to internal settings
- if version <= (2, 80, 15):
- cpart = part.get("cycles", None)
- if cpart:
- part.shape = cpart.get("shape", 0.0)
- part.root_radius = cpart.get("root_width", 1.0)
- part.tip_radius = cpart.get("tip_width", 0.0)
- part.radius_scale = cpart.get("radius_scale", 0.01)
- part.use_close_tip = cpart.get("use_closetip", True)
diff --git a/intern/cycles/blender/blender_camera.cpp b/intern/cycles/blender/blender_camera.cpp
index b3bfaa992a9..592a69585de 100644
--- a/intern/cycles/blender/blender_camera.cpp
+++ b/intern/cycles/blender/blender_camera.cpp
@@ -91,16 +91,31 @@ static void blender_camera_init(BlenderCamera *bcam, BL::RenderSettings &b_rende
{
memset((void *)bcam, 0, sizeof(BlenderCamera));
+ bcam->nearclip = 1e-5f;
+ bcam->farclip = 1e5f;
+
bcam->type = CAMERA_PERSPECTIVE;
+ bcam->ortho_scale = 1.0f;
+
+ bcam->lens = 50.0f;
+ bcam->shuttertime = 1.0f;
+
+ bcam->rolling_shutter_type = Camera::ROLLING_SHUTTER_NONE;
+ bcam->rolling_shutter_duration = 0.1f;
+
+ bcam->aperturesize = 0.0f;
+ bcam->apertureblades = 0;
+ bcam->aperturerotation = 0.0f;
+ bcam->focaldistance = 10.0f;
+
bcam->zoom = 1.0f;
bcam->pixelaspect = make_float2(1.0f, 1.0f);
+ bcam->aperture_ratio = 1.0f;
+
bcam->sensor_width = 36.0f;
bcam->sensor_height = 24.0f;
bcam->sensor_fit = BlenderCamera::AUTO;
- bcam->shuttertime = 1.0f;
bcam->motion_position = Camera::MOTION_POSITION_CENTER;
- bcam->rolling_shutter_type = Camera::ROLLING_SHUTTER_NONE;
- bcam->rolling_shutter_duration = 0.1f;
bcam->border.right = 1.0f;
bcam->border.top = 1.0f;
bcam->pano_viewplane.right = 1.0f;
@@ -108,6 +123,7 @@ static void blender_camera_init(BlenderCamera *bcam, BL::RenderSettings &b_rende
bcam->viewport_camera_border.right = 1.0f;
bcam->viewport_camera_border.top = 1.0f;
bcam->offscreen_dicing_scale = 1.0f;
+ bcam->matrix = transform_identity();
/* render resolution */
bcam->full_width = render_resolution_x(b_render);
@@ -119,10 +135,10 @@ static float blender_camera_focal_distance(BL::RenderEngine &b_engine,
BL::Camera &b_camera,
BlenderCamera *bcam)
{
- BL::Object b_dof_object = b_camera.dof_object();
+ BL::Object b_dof_object = b_camera.dof().focus_object();
if (!b_dof_object)
- return b_camera.dof_distance();
+ return b_camera.dof().focus_distance();
/* for dof object, return distance along camera Z direction */
BL::Array<float, 16> b_ob_matrix;
@@ -191,26 +207,30 @@ static void blender_camera_from_object(BlenderCamera *bcam,
bcam->lens = b_camera.lens();
- /* allow f/stop number to change aperture_size but still
- * give manual control over aperture radius */
- int aperture_type = get_enum(ccamera, "aperture_type");
-
- if (aperture_type == 1) {
- float fstop = RNA_float_get(&ccamera, "aperture_fstop");
+ if (b_camera.dof().use_dof()) {
+ /* allow f/stop number to change aperture_size but still
+ * give manual control over aperture radius */
+ float fstop = b_camera.dof().aperture_fstop();
fstop = max(fstop, 1e-5f);
if (bcam->type == CAMERA_ORTHOGRAPHIC)
bcam->aperturesize = 1.0f / (2.0f * fstop);
else
bcam->aperturesize = (bcam->lens * 1e-3f) / (2.0f * fstop);
- }
- else
- bcam->aperturesize = RNA_float_get(&ccamera, "aperture_size");
- bcam->apertureblades = RNA_int_get(&ccamera, "aperture_blades");
- bcam->aperturerotation = RNA_float_get(&ccamera, "aperture_rotation");
- bcam->focaldistance = blender_camera_focal_distance(b_engine, b_ob, b_camera, bcam);
- bcam->aperture_ratio = RNA_float_get(&ccamera, "aperture_ratio");
+ bcam->apertureblades = b_camera.dof().aperture_blades();
+ bcam->aperturerotation = b_camera.dof().aperture_rotation();
+ bcam->focaldistance = blender_camera_focal_distance(b_engine, b_ob, b_camera, bcam);
+ bcam->aperture_ratio = b_camera.dof().aperture_ratio();
+ }
+ else {
+ /* DOF is turned of for the camera. */
+ bcam->aperturesize = 0.0f;
+ bcam->apertureblades = 0;
+ bcam->aperturerotation = 0.0f;
+ bcam->focaldistance = 0.0f;
+ bcam->aperture_ratio = 1.0f;
+ }
bcam->shift.x = b_engine.camera_shift_x(b_ob, bcam->use_spherical_stereo);
bcam->shift.y = b_camera.shift_y();
@@ -689,6 +709,10 @@ static void blender_camera_from_view(BlenderCamera *bcam,
/* 3d view transform */
bcam->matrix = transform_inverse(get_transform(b_rv3d.view_matrix()));
+
+ /* dimensions */
+ bcam->full_width = width;
+ bcam->full_height = height;
}
static void blender_camera_view_subset(BL::RenderEngine &b_engine,
@@ -705,22 +729,26 @@ static void blender_camera_view_subset(BL::RenderEngine &b_engine,
BoundBox2D cam, view;
float view_aspect, cam_aspect, sensor_size;
- /* get viewport viewplane */
+ /* Get viewport viewplane. */
BlenderCamera view_bcam;
blender_camera_init(&view_bcam, b_render);
blender_camera_from_view(&view_bcam, b_engine, b_scene, b_v3d, b_rv3d, width, height, true);
blender_camera_viewplane(&view_bcam, width, height, &view, &view_aspect, &sensor_size);
- /* get camera viewplane */
+ /* Get camera viewplane. */
BlenderCamera cam_bcam;
blender_camera_init(&cam_bcam, b_render);
blender_camera_from_object(&cam_bcam, b_engine, b_ob, true);
+ /* Camera border is affect by aspect, viewport is not. */
+ cam_bcam.pixelaspect.x = b_render.pixel_aspect_x();
+ cam_bcam.pixelaspect.y = b_render.pixel_aspect_y();
+
blender_camera_viewplane(
&cam_bcam, cam_bcam.full_width, cam_bcam.full_height, &cam, &cam_aspect, &sensor_size);
- /* return */
+ /* Return */
*view_box = view * (1.0f / view_aspect);
*cam_box = cam * (1.0f / cam_aspect);
}
@@ -848,7 +876,8 @@ BufferParams BlenderSync::get_buffer_params(BL::RenderSettings &b_render,
BL::RegionView3D &b_rv3d,
Camera *cam,
int width,
- int height)
+ int height,
+ const bool use_denoiser)
{
BufferParams params;
bool use_border = false;
@@ -879,6 +908,11 @@ BufferParams BlenderSync::get_buffer_params(BL::RenderSettings &b_render,
params.height = height;
}
+ PassType display_pass = update_viewport_display_passes(b_v3d, params.passes);
+
+ /* Can only denoise the combined image pass */
+ params.denoising_data_pass = display_pass == PASS_COMBINED && use_denoiser;
+
return params;
}
diff --git a/intern/cycles/blender/blender_curves.cpp b/intern/cycles/blender/blender_curves.cpp
index d0375ceb79c..82c99631a89 100644
--- a/intern/cycles/blender/blender_curves.cpp
+++ b/intern/cycles/blender/blender_curves.cpp
@@ -17,7 +17,7 @@
#include "render/attribute.h"
#include "render/camera.h"
#include "render/curves.h"
-#include "render/mesh.h"
+#include "render/hair.h"
#include "render/object.h"
#include "render/scene.h"
@@ -38,27 +38,6 @@ ParticleCurveData::~ParticleCurveData()
{
}
-static void interp_weights(float t, float data[4])
-{
- /* Cardinal curve interpolation */
- float t2 = t * t;
- float t3 = t2 * t;
- float fc = 0.71f;
-
- data[0] = -fc * t3 + 2.0f * fc * t2 - fc * t;
- data[1] = (2.0f - fc) * t3 + (fc - 3.0f) * t2 + 1.0f;
- data[2] = (fc - 2.0f) * t3 + (3.0f - 2.0f * fc) * t2 + fc * t;
- data[3] = fc * t3 - fc * t2;
-}
-
-static void curveinterp_v3_v3v3v3v3(
- float3 *p, float3 *v1, float3 *v2, float3 *v3, float3 *v4, const float w[4])
-{
- p->x = v1->x * w[0] + v2->x * w[1] + v3->x * w[2] + v4->x * w[3];
- p->y = v1->y * w[0] + v2->y * w[1] + v3->y * w[2] + v4->y * w[3];
- p->z = v1->z * w[0] + v2->z * w[1] + v3->z * w[2] + v4->z * w[3];
-}
-
static float shaperadius(float shape, float root, float tip, float time)
{
assert(time >= 0.0f);
@@ -76,43 +55,13 @@ static float shaperadius(float shape, float root, float tip, float time)
/* curve functions */
-static void InterpolateKeySegments(
- int seg, int segno, int key, int curve, float3 *keyloc, float *time, ParticleCurveData *CData)
-{
- float3 ckey_loc1 = CData->curvekey_co[key];
- float3 ckey_loc2 = ckey_loc1;
- float3 ckey_loc3 = CData->curvekey_co[key + 1];
- float3 ckey_loc4 = ckey_loc3;
-
- if (key > CData->curve_firstkey[curve])
- ckey_loc1 = CData->curvekey_co[key - 1];
-
- if (key < CData->curve_firstkey[curve] + CData->curve_keynum[curve] - 2)
- ckey_loc4 = CData->curvekey_co[key + 2];
-
- float time1 = CData->curvekey_time[key] / CData->curve_length[curve];
- float time2 = CData->curvekey_time[key + 1] / CData->curve_length[curve];
-
- float dfra = (time2 - time1) / (float)segno;
-
- if (time)
- *time = (dfra * seg) + time1;
-
- float t[4];
-
- interp_weights((float)seg / (float)segno, t);
-
- if (keyloc)
- curveinterp_v3_v3v3v3v3(keyloc, &ckey_loc1, &ckey_loc2, &ckey_loc3, &ckey_loc4, t);
-}
-
static bool ObtainCacheParticleData(
- Mesh *mesh, BL::Mesh *b_mesh, BL::Object *b_ob, ParticleCurveData *CData, bool background)
+ Hair *hair, BL::Mesh *b_mesh, BL::Object *b_ob, ParticleCurveData *CData, bool background)
{
int curvenum = 0;
int keyno = 0;
- if (!(mesh && b_mesh && b_ob && CData))
+ if (!(hair && b_mesh && b_ob && CData))
return false;
Transform tfm = get_transform(b_ob->matrix_world());
@@ -128,7 +77,7 @@ static bool ObtainCacheParticleData(
if ((b_part.render_type() == BL::ParticleSettings::render_type_PATH) &&
(b_part.type() == BL::ParticleSettings::type_HAIR)) {
- int shader = clamp(b_part.material() - 1, 0, mesh->used_shaders.size() - 1);
+ int shader = clamp(b_part.material() - 1, 0, hair->used_shaders.size() - 1);
int display_step = background ? b_part.render_step() : b_part.display_step();
int totparts = b_psys.particles.length();
int totchild = background ? b_psys.child_particles.length() :
@@ -173,19 +122,20 @@ static bool ObtainCacheParticleData(
CData->curve_firstkey.push_back_slow(keyno);
float curve_length = 0.0f;
- float3 pcKey;
+ float3 prev_co_world = make_float3(0.0f, 0.0f, 0.0f);
+ float3 prev_co_object = make_float3(0.0f, 0.0f, 0.0f);
for (int step_no = 0; step_no < ren_step; step_no++) {
- float nco[3];
- b_psys.co_hair(*b_ob, pa_no, step_no, nco);
- float3 cKey = make_float3(nco[0], nco[1], nco[2]);
- cKey = transform_point(&itfm, cKey);
+ float3 co_world = prev_co_world;
+ b_psys.co_hair(*b_ob, pa_no, step_no, &co_world.x);
+ float3 co_object = transform_point(&itfm, co_world);
if (step_no > 0) {
- const float step_length = len(cKey - pcKey);
+ const float step_length = len(co_object - prev_co_object);
curve_length += step_length;
}
- CData->curvekey_co.push_back_slow(cKey);
+ CData->curvekey_co.push_back_slow(co_object);
CData->curvekey_time.push_back_slow(curve_length);
- pcKey = cKey;
+ prev_co_object = co_object;
+ prev_co_world = co_world;
keynum++;
}
keyno += keynum;
@@ -201,14 +151,14 @@ static bool ObtainCacheParticleData(
return true;
}
-static bool ObtainCacheParticleUV(Mesh *mesh,
+static bool ObtainCacheParticleUV(Hair *hair,
BL::Mesh *b_mesh,
BL::Object *b_ob,
ParticleCurveData *CData,
bool background,
int uv_num)
{
- if (!(mesh && b_mesh && b_ob && CData))
+ if (!(hair && b_mesh && b_ob && CData))
return false;
CData->curve_uv.clear();
@@ -264,14 +214,14 @@ static bool ObtainCacheParticleUV(Mesh *mesh,
return true;
}
-static bool ObtainCacheParticleVcol(Mesh *mesh,
+static bool ObtainCacheParticleVcol(Hair *hair,
BL::Mesh *b_mesh,
BL::Object *b_ob,
ParticleCurveData *CData,
bool background,
int vcol_num)
{
- if (!(mesh && b_mesh && b_ob && CData))
+ if (!(hair && b_mesh && b_ob && CData))
return false;
CData->curve_vcol.clear();
@@ -312,7 +262,7 @@ static bool ObtainCacheParticleVcol(Mesh *mesh,
BL::Mesh::vertex_colors_iterator l;
b_mesh->vertex_colors.begin(l);
- float3 vcol = make_float3(0.0f, 0.0f, 0.0f);
+ float4 vcol = make_float4(0.0f, 0.0f, 0.0f, 1.0f);
if (b_mesh->vertex_colors.length())
b_psys.mcol_on_emitter(psmd, *b_pa, pa_no, vcol_num, &vcol.x);
CData->curve_vcol.push_back_slow(vcol);
@@ -327,287 +277,21 @@ static bool ObtainCacheParticleVcol(Mesh *mesh,
return true;
}
-static void ExportCurveTrianglePlanes(Mesh *mesh,
- ParticleCurveData *CData,
- float3 RotCam,
- bool is_ortho)
-{
- int vertexno = mesh->verts.size();
- int vertexindex = vertexno;
- int numverts = 0, numtris = 0;
-
- /* compute and reserve size of arrays */
- for (int sys = 0; sys < CData->psys_firstcurve.size(); sys++) {
- for (int curve = CData->psys_firstcurve[sys];
- curve < CData->psys_firstcurve[sys] + CData->psys_curvenum[sys];
- curve++) {
- numverts += 2 + (CData->curve_keynum[curve] - 1) * 2;
- numtris += (CData->curve_keynum[curve] - 1) * 2;
- }
- }
-
- mesh->reserve_mesh(mesh->verts.size() + numverts, mesh->num_triangles() + numtris);
-
- /* actually export */
- for (int sys = 0; sys < CData->psys_firstcurve.size(); sys++) {
- for (int curve = CData->psys_firstcurve[sys];
- curve < CData->psys_firstcurve[sys] + CData->psys_curvenum[sys];
- curve++) {
- float3 xbasis;
- float3 v1;
- float time = 0.0f;
- float3 ickey_loc = CData->curvekey_co[CData->curve_firstkey[curve]];
- float radius = shaperadius(
- CData->psys_shape[sys], CData->psys_rootradius[sys], CData->psys_tipradius[sys], 0.0f);
- v1 = CData->curvekey_co[CData->curve_firstkey[curve] + 1] -
- CData->curvekey_co[CData->curve_firstkey[curve]];
- if (is_ortho)
- xbasis = normalize(cross(RotCam, v1));
- else
- xbasis = normalize(cross(RotCam - ickey_loc, v1));
- float3 ickey_loc_shfl = ickey_loc - radius * xbasis;
- float3 ickey_loc_shfr = ickey_loc + radius * xbasis;
- mesh->add_vertex(ickey_loc_shfl);
- mesh->add_vertex(ickey_loc_shfr);
- vertexindex += 2;
-
- for (int curvekey = CData->curve_firstkey[curve] + 1;
- curvekey < CData->curve_firstkey[curve] + CData->curve_keynum[curve];
- curvekey++) {
- ickey_loc = CData->curvekey_co[curvekey];
-
- if (curvekey == CData->curve_firstkey[curve] + CData->curve_keynum[curve] - 1)
- v1 = CData->curvekey_co[curvekey] -
- CData->curvekey_co[max(curvekey - 1, CData->curve_firstkey[curve])];
- else
- v1 = CData->curvekey_co[curvekey + 1] - CData->curvekey_co[curvekey - 1];
-
- time = CData->curvekey_time[curvekey] / CData->curve_length[curve];
- radius = shaperadius(
- CData->psys_shape[sys], CData->psys_rootradius[sys], CData->psys_tipradius[sys], time);
-
- if (curvekey == CData->curve_firstkey[curve] + CData->curve_keynum[curve] - 1)
- radius = shaperadius(CData->psys_shape[sys],
- CData->psys_rootradius[sys],
- CData->psys_tipradius[sys],
- 0.95f);
-
- if (CData->psys_closetip[sys] &&
- (curvekey == CData->curve_firstkey[curve] + CData->curve_keynum[curve] - 1))
- radius = shaperadius(CData->psys_shape[sys], CData->psys_rootradius[sys], 0.0f, 0.95f);
-
- if (is_ortho)
- xbasis = normalize(cross(RotCam, v1));
- else
- xbasis = normalize(cross(RotCam - ickey_loc, v1));
- float3 ickey_loc_shfl = ickey_loc - radius * xbasis;
- float3 ickey_loc_shfr = ickey_loc + radius * xbasis;
- mesh->add_vertex(ickey_loc_shfl);
- mesh->add_vertex(ickey_loc_shfr);
- mesh->add_triangle(
- vertexindex - 2, vertexindex, vertexindex - 1, CData->psys_shader[sys], true);
- mesh->add_triangle(
- vertexindex + 1, vertexindex - 1, vertexindex, CData->psys_shader[sys], true);
- vertexindex += 2;
- }
- }
- }
-
- mesh->resize_mesh(mesh->verts.size(), mesh->num_triangles());
- mesh->attributes.remove(ATTR_STD_VERTEX_NORMAL);
- mesh->attributes.remove(ATTR_STD_FACE_NORMAL);
- mesh->add_face_normals();
- mesh->add_vertex_normals();
- mesh->attributes.remove(ATTR_STD_FACE_NORMAL);
-
- /* texture coords still needed */
-}
-
-static void ExportCurveTriangleGeometry(Mesh *mesh, ParticleCurveData *CData, int resolution)
-{
- int vertexno = mesh->verts.size();
- int vertexindex = vertexno;
- int numverts = 0, numtris = 0;
-
- /* compute and reserve size of arrays */
- for (int sys = 0; sys < CData->psys_firstcurve.size(); sys++) {
- for (int curve = CData->psys_firstcurve[sys];
- curve < CData->psys_firstcurve[sys] + CData->psys_curvenum[sys];
- curve++) {
- numverts += (CData->curve_keynum[curve] - 1) * resolution + resolution;
- numtris += (CData->curve_keynum[curve] - 1) * 2 * resolution;
- }
- }
-
- mesh->reserve_mesh(mesh->verts.size() + numverts, mesh->num_triangles() + numtris);
-
- /* actually export */
- for (int sys = 0; sys < CData->psys_firstcurve.size(); sys++) {
- for (int curve = CData->psys_firstcurve[sys];
- curve < CData->psys_firstcurve[sys] + CData->psys_curvenum[sys];
- curve++) {
- float3 firstxbasis = cross(make_float3(1.0f, 0.0f, 0.0f),
- CData->curvekey_co[CData->curve_firstkey[curve] + 1] -
- CData->curvekey_co[CData->curve_firstkey[curve]]);
- if (!is_zero(firstxbasis))
- firstxbasis = normalize(firstxbasis);
- else
- firstxbasis = normalize(cross(make_float3(0.0f, 1.0f, 0.0f),
- CData->curvekey_co[CData->curve_firstkey[curve] + 1] -
- CData->curvekey_co[CData->curve_firstkey[curve]]));
-
- for (int curvekey = CData->curve_firstkey[curve];
- curvekey < CData->curve_firstkey[curve] + CData->curve_keynum[curve] - 1;
- curvekey++) {
- float3 xbasis = firstxbasis;
- float3 v1;
- float3 v2;
-
- if (curvekey == CData->curve_firstkey[curve]) {
- v1 = CData->curvekey_co[min(
- curvekey + 2, CData->curve_firstkey[curve] + CData->curve_keynum[curve] - 1)] -
- CData->curvekey_co[curvekey + 1];
- v2 = CData->curvekey_co[curvekey + 1] - CData->curvekey_co[curvekey];
- }
- else if (curvekey == CData->curve_firstkey[curve] + CData->curve_keynum[curve] - 1) {
- v1 = CData->curvekey_co[curvekey] - CData->curvekey_co[curvekey - 1];
- v2 = CData->curvekey_co[curvekey - 1] -
- CData->curvekey_co[max(curvekey - 2, CData->curve_firstkey[curve])];
- }
- else {
- v1 = CData->curvekey_co[curvekey + 1] - CData->curvekey_co[curvekey];
- v2 = CData->curvekey_co[curvekey] - CData->curvekey_co[curvekey - 1];
- }
-
- xbasis = cross(v1, v2);
-
- if (len_squared(xbasis) >= 0.05f * len_squared(v1) * len_squared(v2)) {
- firstxbasis = normalize(xbasis);
- break;
- }
- }
-
- for (int curvekey = CData->curve_firstkey[curve];
- curvekey < CData->curve_firstkey[curve] + CData->curve_keynum[curve] - 1;
- curvekey++) {
- int subv = 1;
- float3 xbasis;
- float3 ybasis;
- float3 v1;
- float3 v2;
-
- if (curvekey == CData->curve_firstkey[curve]) {
- subv = 0;
- v1 = CData->curvekey_co[min(
- curvekey + 2, CData->curve_firstkey[curve] + CData->curve_keynum[curve] - 1)] -
- CData->curvekey_co[curvekey + 1];
- v2 = CData->curvekey_co[curvekey + 1] - CData->curvekey_co[curvekey];
- }
- else if (curvekey == CData->curve_firstkey[curve] + CData->curve_keynum[curve] - 1) {
- v1 = CData->curvekey_co[curvekey] - CData->curvekey_co[curvekey - 1];
- v2 = CData->curvekey_co[curvekey - 1] -
- CData->curvekey_co[max(curvekey - 2, CData->curve_firstkey[curve])];
- }
- else {
- v1 = CData->curvekey_co[curvekey + 1] - CData->curvekey_co[curvekey];
- v2 = CData->curvekey_co[curvekey] - CData->curvekey_co[curvekey - 1];
- }
-
- xbasis = cross(v1, v2);
-
- if (len_squared(xbasis) >= 0.05f * len_squared(v1) * len_squared(v2)) {
- xbasis = normalize(xbasis);
- firstxbasis = xbasis;
- }
- else
- xbasis = firstxbasis;
-
- ybasis = normalize(cross(xbasis, v2));
-
- for (; subv <= 1; subv++) {
- float3 ickey_loc = make_float3(0.0f, 0.0f, 0.0f);
- float time = 0.0f;
-
- InterpolateKeySegments(subv, 1, curvekey, curve, &ickey_loc, &time, CData);
-
- float radius = shaperadius(CData->psys_shape[sys],
- CData->psys_rootradius[sys],
- CData->psys_tipradius[sys],
- time);
-
- if ((curvekey == CData->curve_firstkey[curve] + CData->curve_keynum[curve] - 2) &&
- (subv == 1))
- radius = shaperadius(CData->psys_shape[sys],
- CData->psys_rootradius[sys],
- CData->psys_tipradius[sys],
- 0.95f);
-
- if (CData->psys_closetip[sys] && (subv == 1) &&
- (curvekey == CData->curve_firstkey[curve] + CData->curve_keynum[curve] - 2))
- radius = shaperadius(CData->psys_shape[sys], CData->psys_rootradius[sys], 0.0f, 0.95f);
-
- float angle = M_2PI_F / (float)resolution;
- for (int section = 0; section < resolution; section++) {
- float3 ickey_loc_shf = ickey_loc + radius * (cosf(angle * section) * xbasis +
- sinf(angle * section) * ybasis);
- mesh->add_vertex(ickey_loc_shf);
- }
-
- if (subv != 0) {
- for (int section = 0; section < resolution - 1; section++) {
- mesh->add_triangle(vertexindex - resolution + section,
- vertexindex + section,
- vertexindex - resolution + section + 1,
- CData->psys_shader[sys],
- true);
- mesh->add_triangle(vertexindex + section + 1,
- vertexindex - resolution + section + 1,
- vertexindex + section,
- CData->psys_shader[sys],
- true);
- }
- mesh->add_triangle(vertexindex - 1,
- vertexindex + resolution - 1,
- vertexindex - resolution,
- CData->psys_shader[sys],
- true);
- mesh->add_triangle(vertexindex,
- vertexindex - resolution,
- vertexindex + resolution - 1,
- CData->psys_shader[sys],
- true);
- }
- vertexindex += resolution;
- }
- }
- }
- }
-
- mesh->resize_mesh(mesh->verts.size(), mesh->num_triangles());
- mesh->attributes.remove(ATTR_STD_VERTEX_NORMAL);
- mesh->attributes.remove(ATTR_STD_FACE_NORMAL);
- mesh->add_face_normals();
- mesh->add_vertex_normals();
- mesh->attributes.remove(ATTR_STD_FACE_NORMAL);
-
- /* texture coords still needed */
-}
-
-static void ExportCurveSegments(Scene *scene, Mesh *mesh, ParticleCurveData *CData)
+static void ExportCurveSegments(Scene *scene, Hair *hair, ParticleCurveData *CData)
{
int num_keys = 0;
int num_curves = 0;
- if (mesh->num_curves())
+ if (hair->num_curves())
return;
Attribute *attr_intercept = NULL;
Attribute *attr_random = NULL;
- if (mesh->need_attribute(scene, ATTR_STD_CURVE_INTERCEPT))
- attr_intercept = mesh->curve_attributes.add(ATTR_STD_CURVE_INTERCEPT);
- if (mesh->need_attribute(scene, ATTR_STD_CURVE_RANDOM))
- attr_random = mesh->curve_attributes.add(ATTR_STD_CURVE_RANDOM);
+ if (hair->need_attribute(scene, ATTR_STD_CURVE_INTERCEPT))
+ attr_intercept = hair->attributes.add(ATTR_STD_CURVE_INTERCEPT);
+ if (hair->need_attribute(scene, ATTR_STD_CURVE_RANDOM))
+ attr_random = hair->attributes.add(ATTR_STD_CURVE_RANDOM);
/* compute and reserve size of arrays */
for (int sys = 0; sys < CData->psys_firstcurve.size(); sys++) {
@@ -620,10 +304,10 @@ static void ExportCurveSegments(Scene *scene, Mesh *mesh, ParticleCurveData *CDa
}
if (num_curves > 0) {
- VLOG(1) << "Exporting curve segments for mesh " << mesh->name;
+ VLOG(1) << "Exporting curve segments for mesh " << hair->name;
}
- mesh->reserve_curves(mesh->num_curves() + num_curves, mesh->curve_keys.size() + num_keys);
+ hair->reserve_curves(hair->num_curves() + num_curves, hair->curve_keys.size() + num_keys);
num_keys = 0;
num_curves = 0;
@@ -648,7 +332,7 @@ static void ExportCurveSegments(Scene *scene, Mesh *mesh, ParticleCurveData *CDa
(curvekey == CData->curve_firstkey[curve] + CData->curve_keynum[curve] - 1)) {
radius = 0.0f;
}
- mesh->add_curve_key(ickey_loc, radius);
+ hair->add_curve_key(ickey_loc, radius);
if (attr_intercept)
attr_intercept->add(time);
@@ -656,19 +340,19 @@ static void ExportCurveSegments(Scene *scene, Mesh *mesh, ParticleCurveData *CDa
}
if (attr_random != NULL) {
- attr_random->add(hash_int_01(num_curves));
+ attr_random->add(hash_uint2_to_float(num_curves, 0));
}
- mesh->add_curve(num_keys, CData->psys_shader[sys]);
+ hair->add_curve(num_keys, CData->psys_shader[sys]);
num_keys += num_curve_keys;
num_curves++;
}
}
/* check allocation */
- if ((mesh->curve_keys.size() != num_keys) || (mesh->num_curves() != num_curves)) {
+ if ((hair->curve_keys.size() != num_keys) || (hair->num_curves() != num_curves)) {
VLOG(1) << "Allocation failed, clearing data";
- mesh->clear();
+ hair->clear();
}
}
@@ -712,24 +396,58 @@ static float4 LerpCurveSegmentMotionCV(ParticleCurveData *CData, int sys, int cu
return lerp(mP, mP2, remainder);
}
-static void ExportCurveSegmentsMotion(Mesh *mesh, ParticleCurveData *CData, int motion_step)
+static void export_hair_motion_validate_attribute(Hair *hair,
+ int motion_step,
+ int num_motion_keys,
+ bool have_motion)
+{
+ Attribute *attr_mP = hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
+ const int num_keys = hair->curve_keys.size();
+
+ if (num_motion_keys != num_keys || !have_motion) {
+ /* No motion or hair "topology" changed, remove attributes again. */
+ if (num_motion_keys != num_keys) {
+ VLOG(1) << "Hair topology changed, removing attribute.";
+ }
+ else {
+ VLOG(1) << "No motion, removing attribute.";
+ }
+ hair->attributes.remove(ATTR_STD_MOTION_VERTEX_POSITION);
+ }
+ else if (motion_step > 0) {
+ VLOG(1) << "Filling in new motion vertex position for motion_step " << motion_step;
+
+ /* Motion, fill up previous steps that we might have skipped because
+ * they had no motion, but we need them anyway now. */
+ for (int step = 0; step < motion_step; step++) {
+ float4 *mP = attr_mP->data_float4() + step * num_keys;
+
+ for (int key = 0; key < num_keys; key++) {
+ mP[key] = float3_to_float4(hair->curve_keys[key]);
+ mP[key].w = hair->curve_radius[key];
+ }
+ }
+ }
+}
+
+static void ExportCurveSegmentsMotion(Hair *hair, ParticleCurveData *CData, int motion_step)
{
- VLOG(1) << "Exporting curve motion segments for mesh " << mesh->name << ", motion step "
+ VLOG(1) << "Exporting curve motion segments for hair " << hair->name << ", motion step "
<< motion_step;
/* find attribute */
- Attribute *attr_mP = mesh->curve_attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
+ Attribute *attr_mP = hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
bool new_attribute = false;
/* add new attribute if it doesn't exist already */
if (!attr_mP) {
VLOG(1) << "Creating new motion vertex position attribute";
- attr_mP = mesh->curve_attributes.add(ATTR_STD_MOTION_VERTEX_POSITION);
+ attr_mP = hair->attributes.add(ATTR_STD_MOTION_VERTEX_POSITION);
new_attribute = true;
}
/* export motion vectors for curve keys */
- size_t numkeys = mesh->curve_keys.size();
+ size_t numkeys = hair->curve_keys.size();
float4 *mP = attr_mP->data_float4() + motion_step * numkeys;
bool have_motion = false;
int i = 0;
@@ -740,24 +458,24 @@ static void ExportCurveSegmentsMotion(Mesh *mesh, ParticleCurveData *CData, int
curve < CData->psys_firstcurve[sys] + CData->psys_curvenum[sys];
curve++) {
/* Curve lengths may not match! Curves can be clipped. */
- int curve_key_end = (num_curves + 1 < (int)mesh->curve_first_key.size() ?
- mesh->curve_first_key[num_curves + 1] :
- (int)mesh->curve_keys.size());
- const int num_center_curve_keys = curve_key_end - mesh->curve_first_key[num_curves];
+ int curve_key_end = (num_curves + 1 < (int)hair->curve_first_key.size() ?
+ hair->curve_first_key[num_curves + 1] :
+ (int)hair->curve_keys.size());
+ const int num_center_curve_keys = curve_key_end - hair->curve_first_key[num_curves];
const int is_num_keys_different = CData->curve_keynum[curve] - num_center_curve_keys;
if (!is_num_keys_different) {
for (int curvekey = CData->curve_firstkey[curve];
curvekey < CData->curve_firstkey[curve] + CData->curve_keynum[curve];
curvekey++) {
- if (i < mesh->curve_keys.size()) {
+ if (i < hair->curve_keys.size()) {
mP[i] = CurveSegmentMotionCV(CData, sys, curve, curvekey);
if (!have_motion) {
/* unlike mesh coordinates, these tend to be slightly different
* between frames due to particle transforms into/out of object
* space, so we use an epsilon to detect actual changes */
- float4 curve_key = float3_to_float4(mesh->curve_keys[i]);
- curve_key.w = mesh->curve_radius[i];
+ float4 curve_key = float3_to_float4(hair->curve_keys[i]);
+ curve_key.w = hair->curve_radius[i];
if (len_squared(mP[i] - curve_key) > 1e-5f * 1e-5f)
have_motion = true;
}
@@ -766,7 +484,7 @@ static void ExportCurveSegmentsMotion(Mesh *mesh, ParticleCurveData *CData, int
}
}
else {
- /* Number of keys has changed. Genereate an interpolated version
+ /* Number of keys has changed. Generate an interpolated version
* to preserve motion blur. */
const float step_size = num_center_curve_keys > 1 ? 1.0f / (num_center_curve_keys - 1) :
0.0f;
@@ -781,276 +499,69 @@ static void ExportCurveSegmentsMotion(Mesh *mesh, ParticleCurveData *CData, int
}
}
- /* in case of new attribute, we verify if there really was any motion */
+ /* In case of new attribute, we verify if there really was any motion. */
if (new_attribute) {
- if (i != numkeys || !have_motion) {
- /* No motion or hair "topology" changed, remove attributes again. */
- if (i != numkeys) {
- VLOG(1) << "Hair topology changed, removing attribute.";
- }
- else {
- VLOG(1) << "No motion, removing attribute.";
- }
- mesh->curve_attributes.remove(ATTR_STD_MOTION_VERTEX_POSITION);
- }
- else if (motion_step > 0) {
- VLOG(1) << "Filling in new motion vertex position for motion_step " << motion_step;
- /* motion, fill up previous steps that we might have skipped because
- * they had no motion, but we need them anyway now */
- for (int step = 0; step < motion_step; step++) {
- float4 *mP = attr_mP->data_float4() + step * numkeys;
-
- for (int key = 0; key < numkeys; key++) {
- mP[key] = float3_to_float4(mesh->curve_keys[key]);
- mP[key].w = mesh->curve_radius[key];
- }
- }
- }
- }
-}
-
-static void ExportCurveTriangleUV(ParticleCurveData *CData,
- int vert_offset,
- int resol,
- float2 *uvdata)
-{
- if (uvdata == NULL)
- return;
- int vertexindex = vert_offset;
-
- for (int sys = 0; sys < CData->psys_firstcurve.size(); sys++) {
- for (int curve = CData->psys_firstcurve[sys];
- curve < CData->psys_firstcurve[sys] + CData->psys_curvenum[sys];
- curve++) {
- for (int curvekey = CData->curve_firstkey[curve];
- curvekey < CData->curve_firstkey[curve] + CData->curve_keynum[curve] - 1;
- curvekey++) {
- for (int section = 0; section < resol; section++) {
- uvdata[vertexindex] = CData->curve_uv[curve];
- vertexindex++;
- uvdata[vertexindex] = CData->curve_uv[curve];
- vertexindex++;
- uvdata[vertexindex] = CData->curve_uv[curve];
- vertexindex++;
- uvdata[vertexindex] = CData->curve_uv[curve];
- vertexindex++;
- uvdata[vertexindex] = CData->curve_uv[curve];
- vertexindex++;
- uvdata[vertexindex] = CData->curve_uv[curve];
- vertexindex++;
- }
- }
- }
- }
-}
-
-static void ExportCurveTriangleVcol(ParticleCurveData *CData,
- int vert_offset,
- int resol,
- uchar4 *cdata)
-{
- if (cdata == NULL)
- return;
-
- int vertexindex = vert_offset;
-
- for (int sys = 0; sys < CData->psys_firstcurve.size(); sys++) {
- for (int curve = CData->psys_firstcurve[sys];
- curve < CData->psys_firstcurve[sys] + CData->psys_curvenum[sys];
- curve++) {
- for (int curvekey = CData->curve_firstkey[curve];
- curvekey < CData->curve_firstkey[curve] + CData->curve_keynum[curve] - 1;
- curvekey++) {
- for (int section = 0; section < resol; section++) {
- /* Encode vertex color using the sRGB curve. */
- cdata[vertexindex] = color_float_to_byte(
- color_srgb_to_linear_v3(CData->curve_vcol[curve]));
- vertexindex++;
- cdata[vertexindex] = color_float_to_byte(
- color_srgb_to_linear_v3(CData->curve_vcol[curve]));
- vertexindex++;
- cdata[vertexindex] = color_float_to_byte(
- color_srgb_to_linear_v3(CData->curve_vcol[curve]));
- vertexindex++;
- cdata[vertexindex] = color_float_to_byte(
- color_srgb_to_linear_v3(CData->curve_vcol[curve]));
- vertexindex++;
- cdata[vertexindex] = color_float_to_byte(
- color_srgb_to_linear_v3(CData->curve_vcol[curve]));
- vertexindex++;
- cdata[vertexindex] = color_float_to_byte(
- color_srgb_to_linear_v3(CData->curve_vcol[curve]));
- vertexindex++;
- }
- }
- }
+ export_hair_motion_validate_attribute(hair, motion_step, i, have_motion);
}
}
/* Hair Curve Sync */
-void BlenderSync::sync_curve_settings()
+bool BlenderSync::object_has_particle_hair(BL::Object b_ob)
{
- PointerRNA csscene = RNA_pointer_get(&b_scene.ptr, "cycles_curves");
-
- CurveSystemManager *curve_system_manager = scene->curve_system_manager;
- CurveSystemManager prev_curve_system_manager = *curve_system_manager;
-
- curve_system_manager->use_curves = get_boolean(csscene, "use_curves");
- curve_system_manager->minimum_width = get_float(csscene, "minimum_width");
- curve_system_manager->maximum_width = get_float(csscene, "maximum_width");
-
- curve_system_manager->primitive = (CurvePrimitiveType)get_enum(
- csscene, "primitive", CURVE_NUM_PRIMITIVE_TYPES, CURVE_LINE_SEGMENTS);
- curve_system_manager->curve_shape = (CurveShapeType)get_enum(
- csscene, "shape", CURVE_NUM_SHAPE_TYPES, CURVE_THICK);
- curve_system_manager->resolution = get_int(csscene, "resolution");
- curve_system_manager->subdivisions = get_int(csscene, "subdivisions");
- curve_system_manager->use_backfacing = !get_boolean(csscene, "cull_backfacing");
-
- /* Triangles */
- if (curve_system_manager->primitive == CURVE_TRIANGLES) {
- /* camera facing planes */
- if (curve_system_manager->curve_shape == CURVE_RIBBON) {
- curve_system_manager->triangle_method = CURVE_CAMERA_TRIANGLES;
- curve_system_manager->resolution = 1;
- }
- else if (curve_system_manager->curve_shape == CURVE_THICK) {
- curve_system_manager->triangle_method = CURVE_TESSELATED_TRIANGLES;
- }
- }
- /* Line Segments */
- else if (curve_system_manager->primitive == CURVE_LINE_SEGMENTS) {
- if (curve_system_manager->curve_shape == CURVE_RIBBON) {
- /* tangent shading */
- curve_system_manager->line_method = CURVE_UNCORRECTED;
- curve_system_manager->use_encasing = true;
- curve_system_manager->use_backfacing = false;
- curve_system_manager->use_tangent_normal_geometry = true;
- }
- else if (curve_system_manager->curve_shape == CURVE_THICK) {
- curve_system_manager->line_method = CURVE_ACCURATE;
- curve_system_manager->use_encasing = false;
- curve_system_manager->use_tangent_normal_geometry = false;
- }
- }
- /* Curve Segments */
- else if (curve_system_manager->primitive == CURVE_SEGMENTS) {
- if (curve_system_manager->curve_shape == CURVE_RIBBON) {
- curve_system_manager->primitive = CURVE_RIBBONS;
- curve_system_manager->use_backfacing = false;
- }
- }
+ /* Test if the object has a particle modifier with hair. */
+ BL::Object::modifiers_iterator b_mod;
+ for (b_ob.modifiers.begin(b_mod); b_mod != b_ob.modifiers.end(); ++b_mod) {
+ if ((b_mod->type() == b_mod->type_PARTICLE_SYSTEM) &&
+ (preview ? b_mod->show_viewport() : b_mod->show_render())) {
+ BL::ParticleSystemModifier psmd((const PointerRNA)b_mod->ptr);
+ BL::ParticleSystem b_psys((const PointerRNA)psmd.particle_system().ptr);
+ BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr);
- if (curve_system_manager->modified_mesh(prev_curve_system_manager)) {
- BL::BlendData::objects_iterator b_ob;
-
- for (b_data.objects.begin(b_ob); b_ob != b_data.objects.end(); ++b_ob) {
- if (object_is_mesh(*b_ob)) {
- BL::Object::particle_systems_iterator b_psys;
- for (b_ob->particle_systems.begin(b_psys); b_psys != b_ob->particle_systems.end();
- ++b_psys) {
- if ((b_psys->settings().render_type() == BL::ParticleSettings::render_type_PATH) &&
- (b_psys->settings().type() == BL::ParticleSettings::type_HAIR)) {
- BL::ID key = BKE_object_is_modified(*b_ob) ? *b_ob : b_ob->data();
- mesh_map.set_recalc(key);
- object_map.set_recalc(*b_ob);
- }
- }
+ if ((b_part.render_type() == BL::ParticleSettings::render_type_PATH) &&
+ (b_part.type() == BL::ParticleSettings::type_HAIR)) {
+ return true;
}
}
}
- if (curve_system_manager->modified(prev_curve_system_manager))
- curve_system_manager->tag_update(scene);
+ return false;
}
-void BlenderSync::sync_curves(
- Mesh *mesh, BL::Mesh &b_mesh, BL::Object &b_ob, bool motion, int motion_step)
+/* Old particle hair. */
+void BlenderSync::sync_particle_hair(
+ Hair *hair, BL::Mesh &b_mesh, BL::Object &b_ob, bool motion, int motion_step)
{
- if (!motion) {
- /* Clear stored curve data */
- mesh->curve_keys.clear();
- mesh->curve_radius.clear();
- mesh->curve_first_key.clear();
- mesh->curve_shader.clear();
- mesh->curve_attributes.clear();
- }
-
/* obtain general settings */
- const bool use_curves = scene->curve_system_manager->use_curves;
-
- if (!(use_curves && b_ob.mode() != b_ob.mode_PARTICLE_EDIT)) {
- if (!motion)
- mesh->compute_bounds();
+ if (b_ob.mode() == b_ob.mode_PARTICLE_EDIT || b_ob.mode() == b_ob.mode_EDIT) {
return;
}
- const int primitive = scene->curve_system_manager->primitive;
- const int triangle_method = scene->curve_system_manager->triangle_method;
- const int resolution = scene->curve_system_manager->resolution;
- const size_t vert_num = mesh->verts.size();
- const size_t tri_num = mesh->num_triangles();
- int used_res = 1;
-
/* extract particle hair data - should be combined with connecting to mesh later*/
ParticleCurveData CData;
- ObtainCacheParticleData(mesh, &b_mesh, &b_ob, &CData, !preview);
-
- /* add hair geometry to mesh */
- if (primitive == CURVE_TRIANGLES) {
- if (triangle_method == CURVE_CAMERA_TRIANGLES) {
- /* obtain camera parameters */
- float3 RotCam;
- Camera *camera = scene->camera;
- Transform &ctfm = camera->matrix;
- if (camera->type == CAMERA_ORTHOGRAPHIC) {
- RotCam = -make_float3(ctfm.x.z, ctfm.y.z, ctfm.z.z);
- }
- else {
- Transform tfm = get_transform(b_ob.matrix_world());
- Transform itfm = transform_quick_inverse(tfm);
- RotCam = transform_point(&itfm, make_float3(ctfm.x.w, ctfm.y.w, ctfm.z.w));
- }
- bool is_ortho = camera->type == CAMERA_ORTHOGRAPHIC;
- ExportCurveTrianglePlanes(mesh, &CData, RotCam, is_ortho);
- }
- else {
- ExportCurveTriangleGeometry(mesh, &CData, resolution);
- used_res = resolution;
- }
- }
- else {
- if (motion)
- ExportCurveSegmentsMotion(mesh, &CData, motion_step);
- else
- ExportCurveSegments(scene, mesh, &CData);
- }
+ ObtainCacheParticleData(hair, &b_mesh, &b_ob, &CData, !preview);
+
+ /* add hair geometry */
+ if (motion)
+ ExportCurveSegmentsMotion(hair, &CData, motion_step);
+ else
+ ExportCurveSegments(scene, hair, &CData);
/* generated coordinates from first key. we should ideally get this from
* blender to handle deforming objects */
if (!motion) {
- if (mesh->need_attribute(scene, ATTR_STD_GENERATED)) {
+ if (hair->need_attribute(scene, ATTR_STD_GENERATED)) {
float3 loc, size;
mesh_texture_space(b_mesh, loc, size);
- if (primitive == CURVE_TRIANGLES) {
- Attribute *attr_generated = mesh->attributes.add(ATTR_STD_GENERATED);
- float3 *generated = attr_generated->data_float3();
+ Attribute *attr_generated = hair->attributes.add(ATTR_STD_GENERATED);
+ float3 *generated = attr_generated->data_float3();
- for (size_t i = vert_num; i < mesh->verts.size(); i++)
- generated[i] = mesh->verts[i] * size - loc;
- }
- else {
- Attribute *attr_generated = mesh->curve_attributes.add(ATTR_STD_GENERATED);
- float3 *generated = attr_generated->data_float3();
-
- for (size_t i = 0; i < mesh->num_curves(); i++) {
- float3 co = mesh->curve_keys[mesh->get_curve(i).first_key];
- generated[i] = co * size - loc;
- }
+ for (size_t i = 0; i < hair->num_curves(); i++) {
+ float3 co = hair->curve_keys[hair->get_curve(i).first_key];
+ generated[i] = co * size - loc;
}
}
}
@@ -1061,32 +572,22 @@ void BlenderSync::sync_curves(
int vcol_num = 0;
for (b_mesh.vertex_colors.begin(l); l != b_mesh.vertex_colors.end(); ++l, vcol_num++) {
- if (!mesh->need_attribute(scene, ustring(l->name().c_str())))
+ if (!hair->need_attribute(scene, ustring(l->name().c_str())))
continue;
- ObtainCacheParticleVcol(mesh, &b_mesh, &b_ob, &CData, !preview, vcol_num);
+ ObtainCacheParticleVcol(hair, &b_mesh, &b_ob, &CData, !preview, vcol_num);
- if (primitive == CURVE_TRIANGLES) {
- Attribute *attr_vcol = mesh->attributes.add(
- ustring(l->name().c_str()), TypeDesc::TypeColor, ATTR_ELEMENT_CORNER_BYTE);
+ Attribute *attr_vcol = hair->attributes.add(
+ ustring(l->name().c_str()), TypeRGBA, ATTR_ELEMENT_CURVE);
- uchar4 *cdata = attr_vcol->data_uchar4();
+ float4 *fdata = attr_vcol->data_float4();
- ExportCurveTriangleVcol(&CData, tri_num * 3, used_res, cdata);
- }
- else {
- Attribute *attr_vcol = mesh->curve_attributes.add(
- ustring(l->name().c_str()), TypeDesc::TypeColor, ATTR_ELEMENT_CURVE);
-
- float3 *fdata = attr_vcol->data_float3();
-
- if (fdata) {
- size_t i = 0;
+ if (fdata) {
+ size_t i = 0;
- /* Encode vertex color using the sRGB curve. */
- for (size_t curve = 0; curve < CData.curve_vcol.size(); curve++) {
- fdata[i++] = color_srgb_to_linear_v3(CData.curve_vcol[curve]);
- }
+ /* Encode vertex color using the sRGB curve. */
+ for (size_t curve = 0; curve < CData.curve_vcol.size(); curve++) {
+ fdata[i++] = color_srgb_to_linear_v4(CData.curve_vcol[curve]);
}
}
}
@@ -1103,42 +604,279 @@ void BlenderSync::sync_curves(
ustring name = ustring(l->name().c_str());
/* UV map */
- if (mesh->need_attribute(scene, name) || mesh->need_attribute(scene, std)) {
+ if (hair->need_attribute(scene, name) || hair->need_attribute(scene, std)) {
Attribute *attr_uv;
- ObtainCacheParticleUV(mesh, &b_mesh, &b_ob, &CData, !preview, uv_num);
+ ObtainCacheParticleUV(hair, &b_mesh, &b_ob, &CData, !preview, uv_num);
- if (primitive == CURVE_TRIANGLES) {
- if (active_render)
- attr_uv = mesh->attributes.add(std, name);
- else
- attr_uv = mesh->attributes.add(name, TypeFloat2, ATTR_ELEMENT_CORNER);
+ if (active_render)
+ attr_uv = hair->attributes.add(std, name);
+ else
+ attr_uv = hair->attributes.add(name, TypeFloat2, ATTR_ELEMENT_CURVE);
- float2 *uv = attr_uv->data_float2();
+ float2 *uv = attr_uv->data_float2();
- ExportCurveTriangleUV(&CData, tri_num * 3, used_res, uv);
+ if (uv) {
+ size_t i = 0;
+
+ for (size_t curve = 0; curve < CData.curve_uv.size(); curve++) {
+ uv[i++] = CData.curve_uv[curve];
+ }
}
- else {
- if (active_render)
- attr_uv = mesh->curve_attributes.add(std, name);
- else
- attr_uv = mesh->curve_attributes.add(name, TypeFloat2, ATTR_ELEMENT_CURVE);
+ }
+ }
+ }
+}
- float2 *uv = attr_uv->data_float2();
+static float4 hair_point_as_float4(BL::HairPoint b_point)
+{
+ float4 mP = float3_to_float4(get_float3(b_point.co()));
+ mP.w = b_point.radius();
+ return mP;
+}
- if (uv) {
- size_t i = 0;
+static float4 interpolate_hair_points(BL::Hair b_hair,
+ const int first_point_index,
+ const int num_points,
+ const float step)
+{
+ const float curve_t = step * (num_points - 1);
+ const int point_a = clamp((int)curve_t, 0, num_points - 1);
+ const int point_b = min(point_a + 1, num_points - 1);
+ const float t = curve_t - (float)point_a;
+ return lerp(hair_point_as_float4(b_hair.points[first_point_index + point_a]),
+ hair_point_as_float4(b_hair.points[first_point_index + point_b]),
+ t);
+}
- for (size_t curve = 0; curve < CData.curve_uv.size(); curve++) {
- uv[i++] = CData.curve_uv[curve];
- }
+static void export_hair_curves(Scene *scene, Hair *hair, BL::Hair b_hair)
+{
+ /* TODO: optimize so we can straight memcpy arrays from Blender? */
+
+ /* Add requested attributes. */
+ Attribute *attr_intercept = NULL;
+ Attribute *attr_random = NULL;
+
+ if (hair->need_attribute(scene, ATTR_STD_CURVE_INTERCEPT)) {
+ attr_intercept = hair->attributes.add(ATTR_STD_CURVE_INTERCEPT);
+ }
+ if (hair->need_attribute(scene, ATTR_STD_CURVE_RANDOM)) {
+ attr_random = hair->attributes.add(ATTR_STD_CURVE_RANDOM);
+ }
+
+ /* Reserve memory. */
+ const int num_keys = b_hair.points.length();
+ const int num_curves = b_hair.curves.length();
+
+ if (num_curves > 0) {
+ VLOG(1) << "Exporting curve segments for hair " << hair->name;
+ }
+
+ hair->reserve_curves(num_curves, num_keys);
+
+ /* Export curves and points. */
+ vector<float> points_length;
+
+ BL::Hair::curves_iterator b_curve_iter;
+ for (b_hair.curves.begin(b_curve_iter); b_curve_iter != b_hair.curves.end(); ++b_curve_iter) {
+ BL::HairCurve b_curve = *b_curve_iter;
+ const int first_point_index = b_curve.first_point_index();
+ const int num_points = b_curve.num_points();
+
+ float3 prev_co = make_float3(0.0f, 0.0f, 0.0f);
+ float length = 0.0f;
+ if (attr_intercept) {
+ points_length.clear();
+ points_length.reserve(num_points);
+ }
+
+ /* Position and radius. */
+ for (int i = 0; i < num_points; i++) {
+ BL::HairPoint b_point = b_hair.points[first_point_index + i];
+
+ const float3 co = get_float3(b_point.co());
+ const float radius = b_point.radius();
+ hair->add_curve_key(co, radius);
+
+ if (attr_intercept) {
+ if (i > 0) {
+ length += len(co - prev_co);
+ points_length.push_back(length);
+ }
+ prev_co = co;
+ }
+ }
+
+ /* Normalized 0..1 attribute along curve. */
+ if (attr_intercept) {
+ for (int i = 0; i < num_points; i++) {
+ attr_intercept->add((length == 0.0f) ? 0.0f : points_length[i] / length);
+ }
+ }
+
+ /* Random number per curve. */
+ if (attr_random != NULL) {
+ attr_random->add(hash_uint2_to_float(b_curve.index(), 0));
+ }
+
+ /* Curve. */
+ const int shader_index = 0;
+ hair->add_curve(first_point_index, shader_index);
+ }
+}
+
+static void export_hair_curves_motion(Hair *hair, BL::Hair b_hair, int motion_step)
+{
+ VLOG(1) << "Exporting curve motion segments for hair " << hair->name << ", motion step "
+ << motion_step;
+
+ /* Find or add attribute. */
+ Attribute *attr_mP = hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
+ bool new_attribute = false;
+
+ if (!attr_mP) {
+ VLOG(1) << "Creating new motion vertex position attribute";
+ attr_mP = hair->attributes.add(ATTR_STD_MOTION_VERTEX_POSITION);
+ new_attribute = true;
+ }
+
+ /* Export motion keys. */
+ const int num_keys = hair->curve_keys.size();
+ float4 *mP = attr_mP->data_float4() + motion_step * num_keys;
+ bool have_motion = false;
+ int num_motion_keys = 0;
+ int curve_index = 0;
+
+ BL::Hair::curves_iterator b_curve_iter;
+ for (b_hair.curves.begin(b_curve_iter); b_curve_iter != b_hair.curves.end(); ++b_curve_iter) {
+ BL::HairCurve b_curve = *b_curve_iter;
+ const int first_point_index = b_curve.first_point_index();
+ const int num_points = b_curve.num_points();
+
+ Hair::Curve curve = hair->get_curve(curve_index);
+ curve_index++;
+
+ if (num_points == curve.num_keys) {
+ /* Number of keys matches. */
+ for (int i = 0; i < num_points; i++) {
+ int point_index = first_point_index + i;
+
+ if (point_index < num_keys) {
+ mP[num_motion_keys] = hair_point_as_float4(b_hair.points[point_index]);
+ num_motion_keys++;
+
+ if (!have_motion) {
+ /* TODO: use epsilon for comparison? Was needed for particles due to
+ * transform, but ideally should not happen anymore. */
+ float4 curve_key = float3_to_float4(hair->curve_keys[i]);
+ curve_key.w = hair->curve_radius[i];
+ have_motion = !(mP[i] == curve_key);
}
}
}
}
+ else {
+ /* Number of keys has changed. Generate an interpolated version
+ * to preserve motion blur. */
+ const float step_size = curve.num_keys > 1 ? 1.0f / (curve.num_keys - 1) : 0.0f;
+ for (int i = 0; i < curve.num_keys; i++) {
+ const float step = i * step_size;
+ mP[num_motion_keys] = interpolate_hair_points(b_hair, first_point_index, num_points, step);
+ num_motion_keys++;
+ }
+ have_motion = true;
+ }
+ }
+
+ /* In case of new attribute, we verify if there really was any motion. */
+ if (new_attribute) {
+ export_hair_motion_validate_attribute(hair, motion_step, num_motion_keys, have_motion);
+ }
+}
+
+/* Hair object. */
+void BlenderSync::sync_hair(Hair *hair, BL::Object &b_ob, bool motion, int motion_step)
+{
+ /* Convert Blender hair to Cycles curves. */
+ BL::Hair b_hair(b_ob.data());
+ if (motion) {
+ export_hair_curves_motion(hair, b_hair, motion_step);
+ }
+ else {
+ export_hair_curves(scene, hair, b_hair);
+ }
+}
+
+void BlenderSync::sync_hair(BL::Depsgraph b_depsgraph,
+ BL::Object b_ob,
+ Hair *hair,
+ const vector<Shader *> &used_shaders)
+{
+ /* Compares curve_keys rather than strands in order to handle quick hair
+ * adjustments in dynamic BVH - other methods could probably do this better. */
+ array<float3> oldcurve_keys;
+ array<float> oldcurve_radius;
+ oldcurve_keys.steal_data(hair->curve_keys);
+ oldcurve_radius.steal_data(hair->curve_radius);
+
+ hair->clear();
+ hair->used_shaders = used_shaders;
+
+ if (view_layer.use_hair) {
+ if (b_ob.type() == BL::Object::type_HAIR) {
+ /* Hair object. */
+ sync_hair(hair, b_ob, false);
+ }
+ else {
+ /* Particle hair. */
+ bool need_undeformed = hair->need_attribute(scene, ATTR_STD_GENERATED);
+ BL::Mesh b_mesh = object_to_mesh(
+ b_data, b_ob, b_depsgraph, need_undeformed, Mesh::SUBDIVISION_NONE);
+
+ if (b_mesh) {
+ sync_particle_hair(hair, b_mesh, b_ob, false);
+ free_object_to_mesh(b_data, b_ob, b_mesh);
+ }
+ }
+ }
+
+ /* tag update */
+ const bool rebuild = ((oldcurve_keys != hair->curve_keys) ||
+ (oldcurve_radius != hair->curve_radius));
+
+ hair->tag_update(scene, rebuild);
+}
+
+void BlenderSync::sync_hair_motion(BL::Depsgraph b_depsgraph,
+ BL::Object b_ob,
+ Hair *hair,
+ int motion_step)
+{
+ /* Skip if nothing exported. */
+ if (hair->num_keys() == 0) {
+ return;
+ }
+
+ /* Export deformed coordinates. */
+ if (ccl::BKE_object_is_deform_modified(b_ob, b_scene, preview)) {
+ if (b_ob.type() == BL::Object::type_HAIR) {
+ /* Hair object. */
+ sync_hair(hair, b_ob, true, motion_step);
+ return;
+ }
+ else {
+ /* Particle hair. */
+ BL::Mesh b_mesh = object_to_mesh(b_data, b_ob, b_depsgraph, false, Mesh::SUBDIVISION_NONE);
+ if (b_mesh) {
+ sync_particle_hair(hair, b_mesh, b_ob, true, motion_step);
+ free_object_to_mesh(b_data, b_ob, b_mesh);
+ return;
+ }
+ }
}
- mesh->compute_bounds();
+ /* No deformation on this frame, copy coordinates if other frames did have it. */
+ hair->copy_center_to_motion_step(motion_step);
}
CCL_NAMESPACE_END
diff --git a/intern/cycles/blender/blender_device.cpp b/intern/cycles/blender/blender_device.cpp
index 98fc0c6dec4..fb9ab9e8c97 100644
--- a/intern/cycles/blender/blender_device.cpp
+++ b/intern/cycles/blender/blender_device.cpp
@@ -17,8 +17,19 @@
#include "blender/blender_device.h"
#include "blender/blender_util.h"
+#include "util/util_foreach.h"
+
CCL_NAMESPACE_BEGIN
+enum ComputeDevice {
+ COMPUTE_DEVICE_CPU = 0,
+ COMPUTE_DEVICE_CUDA = 1,
+ COMPUTE_DEVICE_OPENCL = 2,
+ COMPUTE_DEVICE_OPTIX = 3,
+
+ COMPUTE_DEVICE_NUM
+};
+
int blender_device_threads(BL::Scene &b_scene)
{
BL::RenderSettings b_r = b_scene.render();
@@ -40,7 +51,7 @@ DeviceInfo blender_device_info(BL::Preferences &b_preferences, BL::Scene &b_scen
/* Find network device. */
vector<DeviceInfo> devices = Device::available_devices(DEVICE_MASK_NETWORK);
if (!devices.empty()) {
- device = devices.front();
+ return devices.front();
}
}
else if (get_enum(cscene, "device") == 1) {
@@ -57,13 +68,6 @@ DeviceInfo blender_device_info(BL::Preferences &b_preferences, BL::Scene &b_scen
}
/* Test if we are using GPU devices. */
- enum ComputeDevice {
- COMPUTE_DEVICE_CPU = 0,
- COMPUTE_DEVICE_CUDA = 1,
- COMPUTE_DEVICE_OPENCL = 2,
- COMPUTE_DEVICE_NUM = 3,
- };
-
ComputeDevice compute_device = (ComputeDevice)get_enum(
cpreferences, "compute_device_type", COMPUTE_DEVICE_NUM, COMPUTE_DEVICE_CPU);
@@ -73,6 +77,10 @@ DeviceInfo blender_device_info(BL::Preferences &b_preferences, BL::Scene &b_scen
if (compute_device == COMPUTE_DEVICE_CUDA) {
mask |= DEVICE_MASK_CUDA;
}
+ else if (compute_device == COMPUTE_DEVICE_OPTIX) {
+ /* Cannot use CPU and OptiX device at the same time right now, so replace mask. */
+ mask = DEVICE_MASK_OPTIX;
+ }
else if (compute_device == COMPUTE_DEVICE_OPENCL) {
mask |= DEVICE_MASK_OPENCL;
}
@@ -98,6 +106,10 @@ DeviceInfo blender_device_info(BL::Preferences &b_preferences, BL::Scene &b_scen
device = Device::get_multi_device(used_devices, threads, background);
}
/* Else keep using the CPU device that was set before. */
+
+ if (!get_boolean(cpreferences, "peer_memory")) {
+ device.has_peer_memory = false;
+ }
}
}
diff --git a/intern/cycles/blender/blender_device.h b/intern/cycles/blender/blender_device.h
index fd6c045c966..8d2ecac7483 100644
--- a/intern/cycles/blender/blender_device.h
+++ b/intern/cycles/blender/blender_device.h
@@ -18,9 +18,9 @@
#define __BLENDER_DEVICE_H__
#include "MEM_guardedalloc.h"
-#include "RNA_types.h"
#include "RNA_access.h"
#include "RNA_blender_cpp.h"
+#include "RNA_types.h"
#include "device/device.h"
diff --git a/intern/cycles/blender/blender_geometry.cpp b/intern/cycles/blender/blender_geometry.cpp
new file mode 100644
index 00000000000..f7e4623024d
--- /dev/null
+++ b/intern/cycles/blender/blender_geometry.cpp
@@ -0,0 +1,178 @@
+
+/*
+ * Copyright 2011-2013 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "render/curves.h"
+#include "render/hair.h"
+#include "render/mesh.h"
+#include "render/object.h"
+
+#include "blender/blender_sync.h"
+#include "blender/blender_util.h"
+
+#include "util/util_foreach.h"
+
+CCL_NAMESPACE_BEGIN
+
+Geometry *BlenderSync::sync_geometry(BL::Depsgraph &b_depsgraph,
+ BL::Object &b_ob,
+ BL::Object &b_ob_instance,
+ bool object_updated,
+ bool use_particle_hair)
+{
+ /* Test if we can instance or if the object is modified. */
+ BL::ID b_ob_data = b_ob.data();
+ BL::ID b_key_id = (BKE_object_is_modified(b_ob)) ? b_ob_instance : b_ob_data;
+ GeometryKey key(b_key_id.ptr.data, use_particle_hair);
+ BL::Material material_override = view_layer.material_override;
+ Shader *default_shader = (b_ob.type() == BL::Object::type_VOLUME) ? scene->default_volume :
+ scene->default_surface;
+ Geometry::Type geom_type = (b_ob.type() == BL::Object::type_HAIR || use_particle_hair) ?
+ Geometry::HAIR :
+ Geometry::MESH;
+
+ /* Find shader indices. */
+ vector<Shader *> used_shaders;
+
+ BL::Object::material_slots_iterator slot;
+ for (b_ob.material_slots.begin(slot); slot != b_ob.material_slots.end(); ++slot) {
+ if (material_override) {
+ find_shader(material_override, used_shaders, default_shader);
+ }
+ else {
+ BL::ID b_material(slot->material());
+ find_shader(b_material, used_shaders, default_shader);
+ }
+ }
+
+ if (used_shaders.size() == 0) {
+ if (material_override)
+ find_shader(material_override, used_shaders, default_shader);
+ else
+ used_shaders.push_back(default_shader);
+ }
+
+ /* Test if we need to sync. */
+ Geometry *geom = geometry_map.find(key);
+ bool sync = true;
+ if (geom == NULL) {
+ /* Add new geometry if it did not exist yet. */
+ if (geom_type == Geometry::HAIR) {
+ geom = new Hair();
+ }
+ else {
+ geom = new Mesh();
+ }
+ geometry_map.add(key, geom);
+ }
+ else {
+ /* Test if we need to update existing geometry. */
+ sync = geometry_map.update(geom, b_key_id);
+ }
+
+ if (!sync) {
+ /* If transform was applied to geometry, need full update. */
+ if (object_updated && geom->transform_applied) {
+ ;
+ }
+ /* Test if shaders changed, these can be object level so geometry
+ * does not get tagged for recalc. */
+ else if (geom->used_shaders != used_shaders) {
+ ;
+ }
+ else {
+ /* Even if not tagged for recalc, we may need to sync anyway
+ * because the shader needs different geometry attributes. */
+ bool attribute_recalc = false;
+
+ foreach (Shader *shader, geom->used_shaders) {
+ if (shader->need_update_geometry) {
+ attribute_recalc = true;
+ }
+ }
+
+ if (!attribute_recalc) {
+ return geom;
+ }
+ }
+ }
+
+ /* Ensure we only sync instanced geometry once. */
+ if (geometry_synced.find(geom) != geometry_synced.end()) {
+ return geom;
+ }
+
+ progress.set_sync_status("Synchronizing object", b_ob.name());
+
+ geometry_synced.insert(geom);
+
+ geom->name = ustring(b_ob_data.name().c_str());
+
+ if (b_ob.type() == BL::Object::type_HAIR || use_particle_hair) {
+ Hair *hair = static_cast<Hair *>(geom);
+ sync_hair(b_depsgraph, b_ob, hair, used_shaders);
+ }
+ else if (b_ob.type() == BL::Object::type_VOLUME || object_fluid_gas_domain_find(b_ob)) {
+ Mesh *mesh = static_cast<Mesh *>(geom);
+ sync_volume(b_ob, mesh, used_shaders);
+ }
+ else {
+ Mesh *mesh = static_cast<Mesh *>(geom);
+ sync_mesh(b_depsgraph, b_ob, mesh, used_shaders);
+ }
+
+ return geom;
+}
+
+void BlenderSync::sync_geometry_motion(BL::Depsgraph &b_depsgraph,
+ BL::Object &b_ob,
+ Object *object,
+ float motion_time,
+ bool use_particle_hair)
+{
+ /* Ensure we only sync instanced geometry once. */
+ Geometry *geom = object->geometry;
+
+ if (geometry_motion_synced.find(geom) != geometry_motion_synced.end())
+ return;
+
+ geometry_motion_synced.insert(geom);
+
+ /* Ensure we only motion sync geometry that also had geometry synced, to avoid
+ * unnecessary work and to ensure that its attributes were clear. */
+ if (geometry_synced.find(geom) == geometry_synced.end())
+ return;
+
+ /* Find time matching motion step required by geometry. */
+ int motion_step = geom->motion_step(motion_time);
+ if (motion_step < 0) {
+ return;
+ }
+
+ if (b_ob.type() == BL::Object::type_HAIR || use_particle_hair) {
+ Hair *hair = static_cast<Hair *>(geom);
+ sync_hair_motion(b_depsgraph, b_ob, hair, motion_step);
+ }
+ else if (b_ob.type() == BL::Object::type_VOLUME || object_fluid_gas_domain_find(b_ob)) {
+ /* No volume motion blur support yet. */
+ }
+ else {
+ Mesh *mesh = static_cast<Mesh *>(geom);
+ sync_mesh_motion(b_depsgraph, b_ob, mesh, motion_step);
+ }
+}
+
+CCL_NAMESPACE_END
diff --git a/intern/cycles/blender/blender_id_map.h b/intern/cycles/blender/blender_id_map.h
new file mode 100644
index 00000000000..b5f6aaa67a8
--- /dev/null
+++ b/intern/cycles/blender/blender_id_map.h
@@ -0,0 +1,299 @@
+/*
+ * Copyright 2011-2013 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BLENDER_ID_MAP_H__
+#define __BLENDER_ID_MAP_H__
+
+#include <string.h>
+
+#include "util/util_map.h"
+#include "util/util_set.h"
+#include "util/util_vector.h"
+
+CCL_NAMESPACE_BEGIN
+
+/* ID Map
+ *
+ * Utility class to map between Blender datablocks and Cycles data structures,
+ * and keep track of recalc tags from the dependency graph. */
+
+template<typename K, typename T> class id_map {
+ public:
+ id_map(vector<T *> *scene_data_)
+ {
+ scene_data = scene_data_;
+ }
+
+ T *find(const BL::ID &id)
+ {
+ return find(id.ptr.owner_id);
+ }
+
+ T *find(const K &key)
+ {
+ if (b_map.find(key) != b_map.end()) {
+ T *data = b_map[key];
+ return data;
+ }
+
+ return NULL;
+ }
+
+ void set_recalc(const BL::ID &id)
+ {
+ b_recalc.insert(id.ptr.data);
+ }
+
+ void set_recalc(void *id_ptr)
+ {
+ b_recalc.insert(id_ptr);
+ }
+
+ bool has_recalc()
+ {
+ return !(b_recalc.empty());
+ }
+
+ void pre_sync()
+ {
+ used_set.clear();
+ }
+
+ /* Add new data. */
+ void add(const K &key, T *data)
+ {
+ assert(find(key) == NULL);
+ scene_data->push_back(data);
+ b_map[key] = data;
+ used(data);
+ }
+
+ /* Update existing data. */
+ bool update(T *data, const BL::ID &id)
+ {
+ return update(data, id, id);
+ }
+ bool update(T *data, const BL::ID &id, const BL::ID &parent)
+ {
+ bool recalc = (b_recalc.find(id.ptr.data) != b_recalc.end());
+ if (parent.ptr.data && parent.ptr.data != id.ptr.data) {
+ recalc = recalc || (b_recalc.find(parent.ptr.data) != b_recalc.end());
+ }
+ used(data);
+ return recalc;
+ }
+
+ /* Combined add and update as needed. */
+ bool add_or_update(T **r_data, const BL::ID &id)
+ {
+ return add_or_update(r_data, id, id, id.ptr.owner_id);
+ }
+ bool add_or_update(T **r_data, const BL::ID &id, const K &key)
+ {
+ return add_or_update(r_data, id, id, key);
+ }
+ bool add_or_update(T **r_data, const BL::ID &id, const BL::ID &parent, const K &key)
+ {
+ T *data = find(key);
+ bool recalc;
+
+ if (!data) {
+ /* Add data if it didn't exist yet. */
+ data = new T();
+ add(key, data);
+ recalc = true;
+ }
+ else {
+ /* check if updated needed. */
+ recalc = update(data, id, parent);
+ }
+
+ *r_data = data;
+ return recalc;
+ }
+
+ /* Combined add or update for convenience. */
+
+ bool is_used(const K &key)
+ {
+ T *data = find(key);
+ return (data) ? used_set.find(data) != used_set.end() : false;
+ }
+
+ void used(T *data)
+ {
+ /* tag data as still in use */
+ used_set.insert(data);
+ }
+
+ void set_default(T *data)
+ {
+ b_map[NULL] = data;
+ }
+
+ bool post_sync(bool do_delete = true)
+ {
+ /* remove unused data */
+ vector<T *> new_scene_data;
+ typename vector<T *>::iterator it;
+ bool deleted = false;
+
+ for (it = scene_data->begin(); it != scene_data->end(); it++) {
+ T *data = *it;
+
+ if (do_delete && used_set.find(data) == used_set.end()) {
+ delete data;
+ deleted = true;
+ }
+ else
+ new_scene_data.push_back(data);
+ }
+
+ *scene_data = new_scene_data;
+
+ /* update mapping */
+ map<K, T *> new_map;
+ typedef pair<const K, T *> TMapPair;
+ typename map<K, T *>::iterator jt;
+
+ for (jt = b_map.begin(); jt != b_map.end(); jt++) {
+ TMapPair &pair = *jt;
+
+ if (used_set.find(pair.second) != used_set.end())
+ new_map[pair.first] = pair.second;
+ }
+
+ used_set.clear();
+ b_recalc.clear();
+ b_map = new_map;
+
+ return deleted;
+ }
+
+ const map<K, T *> &key_to_scene_data()
+ {
+ return b_map;
+ }
+
+ protected:
+ vector<T *> *scene_data;
+ map<K, T *> b_map;
+ set<T *> used_set;
+ set<void *> b_recalc;
+};
+
+/* Object Key
+ *
+ * To uniquely identify instances, we use the parent, object and persistent instance ID.
+ * We also export separate object for a mesh and its particle hair. */
+
+enum { OBJECT_PERSISTENT_ID_SIZE = 8 /* MAX_DUPLI_RECUR in Blender. */ };
+
+struct ObjectKey {
+ void *parent;
+ int id[OBJECT_PERSISTENT_ID_SIZE];
+ void *ob;
+ bool use_particle_hair;
+
+ ObjectKey(void *parent_, int id_[OBJECT_PERSISTENT_ID_SIZE], void *ob_, bool use_particle_hair_)
+ : parent(parent_), ob(ob_), use_particle_hair(use_particle_hair_)
+ {
+ if (id_)
+ memcpy(id, id_, sizeof(id));
+ else
+ memset(id, 0, sizeof(id));
+ }
+
+ bool operator<(const ObjectKey &k) const
+ {
+ if (ob < k.ob) {
+ return true;
+ }
+ else if (ob == k.ob) {
+ if (parent < k.parent) {
+ return true;
+ }
+ else if (parent == k.parent) {
+ if (use_particle_hair < k.use_particle_hair) {
+ return true;
+ }
+ else if (use_particle_hair == k.use_particle_hair) {
+ return memcmp(id, k.id, sizeof(id)) < 0;
+ }
+ }
+ }
+
+ return false;
+ }
+};
+
+/* Geometry Key
+ *
+ * We export separate geometry for a mesh and its particle hair, so key needs to
+ * distinguish between them. */
+
+struct GeometryKey {
+ void *id;
+ bool use_particle_hair;
+
+ GeometryKey(void *id, bool use_particle_hair) : id(id), use_particle_hair(use_particle_hair)
+ {
+ }
+
+ bool operator<(const GeometryKey &k) const
+ {
+ if (id < k.id) {
+ return true;
+ }
+ else if (id == k.id) {
+ if (use_particle_hair < k.use_particle_hair) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+};
+
+/* Particle System Key */
+
+struct ParticleSystemKey {
+ void *ob;
+ int id[OBJECT_PERSISTENT_ID_SIZE];
+
+ ParticleSystemKey(void *ob_, int id_[OBJECT_PERSISTENT_ID_SIZE]) : ob(ob_)
+ {
+ if (id_)
+ memcpy(id, id_, sizeof(id));
+ else
+ memset(id, 0, sizeof(id));
+ }
+
+ bool operator<(const ParticleSystemKey &k) const
+ {
+ /* first id is particle index, we don't compare that */
+ if (ob < k.ob)
+ return true;
+ else if (ob == k.ob)
+ return memcmp(id + 1, k.id + 1, sizeof(int) * (OBJECT_PERSISTENT_ID_SIZE - 1)) < 0;
+
+ return false;
+ }
+};
+
+CCL_NAMESPACE_END
+
+#endif /* __BLENDER_ID_MAP_H__ */
diff --git a/intern/cycles/blender/blender_image.cpp b/intern/cycles/blender/blender_image.cpp
new file mode 100644
index 00000000000..459dc1779fb
--- /dev/null
+++ b/intern/cycles/blender/blender_image.cpp
@@ -0,0 +1,220 @@
+/*
+ * Copyright 2011-2013 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "blender/blender_image.h"
+#include "blender/blender_session.h"
+#include "blender/blender_util.h"
+
+CCL_NAMESPACE_BEGIN
+
+/* Packed Images */
+
+BlenderImageLoader::BlenderImageLoader(BL::Image b_image, int frame)
+ : b_image(b_image), frame(frame), free_cache(!b_image.has_data())
+{
+}
+
+bool BlenderImageLoader::load_metadata(ImageMetaData &metadata)
+{
+ metadata.width = b_image.size()[0];
+ metadata.height = b_image.size()[1];
+ metadata.depth = 1;
+ metadata.channels = b_image.channels();
+
+ if (b_image.is_float()) {
+ if (metadata.channels == 1) {
+ metadata.type = IMAGE_DATA_TYPE_FLOAT;
+ }
+ else if (metadata.channels == 4) {
+ metadata.type = IMAGE_DATA_TYPE_FLOAT4;
+ }
+ else {
+ return false;
+ }
+
+ /* Float images are already converted on the Blender side,
+ * no need to do anything in Cycles. */
+ metadata.colorspace = u_colorspace_raw;
+ }
+ else {
+ if (metadata.channels == 1) {
+ metadata.type = IMAGE_DATA_TYPE_BYTE;
+ }
+ else if (metadata.channels == 4) {
+ metadata.type = IMAGE_DATA_TYPE_BYTE4;
+ }
+ else {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool BlenderImageLoader::load_pixels(const ImageMetaData &metadata,
+ void *pixels,
+ const size_t pixels_size,
+ const bool associate_alpha)
+{
+ const size_t num_pixels = ((size_t)metadata.width) * metadata.height;
+ const int channels = metadata.channels;
+ const int tile = 0; /* TODO(lukas): Support tiles here? */
+
+ if (b_image.is_float()) {
+ /* image data */
+ float *image_pixels;
+ image_pixels = image_get_float_pixels_for_frame(b_image, frame, tile);
+
+ if (image_pixels && num_pixels * channels == pixels_size) {
+ memcpy(pixels, image_pixels, pixels_size * sizeof(float));
+ }
+ else {
+ if (channels == 1) {
+ memset(pixels, 0, num_pixels * sizeof(float));
+ }
+ else {
+ const size_t num_pixels_safe = pixels_size / channels;
+ float *fp = (float *)pixels;
+ for (int i = 0; i < num_pixels_safe; i++, fp += channels) {
+ fp[0] = 1.0f;
+ fp[1] = 0.0f;
+ fp[2] = 1.0f;
+ if (channels == 4) {
+ fp[3] = 1.0f;
+ }
+ }
+ }
+ }
+
+ if (image_pixels) {
+ MEM_freeN(image_pixels);
+ }
+ }
+ else {
+ unsigned char *image_pixels = image_get_pixels_for_frame(b_image, frame, tile);
+
+ if (image_pixels && num_pixels * channels == pixels_size) {
+ memcpy(pixels, image_pixels, pixels_size * sizeof(unsigned char));
+ }
+ else {
+ if (channels == 1) {
+ memset(pixels, 0, pixels_size * sizeof(unsigned char));
+ }
+ else {
+ const size_t num_pixels_safe = pixels_size / channels;
+ unsigned char *cp = (unsigned char *)pixels;
+ for (size_t i = 0; i < num_pixels_safe; i++, cp += channels) {
+ cp[0] = 255;
+ cp[1] = 0;
+ cp[2] = 255;
+ if (channels == 4) {
+ cp[3] = 255;
+ }
+ }
+ }
+ }
+
+ if (image_pixels) {
+ MEM_freeN(image_pixels);
+ }
+
+ if (associate_alpha) {
+ /* Premultiply, byte images are always straight for Blender. */
+ unsigned char *cp = (unsigned char *)pixels;
+ for (size_t i = 0; i < num_pixels; i++, cp += channels) {
+ cp[0] = (cp[0] * cp[3]) >> 8;
+ cp[1] = (cp[1] * cp[3]) >> 8;
+ cp[2] = (cp[2] * cp[3]) >> 8;
+ }
+ }
+ }
+
+ /* Free image buffers to save memory during render. */
+ if (free_cache) {
+ b_image.buffers_free();
+ }
+
+ return true;
+}
+
+string BlenderImageLoader::name() const
+{
+ return BL::Image(b_image).name();
+}
+
+bool BlenderImageLoader::equals(const ImageLoader &other) const
+{
+ const BlenderImageLoader &other_loader = (const BlenderImageLoader &)other;
+ return b_image == other_loader.b_image && frame == other_loader.frame;
+}
+
+/* Point Density */
+
+BlenderPointDensityLoader::BlenderPointDensityLoader(BL::Depsgraph b_depsgraph,
+ BL::ShaderNodeTexPointDensity b_node)
+ : b_depsgraph(b_depsgraph), b_node(b_node)
+{
+}
+
+bool BlenderPointDensityLoader::load_metadata(ImageMetaData &metadata)
+{
+ metadata.channels = 4;
+ metadata.width = b_node.resolution();
+ metadata.height = metadata.width;
+ metadata.depth = metadata.width;
+ metadata.type = IMAGE_DATA_TYPE_FLOAT4;
+ return true;
+}
+
+bool BlenderPointDensityLoader::load_pixels(const ImageMetaData &,
+ void *pixels,
+ const size_t,
+ const bool)
+{
+ int length;
+ b_node.calc_point_density(b_depsgraph, &length, (float **)&pixels);
+ return true;
+}
+
+void BlenderSession::builtin_images_load()
+{
+ /* Force builtin images to be loaded along with Blender data sync. This
+ * is needed because we may be reading from depsgraph evaluated data which
+ * can be freed by Blender before Cycles reads it.
+ *
+ * TODO: the assumption that no further access to builtin image data will
+ * happen is really weak, and likely to break in the future. We should find
+ * a better solution to hand over the data directly to the image manager
+ * instead of through callbacks whose timing is difficult to control. */
+ ImageManager *manager = session->scene->image_manager;
+ Device *device = session->device;
+ manager->device_load_builtin(device, session->scene, session->progress);
+}
+
+string BlenderPointDensityLoader::name() const
+{
+ return BL::ShaderNodeTexPointDensity(b_node).name();
+}
+
+bool BlenderPointDensityLoader::equals(const ImageLoader &other) const
+{
+ const BlenderPointDensityLoader &other_loader = (const BlenderPointDensityLoader &)other;
+ return b_node == other_loader.b_node && b_depsgraph == other_loader.b_depsgraph;
+}
+
+CCL_NAMESPACE_END
diff --git a/intern/cycles/blender/blender_image.h b/intern/cycles/blender/blender_image.h
new file mode 100644
index 00000000000..b58a159a6ba
--- /dev/null
+++ b/intern/cycles/blender/blender_image.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2011-2020 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BLENDER_IMAGE_H__
+#define __BLENDER_IMAGE_H__
+
+#include "RNA_blender_cpp.h"
+
+#include "render/image.h"
+
+CCL_NAMESPACE_BEGIN
+
+class BlenderImageLoader : public ImageLoader {
+ public:
+ BlenderImageLoader(BL::Image b_image, int frame);
+
+ bool load_metadata(ImageMetaData &metadata) override;
+ bool load_pixels(const ImageMetaData &metadata,
+ void *pixels,
+ const size_t pixels_size,
+ const bool associate_alpha) override;
+ string name() const override;
+ bool equals(const ImageLoader &other) const override;
+
+ BL::Image b_image;
+ int frame;
+ bool free_cache;
+};
+
+class BlenderPointDensityLoader : public ImageLoader {
+ public:
+ BlenderPointDensityLoader(BL::Depsgraph depsgraph, BL::ShaderNodeTexPointDensity b_node);
+
+ bool load_metadata(ImageMetaData &metadata) override;
+ bool load_pixels(const ImageMetaData &metadata,
+ void *pixels,
+ const size_t pixels_size,
+ const bool associate_alpha) override;
+ string name() const override;
+ bool equals(const ImageLoader &other) const override;
+
+ BL::Depsgraph b_depsgraph;
+ BL::ShaderNodeTexPointDensity b_node;
+};
+
+CCL_NAMESPACE_END
+
+#endif /* __BLENDER_IMAGE_H__ */
diff --git a/intern/cycles/blender/blender_light.cpp b/intern/cycles/blender/blender_light.cpp
new file mode 100644
index 00000000000..6f95821e31e
--- /dev/null
+++ b/intern/cycles/blender/blender_light.cpp
@@ -0,0 +1,212 @@
+
+
+/*
+ * Copyright 2011-2013 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "render/light.h"
+
+#include "blender/blender_sync.h"
+#include "blender/blender_util.h"
+
+#include "util/util_hash.h"
+
+CCL_NAMESPACE_BEGIN
+
+void BlenderSync::sync_light(BL::Object &b_parent,
+ int persistent_id[OBJECT_PERSISTENT_ID_SIZE],
+ BL::Object &b_ob,
+ BL::Object &b_ob_instance,
+ int random_id,
+ Transform &tfm,
+ bool *use_portal)
+{
+ /* test if we need to sync */
+ Light *light;
+ ObjectKey key(b_parent, persistent_id, b_ob_instance, false);
+ BL::Light b_light(b_ob.data());
+
+ /* Update if either object or light data changed. */
+ if (!light_map.add_or_update(&light, b_ob, b_parent, key)) {
+ Shader *shader;
+ if (!shader_map.add_or_update(&shader, b_light)) {
+ if (light->is_portal)
+ *use_portal = true;
+ return;
+ }
+ }
+
+ /* type */
+ switch (b_light.type()) {
+ case BL::Light::type_POINT: {
+ BL::PointLight b_point_light(b_light);
+ light->size = b_point_light.shadow_soft_size();
+ light->type = LIGHT_POINT;
+ break;
+ }
+ case BL::Light::type_SPOT: {
+ BL::SpotLight b_spot_light(b_light);
+ light->size = b_spot_light.shadow_soft_size();
+ light->type = LIGHT_SPOT;
+ light->spot_angle = b_spot_light.spot_size();
+ light->spot_smooth = b_spot_light.spot_blend();
+ break;
+ }
+ /* Hemi were removed from 2.8 */
+ // case BL::Light::type_HEMI: {
+ // light->type = LIGHT_DISTANT;
+ // light->size = 0.0f;
+ // break;
+ // }
+ case BL::Light::type_SUN: {
+ BL::SunLight b_sun_light(b_light);
+ light->angle = b_sun_light.angle();
+ light->type = LIGHT_DISTANT;
+ break;
+ }
+ case BL::Light::type_AREA: {
+ BL::AreaLight b_area_light(b_light);
+ light->size = 1.0f;
+ light->axisu = transform_get_column(&tfm, 0);
+ light->axisv = transform_get_column(&tfm, 1);
+ light->sizeu = b_area_light.size();
+ switch (b_area_light.shape()) {
+ case BL::AreaLight::shape_SQUARE:
+ light->sizev = light->sizeu;
+ light->round = false;
+ break;
+ case BL::AreaLight::shape_RECTANGLE:
+ light->sizev = b_area_light.size_y();
+ light->round = false;
+ break;
+ case BL::AreaLight::shape_DISK:
+ light->sizev = light->sizeu;
+ light->round = true;
+ break;
+ case BL::AreaLight::shape_ELLIPSE:
+ light->sizev = b_area_light.size_y();
+ light->round = true;
+ break;
+ }
+ light->type = LIGHT_AREA;
+ break;
+ }
+ }
+
+ /* strength */
+ light->strength = get_float3(b_light.color());
+ light->strength *= BL::PointLight(b_light).energy();
+
+ /* location and (inverted!) direction */
+ light->co = transform_get_column(&tfm, 3);
+ light->dir = -transform_get_column(&tfm, 2);
+ light->tfm = tfm;
+
+ /* shader */
+ vector<Shader *> used_shaders;
+ find_shader(b_light, used_shaders, scene->default_light);
+ light->shader = used_shaders[0];
+
+ /* shadow */
+ PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
+ PointerRNA clight = RNA_pointer_get(&b_light.ptr, "cycles");
+ light->cast_shadow = get_boolean(clight, "cast_shadow");
+ light->use_mis = get_boolean(clight, "use_multiple_importance_sampling");
+
+ int samples = get_int(clight, "samples");
+ if (get_boolean(cscene, "use_square_samples"))
+ light->samples = samples * samples;
+ else
+ light->samples = samples;
+
+ light->max_bounces = get_int(clight, "max_bounces");
+
+ if (b_ob != b_ob_instance) {
+ light->random_id = random_id;
+ }
+ else {
+ light->random_id = hash_uint2(hash_string(b_ob.name().c_str()), 0);
+ }
+
+ if (light->type == LIGHT_AREA)
+ light->is_portal = get_boolean(clight, "is_portal");
+ else
+ light->is_portal = false;
+
+ if (light->is_portal)
+ *use_portal = true;
+
+ /* visibility */
+ uint visibility = object_ray_visibility(b_ob);
+ light->use_diffuse = (visibility & PATH_RAY_DIFFUSE) != 0;
+ light->use_glossy = (visibility & PATH_RAY_GLOSSY) != 0;
+ light->use_transmission = (visibility & PATH_RAY_TRANSMIT) != 0;
+ light->use_scatter = (visibility & PATH_RAY_VOLUME_SCATTER) != 0;
+
+ /* tag */
+ light->tag_update(scene);
+}
+
+void BlenderSync::sync_background_light(BL::SpaceView3D &b_v3d, bool use_portal)
+{
+ BL::World b_world = b_scene.world();
+
+ if (b_world) {
+ PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
+ PointerRNA cworld = RNA_pointer_get(&b_world.ptr, "cycles");
+
+ enum SamplingMethod { SAMPLING_NONE = 0, SAMPLING_AUTOMATIC, SAMPLING_MANUAL, SAMPLING_NUM };
+ int sampling_method = get_enum(cworld, "sampling_method", SAMPLING_NUM, SAMPLING_AUTOMATIC);
+ bool sample_as_light = (sampling_method != SAMPLING_NONE);
+
+ if (sample_as_light || use_portal) {
+ /* test if we need to sync */
+ Light *light;
+ ObjectKey key(b_world, 0, b_world, false);
+
+ if (light_map.add_or_update(&light, b_world, b_world, key) || world_recalc ||
+ b_world.ptr.data != world_map) {
+ light->type = LIGHT_BACKGROUND;
+ if (sampling_method == SAMPLING_MANUAL) {
+ light->map_resolution = get_int(cworld, "sample_map_resolution");
+ }
+ else {
+ light->map_resolution = 0;
+ }
+ light->shader = scene->default_background;
+ light->use_mis = sample_as_light;
+ light->max_bounces = get_int(cworld, "max_bounces");
+
+ /* force enable light again when world is resynced */
+ light->is_enabled = true;
+
+ int samples = get_int(cworld, "samples");
+ if (get_boolean(cscene, "use_square_samples"))
+ light->samples = samples * samples;
+ else
+ light->samples = samples;
+
+ light->tag_update(scene);
+ light_map.set_recalc(b_world);
+ }
+ }
+ }
+
+ world_map = b_world.ptr.data;
+ world_recalc = false;
+ viewport_parameters = BlenderViewportParameters(b_v3d);
+}
+
+CCL_NAMESPACE_END
diff --git a/intern/cycles/blender/blender_mesh.cpp b/intern/cycles/blender/blender_mesh.cpp
index de594f4fb6c..49407799fcd 100644
--- a/intern/cycles/blender/blender_mesh.cpp
+++ b/intern/cycles/blender/blender_mesh.cpp
@@ -14,20 +14,23 @@
* limitations under the License.
*/
+#include "render/camera.h"
+#include "render/colorspace.h"
#include "render/mesh.h"
#include "render/object.h"
#include "render/scene.h"
-#include "render/camera.h"
-#include "blender/blender_sync.h"
#include "blender/blender_session.h"
+#include "blender/blender_sync.h"
#include "blender/blender_util.h"
#include "subd/subd_patch.h"
#include "subd/subd_split.h"
#include "util/util_algorithm.h"
+#include "util/util_disjoint_set.h"
#include "util/util_foreach.h"
+#include "util/util_hash.h"
#include "util/util_logging.h"
#include "util/util_math.h"
@@ -275,102 +278,99 @@ static void mikk_compute_tangents(
genTangSpaceDefault(&context);
}
-/* Create Volume Attribute */
-
-static void create_mesh_volume_attribute(
- BL::Object &b_ob, Mesh *mesh, ImageManager *image_manager, AttributeStandard std, float frame)
+/* Create sculpt vertex color attributes. */
+static void attr_create_sculpt_vertex_color(Scene *scene,
+ Mesh *mesh,
+ BL::Mesh &b_mesh,
+ bool subdivision)
{
- BL::SmokeDomainSettings b_domain = object_smoke_domain_find(b_ob);
+ BL::Mesh::sculpt_vertex_colors_iterator l;
- if (!b_domain)
- return;
+ for (b_mesh.sculpt_vertex_colors.begin(l); l != b_mesh.sculpt_vertex_colors.end(); ++l) {
+ const bool active_render = l->active_render();
+ AttributeStandard vcol_std = (active_render) ? ATTR_STD_VERTEX_COLOR : ATTR_STD_NONE;
+ ustring vcol_name = ustring(l->name().c_str());
- mesh->volume_isovalue = b_domain.clipping();
-
- Attribute *attr = mesh->attributes.add(std);
- VoxelAttribute *volume_data = attr->data_voxel();
- ImageMetaData metadata;
- bool animated = false;
- bool use_alpha = true;
-
- volume_data->manager = image_manager;
- volume_data->slot = image_manager->add_image(Attribute::standard_name(std),
- b_ob.ptr.data,
- animated,
- frame,
- INTERPOLATION_LINEAR,
- EXTENSION_CLIP,
- use_alpha,
- metadata);
-}
+ const bool need_vcol = mesh->need_attribute(scene, vcol_name) ||
+ mesh->need_attribute(scene, vcol_std);
-static void create_mesh_volume_attributes(Scene *scene, BL::Object &b_ob, Mesh *mesh, float frame)
-{
- /* for smoke volume rendering */
- if (mesh->need_attribute(scene, ATTR_STD_VOLUME_DENSITY))
- create_mesh_volume_attribute(b_ob, mesh, scene->image_manager, ATTR_STD_VOLUME_DENSITY, frame);
- if (mesh->need_attribute(scene, ATTR_STD_VOLUME_COLOR))
- create_mesh_volume_attribute(b_ob, mesh, scene->image_manager, ATTR_STD_VOLUME_COLOR, frame);
- if (mesh->need_attribute(scene, ATTR_STD_VOLUME_FLAME))
- create_mesh_volume_attribute(b_ob, mesh, scene->image_manager, ATTR_STD_VOLUME_FLAME, frame);
- if (mesh->need_attribute(scene, ATTR_STD_VOLUME_HEAT))
- create_mesh_volume_attribute(b_ob, mesh, scene->image_manager, ATTR_STD_VOLUME_HEAT, frame);
- if (mesh->need_attribute(scene, ATTR_STD_VOLUME_TEMPERATURE))
- create_mesh_volume_attribute(
- b_ob, mesh, scene->image_manager, ATTR_STD_VOLUME_TEMPERATURE, frame);
- if (mesh->need_attribute(scene, ATTR_STD_VOLUME_VELOCITY))
- create_mesh_volume_attribute(
- b_ob, mesh, scene->image_manager, ATTR_STD_VOLUME_VELOCITY, frame);
+ if (!need_vcol) {
+ continue;
+ }
+
+ AttributeSet &attributes = (subdivision) ? mesh->subd_attributes : mesh->attributes;
+ Attribute *vcol_attr = attributes.add(vcol_name, TypeRGBA, ATTR_ELEMENT_VERTEX);
+ vcol_attr->std = vcol_std;
+
+ float4 *cdata = vcol_attr->data_float4();
+ int numverts = b_mesh.vertices.length();
+
+ for (int i = 0; i < numverts; i++) {
+ *(cdata++) = get_float4(l->data[i].color());
+ }
+ }
}
/* Create vertex color attributes. */
static void attr_create_vertex_color(Scene *scene, Mesh *mesh, BL::Mesh &b_mesh, bool subdivision)
{
- if (subdivision) {
- BL::Mesh::vertex_colors_iterator l;
+ BL::Mesh::vertex_colors_iterator l;
+
+ for (b_mesh.vertex_colors.begin(l); l != b_mesh.vertex_colors.end(); ++l) {
+ const bool active_render = l->active_render();
+ AttributeStandard vcol_std = (active_render) ? ATTR_STD_VERTEX_COLOR : ATTR_STD_NONE;
+ ustring vcol_name = ustring(l->name().c_str());
- for (b_mesh.vertex_colors.begin(l); l != b_mesh.vertex_colors.end(); ++l) {
- if (!mesh->need_attribute(scene, ustring(l->name().c_str())))
- continue;
+ const bool need_vcol = mesh->need_attribute(scene, vcol_name) ||
+ mesh->need_attribute(scene, vcol_std);
- Attribute *attr = mesh->subd_attributes.add(
- ustring(l->name().c_str()), TypeDesc::TypeColor, ATTR_ELEMENT_CORNER_BYTE);
+ if (!need_vcol) {
+ continue;
+ }
+
+ Attribute *vcol_attr = NULL;
+
+ if (subdivision) {
+ if (active_render) {
+ vcol_attr = mesh->subd_attributes.add(vcol_std, vcol_name);
+ }
+ else {
+ vcol_attr = mesh->subd_attributes.add(vcol_name, TypeRGBA, ATTR_ELEMENT_CORNER_BYTE);
+ }
BL::Mesh::polygons_iterator p;
- uchar4 *cdata = attr->data_uchar4();
+ uchar4 *cdata = vcol_attr->data_uchar4();
for (b_mesh.polygons.begin(p); p != b_mesh.polygons.end(); ++p) {
int n = p->loop_total();
for (int i = 0; i < n; i++) {
- float3 color = get_float3(l->data[p->loop_start() + i].color());
+ float4 color = get_float4(l->data[p->loop_start() + i].color());
/* Compress/encode vertex color using the sRGB curve. */
- *(cdata++) = color_float_to_byte(color_srgb_to_linear_v3(color));
+ *(cdata++) = color_float4_to_uchar4(color_srgb_to_linear_v4(color));
}
}
}
- }
- else {
- BL::Mesh::vertex_colors_iterator l;
- for (b_mesh.vertex_colors.begin(l); l != b_mesh.vertex_colors.end(); ++l) {
- if (!mesh->need_attribute(scene, ustring(l->name().c_str())))
- continue;
-
- Attribute *attr = mesh->attributes.add(
- ustring(l->name().c_str()), TypeDesc::TypeColor, ATTR_ELEMENT_CORNER_BYTE);
+ else {
+ if (active_render) {
+ vcol_attr = mesh->attributes.add(vcol_std, vcol_name);
+ }
+ else {
+ vcol_attr = mesh->attributes.add(vcol_name, TypeRGBA, ATTR_ELEMENT_CORNER_BYTE);
+ }
BL::Mesh::loop_triangles_iterator t;
- uchar4 *cdata = attr->data_uchar4();
+ uchar4 *cdata = vcol_attr->data_uchar4();
for (b_mesh.loop_triangles.begin(t); t != b_mesh.loop_triangles.end(); ++t) {
int3 li = get_int3(t->loops());
- float3 c1 = get_float3(l->data[li[0]].color());
- float3 c2 = get_float3(l->data[li[1]].color());
- float3 c3 = get_float3(l->data[li[2]].color());
+ float4 c1 = get_float4(l->data[li[0]].color());
+ float4 c2 = get_float4(l->data[li[1]].color());
+ float4 c3 = get_float4(l->data[li[2]].color());
/* Compress/encode vertex color using the sRGB curve. */
- cdata[0] = color_float_to_byte(color_srgb_to_linear_v3(c1));
- cdata[1] = color_float_to_byte(color_srgb_to_linear_v3(c2));
- cdata[2] = color_float_to_byte(color_srgb_to_linear_v3(c3));
+ cdata[0] = color_float4_to_uchar4(color_srgb_to_linear_v4(c1));
+ cdata[1] = color_float4_to_uchar4(color_srgb_to_linear_v4(c2));
+ cdata[2] = color_float4_to_uchar4(color_srgb_to_linear_v4(c3));
cdata += 3;
}
}
@@ -678,6 +678,55 @@ static void attr_create_pointiness(Scene *scene, Mesh *mesh, BL::Mesh &b_mesh, b
}
}
+/* The Random Per Island attribute is a random float associated with each
+ * connected component (island) of the mesh. The attribute is computed by
+ * first classifying the vertices into different sets using a Disjoint Set
+ * data structure. Then the index of the root of each vertex (Which is the
+ * representative of the set the vertex belongs to) is hashed and stored.
+ *
+ * We are using a face attribute to avoid interpolation during rendering,
+ * allowing the user to safely hash the output further. Had we used vertex
+ * attribute, the interpolation will introduce very slight variations,
+ * making the output unsafe to hash. */
+static void attr_create_random_per_island(Scene *scene,
+ Mesh *mesh,
+ BL::Mesh &b_mesh,
+ bool subdivision)
+{
+ if (!mesh->need_attribute(scene, ATTR_STD_RANDOM_PER_ISLAND)) {
+ return;
+ }
+
+ int number_of_vertices = b_mesh.vertices.length();
+ if (number_of_vertices == 0) {
+ return;
+ }
+
+ DisjointSet vertices_sets(number_of_vertices);
+
+ BL::Mesh::edges_iterator e;
+ for (b_mesh.edges.begin(e); e != b_mesh.edges.end(); ++e) {
+ vertices_sets.join(e->vertices()[0], e->vertices()[1]);
+ }
+
+ AttributeSet &attributes = (subdivision) ? mesh->subd_attributes : mesh->attributes;
+ Attribute *attribute = attributes.add(ATTR_STD_RANDOM_PER_ISLAND);
+ float *data = attribute->data_float();
+
+ if (!subdivision) {
+ BL::Mesh::loop_triangles_iterator t;
+ for (b_mesh.loop_triangles.begin(t); t != b_mesh.loop_triangles.end(); ++t) {
+ data[t->index()] = hash_uint_to_float(vertices_sets.find(t->vertices()[0]));
+ }
+ }
+ else {
+ BL::Mesh::polygons_iterator p;
+ for (b_mesh.polygons.begin(p); p != b_mesh.polygons.end(); ++p) {
+ data[p->index()] = hash_uint_to_float(vertices_sets.find(p->vertices()[0]));
+ }
+ }
+}
+
/* Create Mesh */
static void create_mesh(Scene *scene,
@@ -798,6 +847,8 @@ static void create_mesh(Scene *scene,
*/
attr_create_pointiness(scene, mesh, b_mesh, subdivision);
attr_create_vertex_color(scene, mesh, b_mesh, subdivision);
+ attr_create_sculpt_vertex_color(scene, mesh, b_mesh, subdivision);
+ attr_create_random_per_island(scene, mesh, b_mesh, subdivision);
if (subdivision) {
attr_create_subd_uv_map(scene, mesh, b_mesh, subdivide_uvs);
@@ -806,9 +857,9 @@ static void create_mesh(Scene *scene,
attr_create_uv_map(scene, mesh, b_mesh);
}
- /* for volume objects, create a matrix to transform from object space to
+ /* For volume objects, create a matrix to transform from object space to
* mesh texture space. this does not work with deformations but that can
- * probably only be done well with a volume grid mapping of coordinates */
+ * probably only be done well with a volume grid mapping of coordinates. */
if (mesh->need_attribute(scene, ATTR_STD_GENERATED_TRANSFORM)) {
Attribute *attr = mesh->attributes.add(ATTR_STD_GENERATED_TRANSFORM);
Transform *tfm = attr->data_transform();
@@ -877,13 +928,13 @@ static void sync_mesh_fluid_motion(BL::Object &b_ob, Scene *scene, Mesh *mesh)
if (scene->need_motion() == Scene::MOTION_NONE)
return;
- BL::DomainFluidSettings b_fluid_domain = object_fluid_domain_find(b_ob);
+ BL::FluidDomainSettings b_fluid_domain = object_fluid_liquid_domain_find(b_ob);
if (!b_fluid_domain)
return;
/* If the mesh has modifiers following the fluid domain we can't export motion. */
- if (b_fluid_domain.fluid_mesh_vertices.length() != mesh->verts.size())
+ if (b_fluid_domain.mesh_vertices.length() != mesh->verts.size())
return;
/* Find or add attribute */
@@ -900,93 +951,21 @@ static void sync_mesh_fluid_motion(BL::Object &b_ob, Scene *scene, Mesh *mesh)
float relative_time = motion_times[step] * scene->motion_shutter_time() * 0.5f;
float3 *mP = attr_mP->data_float3() + step * mesh->verts.size();
- BL::DomainFluidSettings::fluid_mesh_vertices_iterator fvi;
+ BL::FluidDomainSettings::mesh_vertices_iterator svi;
int i = 0;
- for (b_fluid_domain.fluid_mesh_vertices.begin(fvi);
- fvi != b_fluid_domain.fluid_mesh_vertices.end();
- ++fvi, ++i) {
- mP[i] = P[i] + get_float3(fvi->velocity()) * relative_time;
+ for (b_fluid_domain.mesh_vertices.begin(svi); svi != b_fluid_domain.mesh_vertices.end();
+ ++svi, ++i) {
+ mP[i] = P[i] + get_float3(svi->velocity()) * relative_time;
}
}
}
-Mesh *BlenderSync::sync_mesh(BL::Depsgraph &b_depsgraph,
- BL::Object &b_ob,
- BL::Object &b_ob_instance,
- bool object_updated,
- bool show_self,
- bool show_particles)
+void BlenderSync::sync_mesh(BL::Depsgraph b_depsgraph,
+ BL::Object b_ob,
+ Mesh *mesh,
+ const vector<Shader *> &used_shaders)
{
- /* test if we can instance or if the object is modified */
- BL::ID b_ob_data = b_ob.data();
- BL::ID key = (BKE_object_is_modified(b_ob)) ? b_ob_instance : b_ob_data;
- BL::Material material_override = view_layer.material_override;
-
- /* find shader indices */
- vector<Shader *> used_shaders;
-
- BL::Object::material_slots_iterator slot;
- for (b_ob.material_slots.begin(slot); slot != b_ob.material_slots.end(); ++slot) {
- if (material_override) {
- find_shader(material_override, used_shaders, scene->default_surface);
- }
- else {
- BL::ID b_material(slot->material());
- find_shader(b_material, used_shaders, scene->default_surface);
- }
- }
-
- if (used_shaders.size() == 0) {
- if (material_override)
- find_shader(material_override, used_shaders, scene->default_surface);
- else
- used_shaders.push_back(scene->default_surface);
- }
-
- /* test if we need to sync */
- int requested_geometry_flags = Mesh::GEOMETRY_NONE;
- if (view_layer.use_surfaces) {
- requested_geometry_flags |= Mesh::GEOMETRY_TRIANGLES;
- }
- if (view_layer.use_hair) {
- requested_geometry_flags |= Mesh::GEOMETRY_CURVES;
- }
- Mesh *mesh;
-
- if (!mesh_map.sync(&mesh, key)) {
- /* if transform was applied to mesh, need full update */
- if (object_updated && mesh->transform_applied)
- ;
- /* test if shaders changed, these can be object level so mesh
- * does not get tagged for recalc */
- else if (mesh->used_shaders != used_shaders)
- ;
- else if (requested_geometry_flags != mesh->geometry_flags)
- ;
- else {
- /* even if not tagged for recalc, we may need to sync anyway
- * because the shader needs different mesh attributes */
- bool attribute_recalc = false;
-
- foreach (Shader *shader, mesh->used_shaders)
- if (shader->need_update_mesh)
- attribute_recalc = true;
-
- if (!attribute_recalc)
- return mesh;
- }
- }
-
- /* ensure we only sync instanced meshes once */
- if (mesh_synced.find(mesh) != mesh_synced.end())
- return mesh;
-
- progress.set_sync_status("Synchronizing object", b_ob.name());
-
- mesh_synced.insert(mesh);
-
- /* create derived mesh */
array<int> oldtriangles;
array<Mesh::SubdFace> oldsubd_faces;
array<int> oldsubd_face_corners;
@@ -994,146 +973,73 @@ Mesh *BlenderSync::sync_mesh(BL::Depsgraph &b_depsgraph,
oldsubd_faces.steal_data(mesh->subd_faces);
oldsubd_face_corners.steal_data(mesh->subd_face_corners);
- /* compares curve_keys rather than strands in order to handle quick hair
- * adjustments in dynamic BVH - other methods could probably do this better*/
- array<float3> oldcurve_keys;
- array<float> oldcurve_radius;
- oldcurve_keys.steal_data(mesh->curve_keys);
- oldcurve_radius.steal_data(mesh->curve_radius);
-
mesh->clear();
mesh->used_shaders = used_shaders;
- mesh->name = ustring(b_ob_data.name().c_str());
- if (requested_geometry_flags != Mesh::GEOMETRY_NONE) {
+ mesh->subdivision_type = Mesh::SUBDIVISION_NONE;
+
+ if (view_layer.use_surfaces) {
/* Adaptive subdivision setup. Not for baking since that requires
* exact mapping to the Blender mesh. */
- if (scene->bake_manager->get_baking()) {
- mesh->subdivision_type = Mesh::SUBDIVISION_NONE;
- }
- else {
+ if (!scene->bake_manager->get_baking()) {
mesh->subdivision_type = object_subdivision_type(b_ob, preview, experimental);
}
/* For some reason, meshes do not need this... */
bool need_undeformed = mesh->need_attribute(scene, ATTR_STD_GENERATED);
-
BL::Mesh b_mesh = object_to_mesh(
b_data, b_ob, b_depsgraph, need_undeformed, mesh->subdivision_type);
if (b_mesh) {
/* Sync mesh itself. */
- if (view_layer.use_surfaces && show_self) {
- if (mesh->subdivision_type != Mesh::SUBDIVISION_NONE)
- create_subd_mesh(scene, mesh, b_ob, b_mesh, used_shaders, dicing_rate, max_subdivisions);
- else
- create_mesh(scene, mesh, b_mesh, used_shaders, false);
-
- create_mesh_volume_attributes(scene, b_ob, mesh, b_scene.frame_current());
- }
-
- /* Sync hair curves. */
- if (view_layer.use_hair && show_particles &&
- mesh->subdivision_type == Mesh::SUBDIVISION_NONE) {
- sync_curves(mesh, b_mesh, b_ob, false);
- }
+ if (mesh->subdivision_type != Mesh::SUBDIVISION_NONE)
+ create_subd_mesh(
+ scene, mesh, b_ob, b_mesh, mesh->used_shaders, dicing_rate, max_subdivisions);
+ else
+ create_mesh(scene, mesh, b_mesh, mesh->used_shaders, false);
free_object_to_mesh(b_data, b_ob, b_mesh);
}
}
- mesh->geometry_flags = requested_geometry_flags;
- /* fluid motion */
+ /* mesh fluid motion mantaflow */
sync_mesh_fluid_motion(b_ob, scene, mesh);
/* tag update */
bool rebuild = (oldtriangles != mesh->triangles) || (oldsubd_faces != mesh->subd_faces) ||
- (oldsubd_face_corners != mesh->subd_face_corners) ||
- (oldcurve_keys != mesh->curve_keys) || (oldcurve_radius != mesh->curve_radius);
+ (oldsubd_face_corners != mesh->subd_face_corners);
mesh->tag_update(scene, rebuild);
-
- return mesh;
}
-void BlenderSync::sync_mesh_motion(BL::Depsgraph &b_depsgraph,
- BL::Object &b_ob,
- Object *object,
- float motion_time)
+void BlenderSync::sync_mesh_motion(BL::Depsgraph b_depsgraph,
+ BL::Object b_ob,
+ Mesh *mesh,
+ int motion_step)
{
- /* ensure we only sync instanced meshes once */
- Mesh *mesh = object->mesh;
-
- if (mesh_motion_synced.find(mesh) != mesh_motion_synced.end())
- return;
-
- mesh_motion_synced.insert(mesh);
-
- /* ensure we only motion sync meshes that also had mesh synced, to avoid
- * unnecessary work and to ensure that its attributes were clear */
- if (mesh_synced.find(mesh) == mesh_synced.end())
- return;
-
- /* Find time matching motion step required by mesh. */
- int motion_step = mesh->motion_step(motion_time);
- if (motion_step < 0) {
+ /* Fluid motion blur already exported. */
+ BL::FluidDomainSettings b_fluid_domain = object_fluid_liquid_domain_find(b_ob);
+ if (b_fluid_domain) {
return;
}
- /* skip empty meshes */
- const size_t numverts = mesh->verts.size();
- const size_t numkeys = mesh->curve_keys.size();
-
- if (!numverts && !numkeys)
+ /* Skip if no vertices were exported. */
+ size_t numverts = mesh->verts.size();
+ if (numverts == 0) {
return;
+ }
- /* skip objects without deforming modifiers. this is not totally reliable,
- * would need a more extensive check to see which objects are animated */
+ /* Skip objects without deforming modifiers. this is not totally reliable,
+ * would need a more extensive check to see which objects are animated. */
BL::Mesh b_mesh(PointerRNA_NULL);
-
- /* fluid motion is exported immediate with mesh, skip here */
- BL::DomainFluidSettings b_fluid_domain = object_fluid_domain_find(b_ob);
- if (b_fluid_domain)
- return;
-
if (ccl::BKE_object_is_deform_modified(b_ob, b_scene, preview)) {
/* get derived mesh */
b_mesh = object_to_mesh(b_data, b_ob, b_depsgraph, false, Mesh::SUBDIVISION_NONE);
}
- if (!b_mesh) {
- /* if we have no motion blur on this frame, but on other frames, copy */
- if (numverts) {
- /* triangles */
- Attribute *attr_mP = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
-
- if (attr_mP) {
- Attribute *attr_mN = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_NORMAL);
- Attribute *attr_N = mesh->attributes.find(ATTR_STD_VERTEX_NORMAL);
- float3 *P = &mesh->verts[0];
- float3 *N = (attr_N) ? attr_N->data_float3() : NULL;
-
- memcpy(attr_mP->data_float3() + motion_step * numverts, P, sizeof(float3) * numverts);
- if (attr_mN)
- memcpy(attr_mN->data_float3() + motion_step * numverts, N, sizeof(float3) * numverts);
- }
- }
-
- if (numkeys) {
- /* curves */
- Attribute *attr_mP = mesh->curve_attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
-
- if (attr_mP) {
- float3 *keys = &mesh->curve_keys[0];
- memcpy(attr_mP->data_float3() + motion_step * numkeys, keys, sizeof(float3) * numkeys);
- }
- }
-
- return;
- }
-
- /* TODO(sergey): Perform preliminary check for number of verticies. */
- if (numverts) {
+ /* TODO(sergey): Perform preliminary check for number of vertices. */
+ if (b_mesh) {
+ /* Export deformed coordinates. */
/* Find attributes. */
Attribute *attr_mP = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
Attribute *attr_mN = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_NORMAL);
@@ -1198,14 +1104,13 @@ void BlenderSync::sync_mesh_motion(BL::Depsgraph &b_depsgraph,
}
}
}
- }
- /* hair motion */
- if (numkeys)
- sync_curves(mesh, b_mesh, b_ob, true, motion_step);
+ free_object_to_mesh(b_data, b_ob, b_mesh);
+ return;
+ }
- /* free derived mesh */
- free_object_to_mesh(b_data, b_ob, b_mesh);
+ /* No deformation on this frame, copy coordinates if other frames did have it. */
+ mesh->copy_center_to_motion_step(motion_step);
}
CCL_NAMESPACE_END
diff --git a/intern/cycles/blender/blender_object.cpp b/intern/cycles/blender/blender_object.cpp
index 095ecd59985..3ea6892a349 100644
--- a/intern/cycles/blender/blender_object.cpp
+++ b/intern/cycles/blender/blender_object.cpp
@@ -15,14 +15,14 @@
*/
#include "render/camera.h"
-#include "render/integrator.h"
#include "render/graph.h"
+#include "render/integrator.h"
#include "render/light.h"
#include "render/mesh.h"
-#include "render/object.h"
-#include "render/scene.h"
#include "render/nodes.h"
+#include "render/object.h"
#include "render/particles.h"
+#include "render/scene.h"
#include "render/shader.h"
#include "blender/blender_object_cull.h"
@@ -59,7 +59,7 @@ bool BlenderSync::BKE_object_is_modified(BL::Object &b_ob)
return false;
}
-bool BlenderSync::object_is_mesh(BL::Object &b_ob)
+bool BlenderSync::object_is_geometry(BL::Object &b_ob)
{
BL::ID b_ob_data = b_ob.data();
@@ -67,10 +67,16 @@ bool BlenderSync::object_is_mesh(BL::Object &b_ob)
return false;
}
- if (b_ob.type() == BL::Object::type_CURVE) {
+ BL::Object::type_enum type = b_ob.type();
+
+ if (type == BL::Object::type_VOLUME || type == BL::Object::type_HAIR) {
+ /* Will be exported attached to mesh. */
+ return true;
+ }
+ else if (type == BL::Object::type_CURVE) {
/* Skip exporting curves without faces, overhead can be
* significant if there are many for path animation. */
- BL::Curve b_curve(b_ob.data());
+ BL::Curve b_curve(b_ob_data);
return (b_curve.bevel_object() || b_curve.extrude() != 0.0f || b_curve.bevel_depth() != 0.0f ||
b_curve.dimensions() == BL::Curve::dimensions_2D || b_ob.modifiers.length());
@@ -88,204 +94,14 @@ bool BlenderSync::object_is_light(BL::Object &b_ob)
return (b_ob_data && b_ob_data.is_a(&RNA_Light));
}
-static uint object_ray_visibility(BL::Object &b_ob)
-{
- PointerRNA cvisibility = RNA_pointer_get(&b_ob.ptr, "cycles_visibility");
- uint flag = 0;
-
- flag |= get_boolean(cvisibility, "camera") ? PATH_RAY_CAMERA : 0;
- flag |= get_boolean(cvisibility, "diffuse") ? PATH_RAY_DIFFUSE : 0;
- flag |= get_boolean(cvisibility, "glossy") ? PATH_RAY_GLOSSY : 0;
- flag |= get_boolean(cvisibility, "transmission") ? PATH_RAY_TRANSMIT : 0;
- flag |= get_boolean(cvisibility, "shadow") ? PATH_RAY_SHADOW : 0;
- flag |= get_boolean(cvisibility, "scatter") ? PATH_RAY_VOLUME_SCATTER : 0;
-
- return flag;
-}
-
-/* Light */
-
-void BlenderSync::sync_light(BL::Object &b_parent,
- int persistent_id[OBJECT_PERSISTENT_ID_SIZE],
- BL::Object &b_ob,
- BL::Object &b_ob_instance,
- int random_id,
- Transform &tfm,
- bool *use_portal)
-{
- /* test if we need to sync */
- Light *light;
- ObjectKey key(b_parent, persistent_id, b_ob_instance);
-
- if (!light_map.sync(&light, b_ob, b_parent, key)) {
- if (light->is_portal)
- *use_portal = true;
- return;
- }
-
- BL::Light b_light(b_ob.data());
-
- /* type */
- switch (b_light.type()) {
- case BL::Light::type_POINT: {
- BL::PointLight b_point_light(b_light);
- light->size = b_point_light.shadow_soft_size();
- light->type = LIGHT_POINT;
- break;
- }
- case BL::Light::type_SPOT: {
- BL::SpotLight b_spot_light(b_light);
- light->size = b_spot_light.shadow_soft_size();
- light->type = LIGHT_SPOT;
- light->spot_angle = b_spot_light.spot_size();
- light->spot_smooth = b_spot_light.spot_blend();
- break;
- }
- /* Hemi were removed from 2.8 */
- // case BL::Light::type_HEMI: {
- // light->type = LIGHT_DISTANT;
- // light->size = 0.0f;
- // break;
- // }
- case BL::Light::type_SUN: {
- BL::SunLight b_sun_light(b_light);
- light->size = b_sun_light.shadow_soft_size();
- light->type = LIGHT_DISTANT;
- break;
- }
- case BL::Light::type_AREA: {
- BL::AreaLight b_area_light(b_light);
- light->size = 1.0f;
- light->axisu = transform_get_column(&tfm, 0);
- light->axisv = transform_get_column(&tfm, 1);
- light->sizeu = b_area_light.size();
- switch (b_area_light.shape()) {
- case BL::AreaLight::shape_SQUARE:
- light->sizev = light->sizeu;
- light->round = false;
- break;
- case BL::AreaLight::shape_RECTANGLE:
- light->sizev = b_area_light.size_y();
- light->round = false;
- break;
- case BL::AreaLight::shape_DISK:
- light->sizev = light->sizeu;
- light->round = true;
- break;
- case BL::AreaLight::shape_ELLIPSE:
- light->sizev = b_area_light.size_y();
- light->round = true;
- break;
- }
- light->type = LIGHT_AREA;
- break;
- }
- }
-
- /* location and (inverted!) direction */
- light->co = transform_get_column(&tfm, 3);
- light->dir = -transform_get_column(&tfm, 2);
- light->tfm = tfm;
-
- /* shader */
- vector<Shader *> used_shaders;
- find_shader(b_light, used_shaders, scene->default_light);
- light->shader = used_shaders[0];
-
- /* shadow */
- PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
- PointerRNA clight = RNA_pointer_get(&b_light.ptr, "cycles");
- light->cast_shadow = get_boolean(clight, "cast_shadow");
- light->use_mis = get_boolean(clight, "use_multiple_importance_sampling");
-
- int samples = get_int(clight, "samples");
- if (get_boolean(cscene, "use_square_samples"))
- light->samples = samples * samples;
- else
- light->samples = samples;
-
- light->max_bounces = get_int(clight, "max_bounces");
-
- if (b_ob != b_ob_instance) {
- light->random_id = random_id;
- }
- else {
- light->random_id = hash_int_2d(hash_string(b_ob.name().c_str()), 0);
- }
-
- if (light->type == LIGHT_AREA)
- light->is_portal = get_boolean(clight, "is_portal");
- else
- light->is_portal = false;
-
- if (light->is_portal)
- *use_portal = true;
-
- /* visibility */
- uint visibility = object_ray_visibility(b_ob);
- light->use_diffuse = (visibility & PATH_RAY_DIFFUSE) != 0;
- light->use_glossy = (visibility & PATH_RAY_GLOSSY) != 0;
- light->use_transmission = (visibility & PATH_RAY_TRANSMIT) != 0;
- light->use_scatter = (visibility & PATH_RAY_VOLUME_SCATTER) != 0;
-
- /* tag */
- light->tag_update(scene);
-}
-
-void BlenderSync::sync_background_light(bool use_portal)
-{
- BL::World b_world = b_scene.world();
-
- if (b_world) {
- PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
- PointerRNA cworld = RNA_pointer_get(&b_world.ptr, "cycles");
-
- enum SamplingMethod { SAMPLING_NONE = 0, SAMPLING_AUTOMATIC, SAMPLING_MANUAL, SAMPLING_NUM };
- int sampling_method = get_enum(cworld, "sampling_method", SAMPLING_NUM, SAMPLING_AUTOMATIC);
- bool sample_as_light = (sampling_method != SAMPLING_NONE);
-
- if (sample_as_light || use_portal) {
- /* test if we need to sync */
- Light *light;
- ObjectKey key(b_world, 0, b_world);
-
- if (light_map.sync(&light, b_world, b_world, key) || world_recalc ||
- b_world.ptr.data != world_map) {
- light->type = LIGHT_BACKGROUND;
- if (sampling_method == SAMPLING_MANUAL) {
- light->map_resolution = get_int(cworld, "sample_map_resolution");
- }
- else {
- light->map_resolution = 0;
- }
- light->shader = scene->default_background;
- light->use_mis = sample_as_light;
- light->max_bounces = get_int(cworld, "max_bounces");
-
- int samples = get_int(cworld, "samples");
- if (get_boolean(cscene, "use_square_samples"))
- light->samples = samples * samples;
- else
- light->samples = samples;
-
- light->tag_update(scene);
- light_map.set_recalc(b_world);
- }
- }
- }
-
- world_map = b_world.ptr.data;
- world_recalc = false;
-}
-
/* Object */
Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph,
BL::ViewLayer &b_view_layer,
BL::DepsgraphObjectInstance &b_instance,
float motion_time,
- bool show_self,
- bool show_particles,
+ bool use_particle_hair,
+ bool show_lights,
BlenderObjectCulling &culling,
bool *use_portal)
{
@@ -304,11 +120,14 @@ Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph,
/* light is handled separately */
if (!motion && object_is_light(b_ob)) {
+ if (!show_lights) {
+ return NULL;
+ }
+
/* TODO: don't use lights for excluded layers used as mask layer,
* when dynamic overrides are back. */
#if 0
- if(!((layer_flag & view_layer.holdout_layer) &&
- (layer_flag & view_layer.exclude_layer)))
+ if (!((layer_flag & view_layer.holdout_layer) && (layer_flag & view_layer.exclude_layer)))
#endif
{
sync_light(b_parent,
@@ -324,7 +143,7 @@ Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph,
}
/* only interested in object that we can create meshes from */
- if (!object_is_mesh(b_ob)) {
+ if (!object_is_geometry(b_ob)) {
return NULL;
}
@@ -345,13 +164,14 @@ Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph,
/* TODO: make holdout objects on excluded layer invisible for non-camera rays. */
#if 0
- if(use_holdout && (layer_flag & view_layer.exclude_layer)) {
+ if (use_holdout && (layer_flag & view_layer.exclude_layer)) {
visibility &= ~(PATH_RAY_ALL_VISIBILITY - PATH_RAY_CAMERA);
}
#endif
/* Clear camera visibility for indirect only objects. */
- bool use_indirect_only = b_parent.indirect_only_get(PointerRNA_NULL, b_view_layer);
+ bool use_indirect_only = !use_holdout &&
+ b_parent.indirect_only_get(PointerRNA_NULL, b_view_layer);
if (use_indirect_only) {
visibility &= ~PATH_RAY_CAMERA;
}
@@ -362,7 +182,7 @@ Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph,
}
/* key to lookup object */
- ObjectKey key(b_parent, persistent_id, b_ob_instance);
+ ObjectKey key(b_parent, persistent_id, b_ob_instance, use_particle_hair);
Object *object;
/* motion vector case */
@@ -377,8 +197,8 @@ Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph,
}
/* mesh deformation */
- if (object->mesh)
- sync_mesh_motion(b_depsgraph, b_ob, object, motion_time);
+ if (object->geometry)
+ sync_geometry_motion(b_depsgraph, b_ob, object, motion_time, use_particle_hair);
}
return object;
@@ -387,12 +207,12 @@ Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph,
/* test if we need to sync */
bool object_updated = false;
- if (object_map.sync(&object, b_ob, b_parent, key))
+ if (object_map.add_or_update(&object, b_ob, b_parent, key))
object_updated = true;
/* mesh sync */
- object->mesh = sync_mesh(
- b_depsgraph, b_ob, b_ob_instance, object_updated, show_self, show_particles);
+ object->geometry = sync_geometry(
+ b_depsgraph, b_ob, b_ob_instance, object_updated, use_particle_hair);
/* special case not tracked by object update flags */
@@ -414,6 +234,12 @@ Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph,
object_updated = true;
}
+ float shadow_terminator_offset = get_float(cobject, "shadow_terminator_offset");
+ if (shadow_terminator_offset != object->shadow_terminator_offset) {
+ object->shadow_terminator_offset = shadow_terminator_offset;
+ object_updated = true;
+ }
+
/* sync the asset name for Cryptomatte */
BL::Object parent = b_ob.parent();
ustring parent_name;
@@ -434,31 +260,33 @@ Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph,
/* object sync
* transform comparison should not be needed, but duplis don't work perfect
* in the depsgraph and may not signal changes, so this is a workaround */
- if (object_updated || (object->mesh && object->mesh->need_update) || tfm != object->tfm) {
+ if (object_updated || (object->geometry && object->geometry->need_update) ||
+ tfm != object->tfm) {
object->name = b_ob.name().c_str();
object->pass_id = b_ob.pass_index();
+ object->color = get_float3(b_ob.color());
object->tfm = tfm;
object->motion.clear();
/* motion blur */
Scene::MotionType need_motion = scene->need_motion();
- if (need_motion != Scene::MOTION_NONE && object->mesh) {
- Mesh *mesh = object->mesh;
- mesh->use_motion_blur = false;
- mesh->motion_steps = 0;
+ if (need_motion != Scene::MOTION_NONE && object->geometry) {
+ Geometry *geom = object->geometry;
+ geom->use_motion_blur = false;
+ geom->motion_steps = 0;
uint motion_steps;
if (need_motion == Scene::MOTION_BLUR) {
- motion_steps = object_motion_steps(b_parent, b_ob);
- mesh->motion_steps = motion_steps;
+ motion_steps = object_motion_steps(b_parent, b_ob, Object::MAX_MOTION_STEPS);
+ geom->motion_steps = motion_steps;
if (motion_steps && object_use_deform_motion(b_parent, b_ob)) {
- mesh->use_motion_blur = true;
+ geom->use_motion_blur = true;
}
}
else {
motion_steps = 3;
- mesh->motion_steps = motion_steps;
+ geom->motion_steps = motion_steps;
}
object->motion.clear();
@@ -483,7 +311,7 @@ Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph,
else {
object->dupli_generated = make_float3(0.0f, 0.0f, 0.0f);
object->dupli_uv = make_float2(0.0f, 0.0f);
- object->random_id = hash_int_2d(hash_string(object->name.c_str()), 0);
+ object->random_id = hash_uint2(hash_string(object->name.c_str()), 0);
}
object->tag_update(scene);
@@ -499,7 +327,9 @@ Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph,
/* Object Loop */
-void BlenderSync::sync_objects(BL::Depsgraph &b_depsgraph, float motion_time)
+void BlenderSync::sync_objects(BL::Depsgraph &b_depsgraph,
+ BL::SpaceView3D &b_v3d,
+ float motion_time)
{
/* layer data */
bool motion = motion_time != 0.0f;
@@ -507,13 +337,13 @@ void BlenderSync::sync_objects(BL::Depsgraph &b_depsgraph, float motion_time)
if (!motion) {
/* prepare for sync */
light_map.pre_sync();
- mesh_map.pre_sync();
+ geometry_map.pre_sync();
object_map.pre_sync();
particle_system_map.pre_sync();
motion_times.clear();
}
else {
- mesh_motion_synced.clear();
+ geometry_motion_synced.clear();
}
/* initialize culling */
@@ -522,6 +352,7 @@ void BlenderSync::sync_objects(BL::Depsgraph &b_depsgraph, float motion_time)
/* object loop */
bool cancel = false;
bool use_portal = false;
+ const bool show_lights = BlenderViewportParameters(b_v3d).use_scene_lights;
BL::ViewLayer b_view_layer = b_depsgraph.view_layer_eval();
@@ -532,21 +363,35 @@ void BlenderSync::sync_objects(BL::Depsgraph &b_depsgraph, float motion_time)
BL::DepsgraphObjectInstance b_instance = *b_instance_iter;
BL::Object b_ob = b_instance.object();
- /* load per-object culling data */
+ /* Viewport visibility. */
+ const bool show_in_viewport = !b_v3d || b_ob.visible_in_viewport_get(b_v3d);
+ if (show_in_viewport == false) {
+ continue;
+ }
+
+ /* Load per-object culling data. */
culling.init_object(scene, b_ob);
- /* test if object needs to be hidden */
- const bool show_self = b_instance.show_self();
- const bool show_particles = b_instance.show_particles();
+ /* Object itself. */
+ if (b_instance.show_self()) {
+ sync_object(b_depsgraph,
+ b_view_layer,
+ b_instance,
+ motion_time,
+ false,
+ show_lights,
+ culling,
+ &use_portal);
+ }
- if (show_self || show_particles) {
- /* object itself */
+ /* Particle hair as separate object. */
+ if (b_instance.show_particles() && object_has_particle_hair(b_ob)) {
sync_object(b_depsgraph,
b_view_layer,
b_instance,
motion_time,
- show_self,
- show_particles,
+ true,
+ show_lights,
culling,
&use_portal);
}
@@ -557,13 +402,13 @@ void BlenderSync::sync_objects(BL::Depsgraph &b_depsgraph, float motion_time)
progress.set_sync_status("");
if (!cancel && !motion) {
- sync_background_light(use_portal);
+ sync_background_light(b_v3d, use_portal);
/* handle removed data and modified pointers */
if (light_map.post_sync())
scene->light_manager->tag_update(scene);
- if (mesh_map.post_sync())
- scene->mesh_manager->tag_update(scene);
+ if (geometry_map.post_sync())
+ scene->geometry_manager->tag_update(scene);
if (object_map.post_sync())
scene->object_manager->tag_update(scene);
if (particle_system_map.post_sync())
@@ -571,11 +416,12 @@ void BlenderSync::sync_objects(BL::Depsgraph &b_depsgraph, float motion_time)
}
if (motion)
- mesh_motion_synced.clear();
+ geometry_motion_synced.clear();
}
void BlenderSync::sync_motion(BL::RenderSettings &b_render,
BL::Depsgraph &b_depsgraph,
+ BL::SpaceView3D &b_v3d,
BL::Object &b_override,
int width,
int height,
@@ -613,12 +459,15 @@ void BlenderSync::sync_motion(BL::RenderSettings &b_render,
b_engine.frame_set(frame, subframe);
python_thread_state_save(python_thread_state);
sync_camera_motion(b_render, b_cam, width, height, 0.0f);
- sync_objects(b_depsgraph, 0.0f);
+ sync_objects(b_depsgraph, b_v3d, 0.0f);
}
- /* always sample these times for camera motion */
- motion_times.insert(-1.0f);
- motion_times.insert(1.0f);
+ /* Insert motion times from camera. Motion times from other objects
+ * have already been added in a sync_objects call. */
+ uint camera_motion_steps = object_motion_steps(b_cam, b_cam);
+ for (size_t step = 0; step < camera_motion_steps; step++) {
+ motion_times.insert(scene->camera->motion_time(step));
+ }
/* note iteration over motion_times set happens in sorted order */
foreach (float relative_time, motion_times) {
@@ -643,13 +492,11 @@ void BlenderSync::sync_motion(BL::RenderSettings &b_render,
b_engine.frame_set(frame, subframe);
python_thread_state_save(python_thread_state);
- /* sync camera, only supports two times at the moment */
- if (relative_time == -1.0f || relative_time == 1.0f) {
- sync_camera_motion(b_render, b_cam, width, height, relative_time);
- }
+ /* Syncs camera motion if relative_time is one of the camera's motion times. */
+ sync_camera_motion(b_render, b_cam, width, height, relative_time);
/* sync object */
- sync_objects(b_depsgraph, relative_time);
+ sync_objects(b_depsgraph, b_v3d, relative_time);
}
/* we need to set the python thread state again because this
diff --git a/intern/cycles/blender/blender_object_cull.cpp b/intern/cycles/blender/blender_object_cull.cpp
index 74f8fb1dc53..bebecb364eb 100644
--- a/intern/cycles/blender/blender_object_cull.cpp
+++ b/intern/cycles/blender/blender_object_cull.cpp
@@ -19,6 +19,7 @@
#include "render/camera.h"
#include "blender/blender_object_cull.h"
+#include "blender/blender_util.h"
CCL_NAMESPACE_BEGIN
diff --git a/intern/cycles/blender/blender_particles.cpp b/intern/cycles/blender/blender_particles.cpp
index d74f132ed60..e5eab1ae62b 100644
--- a/intern/cycles/blender/blender_particles.cpp
+++ b/intern/cycles/blender/blender_particles.cpp
@@ -39,7 +39,7 @@ bool BlenderSync::sync_dupli_particle(BL::Object &b_ob,
object->hide_on_missing_motion = true;
/* test if we need particle data */
- if (!object->mesh->need_attribute(scene, ATTR_STD_PARTICLE))
+ if (!object->geometry->need_attribute(scene, ATTR_STD_PARTICLE))
return false;
/* don't handle child particles yet */
@@ -53,10 +53,10 @@ bool BlenderSync::sync_dupli_particle(BL::Object &b_ob,
ParticleSystem *psys;
bool first_use = !particle_system_map.is_used(key);
- bool need_update = particle_system_map.sync(&psys, b_ob, b_instance.object(), key);
+ bool need_update = particle_system_map.add_or_update(&psys, b_ob, b_instance.object(), key);
/* no update needed? */
- if (!need_update && !object->mesh->need_update && !scene->object_manager->need_update)
+ if (!need_update && !object->geometry->need_update && !scene->object_manager->need_update)
return true;
/* first time used in this sync loop? clear and tag update */
diff --git a/intern/cycles/blender/blender_python.cpp b/intern/cycles/blender/blender_python.cpp
index ffd1c70a4e4..25c77b74ce3 100644
--- a/intern/cycles/blender/blender_python.cpp
+++ b/intern/cycles/blender/blender_python.cpp
@@ -19,8 +19,9 @@
#include "blender/CCL_api.h"
#include "blender/blender_device.h"
-#include "blender/blender_sync.h"
#include "blender/blender_session.h"
+#include "blender/blender_sync.h"
+#include "blender/blender_util.h"
#include "render/denoising.h"
#include "render/merge.h"
@@ -30,15 +31,17 @@
#include "util/util_logging.h"
#include "util/util_md5.h"
#include "util/util_opengl.h"
+#include "util/util_openimagedenoise.h"
#include "util/util_path.h"
#include "util/util_string.h"
+#include "util/util_task.h"
#include "util/util_types.h"
#ifdef WITH_OSL
# include "render/osl.h"
-# include <OSL/oslquery.h>
# include <OSL/oslconfig.h>
+# include <OSL/oslquery.h>
#endif
#ifdef WITH_OPENCL
@@ -59,6 +62,12 @@ void *pylong_as_voidptr_typesafe(PyObject *object)
return PyLong_AsVoidPtr(object);
}
+PyObject *pyunicode_from_string(const char *str)
+{
+ /* Ignore errors if device API returns invalid UTF-8 strings. */
+ return PyUnicode_DecodeUTF8(str, strlen(str), "ignore");
+}
+
/* Synchronize debug flags from a given Blender scene.
* Return truth when device list needs invalidation.
*/
@@ -81,6 +90,9 @@ bool debug_flags_sync_from_scene(BL::Scene b_scene)
/* Synchronize CUDA flags. */
flags.cuda.adaptive_compile = get_boolean(cscene, "debug_use_cuda_adaptive_compile");
flags.cuda.split_kernel = get_boolean(cscene, "debug_use_cuda_split_kernel");
+ /* Synchronize OptiX flags. */
+ flags.optix.cuda_streams = get_int(cscene, "debug_optix_cuda_streams");
+ flags.optix.curves_api = get_boolean(cscene, "debug_optix_curves_api");
/* Synchronize OpenCL device type. */
switch (get_enum(cscene, "debug_opencl_device_type")) {
case 0:
@@ -138,7 +150,7 @@ static const char *PyC_UnicodeAsByte(PyObject *py_str, PyObject **coerce)
const char *result = _PyUnicode_AsString(py_str);
if (result) {
/* 99% of the time this is enough but we better support non unicode
- * chars since blender doesnt limit this.
+ * chars since blender doesn't limit this.
*/
return result;
}
@@ -151,7 +163,7 @@ static const char *PyC_UnicodeAsByte(PyObject *py_str, PyObject **coerce)
return PyBytes_AS_STRING(*coerce);
}
else {
- /* Clear the error, so Cycles can be at leadt used without
+ /* Clear the error, so Cycles can be at least used without
* GPU and OSL support,
*/
PyErr_Clear();
@@ -177,6 +189,8 @@ static PyObject *init_func(PyObject * /*self*/, PyObject *args)
BlenderSession::headless = headless;
+ DebugFlags().running_inside_blender = true;
+
VLOG(2) << "Debug flags initialized to:\n" << DebugFlags();
Py_RETURN_NONE;
@@ -192,14 +206,15 @@ static PyObject *exit_func(PyObject * /*self*/, PyObject * /*args*/)
static PyObject *create_func(PyObject * /*self*/, PyObject *args)
{
- PyObject *pyengine, *pypreferences, *pydata, *pyregion, *pyv3d, *pyrv3d;
+ PyObject *pyengine, *pypreferences, *pydata, *pyscreen, *pyregion, *pyv3d, *pyrv3d;
int preview_osl;
if (!PyArg_ParseTuple(args,
- "OOOOOOi",
+ "OOOOOOOi",
&pyengine,
&pypreferences,
&pydata,
+ &pyscreen,
&pyregion,
&pyv3d,
&pyrv3d,
@@ -208,6 +223,8 @@ static PyObject *create_func(PyObject * /*self*/, PyObject *args)
}
/* RNA */
+ ID *bScreen = (ID *)PyLong_AsVoidPtr(pyscreen);
+
PointerRNA engineptr;
RNA_pointer_create(NULL, &RNA_RenderEngine, (void *)PyLong_AsVoidPtr(pyengine), &engineptr);
BL::RenderEngine engine(engineptr);
@@ -222,15 +239,15 @@ static PyObject *create_func(PyObject * /*self*/, PyObject *args)
BL::BlendData data(dataptr);
PointerRNA regionptr;
- RNA_pointer_create(NULL, &RNA_Region, pylong_as_voidptr_typesafe(pyregion), &regionptr);
+ RNA_pointer_create(bScreen, &RNA_Region, pylong_as_voidptr_typesafe(pyregion), &regionptr);
BL::Region region(regionptr);
PointerRNA v3dptr;
- RNA_pointer_create(NULL, &RNA_SpaceView3D, pylong_as_voidptr_typesafe(pyv3d), &v3dptr);
+ RNA_pointer_create(bScreen, &RNA_SpaceView3D, pylong_as_voidptr_typesafe(pyv3d), &v3dptr);
BL::SpaceView3D v3d(v3dptr);
PointerRNA rv3dptr;
- RNA_pointer_create(NULL, &RNA_RegionView3D, pylong_as_voidptr_typesafe(pyrv3d), &rv3dptr);
+ RNA_pointer_create(bScreen, &RNA_RegionView3D, pylong_as_voidptr_typesafe(pyrv3d), &rv3dptr);
BL::RegionView3D rv3d(rv3dptr);
/* create session */
@@ -284,22 +301,18 @@ static PyObject *render_func(PyObject * /*self*/, PyObject *args)
static PyObject *bake_func(PyObject * /*self*/, PyObject *args)
{
PyObject *pysession, *pydepsgraph, *pyobject;
- PyObject *pypixel_array, *pyresult;
const char *pass_type;
- int num_pixels, depth, object_id, pass_filter;
+ int pass_filter, width, height;
if (!PyArg_ParseTuple(args,
- "OOOsiiOiiO",
+ "OOOsiii",
&pysession,
&pydepsgraph,
&pyobject,
&pass_type,
&pass_filter,
- &object_id,
- &pypixel_array,
- &num_pixels,
- &depth,
- &pyresult))
+ &width,
+ &height))
return NULL;
BlenderSession *session = (BlenderSession *)PyLong_AsVoidPtr(pysession);
@@ -312,23 +325,9 @@ static PyObject *bake_func(PyObject * /*self*/, PyObject *args)
RNA_id_pointer_create((ID *)PyLong_AsVoidPtr(pyobject), &objectptr);
BL::Object b_object(objectptr);
- void *b_result = PyLong_AsVoidPtr(pyresult);
-
- PointerRNA bakepixelptr;
- RNA_pointer_create(NULL, &RNA_BakePixel, PyLong_AsVoidPtr(pypixel_array), &bakepixelptr);
- BL::BakePixel b_bake_pixel(bakepixelptr);
-
python_thread_state_save(&session->python_thread_state);
- session->bake(b_depsgraph,
- b_object,
- pass_type,
- pass_filter,
- object_id,
- b_bake_pixel,
- (size_t)num_pixels,
- depth,
- (float *)b_result);
+ session->bake(b_depsgraph, b_object, pass_type, pass_filter, width, height);
python_thread_state_restore(&session->python_thread_state);
@@ -420,10 +419,11 @@ static PyObject *available_devices_func(PyObject * /*self*/, PyObject *args)
for (size_t i = 0; i < devices.size(); i++) {
DeviceInfo &device = devices[i];
string type_name = Device::string_from_type(device.type);
- PyObject *device_tuple = PyTuple_New(3);
- PyTuple_SET_ITEM(device_tuple, 0, PyUnicode_FromString(device.description.c_str()));
- PyTuple_SET_ITEM(device_tuple, 1, PyUnicode_FromString(type_name.c_str()));
- PyTuple_SET_ITEM(device_tuple, 2, PyUnicode_FromString(device.id.c_str()));
+ PyObject *device_tuple = PyTuple_New(4);
+ PyTuple_SET_ITEM(device_tuple, 0, pyunicode_from_string(device.description.c_str()));
+ PyTuple_SET_ITEM(device_tuple, 1, pyunicode_from_string(type_name.c_str()));
+ PyTuple_SET_ITEM(device_tuple, 2, pyunicode_from_string(device.id.c_str()));
+ PyTuple_SET_ITEM(device_tuple, 3, PyBool_FromLong(device.has_peer_memory));
PyTuple_SET_ITEM(ret, i, device_tuple);
}
@@ -634,7 +634,7 @@ static PyObject *osl_compile_func(PyObject * /*self*/, PyObject *args)
static PyObject *system_info_func(PyObject * /*self*/, PyObject * /*value*/)
{
string system_info = Device::device_capabilities();
- return PyUnicode_FromString(system_info.c_str());
+ return pyunicode_from_string(system_info.c_str());
}
#ifdef WITH_OPENCL
@@ -937,6 +937,15 @@ static PyObject *set_resumable_chunk_range_func(PyObject * /*self*/, PyObject *a
Py_RETURN_NONE;
}
+static PyObject *clear_resumable_chunk_func(PyObject * /*self*/, PyObject * /*value*/)
+{
+ VLOG(1) << "Clear resumable render";
+ BlenderSession::num_resumable_chunks = 0;
+ BlenderSession::current_resumable_chunk = 0;
+
+ Py_RETURN_NONE;
+}
+
static PyObject *enable_print_stats_func(PyObject * /*self*/, PyObject * /*args*/)
{
BlenderSession::print_render_stats = true;
@@ -946,14 +955,16 @@ static PyObject *enable_print_stats_func(PyObject * /*self*/, PyObject * /*args*
static PyObject *get_device_types_func(PyObject * /*self*/, PyObject * /*args*/)
{
vector<DeviceType> device_types = Device::available_types();
- bool has_cuda = false, has_opencl = false;
+ bool has_cuda = false, has_optix = false, has_opencl = false;
foreach (DeviceType device_type, device_types) {
has_cuda |= (device_type == DEVICE_CUDA);
+ has_optix |= (device_type == DEVICE_OPTIX);
has_opencl |= (device_type == DEVICE_OPENCL);
}
- PyObject *list = PyTuple_New(2);
+ PyObject *list = PyTuple_New(3);
PyTuple_SET_ITEM(list, 0, PyBool_FromLong(has_cuda));
- PyTuple_SET_ITEM(list, 1, PyBool_FromLong(has_opencl));
+ PyTuple_SET_ITEM(list, 1, PyBool_FromLong(has_optix));
+ PyTuple_SET_ITEM(list, 2, PyBool_FromLong(has_opencl));
return list;
}
@@ -992,6 +1003,7 @@ static PyMethodDef methods[] = {
/* Resumable render */
{"set_resumable_chunk", set_resumable_chunk_func, METH_VARARGS, ""},
{"set_resumable_chunk_range", set_resumable_chunk_range_func, METH_VARARGS, ""},
+ {"clear_resumable_chunk", clear_resumable_chunk_func, METH_NOARGS, ""},
/* Compute Device selection */
{"get_device_types", get_device_types_func, METH_VARARGS, ""},
@@ -1066,5 +1078,14 @@ void *CCL_python_module_init()
Py_INCREF(Py_False);
#endif /* WITH_EMBREE */
+ if (ccl::openimagedenoise_supported()) {
+ PyModule_AddObject(mod, "with_openimagedenoise", Py_True);
+ Py_INCREF(Py_True);
+ }
+ else {
+ PyModule_AddObject(mod, "with_openimagedenoise", Py_False);
+ Py_INCREF(Py_False);
+ }
+
return (void *)mod;
}
diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/blender_session.cpp
index 29a97bf6546..391a1b8f473 100644
--- a/intern/cycles/blender/blender_session.cpp
+++ b/intern/cycles/blender/blender_session.cpp
@@ -16,12 +16,13 @@
#include <stdlib.h>
+#include "device/device.h"
#include "render/background.h"
#include "render/buffers.h"
#include "render/camera.h"
-#include "device/device.h"
-#include "render/integrator.h"
+#include "render/colorspace.h"
#include "render/film.h"
+#include "render/integrator.h"
#include "render/light.h"
#include "render/mesh.h"
#include "render/object.h"
@@ -40,8 +41,8 @@
#include "util/util_progress.h"
#include "util/util_time.h"
-#include "blender/blender_sync.h"
#include "blender/blender_session.h"
+#include "blender/blender_sync.h"
#include "blender/blender_util.h"
CCL_NAMESPACE_BEGIN
@@ -113,11 +114,6 @@ BlenderSession::~BlenderSession()
free_session();
}
-void BlenderSession::create()
-{
- create_session();
-}
-
void BlenderSession::create_session()
{
SessionParams session_params = BlenderSync::get_session_params(
@@ -142,21 +138,13 @@ void BlenderSession::create_session()
scene = new Scene(scene_params, session->device);
scene->name = b_scene.name();
- /* setup callbacks for builtin image support */
- scene->image_manager->builtin_image_info_cb = function_bind(
- &BlenderSession::builtin_image_info, this, _1, _2, _3);
- scene->image_manager->builtin_image_pixels_cb = function_bind(
- &BlenderSession::builtin_image_pixels, this, _1, _2, _3, _4, _5);
- scene->image_manager->builtin_image_float_pixels_cb = function_bind(
- &BlenderSession::builtin_image_float_pixels, this, _1, _2, _3, _4, _5);
-
session->scene = scene;
/* There is no single depsgraph to use for the entire render.
* So we need to handle this differently.
*
- * We could loop over the final render result render layers in pipeline and keep Cycles unaware of multiple layers,
- * or perhaps move syncing further down in the pipeline.
+ * We could loop over the final render result render layers in pipeline and keep Cycles unaware
+ * of multiple layers, or perhaps move syncing further down in the pipeline.
*/
/* create sync */
sync = new BlenderSync(b_engine, b_data, b_scene, scene, !background, session->progress);
@@ -170,7 +158,7 @@ void BlenderSession::create_session()
/* set buffer parameters */
BufferParams buffer_params = BlenderSync::get_buffer_params(
- b_render, b_v3d, b_rv3d, scene->camera, width, height);
+ b_render, b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use);
session->reset(buffer_params, session_params.samples);
b_engine.use_highlight_tiles(session_params.progressive_refine == false);
@@ -180,9 +168,13 @@ void BlenderSession::create_session()
void BlenderSession::reset_session(BL::BlendData &b_data, BL::Depsgraph &b_depsgraph)
{
+ /* Update data, scene and depsgraph pointers. These can change after undo. */
this->b_data = b_data;
this->b_depsgraph = b_depsgraph;
this->b_scene = b_depsgraph.scene_eval();
+ if (sync) {
+ sync->reset(this->b_data, this->b_scene);
+ }
if (preview_osl) {
PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
@@ -198,8 +190,12 @@ void BlenderSession::reset_session(BL::BlendData &b_data, BL::Depsgraph &b_depsg
height = render_resolution_y(b_render);
}
- if (session == NULL) {
- create();
+ bool is_new_session = (session == NULL);
+ if (is_new_session) {
+ /* Initialize session and remember it was just created so not to
+ * re-create it below.
+ */
+ create_session();
}
if (b_v3d) {
@@ -218,8 +214,10 @@ void BlenderSession::reset_session(BL::BlendData &b_data, BL::Depsgraph &b_depsg
/* if scene or session parameters changed, it's easier to simply re-create
* them rather than trying to distinguish which settings need to be updated
*/
- free_session();
- create_session();
+ if (!is_new_session) {
+ free_session();
+ create_session();
+ }
return;
}
@@ -241,8 +239,13 @@ void BlenderSession::reset_session(BL::BlendData &b_data, BL::Depsgraph &b_depsg
BL::SpaceView3D b_null_space_view3d(PointerRNA_NULL);
BL::RegionView3D b_null_region_view3d(PointerRNA_NULL);
- BufferParams buffer_params = BlenderSync::get_buffer_params(
- b_render, b_null_space_view3d, b_null_region_view3d, scene->camera, width, height);
+ BufferParams buffer_params = BlenderSync::get_buffer_params(b_render,
+ b_null_space_view3d,
+ b_null_region_view3d,
+ scene->camera,
+ width,
+ height,
+ session_params.denoising.use);
session->reset(buffer_params, session_params.samples);
b_engine.use_highlight_tiles(session_params.progressive_refine == false);
@@ -253,9 +256,7 @@ void BlenderSession::reset_session(BL::BlendData &b_data, BL::Depsgraph &b_depsg
void BlenderSession::free_session()
{
- if (sync)
- delete sync;
-
+ delete sync;
delete session;
}
@@ -276,8 +277,6 @@ static ShaderEvalType get_shader_type(const string &pass_type)
return SHADER_EVAL_GLOSSY_COLOR;
else if (strcmp(shader_type, "TRANSMISSION_COLOR") == 0)
return SHADER_EVAL_TRANSMISSION_COLOR;
- else if (strcmp(shader_type, "SUBSURFACE_COLOR") == 0)
- return SHADER_EVAL_SUBSURFACE_COLOR;
else if (strcmp(shader_type, "EMIT") == 0)
return SHADER_EVAL_EMISSION;
@@ -294,8 +293,6 @@ static ShaderEvalType get_shader_type(const string &pass_type)
return SHADER_EVAL_GLOSSY;
else if (strcmp(shader_type, "TRANSMISSION") == 0)
return SHADER_EVAL_TRANSMISSION;
- else if (strcmp(shader_type, "SUBSURFACE") == 0)
- return SHADER_EVAL_SUBSURFACE;
/* extra */
else if (strcmp(shader_type, "ENVIRONMENT") == 0)
@@ -327,6 +324,7 @@ static void end_render_result(BL::RenderEngine &b_engine,
void BlenderSession::do_write_update_render_tile(RenderTile &rtile,
bool do_update_only,
+ bool do_read_only,
bool highlight)
{
int x = rtile.x - session->tile_manager.params.full_x;
@@ -352,7 +350,23 @@ void BlenderSession::do_write_update_render_tile(RenderTile &rtile,
BL::RenderLayer b_rlay = *b_single_rlay;
- if (do_update_only) {
+ if (do_read_only) {
+ /* copy each pass */
+ BL::RenderLayer::passes_iterator b_iter;
+
+ for (b_rlay.passes.begin(b_iter); b_iter != b_rlay.passes.end(); ++b_iter) {
+ BL::RenderPass b_pass(*b_iter);
+
+ /* find matching pass type */
+ PassType pass_type = BlenderSync::get_pass_type(b_pass);
+ int components = b_pass.channels();
+
+ rtile.buffers->set_pass_rect(pass_type, components, (float *)b_pass.rect());
+ }
+
+ end_render_result(b_engine, b_rr, false, false, false);
+ }
+ else if (do_update_only) {
/* Sample would be zero at initial tile update, which is only needed
* to tag tile form blender side as IN PROGRESS for proper highlight
* no buffers should be sent to blender yet. For denoise we also
@@ -360,21 +374,26 @@ void BlenderSession::do_write_update_render_tile(RenderTile &rtile,
bool merge = (rtile.sample != 0) && (rtile.task != RenderTile::DENOISE);
if (merge) {
- update_render_result(b_rr, b_rlay, rtile);
+ update_render_result(b_rlay, rtile);
}
end_render_result(b_engine, b_rr, true, highlight, merge);
}
else {
/* Write final render result. */
- write_render_result(b_rr, b_rlay, rtile);
+ write_render_result(b_rlay, rtile);
end_render_result(b_engine, b_rr, false, false, true);
}
}
+void BlenderSession::read_render_tile(RenderTile &rtile)
+{
+ do_write_update_render_tile(rtile, false, true, false);
+}
+
void BlenderSession::write_render_tile(RenderTile &rtile)
{
- do_write_update_render_tile(rtile, false, false);
+ do_write_update_render_tile(rtile, false, false, false);
}
void BlenderSession::update_render_tile(RenderTile &rtile, bool highlight)
@@ -384,9 +403,9 @@ void BlenderSession::update_render_tile(RenderTile &rtile, bool highlight)
* would need to be investigated a bit further, but for now shall be fine
*/
if (!b_engine.is_preview())
- do_write_update_render_tile(rtile, true, highlight);
+ do_write_update_render_tile(rtile, true, false, highlight);
else
- do_write_update_render_tile(rtile, false, false);
+ do_write_update_render_tile(rtile, false, false, false);
}
static void add_cryptomatte_layer(BL::RenderResult &b_rr, string name, string manifest)
@@ -454,14 +473,13 @@ void BlenderSession::render(BL::Depsgraph &b_depsgraph_)
session->update_render_tile_cb = function_bind(
&BlenderSession::update_render_tile, this, _1, _2);
+ BL::ViewLayer b_view_layer = b_depsgraph.view_layer_eval();
+
/* get buffer parameters */
SessionParams session_params = BlenderSync::get_session_params(
- b_engine, b_userpref, b_scene, background);
+ b_engine, b_userpref, b_scene, background, b_view_layer);
BufferParams buffer_params = BlenderSync::get_buffer_params(
- b_render, b_v3d, b_rv3d, scene->camera, width, height);
-
- /* render each layer */
- BL::ViewLayer b_view_layer = b_depsgraph.view_layer_eval();
+ b_render, b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use);
/* temporary render result to find needed passes and views */
BL::RenderResult b_rr = begin_render_result(
@@ -471,33 +489,26 @@ void BlenderSession::render(BL::Depsgraph &b_depsgraph_)
BL::RenderLayer b_rlay = *b_single_rlay;
b_rlay_name = b_view_layer.name();
- /* add passes */
- vector<Pass> passes = sync->sync_render_passes(b_rlay, b_view_layer);
- buffer_params.passes = passes;
-
- PointerRNA crl = RNA_pointer_get(&b_view_layer.ptr, "cycles");
- bool full_denoising = get_boolean(crl, "use_denoising");
- bool write_denoising_passes = get_boolean(crl, "denoising_store_passes");
+ /* Update denoising parameters. */
+ session->set_denoising(session_params.denoising);
- bool run_denoising = full_denoising || write_denoising_passes;
+ bool use_denoising = session_params.denoising.use;
+ bool store_denoising_passes = session_params.denoising.store_passes;
- session->tile_manager.schedule_denoising = run_denoising;
- buffer_params.denoising_data_pass = run_denoising;
+ buffer_params.denoising_data_pass = use_denoising || store_denoising_passes;
buffer_params.denoising_clean_pass = (scene->film->denoising_flags & DENOISING_CLEAN_ALL_PASSES);
- buffer_params.denoising_prefiltered_pass = write_denoising_passes;
-
- session->params.run_denoising = run_denoising;
- session->params.full_denoising = full_denoising;
- session->params.write_denoising_passes = write_denoising_passes;
- session->params.denoising.radius = get_int(crl, "denoising_radius");
- session->params.denoising.strength = get_float(crl, "denoising_strength");
- session->params.denoising.feature_strength = get_float(crl, "denoising_feature_strength");
- session->params.denoising.relative_pca = get_boolean(crl, "denoising_relative_pca");
+ buffer_params.denoising_prefiltered_pass = store_denoising_passes &&
+ session_params.denoising.type == DENOISER_NLM;
scene->film->denoising_data_pass = buffer_params.denoising_data_pass;
scene->film->denoising_clean_pass = buffer_params.denoising_clean_pass;
scene->film->denoising_prefiltered_pass = buffer_params.denoising_prefiltered_pass;
+ /* Add passes */
+ vector<Pass> passes = sync->sync_render_passes(
+ b_rlay, b_view_layer, session_params.adaptive_sampling, session_params.denoising);
+ buffer_params.passes = passes;
+
scene->film->pass_alpha_threshold = b_view_layer.pass_alpha_threshold();
scene->film->tag_passes_update(scene, passes);
scene->film->tag_update(scene);
@@ -526,19 +537,20 @@ void BlenderSession::render(BL::Depsgraph &b_depsgraph_)
builtin_images_load();
/* Attempt to free all data which is held by Blender side, since at this
- * point we knwo that we've got everything to render current view layer.
+ * point we know that we've got everything to render current view layer.
*/
- /* At the moment we only free if we are not doing multi-view (or if we are rendering the last view).
- * See T58142/D4239 for discussion.
+ /* At the moment we only free if we are not doing multi-view
+ * (or if we are rendering the last view). See T58142/D4239 for discussion.
*/
if (view_index == num_views - 1) {
free_blender_memory_if_possible();
}
- /* Make sure all views have different noise patterns. - hardcoded value just to make it random */
+ /* Make sure all views have different noise patterns. - hardcoded value just to make it random
+ */
if (view_index != 0) {
- scene->integrator->seed += hash_int_2d(scene->integrator->seed,
- hash_int(view_index * 0xdeadbeef));
+ scene->integrator->seed += hash_uint2(scene->integrator->seed,
+ hash_uint2(view_index * 0xdeadbeef, 0));
scene->integrator->tag_update(scene);
}
@@ -600,25 +612,6 @@ void BlenderSession::render(BL::Depsgraph &b_depsgraph_)
#endif
}
-static void populate_bake_data(BakeData *data,
- const int object_id,
- BL::BakePixel &pixel_array,
- const int num_pixels)
-{
- BL::BakePixel bp = pixel_array;
-
- int i;
- for (i = 0; i < num_pixels; i++) {
- if (bp.object_id() == object_id) {
- data->set(i, bp.primitive_id(), bp.uv(), bp.du_dx(), bp.du_dy(), bp.dv_dx(), bp.dv_dy());
- }
- else {
- data->set_null(i);
- }
- bp = bp.next();
- }
-}
-
static int bake_pass_filter_get(const int pass_filter)
{
int flag = BAKE_FILTER_NONE;
@@ -636,8 +629,6 @@ static int bake_pass_filter_get(const int pass_filter)
flag |= BAKE_FILTER_GLOSSY;
if ((pass_filter & BL::BakeSettings::pass_filter_TRANSMISSION) != 0)
flag |= BAKE_FILTER_TRANSMISSION;
- if ((pass_filter & BL::BakeSettings::pass_filter_SUBSURFACE) != 0)
- flag |= BAKE_FILTER_SUBSURFACE;
if ((pass_filter & BL::BakeSettings::pass_filter_EMIT) != 0)
flag |= BAKE_FILTER_EMISSION;
@@ -651,43 +642,26 @@ void BlenderSession::bake(BL::Depsgraph &b_depsgraph_,
BL::Object &b_object,
const string &pass_type,
const int pass_filter,
- const int object_id,
- BL::BakePixel &pixel_array,
- const size_t num_pixels,
- const int /*depth*/,
- float result[])
+ const int bake_width,
+ const int bake_height)
{
b_depsgraph = b_depsgraph_;
ShaderEvalType shader_type = get_shader_type(pass_type);
-
- /* Set baking flag in advance, so kernel loading can check if we need
- * any baking capabilities.
- */
- scene->bake_manager->set_baking(true);
-
- /* ensure kernels are loaded before we do any scene updates */
- session->load_kernels();
-
- if (shader_type == SHADER_EVAL_UV) {
- /* force UV to be available */
- Pass::add(PASS_UV, scene->film->passes);
- }
-
int bake_pass_filter = bake_pass_filter_get(pass_filter);
- bake_pass_filter = BakeManager::shader_type_to_pass_filter(shader_type, bake_pass_filter);
- /* force use_light_pass to be true if we bake more than just colors */
- if (bake_pass_filter & ~BAKE_FILTER_COLOR) {
- Pass::add(PASS_LIGHT, scene->film->passes);
- }
+ /* Initialize bake manager, before we load the baking kernels. */
+ scene->bake_manager->set(scene, b_object.name(), shader_type, bake_pass_filter);
- /* create device and update scene */
- scene->film->tag_update(scene);
- scene->integrator->tag_update(scene);
+ /* Passes are identified by name, so in order to return the combined pass we need to set the
+ * name. */
+ Pass::add(PASS_COMBINED, scene->film->passes, "Combined");
+
+ session->read_bake_tile_cb = function_bind(&BlenderSession::read_render_tile, this, _1);
+ session->write_render_tile_cb = function_bind(&BlenderSession::write_render_tile, this, _1);
if (!session->progress.get_cancel()) {
- /* update scene */
+ /* Sync scene. */
BL::Object b_camera_override(b_engine.camera_override());
sync->sync_camera(b_render, b_camera_override, width, height, "");
sync->sync_data(
@@ -695,76 +669,46 @@ void BlenderSession::bake(BL::Depsgraph &b_depsgraph_,
builtin_images_load();
}
- BakeData *bake_data = NULL;
+ /* Object might have been disabled for rendering or excluded in some
+ * other way, in that case Blender will report a warning afterwards. */
+ bool object_found = false;
+ foreach (Object *ob, scene->objects) {
+ if (ob->name == b_object.name()) {
+ object_found = true;
+ break;
+ }
+ }
- if (!session->progress.get_cancel()) {
- /* get buffer parameters */
+ if (object_found && !session->progress.get_cancel()) {
+ /* Get session and buffer parameters. */
SessionParams session_params = BlenderSync::get_session_params(
b_engine, b_userpref, b_scene, background);
- BufferParams buffer_params = BlenderSync::get_buffer_params(
- b_render, b_v3d, b_rv3d, scene->camera, width, height);
-
- scene->bake_manager->set_shader_limit((size_t)b_engine.tile_x(), (size_t)b_engine.tile_y());
-
- /* set number of samples */
- session->tile_manager.set_samples(session_params.samples);
- session->reset(buffer_params, session_params.samples);
- session->update_scene();
-
- /* find object index. todo: is arbitrary - copied from mesh_displace.cpp */
- size_t object_index = OBJECT_NONE;
- int tri_offset = 0;
-
- for (size_t i = 0; i < scene->objects.size(); i++) {
- if (strcmp(scene->objects[i]->name.c_str(), b_object.name().c_str()) == 0) {
- object_index = i;
- tri_offset = scene->objects[i]->mesh->tri_offset;
- break;
- }
- }
-
- /* Object might have been disabled for rendering or excluded in some
- * other way, in that case Blender will report a warning afterwards. */
- if (object_index != OBJECT_NONE) {
- int object = object_index;
+ session_params.progressive_refine = false;
- bake_data = scene->bake_manager->init(object, tri_offset, num_pixels);
- populate_bake_data(bake_data, object_id, pixel_array, num_pixels);
- }
+ BufferParams buffer_params;
+ buffer_params.width = bake_width;
+ buffer_params.height = bake_height;
+ buffer_params.passes = scene->film->passes;
- /* set number of samples */
+ /* Update session. */
session->tile_manager.set_samples(session_params.samples);
session->reset(buffer_params, session_params.samples);
- session->update_scene();
session->progress.set_update_callback(
function_bind(&BlenderSession::update_bake_progress, this));
}
/* Perform bake. Check cancel to avoid crash with incomplete scene data. */
- if (!session->progress.get_cancel() && bake_data) {
- scene->bake_manager->bake(scene->device,
- &scene->dscene,
- scene,
- session->progress,
- shader_type,
- bake_pass_filter,
- bake_data,
- result);
+ if (object_found && !session->progress.get_cancel()) {
+ session->start();
+ session->wait();
}
- /* free all memory used (host and device), so we wouldn't leave render
- * engine with extra memory allocated
- */
-
- session->device_free();
-
- delete sync;
- sync = NULL;
+ session->read_bake_tile_cb = function_null;
+ session->write_render_tile_cb = function_null;
}
-void BlenderSession::do_write_update_render_result(BL::RenderResult &b_rr,
- BL::RenderLayer &b_rlay,
+void BlenderSession::do_write_update_render_result(BL::RenderLayer &b_rlay,
RenderTile &rtile,
bool do_update_only)
{
@@ -791,18 +735,13 @@ void BlenderSession::do_write_update_render_result(BL::RenderResult &b_rr,
for (b_rlay.passes.begin(b_iter); b_iter != b_rlay.passes.end(); ++b_iter) {
BL::RenderPass b_pass(*b_iter);
-
- /* find matching pass type */
- PassType pass_type = BlenderSync::get_pass_type(b_pass);
int components = b_pass.channels();
- bool read = false;
- if (pass_type != PASS_NONE) {
- /* copy pixels */
- read = buffers->get_pass_rect(
- pass_type, exposure, sample, components, &pixels[0], b_pass.name());
- }
- else {
+ /* Copy pixels from regular render passes. */
+ bool read = buffers->get_pass_rect(b_pass.name(), exposure, sample, components, &pixels[0]);
+
+ /* If denoising pass, */
+ if (!read) {
int denoising_offset = BlenderSync::get_denoising_pass(b_pass);
if (denoising_offset >= 0) {
read = buffers->get_denoising_pass_rect(
@@ -820,26 +759,19 @@ void BlenderSession::do_write_update_render_result(BL::RenderResult &b_rr,
else {
/* copy combined pass */
BL::RenderPass b_combined_pass(b_rlay.passes.find_by_name("Combined", b_rview_name.c_str()));
- if (buffers->get_pass_rect(PASS_COMBINED, exposure, sample, 4, &pixels[0], "Combined"))
+ if (buffers->get_pass_rect("Combined", exposure, sample, 4, &pixels[0]))
b_combined_pass.rect(&pixels[0]);
}
-
- /* tag result as updated */
- b_engine.update_result(b_rr);
}
-void BlenderSession::write_render_result(BL::RenderResult &b_rr,
- BL::RenderLayer &b_rlay,
- RenderTile &rtile)
+void BlenderSession::write_render_result(BL::RenderLayer &b_rlay, RenderTile &rtile)
{
- do_write_update_render_result(b_rr, b_rlay, rtile, false);
+ do_write_update_render_result(b_rlay, rtile, false);
}
-void BlenderSession::update_render_result(BL::RenderResult &b_rr,
- BL::RenderLayer &b_rlay,
- RenderTile &rtile)
+void BlenderSession::update_render_result(BL::RenderLayer &b_rlay, RenderTile &rtile)
{
- do_write_update_render_result(b_rr, b_rlay, rtile, true);
+ do_write_update_render_result(b_rlay, rtile, true);
}
void BlenderSession::synchronize(BL::Depsgraph &b_depsgraph_)
@@ -857,16 +789,16 @@ void BlenderSession::synchronize(BL::Depsgraph &b_depsgraph_)
if (session->params.modified(session_params) || scene->params.modified(scene_params)) {
free_session();
create_session();
- return;
}
/* increase samples, but never decrease */
session->set_samples(session_params.samples);
+ session->set_denoising_start_sample(session_params.denoising.start_sample);
session->set_pause(session_pause);
/* copy recalc flags, outside of mutex so we can decide to do the real
* synchronization at a later time to not block on running updates */
- sync->sync_recalc(b_depsgraph_);
+ sync->sync_recalc(b_depsgraph_, b_v3d);
/* don't do synchronization if on pause */
if (session_pause) {
@@ -892,21 +824,36 @@ void BlenderSession::synchronize(BL::Depsgraph &b_depsgraph_)
else
sync->sync_camera(b_render, b_camera_override, width, height, "");
- builtin_images_load();
+ /* get buffer parameters */
+ BufferParams buffer_params = BlenderSync::get_buffer_params(
+ b_render, b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use);
- /* unlock */
- session->scene->mutex.unlock();
+ if (!buffer_params.denoising_data_pass) {
+ session_params.denoising.use = false;
+ }
+
+ session->set_denoising(session_params.denoising);
+
+ /* Update film if denoising data was enabled or disabled. */
+ if (scene->film->denoising_data_pass != buffer_params.denoising_data_pass) {
+ scene->film->denoising_data_pass = buffer_params.denoising_data_pass;
+ scene->film->tag_update(scene);
+ }
/* reset if needed */
if (scene->need_reset()) {
- BufferParams buffer_params = BlenderSync::get_buffer_params(
- b_render, b_v3d, b_rv3d, scene->camera, width, height);
session->reset(buffer_params, session_params.samples);
+ /* After session reset, so device is not accessing image data anymore. */
+ builtin_images_load();
+
/* reset time */
start_resize_time = 0.0;
}
+ /* unlock */
+ session->scene->mutex.unlock();
+
/* Start rendering thread, if it's not running already. Do this
* after all scene data has been synced at least once. */
session->start();
@@ -961,7 +908,7 @@ bool BlenderSession::draw(int w, int h)
SessionParams session_params = BlenderSync::get_session_params(
b_engine, b_userpref, b_scene, background);
BufferParams buffer_params = BlenderSync::get_buffer_params(
- b_render, b_v3d, b_rv3d, scene->camera, width, height);
+ b_render, b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use);
bool session_pause = BlenderSync::get_session_pause(b_scene, background);
if (session_pause == false) {
@@ -979,7 +926,7 @@ bool BlenderSession::draw(int w, int h)
/* draw */
BufferParams buffer_params = BlenderSync::get_buffer_params(
- b_render, b_v3d, b_rv3d, scene->camera, width, height);
+ b_render, b_v3d, b_rv3d, scene->camera, width, height, session->params.denoising.use);
DeviceDrawParams draw_params;
if (session->params.display_buffer_linear) {
@@ -1057,8 +1004,9 @@ void BlenderSession::update_status_progress()
}
double current_time = time_dt();
- /* When rendering in a window, redraw the status at least once per second to keep the elapsed and remaining time up-to-date.
- * For headless rendering, only report when something significant changes to keep the console output readable. */
+ /* When rendering in a window, redraw the status at least once per second to keep the elapsed and
+ * remaining time up-to-date. For headless rendering, only report when something significant
+ * changes to keep the console output readable. */
if (status != last_status || (!headless && (current_time - last_status_time) > 1.0)) {
b_engine.update_stats("", (timestatus + scene_status + status).c_str());
b_engine.update_memory_stats(mem_used, mem_peak);
@@ -1119,324 +1067,6 @@ void BlenderSession::test_cancel()
session->progress.set_cancel("Cancelled");
}
-/* builtin image file name is actually an image datablock name with
- * absolute sequence frame number concatenated via '@' character
- *
- * this function splits frame from builtin name
- */
-int BlenderSession::builtin_image_frame(const string &builtin_name)
-{
- int last = builtin_name.find_last_of('@');
- return atoi(builtin_name.substr(last + 1, builtin_name.size() - last - 1).c_str());
-}
-
-void BlenderSession::builtin_image_info(const string &builtin_name,
- void *builtin_data,
- ImageMetaData &metadata)
-{
- /* empty image */
- metadata.width = 1;
- metadata.height = 1;
-
- if (!builtin_data)
- return;
-
- /* recover ID pointer */
- PointerRNA ptr;
- RNA_id_pointer_create((ID *)builtin_data, &ptr);
- BL::ID b_id(ptr);
-
- if (b_id.is_a(&RNA_Image)) {
- /* image data */
- BL::Image b_image(b_id);
-
- metadata.builtin_free_cache = !b_image.has_data();
- metadata.is_float = b_image.is_float();
- metadata.width = b_image.size()[0];
- metadata.height = b_image.size()[1];
- metadata.depth = 1;
- metadata.channels = b_image.channels();
- }
- else if (b_id.is_a(&RNA_Object)) {
- /* smoke volume data */
- BL::Object b_ob(b_id);
- BL::SmokeDomainSettings b_domain = object_smoke_domain_find(b_ob);
-
- metadata.is_float = true;
- metadata.depth = 1;
- metadata.channels = 1;
-
- if (!b_domain)
- return;
-
- if (builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_DENSITY) ||
- builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_FLAME) ||
- builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_HEAT) ||
- builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_TEMPERATURE))
- metadata.channels = 1;
- else if (builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_COLOR))
- metadata.channels = 4;
- else if (builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_VELOCITY))
- metadata.channels = 3;
- else
- return;
-
- int3 resolution = get_int3(b_domain.domain_resolution());
- int amplify = (b_domain.use_high_resolution()) ? b_domain.amplify() + 1 : 1;
-
- /* Velocity and heat data is always low-resolution. */
- if (builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_VELOCITY) ||
- builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_HEAT)) {
- amplify = 1;
- }
-
- metadata.width = resolution.x * amplify;
- metadata.height = resolution.y * amplify;
- metadata.depth = resolution.z * amplify;
- }
- else {
- /* TODO(sergey): Check we're indeed in shader node tree. */
- PointerRNA ptr;
- RNA_pointer_create(NULL, &RNA_Node, builtin_data, &ptr);
- BL::Node b_node(ptr);
- if (b_node.is_a(&RNA_ShaderNodeTexPointDensity)) {
- BL::ShaderNodeTexPointDensity b_point_density_node(b_node);
- metadata.channels = 4;
- metadata.width = b_point_density_node.resolution();
- metadata.height = metadata.width;
- metadata.depth = metadata.width;
- metadata.is_float = true;
- }
- }
-}
-
-bool BlenderSession::builtin_image_pixels(const string &builtin_name,
- void *builtin_data,
- unsigned char *pixels,
- const size_t pixels_size,
- const bool free_cache)
-{
- if (!builtin_data) {
- return false;
- }
-
- const int frame = builtin_image_frame(builtin_name);
-
- PointerRNA ptr;
- RNA_id_pointer_create((ID *)builtin_data, &ptr);
- BL::Image b_image(ptr);
-
- const int width = b_image.size()[0];
- const int height = b_image.size()[1];
- const int channels = b_image.channels();
-
- unsigned char *image_pixels = image_get_pixels_for_frame(b_image, frame);
- const size_t num_pixels = ((size_t)width) * height;
-
- if (image_pixels && num_pixels * channels == pixels_size) {
- memcpy(pixels, image_pixels, pixels_size * sizeof(unsigned char));
- }
- else {
- if (channels == 1) {
- memset(pixels, 0, pixels_size * sizeof(unsigned char));
- }
- else {
- const size_t num_pixels_safe = pixels_size / channels;
- unsigned char *cp = pixels;
- for (size_t i = 0; i < num_pixels_safe; i++, cp += channels) {
- cp[0] = 255;
- cp[1] = 0;
- cp[2] = 255;
- if (channels == 4) {
- cp[3] = 255;
- }
- }
- }
- }
-
- if (image_pixels) {
- MEM_freeN(image_pixels);
- }
-
- /* Free image buffers to save memory during render. */
- if (free_cache) {
- b_image.buffers_free();
- }
-
- /* Premultiply, byte images are always straight for Blender. */
- unsigned char *cp = pixels;
- for (size_t i = 0; i < num_pixels; i++, cp += channels) {
- cp[0] = (cp[0] * cp[3]) >> 8;
- cp[1] = (cp[1] * cp[3]) >> 8;
- cp[2] = (cp[2] * cp[3]) >> 8;
- }
- return true;
-}
-
-bool BlenderSession::builtin_image_float_pixels(const string &builtin_name,
- void *builtin_data,
- float *pixels,
- const size_t pixels_size,
- const bool free_cache)
-{
- if (!builtin_data) {
- return false;
- }
-
- PointerRNA ptr;
- RNA_id_pointer_create((ID *)builtin_data, &ptr);
- BL::ID b_id(ptr);
-
- if (b_id.is_a(&RNA_Image)) {
- /* image data */
- BL::Image b_image(b_id);
- int frame = builtin_image_frame(builtin_name);
-
- const int width = b_image.size()[0];
- const int height = b_image.size()[1];
- const int channels = b_image.channels();
-
- float *image_pixels;
- image_pixels = image_get_float_pixels_for_frame(b_image, frame);
- const size_t num_pixels = ((size_t)width) * height;
-
- if (image_pixels && num_pixels * channels == pixels_size) {
- memcpy(pixels, image_pixels, pixels_size * sizeof(float));
- }
- else {
- if (channels == 1) {
- memset(pixels, 0, num_pixels * sizeof(float));
- }
- else {
- const size_t num_pixels_safe = pixels_size / channels;
- float *fp = pixels;
- for (int i = 0; i < num_pixels_safe; i++, fp += channels) {
- fp[0] = 1.0f;
- fp[1] = 0.0f;
- fp[2] = 1.0f;
- if (channels == 4) {
- fp[3] = 1.0f;
- }
- }
- }
- }
-
- if (image_pixels) {
- MEM_freeN(image_pixels);
- }
-
- /* Free image buffers to save memory during render. */
- if (free_cache) {
- b_image.buffers_free();
- }
-
- return true;
- }
- else if (b_id.is_a(&RNA_Object)) {
- /* smoke volume data */
- BL::Object b_ob(b_id);
- BL::SmokeDomainSettings b_domain = object_smoke_domain_find(b_ob);
-
- if (!b_domain) {
- return false;
- }
-
- int3 resolution = get_int3(b_domain.domain_resolution());
- int length, amplify = (b_domain.use_high_resolution()) ? b_domain.amplify() + 1 : 1;
-
- /* Velocity and heat data is always low-resolution. */
- if (builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_VELOCITY) ||
- builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_HEAT)) {
- amplify = 1;
- }
-
- const int width = resolution.x * amplify;
- const int height = resolution.y * amplify;
- const int depth = resolution.z * amplify;
- const size_t num_pixels = ((size_t)width) * height * depth;
-
- if (builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_DENSITY)) {
- SmokeDomainSettings_density_grid_get_length(&b_domain.ptr, &length);
- if (length == num_pixels) {
- SmokeDomainSettings_density_grid_get(&b_domain.ptr, pixels);
- return true;
- }
- }
- else if (builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_FLAME)) {
- /* this is in range 0..1, and interpreted by the OpenGL smoke viewer
- * as 1500..3000 K with the first part faded to zero density */
- SmokeDomainSettings_flame_grid_get_length(&b_domain.ptr, &length);
- if (length == num_pixels) {
- SmokeDomainSettings_flame_grid_get(&b_domain.ptr, pixels);
- return true;
- }
- }
- else if (builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_COLOR)) {
- /* the RGB is "premultiplied" by density for better interpolation results */
- SmokeDomainSettings_color_grid_get_length(&b_domain.ptr, &length);
- if (length == num_pixels * 4) {
- SmokeDomainSettings_color_grid_get(&b_domain.ptr, pixels);
- return true;
- }
- }
- else if (builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_VELOCITY)) {
- SmokeDomainSettings_velocity_grid_get_length(&b_domain.ptr, &length);
- if (length == num_pixels * 3) {
- SmokeDomainSettings_velocity_grid_get(&b_domain.ptr, pixels);
- return true;
- }
- }
- else if (builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_HEAT)) {
- SmokeDomainSettings_heat_grid_get_length(&b_domain.ptr, &length);
- if (length == num_pixels) {
- SmokeDomainSettings_heat_grid_get(&b_domain.ptr, pixels);
- return true;
- }
- }
- else if (builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_TEMPERATURE)) {
- SmokeDomainSettings_temperature_grid_get_length(&b_domain.ptr, &length);
- if (length == num_pixels) {
- SmokeDomainSettings_temperature_grid_get(&b_domain.ptr, pixels);
- return true;
- }
- }
- else {
- fprintf(
- stderr, "Cycles error: unknown volume attribute %s, skipping\n", builtin_name.c_str());
- pixels[0] = 0.0f;
- return false;
- }
-
- fprintf(stderr, "Cycles error: unexpected smoke volume resolution, skipping\n");
- }
- else {
- /* We originally were passing view_layer here but in reality we need a
- * a depsgraph to pass to the RE_point_density_minmax() function.
- */
- /* TODO(sergey): Check we're indeed in shader node tree. */
- PointerRNA ptr;
- RNA_pointer_create(NULL, &RNA_Node, builtin_data, &ptr);
- BL::Node b_node(ptr);
- if (b_node.is_a(&RNA_ShaderNodeTexPointDensity)) {
- BL::ShaderNodeTexPointDensity b_point_density_node(b_node);
- int length;
- b_point_density_node.calc_point_density(b_depsgraph, &length, &pixels);
- }
- }
-
- return false;
-}
-
-void BlenderSession::builtin_images_load()
-{
- /* Force builtin images to be loaded along with Blender data sync. This
- * is needed because we may be reading from depsgraph evaluated data which
- * can be freed by Blender before Cycles reads it. */
- ImageManager *manager = session->scene->image_manager;
- Device *device = session->device;
- manager->device_load_builtin(device, session->scene, session->progress);
-}
-
void BlenderSession::update_resumable_tile_manager(int num_samples)
{
const int num_resumable_chunks = BlenderSession::num_resumable_chunks,
@@ -1470,8 +1100,8 @@ void BlenderSession::update_resumable_tile_manager(int num_samples)
/* Round after doing the multiplications with num_chunks and num_samples_per_chunk
* to allow for many small chunks. */
- int rounded_range_start_sample = (int)floor(range_start_sample + 0.5f);
- int rounded_range_num_samples = max((int)floor(range_num_samples + 0.5f), 1);
+ int rounded_range_start_sample = (int)floorf(range_start_sample + 0.5f);
+ int rounded_range_num_samples = max((int)floorf(range_num_samples + 0.5f), 1);
/* Make sure we don't overshoot. */
if (rounded_range_start_sample + rounded_range_num_samples > num_samples) {
diff --git a/intern/cycles/blender/blender_session.h b/intern/cycles/blender/blender_session.h
index f0107d4e0b1..34e952e312b 100644
--- a/intern/cycles/blender/blender_session.h
+++ b/intern/cycles/blender/blender_session.h
@@ -17,15 +17,19 @@
#ifndef __BLENDER_SESSION_H__
#define __BLENDER_SESSION_H__
+#include "RNA_blender_cpp.h"
+
#include "device/device.h"
+
+#include "render/bake.h"
#include "render/scene.h"
#include "render/session.h"
-#include "render/bake.h"
#include "util/util_vector.h"
CCL_NAMESPACE_BEGIN
+class BlenderSync;
class ImageMetaData;
class Scene;
class Session;
@@ -49,8 +53,6 @@ class BlenderSession {
~BlenderSession();
- void create();
-
/* session */
void create_session();
void free_session();
@@ -64,18 +66,16 @@ class BlenderSession {
BL::Object &b_object,
const string &pass_type,
const int custom_flag,
- const int object_id,
- BL::BakePixel &pixel_array,
- const size_t num_pixels,
- const int depth,
- float pixels[]);
+ const int bake_width,
+ const int bake_height);
- void write_render_result(BL::RenderResult &b_rr, BL::RenderLayer &b_rlay, RenderTile &rtile);
+ void write_render_result(BL::RenderLayer &b_rlay, RenderTile &rtile);
void write_render_tile(RenderTile &rtile);
+ void read_render_tile(RenderTile &rtile);
/* update functions are used to update display buffer only after sample was rendered
* only needed for better visual feedback */
- void update_render_result(BL::RenderResult &b_rr, BL::RenderLayer &b_rlay, RenderTile &rtile);
+ void update_render_result(BL::RenderLayer &b_rlay, RenderTile &rtile);
void update_render_tile(RenderTile &rtile, bool highlight);
/* interactive updates */
@@ -150,24 +150,14 @@ class BlenderSession {
protected:
void stamp_view_layer_metadata(Scene *scene, const string &view_layer_name);
- void do_write_update_render_result(BL::RenderResult &b_rr,
- BL::RenderLayer &b_rlay,
+ void do_write_update_render_result(BL::RenderLayer &b_rlay,
RenderTile &rtile,
bool do_update_only);
- void do_write_update_render_tile(RenderTile &rtile, bool do_update_only, bool highlight);
-
- int builtin_image_frame(const string &builtin_name);
- void builtin_image_info(const string &builtin_name, void *builtin_data, ImageMetaData &metadata);
- bool builtin_image_pixels(const string &builtin_name,
- void *builtin_data,
- unsigned char *pixels,
- const size_t pixels_size,
- const bool free_cache);
- bool builtin_image_float_pixels(const string &builtin_name,
- void *builtin_data,
- float *pixels,
- const size_t pixels_size,
- const bool free_cache);
+ void do_write_update_render_tile(RenderTile &rtile,
+ bool do_update_only,
+ bool do_read_only,
+ bool highlight);
+
void builtin_images_load();
/* Update tile manager to reflect resumable render settings. */
diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp
index 169c4d414a6..33e73b5a4b9 100644
--- a/intern/cycles/blender/blender_shader.cpp
+++ b/intern/cycles/blender/blender_shader.cpp
@@ -15,6 +15,7 @@
*/
#include "render/background.h"
+#include "render/colorspace.h"
#include "render/graph.h"
#include "render/light.h"
#include "render/nodes.h"
@@ -22,14 +23,15 @@
#include "render/scene.h"
#include "render/shader.h"
-#include "blender/blender_texture.h"
+#include "blender/blender_image.h"
#include "blender/blender_sync.h"
+#include "blender/blender_texture.h"
#include "blender/blender_util.h"
#include "util/util_debug.h"
#include "util/util_foreach.h"
-#include "util/util_string.h"
#include "util/util_set.h"
+#include "util/util_string.h"
#include "util/util_task.h"
CCL_NAMESPACE_BEGIN
@@ -89,6 +91,12 @@ template<typename NodeType> static ExtensionType get_image_extension(NodeType &b
return (ExtensionType)validate_enum_value(value, EXTENSION_NUM_TYPES, EXTENSION_REPEAT);
}
+static ImageAlphaType get_image_alpha_type(BL::Image &b_image)
+{
+ int value = b_image.alpha_mode();
+ return (ImageAlphaType)validate_enum_value(value, IMAGE_ALPHA_NUM_TYPES, IMAGE_ALPHA_AUTO);
+}
+
/* Graph */
static BL::NodeSocket get_node_output(BL::Node &b_node, const string &name)
@@ -201,24 +209,6 @@ static void get_tex_mapping(TextureMapping *mapping, BL::TexMapping &b_mapping)
mapping->z_mapping = (TextureMapping::Mapping)b_mapping.mapping_z();
}
-static void get_tex_mapping(TextureMapping *mapping, BL::ShaderNodeMapping &b_mapping)
-{
- if (!b_mapping)
- return;
-
- mapping->translation = get_float3(b_mapping.translation());
- mapping->rotation = get_float3(b_mapping.rotation());
- mapping->scale = get_float3(b_mapping.scale());
- mapping->type = (TextureMapping::Type)b_mapping.vector_type();
-
- mapping->use_minmax = b_mapping.use_min() || b_mapping.use_max();
-
- if (b_mapping.use_min())
- mapping->min = get_float3(b_mapping.min());
- if (b_mapping.use_max())
- mapping->max = get_float3(b_mapping.max());
-}
-
static ShaderNode *add_node(Scene *scene,
BL::RenderEngine &b_engine,
BL::BlendData &b_data,
@@ -308,18 +298,38 @@ static ShaderNode *add_node(Scene *scene,
else if (b_node.is_a(&RNA_ShaderNodeRGBToBW)) {
node = new RGBToBWNode();
}
+ else if (b_node.is_a(&RNA_ShaderNodeMapRange)) {
+ BL::ShaderNodeMapRange b_map_range_node(b_node);
+ MapRangeNode *map_range_node = new MapRangeNode();
+ map_range_node->clamp = b_map_range_node.clamp();
+ map_range_node->type = (NodeMapRangeType)b_map_range_node.interpolation_type();
+ node = map_range_node;
+ }
+ else if (b_node.is_a(&RNA_ShaderNodeClamp)) {
+ BL::ShaderNodeClamp b_clamp_node(b_node);
+ ClampNode *clamp_node = new ClampNode();
+ clamp_node->type = (NodeClampType)b_clamp_node.clamp_type();
+ node = clamp_node;
+ }
else if (b_node.is_a(&RNA_ShaderNodeMath)) {
BL::ShaderNodeMath b_math_node(b_node);
- MathNode *math = new MathNode();
- math->type = (NodeMath)b_math_node.operation();
- math->use_clamp = b_math_node.use_clamp();
- node = math;
+ MathNode *math_node = new MathNode();
+ math_node->type = (NodeMathType)b_math_node.operation();
+ math_node->use_clamp = b_math_node.use_clamp();
+ node = math_node;
}
else if (b_node.is_a(&RNA_ShaderNodeVectorMath)) {
BL::ShaderNodeVectorMath b_vector_math_node(b_node);
- VectorMathNode *vmath = new VectorMathNode();
- vmath->type = (NodeVectorMath)b_vector_math_node.operation();
- node = vmath;
+ VectorMathNode *vector_math_node = new VectorMathNode();
+ vector_math_node->type = (NodeVectorMathType)b_vector_math_node.operation();
+ node = vector_math_node;
+ }
+ else if (b_node.is_a(&RNA_ShaderNodeVectorRotate)) {
+ BL::ShaderNodeVectorRotate b_vector_rotate_node(b_node);
+ VectorRotateNode *vector_rotate_node = new VectorRotateNode();
+ vector_rotate_node->type = (NodeVectorRotateType)b_vector_rotate_node.rotation_type();
+ vector_rotate_node->invert = b_vector_rotate_node.invert();
+ node = vector_rotate_node;
}
else if (b_node.is_a(&RNA_ShaderNodeVectorTransform)) {
BL::ShaderNodeVectorTransform b_vector_transform_node(b_node);
@@ -341,9 +351,7 @@ static ShaderNode *add_node(Scene *scene,
else if (b_node.is_a(&RNA_ShaderNodeMapping)) {
BL::ShaderNodeMapping b_mapping_node(b_node);
MappingNode *mapping = new MappingNode();
-
- get_tex_mapping(&mapping->tex_mapping, b_mapping_node);
-
+ mapping->type = (NodeMappingType)b_mapping_node.vector_type();
node = mapping;
}
else if (b_node.is_a(&RNA_ShaderNodeFresnel)) {
@@ -376,16 +384,16 @@ static ShaderNode *add_node(Scene *scene,
switch (b_aniso_node.distribution()) {
case BL::ShaderNodeBsdfAnisotropic::distribution_BECKMANN:
- aniso->distribution = CLOSURE_BSDF_MICROFACET_BECKMANN_ANISO_ID;
+ aniso->distribution = CLOSURE_BSDF_MICROFACET_BECKMANN_ID;
break;
case BL::ShaderNodeBsdfAnisotropic::distribution_GGX:
- aniso->distribution = CLOSURE_BSDF_MICROFACET_GGX_ANISO_ID;
+ aniso->distribution = CLOSURE_BSDF_MICROFACET_GGX_ID;
break;
case BL::ShaderNodeBsdfAnisotropic::distribution_MULTI_GGX:
- aniso->distribution = CLOSURE_BSDF_MICROFACET_MULTI_GGX_ANISO_ID;
+ aniso->distribution = CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID;
break;
case BL::ShaderNodeBsdfAnisotropic::distribution_ASHIKHMIN_SHIRLEY:
- aniso->distribution = CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ANISO_ID;
+ aniso->distribution = CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID;
break;
}
@@ -591,6 +599,15 @@ static ShaderNode *add_node(Scene *scene,
else if (b_node.is_a(&RNA_ShaderNodeHairInfo)) {
node = new HairInfoNode();
}
+ else if (b_node.is_a(&RNA_ShaderNodeVolumeInfo)) {
+ node = new VolumeInfoNode();
+ }
+ else if (b_node.is_a(&RNA_ShaderNodeVertexColor)) {
+ BL::ShaderNodeVertexColor b_vertex_color_node(b_node);
+ VertexColorNode *vertex_color_node = new VertexColorNode();
+ vertex_color_node->layer_name = b_vertex_color_node.layer_name();
+ node = vertex_color_node;
+ }
else if (b_node.is_a(&RNA_ShaderNodeBump)) {
BL::ShaderNodeBump b_bump_node(b_node);
BumpNode *bump = new BumpNode();
@@ -603,16 +620,16 @@ static ShaderNode *add_node(Scene *scene,
/* create script node */
BL::ShaderNodeScript b_script_node(b_node);
- OSLShaderManager *manager = (OSLShaderManager *)scene->shader_manager;
+ ShaderManager *manager = scene->shader_manager;
string bytecode_hash = b_script_node.bytecode_hash();
if (!bytecode_hash.empty()) {
- node = manager->osl_node("", bytecode_hash, b_script_node.bytecode());
+ node = OSLShaderManager::osl_node(manager, "", bytecode_hash, b_script_node.bytecode());
}
else {
string absolute_filepath = blender_absolute_path(
b_data, b_ntree, b_script_node.filepath());
- node = manager->osl_node(absolute_filepath, "");
+ node = OSLShaderManager::osl_node(manager, absolute_filepath, "");
}
}
#else
@@ -625,7 +642,27 @@ static ShaderNode *add_node(Scene *scene,
BL::Image b_image(b_image_node.image());
BL::ImageUser b_image_user(b_image_node.image_user());
ImageTextureNode *image = new ImageTextureNode();
+
+ image->interpolation = get_image_interpolation(b_image_node);
+ image->extension = get_image_extension(b_image_node);
+ image->projection = (NodeImageProjection)b_image_node.projection();
+ image->projection_blend = b_image_node.projection_blend();
+ BL::TexMapping b_texture_mapping(b_image_node.texture_mapping());
+ get_tex_mapping(&image->tex_mapping, b_texture_mapping);
+
if (b_image) {
+ PointerRNA colorspace_ptr = b_image.colorspace_settings().ptr;
+ image->colorspace = get_enum_identifier(colorspace_ptr, "name");
+
+ image->animated = b_image_node.image_user().use_auto_refresh();
+ image->alpha_type = get_image_alpha_type(b_image);
+
+ image->tiles.clear();
+ BL::Image::tiles_iterator b_iter;
+ for (b_image.tiles.begin(b_iter); b_iter != b_image.tiles.end(); ++b_iter) {
+ image->tiles.push_back(b_iter->number());
+ }
+
/* builtin images will use callback-based reading because
* they could only be loaded correct from blender side
*/
@@ -642,37 +679,14 @@ static ShaderNode *add_node(Scene *scene,
*/
int scene_frame = b_scene.frame_current();
int image_frame = image_user_frame_number(b_image_user, scene_frame);
- image->filename = b_image.name() + "@" + string_printf("%d", image_frame);
- image->builtin_data = b_image.ptr.data;
+ image->handle = scene->image_manager->add_image(
+ new BlenderImageLoader(b_image, image_frame), image->image_params());
}
else {
- image->filename = image_user_file_path(b_image_user, b_image, b_scene.frame_current());
- image->builtin_data = NULL;
- }
-
- image->animated = b_image_node.image_user().use_auto_refresh();
- image->use_alpha = b_image.use_alpha();
-
- /* TODO: restore */
- /* TODO(sergey): Does not work properly when we change builtin type. */
-#if 0
- if(b_image.is_updated()) {
- scene->image_manager->tag_reload_image(
- image->filename.string(),
- image->builtin_data,
- get_image_interpolation(b_image_node),
- get_image_extension(b_image_node),
- image->use_alpha);
+ image->filename = image_user_file_path(
+ b_image_user, b_image, b_scene.frame_current(), true);
}
-#endif
}
- image->color_space = (NodeImageColorSpace)b_image_node.color_space();
- image->projection = (NodeImageProjection)b_image_node.projection();
- image->interpolation = get_image_interpolation(b_image_node);
- image->extension = get_image_extension(b_image_node);
- image->projection_blend = b_image_node.projection_blend();
- BL::TexMapping b_texture_mapping(b_image_node.texture_mapping());
- get_tex_mapping(&image->tex_mapping, b_texture_mapping);
node = image;
}
else if (b_node.is_a(&RNA_ShaderNodeTexEnvironment)) {
@@ -680,7 +694,19 @@ static ShaderNode *add_node(Scene *scene,
BL::Image b_image(b_env_node.image());
BL::ImageUser b_image_user(b_env_node.image_user());
EnvironmentTextureNode *env = new EnvironmentTextureNode();
+
+ env->interpolation = get_image_interpolation(b_env_node);
+ env->projection = (NodeEnvironmentProjection)b_env_node.projection();
+ BL::TexMapping b_texture_mapping(b_env_node.texture_mapping());
+ get_tex_mapping(&env->tex_mapping, b_texture_mapping);
+
if (b_image) {
+ PointerRNA colorspace_ptr = b_image.colorspace_settings().ptr;
+ env->colorspace = get_enum_identifier(colorspace_ptr, "name");
+
+ env->animated = b_env_node.image_user().use_auto_refresh();
+ env->alpha_type = get_image_alpha_type(b_image);
+
bool is_builtin = b_image.packed_file() || b_image.source() == BL::Image::source_GENERATED ||
b_image.source() == BL::Image::source_MOVIE ||
(b_engine.is_preview() && b_image.source() != BL::Image::source_SEQUENCE);
@@ -688,35 +714,14 @@ static ShaderNode *add_node(Scene *scene,
if (is_builtin) {
int scene_frame = b_scene.frame_current();
int image_frame = image_user_frame_number(b_image_user, scene_frame);
- env->filename = b_image.name() + "@" + string_printf("%d", image_frame);
- env->builtin_data = b_image.ptr.data;
+ env->handle = scene->image_manager->add_image(new BlenderImageLoader(b_image, image_frame),
+ env->image_params());
}
else {
- env->filename = image_user_file_path(b_image_user, b_image, b_scene.frame_current());
- env->builtin_data = NULL;
- }
-
- env->animated = b_env_node.image_user().use_auto_refresh();
- env->use_alpha = b_image.use_alpha();
-
- /* TODO: restore */
- /* TODO(sergey): Does not work properly when we change builtin type. */
-#if 0
- if(b_image.is_updated()) {
- scene->image_manager->tag_reload_image(
- env->filename.string(),
- env->builtin_data,
- get_image_interpolation(b_env_node),
- EXTENSION_REPEAT,
- env->use_alpha);
+ env->filename = image_user_file_path(
+ b_image_user, b_image, b_scene.frame_current(), false);
}
-#endif
}
- env->color_space = (NodeImageColorSpace)b_env_node.color_space();
- env->interpolation = get_image_interpolation(b_env_node);
- env->projection = (NodeEnvironmentProjection)b_env_node.projection();
- BL::TexMapping b_texture_mapping(b_env_node.texture_mapping());
- get_tex_mapping(&env->tex_mapping, b_texture_mapping);
node = env;
}
else if (b_node.is_a(&RNA_ShaderNodeTexGradient)) {
@@ -730,9 +735,9 @@ static ShaderNode *add_node(Scene *scene,
else if (b_node.is_a(&RNA_ShaderNodeTexVoronoi)) {
BL::ShaderNodeTexVoronoi b_voronoi_node(b_node);
VoronoiTextureNode *voronoi = new VoronoiTextureNode();
- voronoi->coloring = (NodeVoronoiColoring)b_voronoi_node.coloring();
- voronoi->metric = (NodeVoronoiDistanceMetric)b_voronoi_node.distance();
+ voronoi->dimensions = b_voronoi_node.voronoi_dimensions();
voronoi->feature = (NodeVoronoiFeature)b_voronoi_node.feature();
+ voronoi->metric = (NodeVoronoiDistanceMetric)b_voronoi_node.distance();
BL::TexMapping b_texture_mapping(b_voronoi_node.texture_mapping());
get_tex_mapping(&voronoi->tex_mapping, b_texture_mapping);
node = voronoi;
@@ -749,6 +754,8 @@ static ShaderNode *add_node(Scene *scene,
BL::ShaderNodeTexWave b_wave_node(b_node);
WaveTextureNode *wave = new WaveTextureNode();
wave->type = (NodeWaveType)b_wave_node.wave_type();
+ wave->bands_direction = (NodeWaveBandsDirection)b_wave_node.bands_direction();
+ wave->rings_direction = (NodeWaveRingsDirection)b_wave_node.rings_direction();
wave->profile = (NodeWaveProfile)b_wave_node.wave_profile();
BL::TexMapping b_texture_mapping(b_wave_node.texture_mapping());
get_tex_mapping(&wave->tex_mapping, b_texture_mapping);
@@ -775,17 +782,19 @@ static ShaderNode *add_node(Scene *scene,
else if (b_node.is_a(&RNA_ShaderNodeTexNoise)) {
BL::ShaderNodeTexNoise b_noise_node(b_node);
NoiseTextureNode *noise = new NoiseTextureNode();
+ noise->dimensions = b_noise_node.noise_dimensions();
BL::TexMapping b_texture_mapping(b_noise_node.texture_mapping());
get_tex_mapping(&noise->tex_mapping, b_texture_mapping);
node = noise;
}
else if (b_node.is_a(&RNA_ShaderNodeTexMusgrave)) {
BL::ShaderNodeTexMusgrave b_musgrave_node(b_node);
- MusgraveTextureNode *musgrave = new MusgraveTextureNode();
- musgrave->type = (NodeMusgraveType)b_musgrave_node.musgrave_type();
+ MusgraveTextureNode *musgrave_node = new MusgraveTextureNode();
+ musgrave_node->type = (NodeMusgraveType)b_musgrave_node.musgrave_type();
+ musgrave_node->dimensions = b_musgrave_node.musgrave_dimensions();
BL::TexMapping b_texture_mapping(b_musgrave_node.texture_mapping());
- get_tex_mapping(&musgrave->tex_mapping, b_texture_mapping);
- node = musgrave;
+ get_tex_mapping(&musgrave_node->tex_mapping, b_texture_mapping);
+ node = musgrave_node;
}
else if (b_node.is_a(&RNA_ShaderNodeTexCoord)) {
BL::ShaderNodeTexCoord b_tex_coord_node(b_node);
@@ -804,6 +813,15 @@ static ShaderNode *add_node(Scene *scene,
sky->sun_direction = normalize(get_float3(b_sky_node.sun_direction()));
sky->turbidity = b_sky_node.turbidity();
sky->ground_albedo = b_sky_node.ground_albedo();
+ sky->sun_disc = b_sky_node.sun_disc();
+ sky->sun_size = b_sky_node.sun_size();
+ sky->sun_intensity = b_sky_node.sun_intensity();
+ sky->sun_elevation = b_sky_node.sun_elevation();
+ sky->sun_rotation = b_sky_node.sun_rotation();
+ sky->altitude = 1000.0f * b_sky_node.altitude();
+ sky->air_density = b_sky_node.air_density();
+ sky->dust_density = b_sky_node.dust_density();
+ sky->ozone_density = b_sky_node.ozone_density();
BL::TexMapping b_texture_mapping(b_sky_node.texture_mapping());
get_tex_mapping(&sky->tex_mapping, b_texture_mapping);
node = sky;
@@ -824,6 +842,12 @@ static ShaderNode *add_node(Scene *scene,
}
node = ies;
}
+ else if (b_node.is_a(&RNA_ShaderNodeTexWhiteNoise)) {
+ BL::ShaderNodeTexWhiteNoise b_tex_white_noise_node(b_node);
+ WhiteNoiseTextureNode *white_noise_node = new WhiteNoiseTextureNode();
+ white_noise_node->dimensions = b_tex_white_noise_node.noise_dimensions();
+ node = white_noise_node;
+ }
else if (b_node.is_a(&RNA_ShaderNodeNormalMap)) {
BL::ShaderNodeNormalMap b_normal_map_node(b_node);
NormalMapNode *nmap = new NormalMapNode();
@@ -849,22 +873,13 @@ static ShaderNode *add_node(Scene *scene,
else if (b_node.is_a(&RNA_ShaderNodeTexPointDensity)) {
BL::ShaderNodeTexPointDensity b_point_density_node(b_node);
PointDensityTextureNode *point_density = new PointDensityTextureNode();
- point_density->filename = b_point_density_node.name();
point_density->space = (NodeTexVoxelSpace)b_point_density_node.space();
point_density->interpolation = get_image_interpolation(b_point_density_node);
- point_density->builtin_data = b_point_density_node.ptr.data;
- point_density->image_manager = scene->image_manager;
-
- /* TODO(sergey): Use more proper update flag. */
- if (true) {
- point_density->add_image();
- b_point_density_node.cache_point_density(b_depsgraph);
- scene->image_manager->tag_reload_image(point_density->filename.string(),
- point_density->builtin_data,
- point_density->interpolation,
- EXTENSION_CLIP,
- true);
- }
+ point_density->handle = scene->image_manager->add_image(
+ new BlenderPointDensityLoader(b_depsgraph, b_point_density_node),
+ point_density->image_params());
+
+ b_point_density_node.cache_point_density(b_depsgraph);
node = point_density;
/* Transformation form world space to texture space.
@@ -899,6 +914,12 @@ static ShaderNode *add_node(Scene *scene,
disp->attribute = "";
node = disp;
}
+ else if (b_node.is_a(&RNA_ShaderNodeOutputAOV)) {
+ BL::ShaderNodeOutputAOV b_aov_node(b_node);
+ OutputAOVNode *aov = new OutputAOVNode();
+ aov->name = b_aov_node.name();
+ node = aov;
+ }
if (node) {
node->name = b_node.name();
@@ -910,7 +931,7 @@ static ShaderNode *add_node(Scene *scene,
static bool node_use_modified_socket_name(ShaderNode *node)
{
- if (node->special_type == SHADER_SPECIAL_TYPE_SCRIPT)
+ if (node->special_type == SHADER_SPECIAL_TYPE_OSL)
return false;
return true;
@@ -1153,8 +1174,10 @@ static void add_nodes(Scene *scene,
BL::NodeTree::links_iterator b_link;
for (b_ntree.links.begin(b_link); b_link != b_ntree.links.end(); ++b_link) {
- /* Ignore invalid links to avoid unwanted cycles created in graph. */
- if (!b_link->is_valid()) {
+ /* Ignore invalid links to avoid unwanted cycles created in graph.
+ * Also ignore links with unavailable sockets. */
+ if (!(b_link->is_valid() && b_link->from_socket().enabled() &&
+ b_link->to_socket().enabled())) {
continue;
}
/* get blender link data */
@@ -1217,12 +1240,11 @@ void BlenderSync::sync_materials(BL::Depsgraph &b_depsgraph, bool update_all)
Shader *shader;
/* test if we need to sync */
- if (shader_map.sync(&shader, b_mat) || shader->need_sync_object || update_all) {
+ if (shader_map.add_or_update(&shader, b_mat) || update_all) {
ShaderGraph *graph = new ShaderGraph();
shader->name = b_mat.name().c_str();
shader->pass_id = b_mat.pass_index();
- shader->need_sync_object = false;
/* create nodes */
if (b_mat.use_nodes() && b_mat.node_tree()) {
@@ -1246,6 +1268,7 @@ void BlenderSync::sync_materials(BL::Depsgraph &b_depsgraph, bool update_all)
shader->heterogeneous_volume = !get_boolean(cmat, "homogeneous_volume");
shader->volume_sampling_method = get_volume_sampling(cmat);
shader->volume_interpolation_method = get_volume_interpolation(cmat);
+ shader->volume_step_rate = get_float(cmat, "volume_step_rate");
shader->displacement_method = get_displacement_method(cmat);
shader->set_graph(graph);
@@ -1284,19 +1307,23 @@ void BlenderSync::sync_materials(BL::Depsgraph &b_depsgraph, bool update_all)
/* Sync World */
-void BlenderSync::sync_world(BL::Depsgraph &b_depsgraph, bool update_all)
+void BlenderSync::sync_world(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d, bool update_all)
{
Background *background = scene->background;
Background prevbackground = *background;
BL::World b_world = b_scene.world();
- if (world_recalc || update_all || b_world.ptr.data != world_map) {
+ BlenderViewportParameters new_viewport_parameters(b_v3d);
+
+ if (world_recalc || update_all || b_world.ptr.data != world_map ||
+ viewport_parameters.modified(new_viewport_parameters)) {
Shader *shader = scene->default_background;
ShaderGraph *graph = new ShaderGraph();
/* create nodes */
- if (b_world && b_world.use_nodes() && b_world.node_tree()) {
+ if (new_viewport_parameters.use_scene_world && b_world && b_world.use_nodes() &&
+ b_world.node_tree()) {
BL::ShaderNodeTree b_ntree(b_world.node_tree());
add_nodes(scene, b_engine, b_data, b_depsgraph, b_scene, graph, b_ntree);
@@ -1306,8 +1333,9 @@ void BlenderSync::sync_world(BL::Depsgraph &b_depsgraph, bool update_all)
shader->heterogeneous_volume = !get_boolean(cworld, "homogeneous_volume");
shader->volume_sampling_method = get_volume_sampling(cworld);
shader->volume_interpolation_method = get_volume_interpolation(cworld);
+ shader->volume_step_rate = get_float(cworld, "volume_step_size");
}
- else if (b_world) {
+ else if (new_viewport_parameters.use_scene_world && b_world) {
BackgroundNode *background = new BackgroundNode();
background->color = get_float3(b_world.color());
graph->add(background);
@@ -1315,6 +1343,61 @@ void BlenderSync::sync_world(BL::Depsgraph &b_depsgraph, bool update_all)
ShaderNode *out = graph->output();
graph->connect(background->output("Background"), out->input("Surface"));
}
+ else if (!new_viewport_parameters.use_scene_world) {
+ float3 world_color;
+ if (b_world) {
+ world_color = get_float3(b_world.color());
+ }
+ else {
+ world_color = make_float3(0.0f, 0.0f, 0.0f);
+ }
+
+ BackgroundNode *background = new BackgroundNode();
+ graph->add(background);
+
+ LightPathNode *light_path = new LightPathNode();
+ graph->add(light_path);
+
+ MixNode *mix_scene_with_background = new MixNode();
+ mix_scene_with_background->color2 = world_color;
+ graph->add(mix_scene_with_background);
+
+ EnvironmentTextureNode *texture_environment = new EnvironmentTextureNode();
+ texture_environment->tex_mapping.type = TextureMapping::VECTOR;
+ texture_environment->tex_mapping.rotation[2] = new_viewport_parameters.studiolight_rotate_z;
+ texture_environment->filename = new_viewport_parameters.studiolight_path;
+ graph->add(texture_environment);
+
+ MixNode *mix_intensity = new MixNode();
+ mix_intensity->type = NODE_MIX_MUL;
+ mix_intensity->fac = 1.0f;
+ mix_intensity->color2 = make_float3(new_viewport_parameters.studiolight_intensity,
+ new_viewport_parameters.studiolight_intensity,
+ new_viewport_parameters.studiolight_intensity);
+ graph->add(mix_intensity);
+
+ TextureCoordinateNode *texture_coordinate = new TextureCoordinateNode();
+ graph->add(texture_coordinate);
+
+ MixNode *mix_background_with_environment = new MixNode();
+ mix_background_with_environment->fac = new_viewport_parameters.studiolight_background_alpha;
+ mix_background_with_environment->color1 = world_color;
+ graph->add(mix_background_with_environment);
+
+ ShaderNode *out = graph->output();
+
+ graph->connect(texture_coordinate->output("Generated"),
+ texture_environment->input("Vector"));
+ graph->connect(texture_environment->output("Color"), mix_intensity->input("Color1"));
+ graph->connect(light_path->output("Is Camera Ray"), mix_scene_with_background->input("Fac"));
+ graph->connect(mix_intensity->output("Color"), mix_scene_with_background->input("Color1"));
+ graph->connect(mix_intensity->output("Color"),
+ mix_background_with_environment->input("Color2"));
+ graph->connect(mix_background_with_environment->output("Color"),
+ mix_scene_with_background->input("Color2"));
+ graph->connect(mix_scene_with_background->output("Color"), background->input("Color"));
+ graph->connect(background->output("Background"), out->input("Surface"));
+ }
if (b_world) {
/* AO */
@@ -1348,16 +1431,7 @@ void BlenderSync::sync_world(BL::Depsgraph &b_depsgraph, bool update_all)
}
PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
-
- /* when doing preview render check for BI's transparency settings,
- * this is so because Blender's preview render routines are not able
- * to tweak all cycles's settings depending on different circumstances
- */
- if (b_engine.is_preview() == false)
- background->transparent = get_boolean(cscene, "film_transparent");
- else
- background->transparent = b_scene.render().alpha_mode() ==
- BL::RenderSettings::alpha_mode_TRANSPARENT;
+ background->transparent = b_scene.render().film_transparent();
if (background->transparent) {
background->transparent_glass = get_boolean(cscene, "film_transparent_glass");
@@ -1368,7 +1442,8 @@ void BlenderSync::sync_world(BL::Depsgraph &b_depsgraph, bool update_all)
background->transparent_roughness_threshold = 0.0f;
}
- background->use_shader = view_layer.use_background_shader;
+ background->use_shader = view_layer.use_background_shader |
+ viewport_parameters.custom_viewport_parameters();
background->use_ao = background->use_ao && view_layer.use_background_ao;
if (background->modified(prevbackground))
@@ -1391,7 +1466,7 @@ void BlenderSync::sync_lights(BL::Depsgraph &b_depsgraph, bool update_all)
Shader *shader;
/* test if we need to sync */
- if (shader_map.sync(&shader, b_light) || update_all) {
+ if (shader_map.add_or_update(&shader, b_light) || update_all) {
ShaderGraph *graph = new ShaderGraph();
/* create nodes */
@@ -1403,16 +1478,9 @@ void BlenderSync::sync_lights(BL::Depsgraph &b_depsgraph, bool update_all)
add_nodes(scene, b_engine, b_data, b_depsgraph, b_scene, graph, b_ntree);
}
else {
- float strength = 1.0f;
-
- if (b_light.type() == BL::Light::type_POINT || b_light.type() == BL::Light::type_SPOT ||
- b_light.type() == BL::Light::type_AREA) {
- strength = 100.0f;
- }
-
EmissionNode *emission = new EmissionNode();
- emission->color = get_float3(b_light.color());
- emission->strength = strength;
+ emission->color = make_float3(1.0f, 1.0f, 1.0f);
+ emission->strength = 1.0f;
graph->add(emission);
ShaderNode *out = graph->output();
@@ -1425,7 +1493,7 @@ void BlenderSync::sync_lights(BL::Depsgraph &b_depsgraph, bool update_all)
}
}
-void BlenderSync::sync_shaders(BL::Depsgraph &b_depsgraph)
+void BlenderSync::sync_shaders(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d)
{
/* for auto refresh images */
bool auto_refresh_update = false;
@@ -1438,12 +1506,9 @@ void BlenderSync::sync_shaders(BL::Depsgraph &b_depsgraph)
shader_map.pre_sync();
- sync_world(b_depsgraph, auto_refresh_update);
+ sync_world(b_depsgraph, b_v3d, auto_refresh_update);
sync_lights(b_depsgraph, auto_refresh_update);
sync_materials(b_depsgraph, auto_refresh_update);
-
- /* false = don't delete unused shaders, not supported */
- shader_map.post_sync(false);
}
CCL_NAMESPACE_END
diff --git a/intern/cycles/blender/blender_sync.cpp b/intern/cycles/blender/blender_sync.cpp
index 28d0c554f22..ee90b4dfbfe 100644
--- a/intern/cycles/blender/blender_sync.cpp
+++ b/intern/cycles/blender/blender_sync.cpp
@@ -16,6 +16,7 @@
#include "render/background.h"
#include "render/camera.h"
+#include "render/curves.h"
#include "render/film.h"
#include "render/graph.h"
#include "render/integrator.h"
@@ -25,19 +26,19 @@
#include "render/object.h"
#include "render/scene.h"
#include "render/shader.h"
-#include "render/curves.h"
#include "device/device.h"
#include "blender/blender_device.h"
-#include "blender/blender_sync.h"
#include "blender/blender_session.h"
+#include "blender/blender_sync.h"
#include "blender/blender_util.h"
#include "util/util_debug.h"
#include "util/util_foreach.h"
-#include "util/util_opengl.h"
#include "util/util_hash.h"
+#include "util/util_opengl.h"
+#include "util/util_openimagedenoise.h"
CCL_NAMESPACE_BEGIN
@@ -56,7 +57,7 @@ BlenderSync::BlenderSync(BL::RenderEngine &b_engine,
b_scene(b_scene),
shader_map(&scene->shaders),
object_map(&scene->objects),
- mesh_map(&scene->meshes),
+ geometry_map(&scene->geometry),
light_map(&scene->lights),
particle_system_map(&scene->particle_systems),
world_map(NULL),
@@ -78,15 +79,21 @@ BlenderSync::~BlenderSync()
{
}
+void BlenderSync::reset(BL::BlendData &b_data, BL::Scene &b_scene)
+{
+ /* Update data and scene pointers in case they change in session reset,
+ * for example after undo. */
+ this->b_data = b_data;
+ this->b_scene = b_scene;
+}
+
/* Sync */
-void BlenderSync::sync_recalc(BL::Depsgraph &b_depsgraph)
+void BlenderSync::sync_recalc(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d)
{
/* Sync recalc flags from blender to cycles. Actual update is done separate,
* so we can do it later on if doing it immediate is not suitable. */
- bool has_updated_objects = b_depsgraph.id_type_updated(BL::DriverTarget::id_type_OBJECT);
-
if (experimental) {
/* Mark all meshes as needing to be exported again if dicing changed. */
PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
@@ -108,10 +115,15 @@ void BlenderSync::sync_recalc(BL::Depsgraph &b_depsgraph)
}
if (dicing_prop_changed) {
- for (const pair<void *, Mesh *> &iter : mesh_map.key_to_scene_data()) {
- Mesh *mesh = iter.second;
- if (mesh->subdivision_type != Mesh::SUBDIVISION_NONE) {
- mesh_map.set_recalc(iter.first);
+ for (const pair<const GeometryKey, Geometry *> &iter : geometry_map.key_to_scene_data()) {
+ Geometry *geom = iter.second;
+ if (geom->type == Geometry::MESH) {
+ Mesh *mesh = static_cast<Mesh *>(geom);
+ if (mesh->subdivision_type != Mesh::SUBDIVISION_NONE) {
+ PointerRNA id_ptr;
+ RNA_id_pointer_create((::ID *)iter.first.id, &id_ptr);
+ geometry_map.set_recalc(BL::ID(id_ptr));
+ }
}
}
}
@@ -135,36 +147,49 @@ void BlenderSync::sync_recalc(BL::Depsgraph &b_depsgraph)
/* Object */
else if (b_id.is_a(&RNA_Object)) {
BL::Object b_ob(b_id);
- const bool updated_geometry = b_update->is_updated_geometry();
-
- if (b_update->is_updated_transform()) {
- object_map.set_recalc(b_ob);
- light_map.set_recalc(b_ob);
- }
-
- if (object_is_mesh(b_ob)) {
- if (updated_geometry ||
- (object_subdivision_type(b_ob, preview, experimental) != Mesh::SUBDIVISION_NONE)) {
- BL::ID key = BKE_object_is_modified(b_ob) ? b_ob : b_ob.data();
- mesh_map.set_recalc(key);
+ const bool is_geometry = object_is_geometry(b_ob);
+ const bool is_light = !is_geometry && object_is_light(b_ob);
+
+ if (is_geometry || is_light) {
+ const bool updated_geometry = b_update->is_updated_geometry();
+
+ /* Geometry (mesh, hair, volume). */
+ if (is_geometry) {
+ if (b_update->is_updated_transform() || b_update->is_updated_shading()) {
+ object_map.set_recalc(b_ob);
+ }
+
+ if (updated_geometry ||
+ (object_subdivision_type(b_ob, preview, experimental) != Mesh::SUBDIVISION_NONE)) {
+ BL::ID key = BKE_object_is_modified(b_ob) ? b_ob : b_ob.data();
+ geometry_map.set_recalc(key);
+ }
+
+ if (updated_geometry) {
+ BL::Object::particle_systems_iterator b_psys;
+ for (b_ob.particle_systems.begin(b_psys); b_psys != b_ob.particle_systems.end();
+ ++b_psys) {
+ particle_system_map.set_recalc(b_ob);
+ }
+ }
}
- }
- else if (object_is_light(b_ob)) {
- if (updated_geometry) {
- light_map.set_recalc(b_ob);
+ /* Light */
+ else if (is_light) {
+ if (b_update->is_updated_transform() || b_update->is_updated_shading()) {
+ object_map.set_recalc(b_ob);
+ light_map.set_recalc(b_ob);
+ }
+
+ if (updated_geometry) {
+ light_map.set_recalc(b_ob);
+ }
}
}
-
- if (updated_geometry) {
- BL::Object::particle_systems_iterator b_psys;
- for (b_ob.particle_systems.begin(b_psys); b_psys != b_ob.particle_systems.end(); ++b_psys)
- particle_system_map.set_recalc(b_ob);
- }
}
/* Mesh */
else if (b_id.is_a(&RNA_Mesh)) {
BL::Mesh b_mesh(b_id);
- mesh_map.set_recalc(b_mesh);
+ geometry_map.set_recalc(b_mesh);
}
/* World */
else if (b_id.is_a(&RNA_World)) {
@@ -173,19 +198,16 @@ void BlenderSync::sync_recalc(BL::Depsgraph &b_depsgraph)
world_recalc = true;
}
}
- }
-
- /* Updates shader with object dependency if objects changed. */
- if (has_updated_objects) {
- if (scene->default_background->has_object_dependency) {
- world_recalc = true;
+ /* Volume */
+ else if (b_id.is_a(&RNA_Volume)) {
+ BL::Volume b_volume(b_id);
+ geometry_map.set_recalc(b_volume);
}
+ }
- foreach (Shader *shader, scene->shaders) {
- if (shader->has_object_dependency) {
- shader->need_sync_object = true;
- }
- }
+ BlenderViewportParameters new_viewport_parameters(b_v3d);
+ if (viewport_parameters.modified(new_viewport_parameters)) {
+ world_recalc = true;
}
}
@@ -201,20 +223,23 @@ void BlenderSync::sync_data(BL::RenderSettings &b_render,
sync_view_layer(b_v3d, b_view_layer);
sync_integrator();
- sync_film();
- sync_shaders(b_depsgraph);
+ sync_film(b_v3d);
+ sync_shaders(b_depsgraph, b_v3d);
sync_images();
- sync_curve_settings();
- mesh_synced.clear(); /* use for objects and motion sync */
+ geometry_synced.clear(); /* use for objects and motion sync */
if (scene->need_motion() == Scene::MOTION_PASS || scene->need_motion() == Scene::MOTION_NONE ||
scene->camera->motion_position == Camera::MOTION_POSITION_CENTER) {
- sync_objects(b_depsgraph);
+ sync_objects(b_depsgraph, b_v3d);
}
- sync_motion(b_render, b_depsgraph, b_override, width, height, python_thread_state);
+ sync_motion(b_render, b_depsgraph, b_v3d, b_override, width, height, python_thread_state);
- mesh_synced.clear();
+ geometry_synced.clear();
+
+ /* Shader sync done at the end, since object sync uses it.
+ * false = don't delete unused shaders, not supported. */
+ shader_map.post_sync(false);
free_data_after_sync(b_depsgraph);
}
@@ -231,6 +256,7 @@ void BlenderSync::sync_integrator()
Integrator *integrator = scene->integrator;
Integrator previntegrator = *integrator;
+ integrator->min_bounce = get_int(cscene, "min_light_bounces");
integrator->max_bounce = get_int(cscene, "max_bounces");
integrator->max_diffuse_bounce = get_int(cscene, "diffuse_bounces");
@@ -238,10 +264,12 @@ void BlenderSync::sync_integrator()
integrator->max_transmission_bounce = get_int(cscene, "transmission_bounces");
integrator->max_volume_bounce = get_int(cscene, "volume_bounces");
+ integrator->transparent_min_bounce = get_int(cscene, "min_transparent_bounces");
integrator->transparent_max_bounce = get_int(cscene, "transparent_max_bounces");
integrator->volume_max_steps = get_int(cscene, "volume_max_steps");
- integrator->volume_step_size = get_float(cscene, "volume_step_size");
+ integrator->volume_step_rate = (preview) ? get_float(cscene, "volume_preview_step_rate") :
+ get_float(cscene, "volume_step_rate");
integrator->caustics_reflective = get_boolean(cscene, "caustics_reflective");
integrator->caustics_refractive = get_boolean(cscene, "caustics_refractive");
@@ -249,13 +277,13 @@ void BlenderSync::sync_integrator()
integrator->seed = get_int(cscene, "seed");
if (get_boolean(cscene, "use_animated_seed")) {
- integrator->seed = hash_int_2d(b_scene.frame_current(), get_int(cscene, "seed"));
+ integrator->seed = hash_uint2(b_scene.frame_current(), get_int(cscene, "seed"));
if (b_scene.frame_subframe() != 0.0f) {
/* TODO(sergey): Ideally should be some sort of hash_merge,
* but this is good enough for now.
*/
- integrator->seed += hash_int_2d((int)(b_scene.frame_subframe() * (float)INT_MAX),
- get_int(cscene, "seed"));
+ integrator->seed += hash_uint2((int)(b_scene.frame_subframe() * (float)INT_MAX),
+ get_int(cscene, "seed"));
}
}
@@ -287,6 +315,16 @@ void BlenderSync::sync_integrator()
integrator->sample_all_lights_indirect = get_boolean(cscene, "sample_all_lights_indirect");
integrator->light_sampling_threshold = get_float(cscene, "light_sampling_threshold");
+ if (RNA_boolean_get(&cscene, "use_adaptive_sampling")) {
+ integrator->sampling_pattern = SAMPLING_PATTERN_PMJ;
+ integrator->adaptive_min_samples = get_int(cscene, "adaptive_min_samples");
+ integrator->adaptive_threshold = get_float(cscene, "adaptive_threshold");
+ }
+ else {
+ integrator->adaptive_min_samples = INT_MAX;
+ integrator->adaptive_threshold = 0.0f;
+ }
+
int diffuse_samples = get_int(cscene, "diffuse_samples");
int glossy_samples = get_int(cscene, "glossy_samples");
int transmission_samples = get_int(cscene, "transmission_samples");
@@ -303,6 +341,8 @@ void BlenderSync::sync_integrator()
integrator->mesh_light_samples = mesh_light_samples * mesh_light_samples;
integrator->subsurface_samples = subsurface_samples * subsurface_samples;
integrator->volume_samples = volume_samples * volume_samples;
+ integrator->adaptive_min_samples = min(
+ integrator->adaptive_min_samples * integrator->adaptive_min_samples, INT_MAX);
}
else {
integrator->diffuse_samples = diffuse_samples;
@@ -337,13 +377,17 @@ void BlenderSync::sync_integrator()
/* Film */
-void BlenderSync::sync_film()
+void BlenderSync::sync_film(BL::SpaceView3D &b_v3d)
{
PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
Film *film = scene->film;
Film prevfilm = *film;
+ if (b_v3d) {
+ film->display_pass = update_viewport_display_passes(b_v3d, film->passes);
+ }
+
film->exposure = get_float(cscene, "film_exposure");
film->filter_type = (FilterType)get_enum(
cscene, "pixel_filter_type", FILTER_NUM_TYPES, FILTER_BLACKMAN_HARRIS);
@@ -369,8 +413,10 @@ void BlenderSync::sync_film()
}
}
- if (film->modified(prevfilm))
+ if (film->modified(prevfilm)) {
film->tag_update(scene);
+ film->tag_passes_update(scene, prevfilm.passes, false);
+ }
}
/* Render Layer */
@@ -383,6 +429,7 @@ void BlenderSync::sync_view_layer(BL::SpaceView3D & /*b_v3d*/, BL::ViewLayer &b_
view_layer.use_background_ao = b_view_layer.use_ao();
view_layer.use_surfaces = b_view_layer.use_solid();
view_layer.use_hair = b_view_layer.use_strand();
+ view_layer.use_volumes = b_view_layer.use_volumes();
/* Material override. */
view_layer.material_override = b_view_layer.material_override();
@@ -451,25 +498,25 @@ PassType BlenderSync::get_pass_type(BL::RenderPass &b_pass)
MAP_PASS("DiffDir", PASS_DIFFUSE_DIRECT);
MAP_PASS("GlossDir", PASS_GLOSSY_DIRECT);
MAP_PASS("TransDir", PASS_TRANSMISSION_DIRECT);
- MAP_PASS("SubsurfaceDir", PASS_SUBSURFACE_DIRECT);
MAP_PASS("VolumeDir", PASS_VOLUME_DIRECT);
MAP_PASS("DiffInd", PASS_DIFFUSE_INDIRECT);
MAP_PASS("GlossInd", PASS_GLOSSY_INDIRECT);
MAP_PASS("TransInd", PASS_TRANSMISSION_INDIRECT);
- MAP_PASS("SubsurfaceInd", PASS_SUBSURFACE_INDIRECT);
MAP_PASS("VolumeInd", PASS_VOLUME_INDIRECT);
MAP_PASS("DiffCol", PASS_DIFFUSE_COLOR);
MAP_PASS("GlossCol", PASS_GLOSSY_COLOR);
MAP_PASS("TransCol", PASS_TRANSMISSION_COLOR);
- MAP_PASS("SubsurfaceCol", PASS_SUBSURFACE_COLOR);
MAP_PASS("Emit", PASS_EMISSION);
MAP_PASS("Env", PASS_BACKGROUND);
MAP_PASS("AO", PASS_AO);
MAP_PASS("Shadow", PASS_SHADOW);
+ MAP_PASS("BakePrimitive", PASS_BAKE_PRIMITIVE);
+ MAP_PASS("BakeDifferential", PASS_BAKE_DIFFERENTIAL);
+
#ifdef __KERNEL_DEBUG__
MAP_PASS("Debug BVH Traversed Nodes", PASS_BVH_TRAVERSED_NODES);
MAP_PASS("Debug BVH Traversed Instances", PASS_BVH_TRAVERSED_INSTANCES);
@@ -477,6 +524,8 @@ PassType BlenderSync::get_pass_type(BL::RenderPass &b_pass)
MAP_PASS("Debug Ray Bounces", PASS_RAY_BOUNCES);
#endif
MAP_PASS("Debug Render Time", PASS_RENDER_TIME);
+ MAP_PASS("AdaptiveAuxBuffer", PASS_ADAPTIVE_AUX_BUFFER);
+ MAP_PASS("Debug Sample Count", PASS_SAMPLE_COUNT);
if (string_startswith(name, cryptomatte_prefix)) {
return PASS_CRYPTOMATTE;
}
@@ -512,10 +561,12 @@ int BlenderSync::get_denoising_pass(BL::RenderPass &b_pass)
return -1;
}
-vector<Pass> BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay, BL::ViewLayer &b_view_layer)
+vector<Pass> BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay,
+ BL::ViewLayer &b_view_layer,
+ bool adaptive_sampling,
+ const DenoiseParams &denoising)
{
vector<Pass> passes;
- Pass::add(PASS_COMBINED, passes);
/* loop over passes */
BL::RenderLayer::passes_iterator b_pass_iter;
@@ -527,80 +578,85 @@ vector<Pass> BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay, BL::ViewLa
if (pass_type == PASS_MOTION && scene->integrator->motion_blur)
continue;
if (pass_type != PASS_NONE)
- Pass::add(pass_type, passes);
+ Pass::add(pass_type, passes, b_pass.name().c_str());
}
- PointerRNA crp = RNA_pointer_get(&b_view_layer.ptr, "cycles");
- bool full_denoising = get_boolean(crp, "use_denoising");
- bool write_denoising_passes = get_boolean(crp, "denoising_store_passes");
+ PointerRNA crl = RNA_pointer_get(&b_view_layer.ptr, "cycles");
scene->film->denoising_flags = 0;
- if (full_denoising || write_denoising_passes) {
+ if (denoising.use || denoising.store_passes) {
+ if (denoising.type == DENOISER_NLM) {
#define MAP_OPTION(name, flag) \
- if (!get_boolean(crp, name)) \
+ if (!get_boolean(crl, name)) \
scene->film->denoising_flags |= flag;
- MAP_OPTION("denoising_diffuse_direct", DENOISING_CLEAN_DIFFUSE_DIR);
- MAP_OPTION("denoising_diffuse_indirect", DENOISING_CLEAN_DIFFUSE_IND);
- MAP_OPTION("denoising_glossy_direct", DENOISING_CLEAN_GLOSSY_DIR);
- MAP_OPTION("denoising_glossy_indirect", DENOISING_CLEAN_GLOSSY_IND);
- MAP_OPTION("denoising_transmission_direct", DENOISING_CLEAN_TRANSMISSION_DIR);
- MAP_OPTION("denoising_transmission_indirect", DENOISING_CLEAN_TRANSMISSION_IND);
- MAP_OPTION("denoising_subsurface_direct", DENOISING_CLEAN_SUBSURFACE_DIR);
- MAP_OPTION("denoising_subsurface_indirect", DENOISING_CLEAN_SUBSURFACE_IND);
+ MAP_OPTION("denoising_diffuse_direct", DENOISING_CLEAN_DIFFUSE_DIR);
+ MAP_OPTION("denoising_diffuse_indirect", DENOISING_CLEAN_DIFFUSE_IND);
+ MAP_OPTION("denoising_glossy_direct", DENOISING_CLEAN_GLOSSY_DIR);
+ MAP_OPTION("denoising_glossy_indirect", DENOISING_CLEAN_GLOSSY_IND);
+ MAP_OPTION("denoising_transmission_direct", DENOISING_CLEAN_TRANSMISSION_DIR);
+ MAP_OPTION("denoising_transmission_indirect", DENOISING_CLEAN_TRANSMISSION_IND);
#undef MAP_OPTION
+ }
b_engine.add_pass("Noisy Image", 4, "RGBA", b_view_layer.name().c_str());
}
- if (write_denoising_passes) {
+ if (denoising.store_passes) {
b_engine.add_pass("Denoising Normal", 3, "XYZ", b_view_layer.name().c_str());
b_engine.add_pass("Denoising Albedo", 3, "RGB", b_view_layer.name().c_str());
b_engine.add_pass("Denoising Depth", 1, "Z", b_view_layer.name().c_str());
- b_engine.add_pass("Denoising Shadowing", 1, "X", b_view_layer.name().c_str());
- b_engine.add_pass("Denoising Variance", 3, "RGB", b_view_layer.name().c_str());
- b_engine.add_pass("Denoising Intensity", 1, "X", b_view_layer.name().c_str());
+ if (denoising.type == DENOISER_NLM) {
+ b_engine.add_pass("Denoising Shadowing", 1, "X", b_view_layer.name().c_str());
+ b_engine.add_pass("Denoising Variance", 3, "RGB", b_view_layer.name().c_str());
+ b_engine.add_pass("Denoising Intensity", 1, "X", b_view_layer.name().c_str());
+ }
if (scene->film->denoising_flags & DENOISING_CLEAN_ALL_PASSES) {
b_engine.add_pass("Denoising Clean", 3, "RGB", b_view_layer.name().c_str());
}
}
+
#ifdef __KERNEL_DEBUG__
- if (get_boolean(crp, "pass_debug_bvh_traversed_nodes")) {
+ if (get_boolean(crl, "pass_debug_bvh_traversed_nodes")) {
b_engine.add_pass("Debug BVH Traversed Nodes", 1, "X", b_view_layer.name().c_str());
- Pass::add(PASS_BVH_TRAVERSED_NODES, passes);
+ Pass::add(PASS_BVH_TRAVERSED_NODES, passes, "Debug BVH Traversed Nodes");
}
- if (get_boolean(crp, "pass_debug_bvh_traversed_instances")) {
+ if (get_boolean(crl, "pass_debug_bvh_traversed_instances")) {
b_engine.add_pass("Debug BVH Traversed Instances", 1, "X", b_view_layer.name().c_str());
- Pass::add(PASS_BVH_TRAVERSED_INSTANCES, passes);
+ Pass::add(PASS_BVH_TRAVERSED_INSTANCES, passes, "Debug BVH Traversed Instances");
}
- if (get_boolean(crp, "pass_debug_bvh_intersections")) {
+ if (get_boolean(crl, "pass_debug_bvh_intersections")) {
b_engine.add_pass("Debug BVH Intersections", 1, "X", b_view_layer.name().c_str());
- Pass::add(PASS_BVH_INTERSECTIONS, passes);
+ Pass::add(PASS_BVH_INTERSECTIONS, passes, "Debug BVH Intersections");
}
- if (get_boolean(crp, "pass_debug_ray_bounces")) {
+ if (get_boolean(crl, "pass_debug_ray_bounces")) {
b_engine.add_pass("Debug Ray Bounces", 1, "X", b_view_layer.name().c_str());
- Pass::add(PASS_RAY_BOUNCES, passes);
+ Pass::add(PASS_RAY_BOUNCES, passes, "Debug Ray Bounces");
}
#endif
- if (get_boolean(crp, "pass_debug_render_time")) {
+ if (get_boolean(crl, "pass_debug_render_time")) {
b_engine.add_pass("Debug Render Time", 1, "X", b_view_layer.name().c_str());
- Pass::add(PASS_RENDER_TIME, passes);
+ Pass::add(PASS_RENDER_TIME, passes, "Debug Render Time");
+ }
+ if (get_boolean(crl, "pass_debug_sample_count")) {
+ b_engine.add_pass("Debug Sample Count", 1, "X", b_view_layer.name().c_str());
+ Pass::add(PASS_SAMPLE_COUNT, passes, "Debug Sample Count");
}
- if (get_boolean(crp, "use_pass_volume_direct")) {
+ if (get_boolean(crl, "use_pass_volume_direct")) {
b_engine.add_pass("VolumeDir", 3, "RGB", b_view_layer.name().c_str());
- Pass::add(PASS_VOLUME_DIRECT, passes);
+ Pass::add(PASS_VOLUME_DIRECT, passes, "VolumeDir");
}
- if (get_boolean(crp, "use_pass_volume_indirect")) {
+ if (get_boolean(crl, "use_pass_volume_indirect")) {
b_engine.add_pass("VolumeInd", 3, "RGB", b_view_layer.name().c_str());
- Pass::add(PASS_VOLUME_INDIRECT, passes);
+ Pass::add(PASS_VOLUME_INDIRECT, passes, "VolumeInd");
}
/* Cryptomatte stores two ID/weight pairs per RGBA layer.
- * User facing paramter is the number of pairs. */
- int crypto_depth = min(16, get_int(crp, "pass_crypto_depth")) / 2;
+ * User facing parameter is the number of pairs. */
+ int crypto_depth = divide_up(min(16, get_int(crl, "pass_crypto_depth")), 2);
scene->film->cryptomatte_depth = crypto_depth;
scene->film->cryptomatte_passes = CRYPT_NONE;
- if (get_boolean(crp, "use_pass_crypto_object")) {
- for (int i = 0; i < crypto_depth; ++i) {
+ if (get_boolean(crl, "use_pass_crypto_object")) {
+ for (int i = 0; i < crypto_depth; i++) {
string passname = cryptomatte_prefix + string_printf("Object%02d", i);
b_engine.add_pass(passname.c_str(), 4, "RGBA", b_view_layer.name().c_str());
Pass::add(PASS_CRYPTOMATTE, passes, passname.c_str());
@@ -608,8 +664,8 @@ vector<Pass> BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay, BL::ViewLa
scene->film->cryptomatte_passes = (CryptomatteType)(scene->film->cryptomatte_passes |
CRYPT_OBJECT);
}
- if (get_boolean(crp, "use_pass_crypto_material")) {
- for (int i = 0; i < crypto_depth; ++i) {
+ if (get_boolean(crl, "use_pass_crypto_material")) {
+ for (int i = 0; i < crypto_depth; i++) {
string passname = cryptomatte_prefix + string_printf("Material%02d", i);
b_engine.add_pass(passname.c_str(), 4, "RGBA", b_view_layer.name().c_str());
Pass::add(PASS_CRYPTOMATTE, passes, passname.c_str());
@@ -617,8 +673,8 @@ vector<Pass> BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay, BL::ViewLa
scene->film->cryptomatte_passes = (CryptomatteType)(scene->film->cryptomatte_passes |
CRYPT_MATERIAL);
}
- if (get_boolean(crp, "use_pass_crypto_asset")) {
- for (int i = 0; i < crypto_depth; ++i) {
+ if (get_boolean(crl, "use_pass_crypto_asset")) {
+ for (int i = 0; i < crypto_depth; i++) {
string passname = cryptomatte_prefix + string_printf("Asset%02d", i);
b_engine.add_pass(passname.c_str(), 4, "RGBA", b_view_layer.name().c_str());
Pass::add(PASS_CRYPTOMATTE, passes, passname.c_str());
@@ -626,11 +682,33 @@ vector<Pass> BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay, BL::ViewLa
scene->film->cryptomatte_passes = (CryptomatteType)(scene->film->cryptomatte_passes |
CRYPT_ASSET);
}
- if (get_boolean(crp, "pass_crypto_accurate") && scene->film->cryptomatte_passes != CRYPT_NONE) {
+ if (get_boolean(crl, "pass_crypto_accurate") && scene->film->cryptomatte_passes != CRYPT_NONE) {
scene->film->cryptomatte_passes = (CryptomatteType)(scene->film->cryptomatte_passes |
CRYPT_ACCURATE);
}
+ if (adaptive_sampling) {
+ Pass::add(PASS_ADAPTIVE_AUX_BUFFER, passes);
+ if (!get_boolean(crl, "pass_debug_sample_count")) {
+ Pass::add(PASS_SAMPLE_COUNT, passes);
+ }
+ }
+
+ RNA_BEGIN (&crl, b_aov, "aovs") {
+ bool is_color = (get_enum(b_aov, "type") == 1);
+ string name = get_string(b_aov, "name");
+
+ if (is_color) {
+ b_engine.add_pass(name.c_str(), 4, "RGBA", b_view_layer.name().c_str());
+ Pass::add(PASS_AOV_COLOR, passes, name.c_str());
+ }
+ else {
+ b_engine.add_pass(name.c_str(), 1, "X", b_view_layer.name().c_str());
+ Pass::add(PASS_AOV_VALUE, passes, name.c_str());
+ }
+ }
+ RNA_END;
+
return passes;
}
@@ -677,6 +755,11 @@ SceneParams BlenderSync::get_scene_params(BL::Scene &b_scene, bool background)
params.use_bvh_unaligned_nodes = RNA_boolean_get(&cscene, "debug_use_hair_bvh");
params.num_bvh_time_steps = RNA_int_get(&cscene, "debug_bvh_time_steps");
+ PointerRNA csscene = RNA_pointer_get(&b_scene.ptr, "cycles_curves");
+ params.hair_subdivisions = get_int(csscene, "subdivisions");
+ params.hair_shape = (CurveShapeType)get_enum(
+ csscene, "shape", CURVE_NUM_SHAPE_TYPES, CURVE_THICK);
+
if (background && params.shadingsystem != SHADINGSYSTEM_OSL)
params.persistent_data = r.use_persistent_data();
else
@@ -696,20 +779,10 @@ SceneParams BlenderSync::get_scene_params(BL::Scene &b_scene, bool background)
params.texture_limit = 0;
}
- /* TODO(sergey): Once OSL supports per-microarchitecture optimization get
- * rid of this.
- */
- if (params.shadingsystem == SHADINGSYSTEM_OSL) {
- params.bvh_layout = BVH_LAYOUT_BVH4;
- }
- else {
- params.bvh_layout = DebugFlags().cpu.bvh_layout;
- }
+ params.bvh_layout = DebugFlags().cpu.bvh_layout;
+
+ params.background = background;
-#ifdef WITH_EMBREE
- params.bvh_layout = RNA_boolean_get(&cscene, "use_bvh_embree") ? BVH_LAYOUT_EMBREE :
- params.bvh_layout;
-#endif
return params;
}
@@ -724,7 +797,8 @@ bool BlenderSync::get_session_pause(BL::Scene &b_scene, bool background)
SessionParams BlenderSync::get_session_params(BL::RenderEngine &b_engine,
BL::Preferences &b_preferences,
BL::Scene &b_scene,
- bool background)
+ bool background,
+ BL::ViewLayer b_view_layer)
{
SessionParams params;
PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
@@ -753,7 +827,7 @@ SessionParams BlenderSync::get_session_params(BL::RenderEngine &b_engine,
preview_samples = preview_samples * preview_samples;
}
- if (get_enum(cscene, "progressive") == 0) {
+ if (get_enum(cscene, "progressive") == 0 && (params.device.type != DEVICE_OPTIX)) {
if (background) {
params.samples = aa_samples;
}
@@ -802,7 +876,21 @@ SessionParams BlenderSync::get_session_params(BL::RenderEngine &b_engine,
params.tile_order = TILE_BOTTOM_TO_TOP;
}
- /* other parameters */
+ /* Denoising */
+ params.denoising = get_denoise_params(b_scene, b_view_layer, background);
+
+ if (params.denoising.use) {
+ /* Add additional denoising devices if we are rendering and denoising
+ * with different devices. */
+ params.device.add_denoising_devices(params.denoising.type);
+
+ /* Check if denoiser is supported by device. */
+ if (!(params.device.denoisers & params.denoising.type)) {
+ params.denoising.use = false;
+ }
+ }
+
+ /* Viewport Performance */
params.start_resolution = get_int(cscene, "preview_start_resolution");
params.pixel_size = b_engine.get_preview_pixel_size(b_scene);
@@ -813,20 +901,10 @@ SessionParams BlenderSync::get_session_params(BL::RenderEngine &b_engine,
/* progressive refine */
BL::RenderSettings b_r = b_scene.render();
- params.progressive_refine = (b_engine.is_preview() ||
- get_boolean(cscene, "use_progressive_refine")) &&
- !b_r.use_save_buffers();
-
- if (params.progressive_refine) {
- BL::Scene::view_layers_iterator b_view_layer;
- for (b_scene.view_layers.begin(b_view_layer); b_view_layer != b_scene.view_layers.end();
- ++b_view_layer) {
- PointerRNA crl = RNA_pointer_get(&b_view_layer->ptr, "cycles");
- if (get_boolean(crl, "use_denoising")) {
- params.progressive_refine = false;
- }
- }
- }
+ params.progressive_refine = b_engine.is_preview() ||
+ get_boolean(cscene, "use_progressive_refine");
+ if (b_r.use_save_buffers())
+ params.progressive_refine = false;
if (background) {
if (params.progressive_refine)
@@ -861,7 +939,66 @@ SessionParams BlenderSync::get_session_params(BL::RenderEngine &b_engine,
params.use_profiling = params.device.has_profiling && !b_engine.is_preview() && background &&
BlenderSession::print_render_stats;
+ params.adaptive_sampling = RNA_boolean_get(&cscene, "use_adaptive_sampling");
+
return params;
}
+DenoiseParams BlenderSync::get_denoise_params(BL::Scene &b_scene,
+ BL::ViewLayer &b_view_layer,
+ bool background)
+{
+ DenoiseParams denoising;
+ PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
+
+ if (background) {
+ /* Final Render Denoising */
+ denoising.use = get_boolean(cscene, "use_denoising");
+ denoising.type = (DenoiserType)get_enum(cscene, "denoiser", DENOISER_NUM, DENOISER_NONE);
+
+ if (b_view_layer) {
+ PointerRNA clayer = RNA_pointer_get(&b_view_layer.ptr, "cycles");
+ if (!get_boolean(clayer, "use_denoising")) {
+ denoising.use = false;
+ }
+
+ denoising.radius = get_int(clayer, "denoising_radius");
+ denoising.strength = get_float(clayer, "denoising_strength");
+ denoising.feature_strength = get_float(clayer, "denoising_feature_strength");
+ denoising.relative_pca = get_boolean(clayer, "denoising_relative_pca");
+
+ denoising.input_passes = (DenoiserInput)get_enum(
+ clayer,
+ (denoising.type == DENOISER_OPTIX) ? "denoising_optix_input_passes" :
+ "denoising_openimagedenoise_input_passes",
+ DENOISER_INPUT_NUM,
+ DENOISER_INPUT_RGB_ALBEDO_NORMAL);
+
+ denoising.store_passes = get_boolean(clayer, "denoising_store_passes");
+ }
+ }
+ else {
+ /* Viewport Denoising */
+ denoising.use = get_boolean(cscene, "use_preview_denoising");
+ denoising.type = (DenoiserType)get_enum(
+ cscene, "preview_denoiser", DENOISER_NUM, DENOISER_NONE);
+ denoising.start_sample = get_int(cscene, "preview_denoising_start_sample");
+
+ /* Auto select fastest denoiser. */
+ if (denoising.type == DENOISER_NONE) {
+ if (!Device::available_devices(DEVICE_MASK_OPTIX).empty()) {
+ denoising.type = DENOISER_OPTIX;
+ }
+ else if (openimagedenoise_supported()) {
+ denoising.type = DENOISER_OPENIMAGEDENOISE;
+ }
+ else {
+ denoising.use = false;
+ }
+ }
+ }
+
+ return denoising;
+}
+
CCL_NAMESPACE_END
diff --git a/intern/cycles/blender/blender_sync.h b/intern/cycles/blender/blender_sync.h
index 00afceebde3..a551ec31e04 100644
--- a/intern/cycles/blender/blender_sync.h
+++ b/intern/cycles/blender/blender_sync.h
@@ -18,11 +18,12 @@
#define __BLENDER_SYNC_H__
#include "MEM_guardedalloc.h"
-#include "RNA_types.h"
#include "RNA_access.h"
#include "RNA_blender_cpp.h"
+#include "RNA_types.h"
-#include "blender/blender_util.h"
+#include "blender/blender_id_map.h"
+#include "blender/blender_viewport.h"
#include "render/scene.h"
#include "render/session.h"
@@ -36,8 +37,10 @@ CCL_NAMESPACE_BEGIN
class Background;
class BlenderObjectCulling;
+class BlenderViewportParameters;
class Camera;
class Film;
+class Hair;
class Light;
class Mesh;
class Object;
@@ -58,8 +61,10 @@ class BlenderSync {
Progress &progress);
~BlenderSync();
+ void reset(BL::BlendData &b_data, BL::Scene &b_scene);
+
/* sync */
- void sync_recalc(BL::Depsgraph &b_depsgraph);
+ void sync_recalc(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d);
void sync_data(BL::RenderSettings &b_render,
BL::Depsgraph &b_depsgraph,
BL::SpaceView3D &b_v3d,
@@ -68,7 +73,10 @@ class BlenderSync {
int height,
void **python_thread_state);
void sync_view_layer(BL::SpaceView3D &b_v3d, BL::ViewLayer &b_view_layer);
- vector<Pass> sync_render_passes(BL::RenderLayer &b_render_layer, BL::ViewLayer &b_view_layer);
+ vector<Pass> sync_render_passes(BL::RenderLayer &b_render_layer,
+ BL::ViewLayer &b_view_layer,
+ bool adaptive_sampling,
+ const DenoiseParams &denoising);
void sync_integrator();
void sync_camera(BL::RenderSettings &b_render,
BL::Object &b_override,
@@ -87,55 +95,96 @@ class BlenderSync {
/* get parameters */
static SceneParams get_scene_params(BL::Scene &b_scene, bool background);
- static SessionParams get_session_params(BL::RenderEngine &b_engine,
- BL::Preferences &b_userpref,
- BL::Scene &b_scene,
- bool background);
+ static SessionParams get_session_params(
+ BL::RenderEngine &b_engine,
+ BL::Preferences &b_userpref,
+ BL::Scene &b_scene,
+ bool background,
+ BL::ViewLayer b_view_layer = BL::ViewLayer(PointerRNA_NULL));
static bool get_session_pause(BL::Scene &b_scene, bool background);
static BufferParams get_buffer_params(BL::RenderSettings &b_render,
BL::SpaceView3D &b_v3d,
BL::RegionView3D &b_rv3d,
Camera *cam,
int width,
- int height);
+ int height,
+ const bool use_denoiser);
static PassType get_pass_type(BL::RenderPass &b_pass);
static int get_denoising_pass(BL::RenderPass &b_pass);
private:
+ static DenoiseParams get_denoise_params(BL::Scene &b_scene,
+ BL::ViewLayer &b_view_layer,
+ bool background);
+
/* sync */
void sync_lights(BL::Depsgraph &b_depsgraph, bool update_all);
void sync_materials(BL::Depsgraph &b_depsgraph, bool update_all);
- void sync_objects(BL::Depsgraph &b_depsgraph, float motion_time = 0.0f);
+ void sync_objects(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d, float motion_time = 0.0f);
void sync_motion(BL::RenderSettings &b_render,
BL::Depsgraph &b_depsgraph,
+ BL::SpaceView3D &b_v3d,
BL::Object &b_override,
int width,
int height,
void **python_thread_state);
- void sync_film();
+ void sync_film(BL::SpaceView3D &b_v3d);
void sync_view();
- void sync_world(BL::Depsgraph &b_depsgraph, bool update_all);
- void sync_shaders(BL::Depsgraph &b_depsgraph);
- void sync_curve_settings();
+ /* Shader */
+ void sync_world(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d, bool update_all);
+ void sync_shaders(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d);
void sync_nodes(Shader *shader, BL::ShaderNodeTree &b_ntree);
- Mesh *sync_mesh(BL::Depsgraph &b_depsgrpah,
- BL::Object &b_ob,
- BL::Object &b_ob_instance,
- bool object_updated,
- bool show_self,
- bool show_particles);
- void sync_curves(
- Mesh *mesh, BL::Mesh &b_mesh, BL::Object &b_ob, bool motion, int motion_step = 0);
+
+ /* Object */
Object *sync_object(BL::Depsgraph &b_depsgraph,
BL::ViewLayer &b_view_layer,
BL::DepsgraphObjectInstance &b_instance,
float motion_time,
- bool show_self,
- bool show_particles,
+ bool use_particle_hair,
+ bool show_lights,
BlenderObjectCulling &culling,
bool *use_portal);
+
+ /* Volume */
+ void sync_volume(BL::Object &b_ob, Mesh *mesh, const vector<Shader *> &used_shaders);
+
+ /* Mesh */
+ void sync_mesh(BL::Depsgraph b_depsgraph,
+ BL::Object b_ob,
+ Mesh *mesh,
+ const vector<Shader *> &used_shaders);
+ void sync_mesh_motion(BL::Depsgraph b_depsgraph, BL::Object b_ob, Mesh *mesh, int motion_step);
+
+ /* Hair */
+ void sync_hair(BL::Depsgraph b_depsgraph,
+ BL::Object b_ob,
+ Hair *hair,
+ const vector<Shader *> &used_shaders);
+ void sync_hair_motion(BL::Depsgraph b_depsgraph, BL::Object b_ob, Hair *hair, int motion_step);
+ void sync_hair(Hair *hair, BL::Object &b_ob, bool motion, int motion_step = 0);
+ void sync_particle_hair(
+ Hair *hair, BL::Mesh &b_mesh, BL::Object &b_ob, bool motion, int motion_step = 0);
+ bool object_has_particle_hair(BL::Object b_ob);
+
+ /* Camera */
+ void sync_camera_motion(
+ BL::RenderSettings &b_render, BL::Object &b_ob, int width, int height, float motion_time);
+
+ /* Geometry */
+ Geometry *sync_geometry(BL::Depsgraph &b_depsgrpah,
+ BL::Object &b_ob,
+ BL::Object &b_ob_instance,
+ bool object_updated,
+ bool use_particle_hair);
+ void sync_geometry_motion(BL::Depsgraph &b_depsgraph,
+ BL::Object &b_ob,
+ Object *object,
+ float motion_time,
+ bool use_particle_hair);
+
+ /* Light */
void sync_light(BL::Object &b_parent,
int persistent_id[OBJECT_PERSISTENT_ID_SIZE],
BL::Object &b_ob,
@@ -143,15 +192,9 @@ class BlenderSync {
int random_id,
Transform &tfm,
bool *use_portal);
- void sync_background_light(bool use_portal);
- void sync_mesh_motion(BL::Depsgraph &b_depsgraph,
- BL::Object &b_ob,
- Object *object,
- float motion_time);
- void sync_camera_motion(
- BL::RenderSettings &b_render, BL::Object &b_ob, int width, int height, float motion_time);
+ void sync_background_light(BL::SpaceView3D &b_v3d, bool use_portal);
- /* particles */
+ /* Particles */
bool sync_dupli_particle(BL::Object &b_ob,
BL::DepsgraphObjectInstance &b_instance,
Object *object);
@@ -165,7 +208,7 @@ class BlenderSync {
/* util */
void find_shader(BL::ID &id, vector<Shader *> &used_shaders, Shader *default_shader);
bool BKE_object_is_modified(BL::Object &b_ob);
- bool object_is_mesh(BL::Object &b_ob);
+ bool object_is_geometry(BL::Object &b_ob);
bool object_is_light(BL::Object &b_ob);
/* variables */
@@ -175,14 +218,15 @@ class BlenderSync {
id_map<void *, Shader> shader_map;
id_map<ObjectKey, Object> object_map;
- id_map<void *, Mesh> mesh_map;
+ id_map<GeometryKey, Geometry> geometry_map;
id_map<ObjectKey, Light> light_map;
id_map<ParticleSystemKey, ParticleSystem> particle_system_map;
- set<Mesh *> mesh_synced;
- set<Mesh *> mesh_motion_synced;
+ set<Geometry *> geometry_synced;
+ set<Geometry *> geometry_motion_synced;
set<float> motion_times;
void *world_map;
bool world_recalc;
+ BlenderViewportParameters viewport_parameters;
Scene *scene;
bool preview;
@@ -198,6 +242,7 @@ class BlenderSync {
use_background_ao(true),
use_surfaces(true),
use_hair(true),
+ use_volumes(true),
samples(0),
bound_samples(false)
{
@@ -209,6 +254,7 @@ class BlenderSync {
bool use_background_ao;
bool use_surfaces;
bool use_hair;
+ bool use_volumes;
int samples;
bool bound_samples;
} view_layer;
diff --git a/intern/cycles/blender/blender_texture.h b/intern/cycles/blender/blender_texture.h
index 896bf62da70..8ab061aaed9 100644
--- a/intern/cycles/blender/blender_texture.h
+++ b/intern/cycles/blender/blender_texture.h
@@ -17,8 +17,8 @@
#ifndef __BLENDER_TEXTURE_H__
#define __BLENDER_TEXTURE_H__
-#include <stdlib.h>
#include "blender/blender_sync.h"
+#include <stdlib.h>
CCL_NAMESPACE_BEGIN
diff --git a/intern/cycles/blender/blender_util.h b/intern/cycles/blender/blender_util.h
index e68f92474bf..ad90a5f8d52 100644
--- a/intern/cycles/blender/blender_util.h
+++ b/intern/cycles/blender/blender_util.h
@@ -32,10 +32,10 @@
* todo: clean this up ... */
extern "C" {
-void BKE_image_user_frame_calc(void *iuser, int cfra);
+void BKE_image_user_frame_calc(void *ima, void *iuser, int cfra);
void BKE_image_user_file_path(void *iuser, void *ima, char *path);
-unsigned char *BKE_image_get_pixels_for_frame(void *image, int frame);
-float *BKE_image_get_float_pixels_for_frame(void *image, int frame);
+unsigned char *BKE_image_get_pixels_for_frame(void *image, int frame, int tile);
+float *BKE_image_get_float_pixels_for_frame(void *image, int frame, int tile);
}
CCL_NAMESPACE_BEGIN
@@ -43,10 +43,10 @@ CCL_NAMESPACE_BEGIN
void python_thread_state_save(void **python_thread_state);
void python_thread_state_restore(void **python_thread_state);
-static inline BL::Mesh object_to_mesh(BL::BlendData &data,
+static inline BL::Mesh object_to_mesh(BL::BlendData & /*data*/,
BL::Object &object,
- BL::Depsgraph &depsgraph,
- bool calc_undeformed,
+ BL::Depsgraph & /*depsgraph*/,
+ bool /*calc_undeformed*/,
Mesh::SubdivisionType subdivision_type)
{
/* TODO: make this work with copy-on-write, modifiers are already evaluated. */
@@ -54,8 +54,8 @@ static inline BL::Mesh object_to_mesh(BL::BlendData &data,
bool subsurf_mod_show_render = false;
bool subsurf_mod_show_viewport = false;
- if(subdivision_type != Mesh::SUBDIVISION_NONE) {
- BL::Modifier subsurf_mod = object.modifiers[object.modifiers.length()-1];
+ if (subdivision_type != Mesh::SUBDIVISION_NONE) {
+ BL::Modifier subsurf_mod = object.modifiers[object.modifiers.length() - 1];
subsurf_mod_show_render = subsurf_mod.show_render();
subsurf_mod_show_viewport = subsurf_mod.show_viewport();
@@ -75,16 +75,18 @@ static inline BL::Mesh object_to_mesh(BL::BlendData &data,
* UV are not empty. */
if (mesh.is_editmode() ||
(mesh.use_auto_smooth() && subdivision_type == Mesh::SUBDIVISION_NONE)) {
- mesh = data.meshes.new_from_object(depsgraph, object, false, false);
+ BL::Depsgraph depsgraph(PointerRNA_NULL);
+ mesh = object.to_mesh(false, depsgraph);
}
}
else {
- mesh = data.meshes.new_from_object(depsgraph, object, true, calc_undeformed);
+ BL::Depsgraph depsgraph(PointerRNA_NULL);
+ mesh = object.to_mesh(false, depsgraph);
}
#if 0
- if(subdivision_type != Mesh::SUBDIVISION_NONE) {
- BL::Modifier subsurf_mod = object.modifiers[object.modifiers.length()-1];
+ if (subdivision_type != Mesh::SUBDIVISION_NONE) {
+ BL::Modifier subsurf_mod = object.modifiers[object.modifiers.length() - 1];
subsurf_mod.show_render(subsurf_mod_show_render);
subsurf_mod.show_viewport(subsurf_mod_show_viewport);
@@ -102,11 +104,13 @@ static inline BL::Mesh object_to_mesh(BL::BlendData &data,
return mesh;
}
-static inline void free_object_to_mesh(BL::BlendData &data, BL::Object &object, BL::Mesh &mesh)
+static inline void free_object_to_mesh(BL::BlendData & /*data*/,
+ BL::Object &object,
+ BL::Mesh &mesh)
{
/* Free mesh if we didn't just use the existing one. */
if (object.data().ptr.data != mesh.ptr.data) {
- data.meshes.remove(mesh, false, true, false);
+ object.to_mesh_clear();
}
}
@@ -155,7 +159,7 @@ static inline void curvemapping_to_array(BL::CurveMapping &cumap, array<float> &
data.resize(size);
for (int i = 0; i < size; i++) {
float t = (float)i / (float)(size - 1);
- data[i] = curve.evaluate(t);
+ data[i] = cumap.evaluate(curve, t);
}
}
@@ -193,15 +197,16 @@ static inline void curvemapping_color_to_array(BL::CurveMapping &cumap,
BL::CurveMap mapI = cumap.curves[3];
for (int i = 0; i < size; i++) {
const float t = min_x + (float)i / (float)(size - 1) * range_x;
- data[i] = make_float3(mapR.evaluate(mapI.evaluate(t)),
- mapG.evaluate(mapI.evaluate(t)),
- mapB.evaluate(mapI.evaluate(t)));
+ data[i] = make_float3(cumap.evaluate(mapR, cumap.evaluate(mapI, t)),
+ cumap.evaluate(mapG, cumap.evaluate(mapI, t)),
+ cumap.evaluate(mapB, cumap.evaluate(mapI, t)));
}
}
else {
for (int i = 0; i < size; i++) {
float t = min_x + (float)i / (float)(size - 1) * range_x;
- data[i] = make_float3(mapR.evaluate(t), mapG.evaluate(t), mapB.evaluate(t));
+ data[i] = make_float3(
+ cumap.evaluate(mapR, t), cumap.evaluate(mapG, t), cumap.evaluate(mapB, t));
}
}
}
@@ -226,28 +231,37 @@ static inline int render_resolution_y(BL::RenderSettings &b_render)
return b_render.resolution_y() * b_render.resolution_percentage() / 100;
}
-static inline string image_user_file_path(BL::ImageUser &iuser, BL::Image &ima, int cfra)
+static inline string image_user_file_path(BL::ImageUser &iuser,
+ BL::Image &ima,
+ int cfra,
+ bool load_tiled)
{
char filepath[1024];
- BKE_image_user_frame_calc(iuser.ptr.data, cfra);
+ iuser.tile(0);
+ BKE_image_user_frame_calc(NULL, iuser.ptr.data, cfra);
BKE_image_user_file_path(iuser.ptr.data, ima.ptr.data, filepath);
- return string(filepath);
+
+ string filepath_str = string(filepath);
+ if (load_tiled && ima.source() == BL::Image::source_TILED) {
+ string_replace(filepath_str, "1001", "<UDIM>");
+ }
+ return filepath_str;
}
static inline int image_user_frame_number(BL::ImageUser &iuser, int cfra)
{
- BKE_image_user_frame_calc(iuser.ptr.data, cfra);
+ BKE_image_user_frame_calc(NULL, iuser.ptr.data, cfra);
return iuser.frame_current();
}
-static inline unsigned char *image_get_pixels_for_frame(BL::Image &image, int frame)
+static inline unsigned char *image_get_pixels_for_frame(BL::Image &image, int frame, int tile)
{
- return BKE_image_get_pixels_for_frame(image.ptr.data, frame);
+ return BKE_image_get_pixels_for_frame(image.ptr.data, frame, tile);
}
-static inline float *image_get_float_pixels_for_frame(BL::Image &image, int frame)
+static inline float *image_get_float_pixels_for_frame(BL::Image &image, int frame, int tile)
{
- return BKE_image_get_float_pixels_for_frame(image.ptr.data, frame);
+ return BKE_image_get_float_pixels_for_frame(image.ptr.data, frame, tile);
}
static inline void render_add_metadata(BL::RenderResult &b_rr, string name, string value)
@@ -469,7 +483,9 @@ static inline void mesh_texture_space(BL::Mesh &b_mesh, float3 &loc, float3 &siz
}
/* Object motion steps, returns 0 if no motion blur needed. */
-static inline uint object_motion_steps(BL::Object &b_parent, BL::Object &b_ob)
+static inline uint object_motion_steps(BL::Object &b_parent,
+ BL::Object &b_ob,
+ const int max_steps = INT_MAX)
{
/* Get motion enabled and steps from object itself. */
PointerRNA cobject = RNA_pointer_get(&b_ob.ptr, "cycles");
@@ -478,7 +494,7 @@ static inline uint object_motion_steps(BL::Object &b_parent, BL::Object &b_ob)
return 0;
}
- uint steps = max(1, get_int(cobject, "motion_steps"));
+ int steps = max(1, get_int(cobject, "motion_steps"));
/* Also check parent object, so motion blur and steps can be
* controlled by dupligroup duplicator for linked groups. */
@@ -496,7 +512,7 @@ static inline uint object_motion_steps(BL::Object &b_parent, BL::Object &b_ob)
/* Use uneven number of steps so we get one keyframe at the current frame,
* and use 2^(steps - 1) so objects with more/fewer steps still have samples
* at the same times, to avoid sampling at many different times. */
- return (2 << (steps - 1)) + 1;
+ return min((2 << (steps - 1)) + 1, max_steps);
}
/* object uses deformation motion blur */
@@ -517,37 +533,40 @@ static inline bool object_use_deform_motion(BL::Object &b_parent, BL::Object &b_
return use_deform_motion;
}
-static inline BL::SmokeDomainSettings object_smoke_domain_find(BL::Object &b_ob)
+static inline BL::FluidDomainSettings object_fluid_liquid_domain_find(BL::Object &b_ob)
{
BL::Object::modifiers_iterator b_mod;
for (b_ob.modifiers.begin(b_mod); b_mod != b_ob.modifiers.end(); ++b_mod) {
- if (b_mod->is_a(&RNA_SmokeModifier)) {
- BL::SmokeModifier b_smd(*b_mod);
+ if (b_mod->is_a(&RNA_FluidModifier)) {
+ BL::FluidModifier b_mmd(*b_mod);
- if (b_smd.smoke_type() == BL::SmokeModifier::smoke_type_DOMAIN)
- return b_smd.domain_settings();
+ if (b_mmd.fluid_type() == BL::FluidModifier::fluid_type_DOMAIN &&
+ b_mmd.domain_settings().domain_type() == BL::FluidDomainSettings::domain_type_LIQUID) {
+ return b_mmd.domain_settings();
+ }
}
}
- return BL::SmokeDomainSettings(PointerRNA_NULL);
+ return BL::FluidDomainSettings(PointerRNA_NULL);
}
-static inline BL::DomainFluidSettings object_fluid_domain_find(BL::Object b_ob)
+static inline BL::FluidDomainSettings object_fluid_gas_domain_find(BL::Object &b_ob)
{
BL::Object::modifiers_iterator b_mod;
for (b_ob.modifiers.begin(b_mod); b_mod != b_ob.modifiers.end(); ++b_mod) {
- if (b_mod->is_a(&RNA_FluidSimulationModifier)) {
- BL::FluidSimulationModifier b_fmd(*b_mod);
- BL::FluidSettings fss = b_fmd.settings();
+ if (b_mod->is_a(&RNA_FluidModifier)) {
+ BL::FluidModifier b_mmd(*b_mod);
- if (fss.type() == BL::FluidSettings::type_DOMAIN)
- return (BL::DomainFluidSettings)b_fmd.settings();
+ if (b_mmd.fluid_type() == BL::FluidModifier::fluid_type_DOMAIN &&
+ b_mmd.domain_settings().domain_type() == BL::FluidDomainSettings::domain_type_GAS) {
+ return b_mmd.domain_settings();
+ }
}
}
- return BL::DomainFluidSettings(PointerRNA_NULL);
+ return BL::FluidDomainSettings(PointerRNA_NULL);
}
static inline Mesh::SubdivisionType object_subdivision_type(BL::Object &b_ob,
@@ -576,209 +595,20 @@ static inline Mesh::SubdivisionType object_subdivision_type(BL::Object &b_ob,
return Mesh::SUBDIVISION_NONE;
}
-/* ID Map
- *
- * Utility class to keep in sync with blender data.
- * Used for objects, meshes, lights and shaders. */
-
-template<typename K, typename T> class id_map {
- public:
- id_map(vector<T *> *scene_data_)
- {
- scene_data = scene_data_;
- }
-
- T *find(const BL::ID &id)
- {
- return find(id.ptr.id.data);
- }
-
- T *find(const K &key)
- {
- if (b_map.find(key) != b_map.end()) {
- T *data = b_map[key];
- return data;
- }
-
- return NULL;
- }
-
- void set_recalc(const BL::ID &id)
- {
- b_recalc.insert(id.ptr.data);
- }
-
- void set_recalc(void *id_ptr)
- {
- b_recalc.insert(id_ptr);
- }
-
- bool has_recalc()
- {
- return !(b_recalc.empty());
- }
-
- void pre_sync()
- {
- used_set.clear();
- }
-
- bool sync(T **r_data, const BL::ID &id)
- {
- return sync(r_data, id, id, id.ptr.id.data);
- }
-
- bool sync(T **r_data, const BL::ID &id, const BL::ID &parent, const K &key)
- {
- T *data = find(key);
- bool recalc;
-
- if (!data) {
- /* add data if it didn't exist yet */
- data = new T();
- scene_data->push_back(data);
- b_map[key] = data;
- recalc = true;
- }
- else {
- recalc = (b_recalc.find(id.ptr.data) != b_recalc.end());
- if (parent.ptr.data)
- recalc = recalc || (b_recalc.find(parent.ptr.data) != b_recalc.end());
- }
-
- used(data);
-
- *r_data = data;
- return recalc;
- }
-
- bool is_used(const K &key)
- {
- T *data = find(key);
- return (data) ? used_set.find(data) != used_set.end() : false;
- }
-
- void used(T *data)
- {
- /* tag data as still in use */
- used_set.insert(data);
- }
-
- void set_default(T *data)
- {
- b_map[NULL] = data;
- }
-
- bool post_sync(bool do_delete = true)
- {
- /* remove unused data */
- vector<T *> new_scene_data;
- typename vector<T *>::iterator it;
- bool deleted = false;
-
- for (it = scene_data->begin(); it != scene_data->end(); it++) {
- T *data = *it;
-
- if (do_delete && used_set.find(data) == used_set.end()) {
- delete data;
- deleted = true;
- }
- else
- new_scene_data.push_back(data);
- }
-
- *scene_data = new_scene_data;
-
- /* update mapping */
- map<K, T *> new_map;
- typedef pair<const K, T *> TMapPair;
- typename map<K, T *>::iterator jt;
-
- for (jt = b_map.begin(); jt != b_map.end(); jt++) {
- TMapPair &pair = *jt;
-
- if (used_set.find(pair.second) != used_set.end())
- new_map[pair.first] = pair.second;
- }
-
- used_set.clear();
- b_recalc.clear();
- b_map = new_map;
-
- return deleted;
- }
-
- const map<K, T *> &key_to_scene_data()
- {
- return b_map;
- }
-
- protected:
- vector<T *> *scene_data;
- map<K, T *> b_map;
- set<T *> used_set;
- set<void *> b_recalc;
-};
-
-/* Object Key */
-
-enum { OBJECT_PERSISTENT_ID_SIZE = 16 };
-
-struct ObjectKey {
- void *parent;
- int id[OBJECT_PERSISTENT_ID_SIZE];
- void *ob;
-
- ObjectKey(void *parent_, int id_[OBJECT_PERSISTENT_ID_SIZE], void *ob_)
- : parent(parent_), ob(ob_)
- {
- if (id_)
- memcpy(id, id_, sizeof(id));
- else
- memset(id, 0, sizeof(id));
- }
-
- bool operator<(const ObjectKey &k) const
- {
- if (ob < k.ob) {
- return true;
- }
- else if (ob == k.ob) {
- if (parent < k.parent)
- return true;
- else if (parent == k.parent)
- return memcmp(id, k.id, sizeof(id)) < 0;
- }
-
- return false;
- }
-};
-
-/* Particle System Key */
-
-struct ParticleSystemKey {
- void *ob;
- int id[OBJECT_PERSISTENT_ID_SIZE];
-
- ParticleSystemKey(void *ob_, int id_[OBJECT_PERSISTENT_ID_SIZE]) : ob(ob_)
- {
- if (id_)
- memcpy(id, id_, sizeof(id));
- else
- memset(id, 0, sizeof(id));
- }
+static inline uint object_ray_visibility(BL::Object &b_ob)
+{
+ PointerRNA cvisibility = RNA_pointer_get(&b_ob.ptr, "cycles_visibility");
+ uint flag = 0;
- bool operator<(const ParticleSystemKey &k) const
- {
- /* first id is particle index, we don't compare that */
- if (ob < k.ob)
- return true;
- else if (ob == k.ob)
- return memcmp(id + 1, k.id + 1, sizeof(int) * (OBJECT_PERSISTENT_ID_SIZE - 1)) < 0;
+ flag |= get_boolean(cvisibility, "camera") ? PATH_RAY_CAMERA : 0;
+ flag |= get_boolean(cvisibility, "diffuse") ? PATH_RAY_DIFFUSE : 0;
+ flag |= get_boolean(cvisibility, "glossy") ? PATH_RAY_GLOSSY : 0;
+ flag |= get_boolean(cvisibility, "transmission") ? PATH_RAY_TRANSMIT : 0;
+ flag |= get_boolean(cvisibility, "shadow") ? PATH_RAY_SHADOW : 0;
+ flag |= get_boolean(cvisibility, "scatter") ? PATH_RAY_VOLUME_SCATTER : 0;
- return false;
- }
-};
+ return flag;
+}
class EdgeMap {
public:
diff --git a/intern/cycles/blender/blender_viewport.cpp b/intern/cycles/blender/blender_viewport.cpp
new file mode 100644
index 00000000000..73ef5f94720
--- /dev/null
+++ b/intern/cycles/blender/blender_viewport.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2019 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "blender_viewport.h"
+
+#include "blender_util.h"
+
+CCL_NAMESPACE_BEGIN
+
+BlenderViewportParameters::BlenderViewportParameters()
+ : use_scene_world(true),
+ use_scene_lights(true),
+ studiolight_rotate_z(0.0f),
+ studiolight_intensity(1.0f),
+ studiolight_background_alpha(1.0f),
+ studiolight_path(ustring())
+{
+}
+
+BlenderViewportParameters::BlenderViewportParameters(BL::SpaceView3D &b_v3d)
+ : BlenderViewportParameters()
+{
+ /* We only copy the parameters if we are in look dev mode. otherwise
+ * defaults are being used. These defaults mimic normal render settings */
+ if (b_v3d && b_v3d.shading().type() == BL::View3DShading::type_RENDERED) {
+ use_scene_world = b_v3d.shading().use_scene_world_render();
+ use_scene_lights = b_v3d.shading().use_scene_lights_render();
+ if (!use_scene_world) {
+ studiolight_rotate_z = b_v3d.shading().studiolight_rotate_z();
+ studiolight_intensity = b_v3d.shading().studiolight_intensity();
+ studiolight_background_alpha = b_v3d.shading().studiolight_background_alpha();
+ studiolight_path = b_v3d.shading().selected_studio_light().path();
+ }
+ }
+}
+
+/* Check if two instances are different. */
+const bool BlenderViewportParameters::modified(const BlenderViewportParameters &other) const
+{
+ return use_scene_world != other.use_scene_world || use_scene_lights != other.use_scene_lights ||
+ studiolight_rotate_z != other.studiolight_rotate_z ||
+ studiolight_intensity != other.studiolight_intensity ||
+ studiolight_background_alpha != other.studiolight_background_alpha ||
+ studiolight_path != other.studiolight_path;
+}
+
+const bool BlenderViewportParameters::custom_viewport_parameters() const
+{
+ return !(use_scene_world && use_scene_lights);
+}
+
+PassType BlenderViewportParameters::get_viewport_display_render_pass(BL::SpaceView3D &b_v3d)
+{
+ PassType display_pass = PASS_NONE;
+ if (b_v3d) {
+ BL::View3DShading b_view3dshading = b_v3d.shading();
+ PointerRNA cshading = RNA_pointer_get(&b_view3dshading.ptr, "cycles");
+ display_pass = (PassType)get_enum(cshading, "render_pass", -1, -1);
+ }
+ return display_pass;
+}
+
+PassType update_viewport_display_passes(BL::SpaceView3D &b_v3d, vector<Pass> &passes)
+{
+ if (b_v3d) {
+ PassType display_pass = BlenderViewportParameters::get_viewport_display_render_pass(b_v3d);
+
+ passes.clear();
+ Pass::add(display_pass, passes);
+
+ return display_pass;
+ }
+ return PASS_NONE;
+}
+
+CCL_NAMESPACE_END
diff --git a/intern/cycles/blender/blender_viewport.h b/intern/cycles/blender/blender_viewport.h
new file mode 100644
index 00000000000..7c6c9c4d274
--- /dev/null
+++ b/intern/cycles/blender/blender_viewport.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2019 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BLENDER_VIEWPORT_H__
+#define __BLENDER_VIEWPORT_H__
+
+#include "MEM_guardedalloc.h"
+#include "RNA_access.h"
+#include "RNA_blender_cpp.h"
+#include "RNA_types.h"
+
+#include "render/film.h"
+#include "util/util_param.h"
+
+CCL_NAMESPACE_BEGIN
+
+class BlenderViewportParameters {
+ private:
+ bool use_scene_world;
+ bool use_scene_lights;
+ float studiolight_rotate_z;
+ float studiolight_intensity;
+ float studiolight_background_alpha;
+ ustring studiolight_path;
+
+ BlenderViewportParameters();
+ BlenderViewportParameters(BL::SpaceView3D &b_v3d);
+
+ const bool modified(const BlenderViewportParameters &other) const;
+ const bool custom_viewport_parameters() const;
+ friend class BlenderSync;
+
+ public:
+ /* Retrieve the render pass that needs to be displayed on the given `SpaceView3D`
+ * When the `b_v3d` parameter is not given `PASS_NONE` will be returned. */
+ static PassType get_viewport_display_render_pass(BL::SpaceView3D &b_v3d);
+};
+
+PassType update_viewport_display_passes(BL::SpaceView3D &b_v3d, vector<Pass> &passes);
+
+CCL_NAMESPACE_END
+
+#endif
diff --git a/intern/cycles/blender/blender_volume.cpp b/intern/cycles/blender/blender_volume.cpp
new file mode 100644
index 00000000000..80591e0eec8
--- /dev/null
+++ b/intern/cycles/blender/blender_volume.cpp
@@ -0,0 +1,387 @@
+/*
+ * Copyright 2011-2013 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "render/colorspace.h"
+#include "render/image.h"
+#include "render/image_vdb.h"
+#include "render/mesh.h"
+#include "render/object.h"
+
+#include "blender/blender_sync.h"
+#include "blender/blender_util.h"
+
+#ifdef WITH_OPENVDB
+# include <openvdb/openvdb.h>
+openvdb::GridBase::ConstPtr BKE_volume_grid_openvdb_for_read(const struct Volume *volume,
+ struct VolumeGrid *grid);
+#endif
+
+CCL_NAMESPACE_BEGIN
+
+/* TODO: verify this is not loading unnecessary attributes. */
+class BlenderSmokeLoader : public ImageLoader {
+ public:
+ BlenderSmokeLoader(BL::Object &b_ob, AttributeStandard attribute)
+ : b_domain(object_fluid_gas_domain_find(b_ob)), attribute(attribute)
+ {
+ BL::Mesh b_mesh(b_ob.data());
+ mesh_texture_space(b_mesh, texspace_loc, texspace_size);
+ }
+
+ bool load_metadata(ImageMetaData &metadata) override
+ {
+ if (!b_domain) {
+ return false;
+ }
+
+ if (attribute == ATTR_STD_VOLUME_DENSITY || attribute == ATTR_STD_VOLUME_FLAME ||
+ attribute == ATTR_STD_VOLUME_HEAT || attribute == ATTR_STD_VOLUME_TEMPERATURE) {
+ metadata.type = IMAGE_DATA_TYPE_FLOAT;
+ metadata.channels = 1;
+ }
+ else if (attribute == ATTR_STD_VOLUME_COLOR) {
+ metadata.type = IMAGE_DATA_TYPE_FLOAT4;
+ metadata.channels = 4;
+ }
+ else if (attribute == ATTR_STD_VOLUME_VELOCITY) {
+ metadata.type = IMAGE_DATA_TYPE_FLOAT4;
+ metadata.channels = 3;
+ }
+ else {
+ return false;
+ }
+
+ int3 resolution = get_int3(b_domain.domain_resolution());
+ int amplify = (b_domain.use_noise()) ? b_domain.noise_scale() : 1;
+
+ /* Velocity and heat data is always low-resolution. */
+ if (attribute == ATTR_STD_VOLUME_VELOCITY || attribute == ATTR_STD_VOLUME_HEAT) {
+ amplify = 1;
+ }
+
+ metadata.width = resolution.x * amplify;
+ metadata.height = resolution.y * amplify;
+ metadata.depth = resolution.z * amplify;
+
+ /* Create a matrix to transform from object space to mesh texture space.
+ * This does not work with deformations but that can probably only be done
+ * well with a volume grid mapping of coordinates. */
+ metadata.transform_3d = transform_translate(-texspace_loc) * transform_scale(texspace_size);
+ metadata.use_transform_3d = true;
+
+ return true;
+ }
+
+ bool load_pixels(const ImageMetaData &, void *pixels, const size_t, const bool) override
+ {
+ if (!b_domain) {
+ return false;
+ }
+#ifdef WITH_FLUID
+ int3 resolution = get_int3(b_domain.domain_resolution());
+ int length, amplify = (b_domain.use_noise()) ? b_domain.noise_scale() : 1;
+
+ /* Velocity and heat data is always low-resolution. */
+ if (attribute == ATTR_STD_VOLUME_VELOCITY || attribute == ATTR_STD_VOLUME_HEAT) {
+ amplify = 1;
+ }
+
+ const int width = resolution.x * amplify;
+ const int height = resolution.y * amplify;
+ const int depth = resolution.z * amplify;
+ const size_t num_pixels = ((size_t)width) * height * depth;
+
+ float *fpixels = (float *)pixels;
+
+ if (attribute == ATTR_STD_VOLUME_DENSITY) {
+ FluidDomainSettings_density_grid_get_length(&b_domain.ptr, &length);
+ if (length == num_pixels) {
+ FluidDomainSettings_density_grid_get(&b_domain.ptr, fpixels);
+ return true;
+ }
+ }
+ else if (attribute == ATTR_STD_VOLUME_FLAME) {
+ /* this is in range 0..1, and interpreted by the OpenGL smoke viewer
+ * as 1500..3000 K with the first part faded to zero density */
+ FluidDomainSettings_flame_grid_get_length(&b_domain.ptr, &length);
+ if (length == num_pixels) {
+ FluidDomainSettings_flame_grid_get(&b_domain.ptr, fpixels);
+ return true;
+ }
+ }
+ else if (attribute == ATTR_STD_VOLUME_COLOR) {
+ /* the RGB is "premultiplied" by density for better interpolation results */
+ FluidDomainSettings_color_grid_get_length(&b_domain.ptr, &length);
+ if (length == num_pixels * 4) {
+ FluidDomainSettings_color_grid_get(&b_domain.ptr, fpixels);
+ return true;
+ }
+ }
+ else if (attribute == ATTR_STD_VOLUME_VELOCITY) {
+ FluidDomainSettings_velocity_grid_get_length(&b_domain.ptr, &length);
+ if (length == num_pixels * 3) {
+ FluidDomainSettings_velocity_grid_get(&b_domain.ptr, fpixels);
+ return true;
+ }
+ }
+ else if (attribute == ATTR_STD_VOLUME_HEAT) {
+ FluidDomainSettings_heat_grid_get_length(&b_domain.ptr, &length);
+ if (length == num_pixels) {
+ FluidDomainSettings_heat_grid_get(&b_domain.ptr, fpixels);
+ return true;
+ }
+ }
+ else if (attribute == ATTR_STD_VOLUME_TEMPERATURE) {
+ FluidDomainSettings_temperature_grid_get_length(&b_domain.ptr, &length);
+ if (length == num_pixels) {
+ FluidDomainSettings_temperature_grid_get(&b_domain.ptr, fpixels);
+ return true;
+ }
+ }
+ else {
+ fprintf(stderr,
+ "Cycles error: unknown volume attribute %s, skipping\n",
+ Attribute::standard_name(attribute));
+ fpixels[0] = 0.0f;
+ return false;
+ }
+#else
+ (void)pixels;
+#endif
+ fprintf(stderr, "Cycles error: unexpected smoke volume resolution, skipping\n");
+ return false;
+ }
+
+ string name() const override
+ {
+ return Attribute::standard_name(attribute);
+ }
+
+ bool equals(const ImageLoader &other) const override
+ {
+ const BlenderSmokeLoader &other_loader = (const BlenderSmokeLoader &)other;
+ return b_domain == other_loader.b_domain && attribute == other_loader.attribute;
+ }
+
+ BL::FluidDomainSettings b_domain;
+ float3 texspace_loc, texspace_size;
+ AttributeStandard attribute;
+};
+
+static void sync_smoke_volume(Scene *scene, BL::Object &b_ob, Mesh *mesh, float frame)
+{
+ BL::FluidDomainSettings b_domain = object_fluid_gas_domain_find(b_ob);
+ if (!b_domain) {
+ return;
+ }
+
+ AttributeStandard attributes[] = {ATTR_STD_VOLUME_DENSITY,
+ ATTR_STD_VOLUME_COLOR,
+ ATTR_STD_VOLUME_FLAME,
+ ATTR_STD_VOLUME_HEAT,
+ ATTR_STD_VOLUME_TEMPERATURE,
+ ATTR_STD_VOLUME_VELOCITY,
+ ATTR_STD_NONE};
+
+ for (int i = 0; attributes[i] != ATTR_STD_NONE; i++) {
+ AttributeStandard std = attributes[i];
+ if (!mesh->need_attribute(scene, std)) {
+ continue;
+ }
+
+ mesh->volume_clipping = b_domain.clipping();
+
+ Attribute *attr = mesh->attributes.add(std);
+
+ ImageLoader *loader = new BlenderSmokeLoader(b_ob, std);
+ ImageParams params;
+ params.frame = frame;
+
+ attr->data_voxel() = scene->image_manager->add_image(loader, params);
+ }
+}
+
+class BlenderVolumeLoader : public VDBImageLoader {
+ public:
+ BlenderVolumeLoader(BL::BlendData &b_data, BL::Volume &b_volume, const string &grid_name)
+ : VDBImageLoader(grid_name), b_data(b_data), b_volume(b_volume), unload(false)
+ {
+ }
+
+ bool load_metadata(ImageMetaData &metadata) override
+ {
+ b_volume.grids.load(b_data.ptr.data);
+ BL::VolumeGrid b_volume_grid = find_grid();
+
+ if (!b_volume_grid) {
+ return false;
+ }
+
+ unload = !b_volume_grid.is_loaded();
+
+#ifdef WITH_OPENVDB
+ Volume *volume = (Volume *)b_volume.ptr.data;
+ VolumeGrid *volume_grid = (VolumeGrid *)b_volume_grid.ptr.data;
+ grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid);
+#endif
+
+ return VDBImageLoader::load_metadata(metadata);
+ }
+
+ bool load_pixels(const ImageMetaData &metadata,
+ void *pixels,
+ const size_t pixel_size,
+ const bool associate_alpha) override
+ {
+ b_volume.grids.load(b_data.ptr.data);
+ BL::VolumeGrid b_volume_grid = find_grid();
+
+ if (!b_volume_grid) {
+ return false;
+ }
+
+ return VDBImageLoader::load_pixels(metadata, pixels, pixel_size, associate_alpha);
+ }
+
+ bool equals(const ImageLoader &other) const override
+ {
+ /* TODO: detect multiple volume datablocks with the same filepath. */
+ const BlenderVolumeLoader &other_loader = (const BlenderVolumeLoader &)other;
+ return b_volume == other_loader.b_volume && grid_name == other_loader.grid_name;
+ }
+
+ void cleanup() override
+ {
+ VDBImageLoader::cleanup();
+
+ BL::VolumeGrid b_volume_grid = find_grid();
+ if (b_volume_grid && unload) {
+ b_volume_grid.unload();
+ }
+ }
+
+ /* Find grid with matching name. Grid point not stored in the class since
+ * grids may be unloaded before we load the pixels, for example for motion
+ * blur where we move between frames. */
+ BL::VolumeGrid find_grid()
+ {
+#ifdef WITH_OPENVDB
+ BL::Volume::grids_iterator b_grid_iter;
+ for (b_volume.grids.begin(b_grid_iter); b_grid_iter != b_volume.grids.end(); ++b_grid_iter) {
+ if (b_grid_iter->name() == grid_name) {
+ return *b_grid_iter;
+ }
+ }
+#endif
+
+ return BL::VolumeGrid(PointerRNA_NULL);
+ }
+
+ BL::BlendData b_data;
+ BL::Volume b_volume;
+ bool unload;
+};
+
+static void sync_volume_object(BL::BlendData &b_data, BL::Object &b_ob, Scene *scene, Mesh *mesh)
+{
+ BL::Volume b_volume(b_ob.data());
+ b_volume.grids.load(b_data.ptr.data);
+
+ BL::VolumeRender b_render(b_volume.render());
+
+ mesh->volume_clipping = b_render.clipping();
+ mesh->volume_step_size = b_render.step_size();
+ mesh->volume_object_space = (b_render.space() == BL::VolumeRender::space_OBJECT);
+
+ /* Find grid with matching name. */
+ BL::Volume::grids_iterator b_grid_iter;
+ for (b_volume.grids.begin(b_grid_iter); b_grid_iter != b_volume.grids.end(); ++b_grid_iter) {
+ BL::VolumeGrid b_grid = *b_grid_iter;
+ ustring name = ustring(b_grid.name());
+ AttributeStandard std = ATTR_STD_NONE;
+
+ if (name == Attribute::standard_name(ATTR_STD_VOLUME_DENSITY)) {
+ std = ATTR_STD_VOLUME_DENSITY;
+ }
+ else if (name == Attribute::standard_name(ATTR_STD_VOLUME_COLOR)) {
+ std = ATTR_STD_VOLUME_COLOR;
+ }
+ else if (name == Attribute::standard_name(ATTR_STD_VOLUME_FLAME)) {
+ std = ATTR_STD_VOLUME_FLAME;
+ }
+ else if (name == Attribute::standard_name(ATTR_STD_VOLUME_HEAT)) {
+ std = ATTR_STD_VOLUME_HEAT;
+ }
+ else if (name == Attribute::standard_name(ATTR_STD_VOLUME_TEMPERATURE)) {
+ std = ATTR_STD_VOLUME_TEMPERATURE;
+ }
+ else if (name == Attribute::standard_name(ATTR_STD_VOLUME_VELOCITY)) {
+ std = ATTR_STD_VOLUME_VELOCITY;
+ }
+
+ if ((std != ATTR_STD_NONE && mesh->need_attribute(scene, std)) ||
+ mesh->need_attribute(scene, name)) {
+ Attribute *attr = (std != ATTR_STD_NONE) ?
+ mesh->attributes.add(std) :
+ mesh->attributes.add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_VOXEL);
+
+ ImageLoader *loader = new BlenderVolumeLoader(b_data, b_volume, name.string());
+ ImageParams params;
+ params.frame = b_volume.grids.frame();
+
+ attr->data_voxel() = scene->image_manager->add_image(loader, params);
+ }
+ }
+}
+
+/* If the voxel attributes change, we need to rebuild the bounding mesh. */
+static vector<int> get_voxel_image_slots(Mesh *mesh)
+{
+ vector<int> slots;
+ for (const Attribute &attr : mesh->attributes.attributes) {
+ if (attr.element == ATTR_ELEMENT_VOXEL) {
+ slots.push_back(attr.data_voxel().svm_slot());
+ }
+ }
+
+ return slots;
+}
+
+void BlenderSync::sync_volume(BL::Object &b_ob, Mesh *mesh, const vector<Shader *> &used_shaders)
+{
+ vector<int> old_voxel_slots = get_voxel_image_slots(mesh);
+
+ mesh->clear();
+ mesh->used_shaders = used_shaders;
+
+ if (view_layer.use_volumes) {
+ if (b_ob.type() == BL::Object::type_VOLUME) {
+ /* Volume object. Create only attributes, bounding mesh will then
+ * be automatically generated later. */
+ sync_volume_object(b_data, b_ob, scene, mesh);
+ }
+ else {
+ /* Smoke domain. */
+ sync_smoke_volume(scene, b_ob, mesh, b_scene.frame_current());
+ }
+ }
+
+ /* Tag update. */
+ bool rebuild = (old_voxel_slots != get_voxel_image_slots(mesh));
+ mesh->tag_update(scene, rebuild);
+}
+
+CCL_NAMESPACE_END