diff options
author | Sam Kottler <dev@samkottler.net> | 2020-08-12 19:59:35 +0300 |
---|---|---|
committer | Sam Kottler <dev@samkottler.net> | 2020-08-12 19:59:35 +0300 |
commit | ac0bab2d432fe8834d21b3549721ed7feecd863a (patch) | |
tree | 39d3cb6b955bc7f61cd538e4469bf934af0ca306 /intern/cycles/blender | |
parent | df8bbe98c46b8999463767b73cb0764b7500a195 (diff) | |
parent | 0e280b96ca82eef0d9039ca06b150cfa05a33a65 (diff) |
Merge branch 'blender-v2.90-release' into soc-2020-production-ready-light-tree-2
Diffstat (limited to 'intern/cycles/blender')
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), ®ionptr); + RNA_pointer_create(bScreen, &RNA_Region, pylong_as_voidptr_typesafe(pyregion), ®ionptr); 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 |