From 689f18279270efe943a5b9aa0684b3f5b87d76ef Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 25 Sep 2025 16:18:50 +0200 Subject: [PATCH] Cycles: Make adaptive subdivision a non-experimental feature * Add adaptive subdivision properties natively on the subdivision surface modifier, so that other engines may reuse them in the future. This also resolve issues where they would not get copied properly. * Remove "Feature Set" option in the render properties, this was the last experimental one. * Add space choice between "Pixel" and "Object". The latter is new and can be used for object space dicing that works with instances. Instead of a pixel size an object space edge length is specified. * Add object space subdivision test. Ref #53901 Pull Request: https://projects.blender.org/blender/blender/pulls/146723 --- intern/cycles/blender/addon/properties.py | 39 +--------- intern/cycles/blender/addon/ui.py | 7 -- intern/cycles/blender/mesh.cpp | 15 +++- intern/cycles/blender/sync.cpp | 6 +- intern/cycles/blender/sync.h | 1 - intern/cycles/blender/util.h | 12 ++- intern/cycles/scene/geometry.cpp | 6 +- intern/cycles/scene/mesh.cpp | 11 ++- intern/cycles/scene/mesh.h | 6 ++ intern/cycles/session/session.h | 10 +-- .../blender/blenkernel/BKE_blender_version.h | 2 +- source/blender/blenkernel/BKE_scene.hh | 8 -- source/blender/blenkernel/intern/scene.cc | 14 ---- .../blenloader/intern/versioning_500.cc | 42 ++++++++++ .../blender/makesdna/DNA_modifier_defaults.h | 3 + source/blender/makesdna/DNA_modifier_types.h | 12 ++- .../blender/makesrna/intern/rna_modifier.cc | 41 ++++++++++ .../blender/modifiers/intern/MOD_subsurf.cc | 77 ++++++++----------- .../cycles_renders/object_dicing.png | 3 + .../eevee_renders/object_dicing.png | 3 + .../render/displacement/object_dicing.blend | 3 + .../storm_hydra_renders/object_dicing.png | 3 + .../storm_usd_renders/object_dicing.png | 3 + .../workbench_renders/object_dicing.png | 3 + 24 files changed, 195 insertions(+), 135 deletions(-) create mode 100644 tests/files/render/displacement/cycles_renders/object_dicing.png create mode 100644 tests/files/render/displacement/eevee_renders/object_dicing.png create mode 100644 tests/files/render/displacement/object_dicing.blend create mode 100644 tests/files/render/displacement/storm_hydra_renders/object_dicing.png create mode 100644 tests/files/render/displacement/storm_usd_renders/object_dicing.png create mode 100644 tests/files/render/displacement/workbench_renders/object_dicing.png diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index a5450642245..0012948ad08 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -33,17 +33,6 @@ enum_devices = ( "Use GPU compute device for rendering, configured in the system tab in the user preferences"), ) -enum_feature_set = ( - ('SUPPORTED', - "Supported", - "Only use finished and supported features"), - ('EXPERIMENTAL', - "Experimental", - "Use experimental and incomplete features that might be broken or change in the future", - 'ERROR', - 1), -) - enum_bvh_layouts = ( ('BVH2', "BVH2", "", 1), ('EMBREE', "Embree", "", 4), @@ -395,13 +384,6 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): items=enum_devices, default='CPU', ) - feature_set: EnumProperty( - name="Feature Set", - description="Feature set to use for rendering", - items=enum_feature_set, - default='SUPPORTED', - update=update_render_engine, - ) shading_system: BoolProperty( name="Open Shading Language", description="Use Open Shading Language", @@ -806,22 +788,20 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): dicing_rate: FloatProperty( name="Dicing Rate", - description="Size of a micropolygon in pixels", + description="Multiplier for per object adaptive subdivision size", min=0.1, max=1000.0, soft_min=0.5, default=1.0, - subtype='PIXEL' ) preview_dicing_rate: FloatProperty( name="Viewport Dicing Rate", - description="Size of a micropolygon in pixels during preview render", + description="Multiplier for per object adaptive subdivision size in the viewport", min=0.1, max=1000.0, soft_min=0.5, default=8.0, - subtype='PIXEL' ) max_subdivisions: IntProperty( name="Max Subdivisions", - description="Stop subdividing when this level is reached even if the dice rate would produce finer tessellation", + description="Stop subdividing when this level is reached even if the dicing rate would produce finer tessellation", min=0, max=16, default=12, @@ -1378,19 +1358,6 @@ class CyclesObjectSettings(bpy.types.PropertyGroup): default=False, ) - use_adaptive_subdivision: BoolProperty( - name="Use Adaptive Subdivision", - description="Use adaptive render time subdivision", - default=False, - ) - - dicing_rate: FloatProperty( - name="Dicing Scale", - description="Multiplier for scene dicing rate (located in the Subdivision panel)", - min=0.1, max=1000.0, soft_min=0.5, - default=1.0, - ) - shadow_terminator_offset: FloatProperty( name="Shadow Terminator Shading Offset", description="Push the shadow terminator towards the light to hide artifacts on low poly geometry", diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index e4af0466b01..fc181744180 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -524,10 +524,6 @@ class CYCLES_RENDER_PT_subdivision(CyclesButtonsPanel, Panel): bl_label = "Subdivision" bl_options = {'DEFAULT_CLOSED'} - @classmethod - def poll(cls, context): - return (context.scene.render.engine == 'CYCLES') and (context.scene.cycles.feature_set == 'EXPERIMENTAL') - def draw(self, context): layout = self.layout layout.use_property_split = True @@ -2450,9 +2446,6 @@ def draw_device(self, context): from . import engine cscene = scene.cycles - col = layout.column() - col.prop(cscene, "feature_set") - col = layout.column() col.active = show_device_active(context) col.prop(cscene, "device") diff --git a/intern/cycles/blender/mesh.cpp b/intern/cycles/blender/mesh.cpp index 55a3ee6bf85..b5798288b0b 100644 --- a/intern/cycles/blender/mesh.cpp +++ b/intern/cycles/blender/mesh.cpp @@ -834,9 +834,20 @@ static void create_subd_mesh(Scene *scene, } /* Set subd parameters. */ - PointerRNA cobj = RNA_pointer_get(&b_ob.ptr, "cycles"); - const float subd_dicing_rate = max(0.1f, RNA_float_get(&cobj, "dicing_rate") * dicing_rate); + Mesh::SubdivisionAdaptiveSpace space = Mesh::SUBDIVISION_ADAPTIVE_SPACE_PIXEL; + switch (subsurf_mod.adaptive_space()) { + case BL::SubsurfModifier::adaptive_space_OBJECT: + space = Mesh::SUBDIVISION_ADAPTIVE_SPACE_OBJECT; + break; + case BL::SubsurfModifier::adaptive_space_PIXEL: + space = Mesh::SUBDIVISION_ADAPTIVE_SPACE_PIXEL; + break; + } + const float subd_dicing_rate = (space == Mesh::SUBDIVISION_ADAPTIVE_SPACE_PIXEL) ? + max(0.1f, subsurf_mod.adaptive_pixel_size() * dicing_rate) : + subsurf_mod.adaptive_object_edge_length() * dicing_rate; + mesh->set_subd_adaptive_space(space); mesh->set_subd_dicing_rate(subd_dicing_rate); mesh->set_subd_max_level(max_subdivisions); mesh->set_subd_objecttoworld(get_transform(b_ob.matrix_world())); diff --git a/intern/cycles/blender/sync.cpp b/intern/cycles/blender/sync.cpp index 6247e4db89c..6401e9bc4eb 100644 --- a/intern/cycles/blender/sync.cpp +++ b/intern/cycles/blender/sync.cpp @@ -337,8 +337,7 @@ void BlenderSync::sync_integrator(BL::ViewLayer &b_view_layer, PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles"); /* No adaptive subdivision for baking, mesh needs to match Blender exactly. */ - use_adaptive_subdivision = (get_enum(cscene, "feature_set") != 0) && !b_bake_target; - use_experimental_procedural = (get_enum(cscene, "feature_set") != 0); + use_adaptive_subdivision = !b_bake_target; Integrator *integrator = scene->integrator; @@ -936,9 +935,6 @@ SessionParams BlenderSync::get_session_params(BL::RenderEngine &b_engine, params.temp_dir = b_engine.temporary_directory(); } - /* feature set */ - params.experimental = (get_enum(cscene, "feature_set") != 0); - /* Headless and background rendering. */ params.headless = BlenderSession::headless; params.background = background; diff --git a/intern/cycles/blender/sync.h b/intern/cycles/blender/sync.h index 75b99f6b615..11cde3a641b 100644 --- a/intern/cycles/blender/sync.h +++ b/intern/cycles/blender/sync.h @@ -241,7 +241,6 @@ class BlenderSync { Scene *scene; bool preview; - bool use_experimental_procedural = false; bool use_adaptive_subdivision = false; bool use_developer_ui; diff --git a/intern/cycles/blender/util.h b/intern/cycles/blender/util.h index 03a1686a71b..01720d61204 100644 --- a/intern/cycles/blender/util.h +++ b/intern/cycles/blender/util.h @@ -667,17 +667,15 @@ static inline BL::MeshSequenceCacheModifier object_mesh_cache_find(BL::Object &b static BL::SubsurfModifier object_subdivision_modifier(BL::Object &b_ob, const bool preview) { - PointerRNA cobj = RNA_pointer_get(&b_ob.ptr, "cycles"); - - if (cobj.data && !b_ob.modifiers.empty()) { + if (!b_ob.modifiers.empty()) { BL::Modifier mod = b_ob.modifiers[b_ob.modifiers.length() - 1]; const bool enabled = preview ? mod.show_viewport() : mod.show_render(); - if (enabled && mod.type() == BL::Modifier::type_SUBSURF && - RNA_boolean_get(&cobj, "use_adaptive_subdivision")) - { + if (enabled && mod.type() == BL::Modifier::type_SUBSURF) { BL::SubsurfModifier subsurf(mod); - return subsurf; + if (subsurf.use_adaptive_subdivision()) { + return subsurf; + } } } diff --git a/intern/cycles/scene/geometry.cpp b/intern/cycles/scene/geometry.cpp index f31a2f6c20d..ebb29be436e 100644 --- a/intern/cycles/scene/geometry.cpp +++ b/intern/cycles/scene/geometry.cpp @@ -805,8 +805,10 @@ void GeometryManager::device_update(Device *device, SubdParams subd_params(mesh); subd_params.dicing_rate = mesh->get_subd_dicing_rate(); subd_params.max_level = mesh->get_subd_max_level(); - subd_params.objecttoworld = mesh->get_subd_objecttoworld(); - subd_params.camera = dicing_camera; + if (mesh->get_subd_adaptive_space() == Mesh::SUBDIVISION_ADAPTIVE_SPACE_PIXEL) { + subd_params.objecttoworld = mesh->get_subd_objecttoworld(); + subd_params.camera = dicing_camera; + } mesh->tessellate(subd_params); } diff --git a/intern/cycles/scene/mesh.cpp b/intern/cycles/scene/mesh.cpp index f7487442137..b1036d13b72 100644 --- a/intern/cycles/scene/mesh.cpp +++ b/intern/cycles/scene/mesh.cpp @@ -305,6 +305,14 @@ NODE_DEFINE(Mesh) SOCKET_INT_ARRAY(subd_ptex_offset, "Subdivision Face PTex Offset", array()); /* Subdivisions parameters */ + static NodeEnum subd_adaptive_space_enum; + subd_adaptive_space_enum.insert("pixel", SUBDIVISION_ADAPTIVE_SPACE_PIXEL); + subd_adaptive_space_enum.insert("object", SUBDIVISION_ADAPTIVE_SPACE_OBJECT); + + SOCKET_ENUM(subd_adaptive_space, + "Subdivision Adaptive Space", + subd_adaptive_space_enum, + SUBDIVISION_ADAPTIVE_SPACE_PIXEL); SOCKET_FLOAT(subd_dicing_rate, "Subdivision Dicing Rate", 1.0f) SOCKET_INT(subd_max_level, "Max Subdivision Level", 1); SOCKET_TRANSFORM(subd_objecttoworld, "Subdivision Object Transform", transform_identity()); @@ -316,7 +324,8 @@ bool Mesh::need_tesselation() { return (subdivision_type != SUBDIVISION_NONE) && (verts_is_modified() || subd_dicing_rate_is_modified() || - subd_objecttoworld_is_modified() || subd_max_level_is_modified()); + subd_adaptive_space_is_modified() || subd_objecttoworld_is_modified() || + subd_max_level_is_modified()); } Mesh::Mesh(const NodeType *node_type, Type geom_type_) diff --git a/intern/cycles/scene/mesh.h b/intern/cycles/scene/mesh.h index cb442db6f80..e340ce83a97 100644 --- a/intern/cycles/scene/mesh.h +++ b/intern/cycles/scene/mesh.h @@ -135,6 +135,11 @@ class Mesh : public Geometry { SUBDIVISION_FVAR_LINEAR_ALL, }; + enum SubdivisionAdaptiveSpace { + SUBDIVISION_ADAPTIVE_SPACE_PIXEL, + SUBDIVISION_ADAPTIVE_SPACE_OBJECT, + }; + NODE_SOCKET_API(SubdivisionType, subdivision_type) NODE_SOCKET_API(SubdivisionBoundaryInterpolation, subdivision_boundary_interpolation) NODE_SOCKET_API(SubdivisionFVarInterpolation, subdivision_fvar_interpolation) @@ -161,6 +166,7 @@ class Mesh : public Geometry { NODE_SOCKET_API_ARRAY(array, subd_vert_creases_weight) /* Subdivisions parameters */ + NODE_SOCKET_API(SubdivisionAdaptiveSpace, subd_adaptive_space) NODE_SOCKET_API(float, subd_dicing_rate) NODE_SOCKET_API(int, subd_max_level) NODE_SOCKET_API(Transform, subd_objecttoworld) diff --git a/intern/cycles/session/session.h b/intern/cycles/session/session.h index cf8a675b571..9f397770d4f 100644 --- a/intern/cycles/session/session.h +++ b/intern/cycles/session/session.h @@ -44,7 +44,6 @@ class SessionParams { bool headless; bool background; - bool experimental; int samples; bool use_sample_subset; int sample_subset_offset; @@ -73,7 +72,6 @@ class SessionParams { headless = false; background = false; - experimental = false; samples = 1024; use_sample_subset = false; sample_subset_offset = 0; @@ -97,10 +95,10 @@ class SessionParams { /* Modified means we have to recreate the session, any parameter changes * that can be handled by an existing Session are omitted. */ return !(device == params.device && headless == params.headless && - background == params.background && experimental == params.experimental && - pixel_size == params.pixel_size && threads == params.threads && - use_profiling == params.use_profiling && shadingsystem == params.shadingsystem && - use_auto_tile == params.use_auto_tile && tile_size == params.tile_size); + background == params.background && pixel_size == params.pixel_size && + threads == params.threads && use_profiling == params.use_profiling && + shadingsystem == params.shadingsystem && use_auto_tile == params.use_auto_tile && + tile_size == params.tile_size); } }; diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index f7903bb90e6..932b26bcc7a 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -27,7 +27,7 @@ /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 91 +#define BLENDER_FILE_SUBVERSION 92 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and cancel loading the file, showing a warning to diff --git a/source/blender/blenkernel/BKE_scene.hh b/source/blender/blenkernel/BKE_scene.hh index 5b43e9340f3..7b6986ada01 100644 --- a/source/blender/blenkernel/BKE_scene.hh +++ b/source/blender/blenkernel/BKE_scene.hh @@ -220,14 +220,6 @@ bool BKE_scene_uses_cycles(const Scene *scene); bool BKE_scene_uses_shader_previews(const Scene *scene); -/** - * Return whether the Cycles experimental feature is enabled. It is invalid to call without first - * ensuring that Cycles is the active render engine (e.g. with #BKE_scene_uses_cycles). - * - * \note We cannot use `const` as RNA_id_pointer_create is not using a const ID. - */ -bool BKE_scene_uses_cycles_experimental_features(Scene *scene); - void BKE_scene_copy_data_eevee(Scene *sce_dst, const Scene *sce_src); void BKE_scene_disable_color_management(Scene *scene); diff --git a/source/blender/blenkernel/intern/scene.cc b/source/blender/blenkernel/intern/scene.cc index 40a061d0a57..3d1578418f1 100644 --- a/source/blender/blenkernel/intern/scene.cc +++ b/source/blender/blenkernel/intern/scene.cc @@ -2801,20 +2801,6 @@ enum eCyclesFeatureSet { CYCLES_FEATURES_EXPERIMENTAL = 1, }; -bool BKE_scene_uses_cycles_experimental_features(Scene *scene) -{ - BLI_assert(BKE_scene_uses_cycles(scene)); - PointerRNA scene_ptr = RNA_id_pointer_create(&scene->id); - PointerRNA cycles_ptr = RNA_pointer_get(&scene_ptr, "cycles"); - - if (RNA_pointer_is_null(&cycles_ptr)) { - /* The pointer only exists if Cycles is enabled. */ - return false; - } - - return RNA_enum_get(&cycles_ptr, "feature_set") == CYCLES_FEATURES_EXPERIMENTAL; -} - void BKE_scene_base_flag_to_objects(const Scene *scene, ViewLayer *view_layer) { BKE_view_layer_synced_ensure(scene, view_layer); diff --git a/source/blender/blenloader/intern/versioning_500.cc b/source/blender/blenloader/intern/versioning_500.cc index 08eb2ba0a11..e561723e5f5 100644 --- a/source/blender/blenloader/intern/versioning_500.cc +++ b/source/blender/blenloader/intern/versioning_500.cc @@ -2658,6 +2658,44 @@ static void sequencer_remove_listbase_pointers(Scene &scene) blender::seq::meta_stack_set(&scene, last_meta_stack->parent_strip); } +static void do_version_adaptive_subdivision(Main *bmain) +{ + /* Move cycles properties natively into subdivision surface modifier. */ + bool experimental_features = false; + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + IDProperty *idprop = version_cycles_properties_from_ID(&scene->id); + if (idprop) { + experimental_features |= version_cycles_property_boolean(idprop, "feature_set", false); + } + } + + LISTBASE_FOREACH (Object *, object, &bmain->objects) { + bool use_adaptive_subdivision = false; + float dicing_rate = 1.0f; + + IDProperty *idprop = version_cycles_properties_from_ID(&object->id); + if (idprop) { + if (experimental_features) { + use_adaptive_subdivision = version_cycles_property_boolean( + idprop, "use_adaptive_subdivision", false); + } + dicing_rate = version_cycles_property_float(idprop, "dicing_rate", 1.0f); + } + + LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) { + if (md->type == eModifierType_Subsurf) { + SubsurfModifierData *smd = (SubsurfModifierData *)md; + if (use_adaptive_subdivision) { + smd->flags |= eSubsurfModifierFlag_UseAdaptiveSubdivision; + smd->adaptive_space = SUBSURF_ADAPTIVE_SPACE_PIXEL; + smd->adaptive_pixel_size = dicing_rate; + smd->adaptive_object_edge_length = 0.01f; + } + } + } + } +} + void blo_do_versions_500(FileData *fd, Library * /*lib*/, Main *bmain) { using namespace blender; @@ -3625,6 +3663,10 @@ void blo_do_versions_500(FileData *fd, Library * /*lib*/, Main *bmain) } } + if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 92)) { + do_version_adaptive_subdivision(bmain); + } + /** * Always bump subversion in BKE_blender_version.h when adding versioning * code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check. diff --git a/source/blender/makesdna/DNA_modifier_defaults.h b/source/blender/makesdna/DNA_modifier_defaults.h index c30dfcc865d..5ec3f24f122 100644 --- a/source/blender/makesdna/DNA_modifier_defaults.h +++ b/source/blender/makesdna/DNA_modifier_defaults.h @@ -615,6 +615,9 @@ .uv_smooth = SUBSURF_UV_SMOOTH_PRESERVE_BOUNDARIES, \ .quality = 3, \ .boundary_smooth = SUBSURF_BOUNDARY_SMOOTH_ALL, \ + .adaptive_space = SUBSURF_ADAPTIVE_SPACE_PIXEL, \ + .adaptive_pixel_size = 1.0f, \ + .adaptive_object_edge_length = 0.01f, \ } #define _DNA_DEFAULT_SurfaceModifierData \ diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index 2c8a08e42fd..a36099f0027 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -234,8 +234,14 @@ typedef enum { eSubsurfModifierFlag_UseCrease = (1 << 4), eSubsurfModifierFlag_UseCustomNormals = (1 << 5), eSubsurfModifierFlag_UseRecursiveSubdivision = (1 << 6), + eSubsurfModifierFlag_UseAdaptiveSubdivision = (1 << 7), } SubsurfModifierFlag; +typedef enum { + SUBSURF_ADAPTIVE_SPACE_PIXEL = 0, + SUBSURF_ADAPTIVE_SPACE_OBJECT = 1, +} eSubsurfAdaptiveSpace; + typedef enum { SUBSURF_TYPE_CATMULL_CLARK = 0, SUBSURF_TYPE_SIMPLE = 1, @@ -268,7 +274,11 @@ typedef struct SubsurfModifierData { short quality; /** #eSubsurfBoundarySmooth. */ short boundary_smooth; - char _pad[2]; + /* Adaptive subdivision. */ + /** #eSubsurfAdaptiveSpace */ + short adaptive_space; + float adaptive_pixel_size; + float adaptive_object_edge_length; } SubsurfModifierData; typedef struct LatticeModifierData { diff --git a/source/blender/makesrna/intern/rna_modifier.cc b/source/blender/makesrna/intern/rna_modifier.cc index 2a3ab3231aa..d6370472d02 100644 --- a/source/blender/makesrna/intern/rna_modifier.cc +++ b/source/blender/makesrna/intern/rna_modifier.cc @@ -2451,6 +2451,21 @@ static void rna_def_modifier_subsurf(BlenderRNA *brna) {0, nullptr, 0, nullptr, nullptr}, }; + static const EnumPropertyItem prop_adaptive_space_items[] = { + {SUBSURF_ADAPTIVE_SPACE_PIXEL, + "PIXEL", + 0, + "Pixel", + "Subdivide polygons to reach a specified pixel size on screen"}, + {SUBSURF_ADAPTIVE_SPACE_OBJECT, + "OBJECT", + 0, + "Object", + "Subdivide to reach a specified edge length in object space. This is required to use " + "adaptive subdivision for instanced meshes"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + StructRNA *srna; PropertyRNA *prop; @@ -2510,6 +2525,32 @@ static void rna_def_modifier_subsurf(BlenderRNA *brna) "levels of subdivision (smoothest possible shape)"); RNA_def_property_update(prop, 0, "rna_Modifier_update"); + prop = RNA_def_property(srna, "use_adaptive_subdivision", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, nullptr, "flags", eSubsurfModifierFlag_UseAdaptiveSubdivision); + RNA_def_property_ui_text( + prop, "Use Adaptive Subdivision", "Adaptively subdivide mesh based on camera distance"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "adaptive_space", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, prop_adaptive_space_items); + RNA_def_property_ui_text(prop, "Adaptive Space", "How to adaptively subdivide the mesh"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "adaptive_pixel_size", PROP_FLOAT, PROP_PIXEL); + RNA_def_property_ui_text( + prop, "Pixel Size", "Target polygon pixel size for adaptive subdivision"); + RNA_def_property_range(prop, 0.1f, 1000.0f); + RNA_def_property_ui_range(prop, 0.5f, 1000.0f, 10, 3); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "adaptive_object_edge_length", PROP_FLOAT, PROP_DISTANCE); + RNA_def_property_ui_text( + prop, "Edge Length", "Target object space edge length for adaptive subdivision"); + RNA_def_property_range(prop, 0.0001f, 1000.0f); + RNA_def_property_ui_range(prop, 0.001f, 1000.0f, 10, 3); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + rna_def_modifier_panel_open_prop(srna, "open_adaptive_subdivision_panel", 0); rna_def_modifier_panel_open_prop(srna, "open_advanced_panel", 1); diff --git a/source/blender/modifiers/intern/MOD_subsurf.cc b/source/blender/modifiers/intern/MOD_subsurf.cc index 021f0f95ebf..fc43e000276 100644 --- a/source/blender/modifiers/intern/MOD_subsurf.cc +++ b/source/blender/modifiers/intern/MOD_subsurf.cc @@ -19,6 +19,7 @@ #include "DNA_defaults.h" #include "DNA_mesh_types.h" +#include "DNA_modifier_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_screen_types.h" @@ -315,7 +316,6 @@ static void deform_matrices(ModifierData *md, } } -#ifdef WITH_CYCLES static bool get_show_adaptive_options(const bContext *C, Panel *panel) { /* Don't show adaptive options if cycles isn't the active engine. */ @@ -331,15 +331,8 @@ static bool get_show_adaptive_options(const bContext *C, Panel *panel) return false; } - /* Don't show adaptive options if the cycles experimental feature set is disabled. */ - Scene *scene = CTX_data_scene(C); - if (!BKE_scene_uses_cycles_experimental_features(scene)) { - return false; - } - return true; } -#endif static void panel_draw(const bContext *C, Panel *panel) { @@ -348,27 +341,6 @@ static void panel_draw(const bContext *C, Panel *panel) PointerRNA ob_ptr; PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr); - /* Only test for adaptive subdivision if built with cycles. */ - bool show_adaptive_options = false; - bool ob_use_adaptive_subdivision = false; - PointerRNA cycles_ptr = {}; - PointerRNA ob_cycles_ptr = {}; -#ifdef WITH_CYCLES - Scene *scene = CTX_data_scene(C); - PointerRNA scene_ptr = RNA_id_pointer_create(&scene->id); - if (BKE_scene_uses_cycles(scene)) { - cycles_ptr = RNA_pointer_get(&scene_ptr, "cycles"); - ob_cycles_ptr = RNA_pointer_get(&ob_ptr, "cycles"); - if (!RNA_pointer_is_null(&ob_cycles_ptr)) { - show_adaptive_options = get_show_adaptive_options(C, panel); - ob_use_adaptive_subdivision = show_adaptive_options && - RNA_boolean_get(&ob_cycles_ptr, "use_adaptive_subdivision"); - } - } -#else - UNUSED_VARS(C); -#endif - layout->prop(ptr, "subdivision_type", UI_ITEM_R_EXPAND, std::nullopt, ICON_NONE); layout->use_property_split_set(true); @@ -402,31 +374,47 @@ static void panel_draw(const bContext *C, Panel *panel) } } - if (show_adaptive_options) { + if (get_show_adaptive_options(C, panel)) { PanelLayout adaptive_panel = layout->panel_prop_with_bool_header( C, ptr, "open_adaptive_subdivision_panel", - &ob_cycles_ptr, + ptr, "use_adaptive_subdivision", IFACE_("Adaptive Subdivision")); if (adaptive_panel.body) { - adaptive_panel.body->active_set(ob_use_adaptive_subdivision); - adaptive_panel.body->prop( - &ob_cycles_ptr, "dicing_rate", UI_ITEM_NONE, std::nullopt, ICON_NONE); + Scene *scene = CTX_data_scene(C); + PointerRNA scene_ptr = RNA_id_pointer_create(&scene->id); + PointerRNA cycles_ptr = RNA_pointer_get(&scene_ptr, "cycles"); + const float render_rate = RNA_float_get(&cycles_ptr, "dicing_rate"); + const float preview_rate = RNA_float_get(&cycles_ptr, "preview_dicing_rate"); + std::string render_str, preview_str; - float render = std::max(RNA_float_get(&cycles_ptr, "dicing_rate") * - RNA_float_get(&ob_cycles_ptr, "dicing_rate"), - 0.1f); - float preview = std::max(RNA_float_get(&cycles_ptr, "preview_dicing_rate") * - RNA_float_get(&ob_cycles_ptr, "dicing_rate"), - 0.1f); + adaptive_panel.body->active_set(smd->flags & eSubsurfModifierFlag_UseAdaptiveSubdivision); + adaptive_panel.body->prop(ptr, "adaptive_space", UI_ITEM_NONE, IFACE_("Space"), ICON_NONE); + if (smd->adaptive_space == SUBSURF_ADAPTIVE_SPACE_OBJECT) { + adaptive_panel.body->prop( + ptr, "adaptive_object_edge_length", UI_ITEM_NONE, std::nullopt, ICON_NONE); + preview_str = fmt::format("{:.5g}", preview_rate * smd->adaptive_object_edge_length); + render_str = fmt::format("{:.5g}", render_rate * smd->adaptive_object_edge_length); + } + else { + adaptive_panel.body->prop( + ptr, "adaptive_pixel_size", UI_ITEM_NONE, std::nullopt, ICON_NONE); + preview_str = fmt::format("{:.2f} px", + std::max(preview_rate * smd->adaptive_pixel_size, 0.1f)); + render_str = fmt::format("{:.2f} px", + std::max(render_rate * smd->adaptive_pixel_size, 0.1f)); + } uiLayout *split = &adaptive_panel.body->split(0.4f, false); - split->column(true).label("", ICON_NONE); uiLayout *col = &split->column(true); - col->label(fmt::format(fmt::runtime(RPT_("Viewport {:.2f} px")), preview), ICON_NONE); - col->label(fmt::format(fmt::runtime(RPT_("Render {:.2f} px")), render), ICON_NONE); + col->alignment_set(blender::ui::LayoutAlign::Right); + col->label(IFACE_("Viewport"), ICON_NONE); + col->label(IFACE_("Render"), ICON_NONE); + col = &split->column(true); + col->label(preview_str, ICON_NONE); + col->label(render_str, ICON_NONE); } } @@ -438,7 +426,8 @@ static void panel_draw(const bContext *C, Panel *panel) advanced_layout->prop(ptr, "use_limit_surface", UI_ITEM_NONE, std::nullopt, ICON_NONE); uiLayout *col = &advanced_layout->column(true); - col->active_set(ob_use_adaptive_subdivision || RNA_boolean_get(ptr, "use_limit_surface")); + col->active_set((smd->flags & eSubsurfModifierFlag_UseAdaptiveSubdivision) || + RNA_boolean_get(ptr, "use_limit_surface")); col->prop(ptr, "quality", UI_ITEM_NONE, std::nullopt, ICON_NONE); advanced_layout->prop(ptr, "uv_smooth", UI_ITEM_NONE, std::nullopt, ICON_NONE); diff --git a/tests/files/render/displacement/cycles_renders/object_dicing.png b/tests/files/render/displacement/cycles_renders/object_dicing.png new file mode 100644 index 00000000000..00438d466eb --- /dev/null +++ b/tests/files/render/displacement/cycles_renders/object_dicing.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:be411a0745e722a6e6619ad8229d94a9fad81ac1c5fc6c876d9094073d538a9d +size 12376 diff --git a/tests/files/render/displacement/eevee_renders/object_dicing.png b/tests/files/render/displacement/eevee_renders/object_dicing.png new file mode 100644 index 00000000000..58a1466c91e --- /dev/null +++ b/tests/files/render/displacement/eevee_renders/object_dicing.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d27bac6eab771812f36d2ef66c1bbbb471422533f74570ef86aed7236d20f264 +size 12270 diff --git a/tests/files/render/displacement/object_dicing.blend b/tests/files/render/displacement/object_dicing.blend new file mode 100644 index 00000000000..28ca2203974 --- /dev/null +++ b/tests/files/render/displacement/object_dicing.blend @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:11efc58c0b68cd68fb2d97b00e311407ada95b7b68a0f6d32a57415a6f9e5a16 +size 104600 diff --git a/tests/files/render/displacement/storm_hydra_renders/object_dicing.png b/tests/files/render/displacement/storm_hydra_renders/object_dicing.png new file mode 100644 index 00000000000..e5d9aca05f4 --- /dev/null +++ b/tests/files/render/displacement/storm_hydra_renders/object_dicing.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2694fccb3c2215c6fb2200bc836b83706f1b99873e69ae7747c46904490c3b9c +size 659 diff --git a/tests/files/render/displacement/storm_usd_renders/object_dicing.png b/tests/files/render/displacement/storm_usd_renders/object_dicing.png new file mode 100644 index 00000000000..3268196ab15 --- /dev/null +++ b/tests/files/render/displacement/storm_usd_renders/object_dicing.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d75a8011298333116d8a895023e607e1e8d21f3bc1ebc0facb48a9b682fdad74 +size 659 diff --git a/tests/files/render/displacement/workbench_renders/object_dicing.png b/tests/files/render/displacement/workbench_renders/object_dicing.png new file mode 100644 index 00000000000..c337340d0a3 --- /dev/null +++ b/tests/files/render/displacement/workbench_renders/object_dicing.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5cf3af879242b551cda559606090a094848e49833e8f45dae22018e8ba51915d +size 6945