diff options
author | Hans Goudey <h.goudey@me.com> | 2022-07-20 05:50:45 +0300 |
---|---|---|
committer | Hans Goudey <h.goudey@me.com> | 2022-07-20 05:50:45 +0300 |
commit | 8c5d90f5ce928e45249ddd9f642c24b6dd69d64c (patch) | |
tree | ccbbf8f065eadbe85cdc1f8d70a9d036e9c030b5 | |
parent | 111bf7d76b080555fd524b2b4a138bcad2e5b206 (diff) | |
parent | 215f805ce6b540177dedd71721e62c56a764a5ea (diff) |
Merge branch 'master' into temp-legacy-mesh-format-optiontemp-legacy-mesh-format-option
613 files changed, 9562 insertions, 6504 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 1416b5b4189..c998919622e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -565,15 +565,19 @@ endif() option(WITH_OPENGL "When off limits visibility of the opengl headers to just bf_gpu and gawain (temporary option for development purposes)" ON) option(WITH_GLEW_ES "Switches to experimental copy of GLEW that has support for OpenGL ES. (temporary option for development purposes)" OFF) option(WITH_GL_PROFILE_ES20 "Support using OpenGL ES 2.0. (through either EGL or the AGL/WGL/XGL 'es20' profile)" OFF) -option(WITH_GPU_SHADER_BUILDER "Shader builder is a developer option enabling linting on GLSL during compilation" OFF) +option(WITH_GPU_BUILDTIME_SHADER_BUILDER "Shader builder is a developer option enabling linting on GLSL during compilation" OFF) mark_as_advanced( WITH_OPENGL WITH_GLEW_ES WITH_GL_PROFILE_ES20 - WITH_GPU_SHADER_BUILDER + WITH_GPU_BUILDTIME_SHADER_BUILDER ) +if(WITH_HEADLESS) + set(WITH_OPENGL OFF) +endif() + # Metal if (APPLE) diff --git a/build_files/build_environment/cmake/dpcpp.cmake b/build_files/build_environment/cmake/dpcpp.cmake index 563bc7aeff4..3c3fe201073 100644 --- a/build_files/build_environment/cmake/dpcpp.cmake +++ b/build_files/build_environment/cmake/dpcpp.cmake @@ -63,6 +63,8 @@ set(DPCPP_EXTRA_ARGS -DPython3_ROOT_DIR=${LIBDIR}/python/ -DPython3_EXECUTABLE=${PYTHON_BINARY} -DPYTHON_EXECUTABLE=${PYTHON_BINARY} + -DLLDB_ENABLE_CURSES=OFF + -DLLVM_ENABLE_TERMINFO=OFF ) if(WIN32) diff --git a/build_files/build_environment/cmake/embree.cmake b/build_files/build_environment/cmake/embree.cmake index 2eafc729111..8c689cf000b 100644 --- a/build_files/build_environment/cmake/embree.cmake +++ b/build_files/build_environment/cmake/embree.cmake @@ -10,18 +10,12 @@ set(EMBREE_EXTRA_ARGS -DEMBREE_RAY_MASK=ON -DEMBREE_FILTER_FUNCTION=ON -DEMBREE_BACKFACE_CULLING=OFF - -DEMBREE_MAX_ISA=AVX2 -DEMBREE_TASKING_SYSTEM=TBB -DEMBREE_TBB_ROOT=${LIBDIR}/tbb -DTBB_ROOT=${LIBDIR}/tbb - -DTBB_STATIC_LIB=${TBB_STATIC_LIBRARY} ) -if(BLENDER_PLATFORM_ARM) - set(EMBREE_EXTRA_ARGS - ${EMBREE_EXTRA_ARGS} - -DEMBREE_MAX_ISA=NEON) -else() +if (NOT BLENDER_PLATFORM_ARM) set(EMBREE_EXTRA_ARGS ${EMBREE_EXTRA_ARGS} -DEMBREE_MAX_ISA=AVX2) @@ -30,23 +24,10 @@ endif() if(TBB_STATIC_LIBRARY) set(EMBREE_EXTRA_ARGS ${EMBREE_EXTRA_ARGS} - -DEMBREE_TBB_LIBRARY_NAME=tbb_static - -DEMBREE_TBBMALLOC_LIBRARY_NAME=tbbmalloc_static + -DEMBREE_TBB_COMPONENT=tbb_static ) endif() -if(WIN32) - set(EMBREE_BUILD_DIR ${BUILD_MODE}/) - if(BUILD_MODE STREQUAL Debug) - list(APPEND EMBREE_EXTRA_ARGS - -DEMBREE_TBBMALLOC_LIBRARY_NAME=tbbmalloc_debug - -DEMBREE_TBB_LIBRARY_NAME=tbb_debug - ) - endif() -else() - set(EMBREE_BUILD_DIR) -endif() - ExternalProject_Add(external_embree URL file://${PACKAGE_DIR}/${EMBREE_FILE} DOWNLOAD_DIR ${DOWNLOAD_DIR} diff --git a/build_files/build_environment/cmake/versions.cmake b/build_files/build_environment/cmake/versions.cmake index 1a4ad291581..42c82b68654 100644 --- a/build_files/build_environment/cmake/versions.cmake +++ b/build_files/build_environment/cmake/versions.cmake @@ -410,9 +410,9 @@ set(SQLITE_HASH fb558c49ee21a837713c4f1e7e413309aabdd9c7) set(SQLITE_HASH_TYPE SHA1) set(SQLITE_FILE sqlite-src-3240000.zip) -set(EMBREE_VERSION 3.13.3) +set(EMBREE_VERSION 3.13.4) set(EMBREE_URI https://github.com/embree/embree/archive/v${EMBREE_VERSION}.zip) -set(EMBREE_HASH f62766ba54e48a2f327c3a22596e7133) +set(EMBREE_HASH 52d0be294d6c88ba7a6c9e046796e7be) set(EMBREE_HASH_TYPE MD5) set(EMBREE_FILE embree-v${EMBREE_VERSION}.zip) diff --git a/build_files/build_environment/install_deps.sh b/build_files/build_environment/install_deps.sh index cfba4f12c73..2441b9ad89b 100755 --- a/build_files/build_environment/install_deps.sh +++ b/build_files/build_environment/install_deps.sh @@ -567,7 +567,7 @@ OPENCOLLADA_FORCE_BUILD=false OPENCOLLADA_FORCE_REBUILD=false OPENCOLLADA_SKIP=false -EMBREE_VERSION="3.13.3" +EMBREE_VERSION="3.13.4" EMBREE_VERSION_SHORT="3.13" EMBREE_VERSION_MIN="3.13" EMBREE_VERSION_MEX="4.0" diff --git a/build_files/build_environment/patches/embree.diff b/build_files/build_environment/patches/embree.diff index e83d754a465..e448fe5ee2e 100644 --- a/build_files/build_environment/patches/embree.diff +++ b/build_files/build_environment/patches/embree.diff @@ -1,30 +1,37 @@ -diff -Naur orig/common/sys/platform.h external_embree/common/sys/platform.h ---- orig/common/sys/platform.h 2020-05-13 23:08:53 -0600 -+++ external_embree/common/sys/platform.h 2020-06-13 17:40:26 -0600 -@@ -84,8 +84,8 @@ - //////////////////////////////////////////////////////////////////////////////// +diff -Naur org/kernels/rtcore_config.h.in embree-3.13.4/kernels/rtcore_config.h.in +--- org/kernels/rtcore_config.h.in 2022-06-14 22:13:52 -0600 ++++ embree-3.13.4/kernels/rtcore_config.h.in 2022-06-24 15:20:12 -0600 +@@ -14,6 +14,7 @@ + #cmakedefine01 EMBREE_MIN_WIDTH + #define RTC_MIN_WIDTH EMBREE_MIN_WIDTH + ++#cmakedefine EMBREE_STATIC_LIB + #cmakedefine EMBREE_API_NAMESPACE + + #if defined(EMBREE_API_NAMESPACE) +diff --git a/kernels/CMakeLists.txt b/kernels/CMakeLists.txt +index 7c2f43d..106b1d5 100644 +--- a/kernels/CMakeLists.txt ++++ b/kernels/CMakeLists.txt +@@ -201,6 +201,12 @@ embree_files(EMBREE_LIBRARY_FILES_AVX512 ${AVX512}) + #message("AVX2: ${EMBREE_LIBRARY_FILES_AVX2}") + #message("AVX512: ${EMBREE_LIBRARY_FILES_AVX512}") - #ifdef __WIN32__ --#define dll_export __declspec(dllexport) --#define dll_import __declspec(dllimport) -+#define dll_export -+#define dll_import - #else - #define dll_export __attribute__ ((visibility ("default"))) - #define dll_import -diff --git orig/common/tasking/CMakeLists.txt external_embree/common/tasking/CMakeLists.txt ---- orig/common/tasking/CMakeLists.txt -+++ external_embree/common/tasking/CMakeLists.txt -@@ -27,7 +27,11 @@ - else() - # If not found try getting older TBB via module (FindTBB.cmake) - unset(TBB_DIR CACHE) -- find_package(TBB 4.1 REQUIRED tbb) -+ if (TBB_STATIC_LIB) -+ find_package(TBB 4.1 REQUIRED tbb_static) -+ else() -+ find_package(TBB 4.1 REQUIRED tbb) -+ endif() - if (TBB_FOUND) - TARGET_LINK_LIBRARIES(tasking PUBLIC TBB) - TARGET_INCLUDE_DIRECTORIES(tasking PUBLIC "${TBB_INCLUDE_DIRS}") ++# Bundle Neon2x into the main static library. ++IF(EMBREE_ISA_NEON2X AND EMBREE_STATIC_LIB) ++ LIST(APPEND EMBREE_LIBRARY_FILES ${EMBREE_LIBRARY_FILES_AVX2}) ++ LIST(REMOVE_DUPLICATES EMBREE_LIBRARY_FILES) ++ENDIF() ++ + # replaces all .cpp files with a dummy file that includes that .cpp file + # this is to work around an ICC name mangling issue related to lambda functions under windows + MACRO (CreateISADummyFiles list isa) +@@ -277,7 +283,7 @@ IF (EMBREE_ISA_AVX AND EMBREE_LIBRARY_FILES_AVX) + ENDIF() + ENDIF() + +-IF (EMBREE_ISA_AVX2 AND EMBREE_LIBRARY_FILES_AVX2) ++IF (EMBREE_ISA_AVX2 AND EMBREE_LIBRARY_FILES_AVX2 AND NOT (EMBREE_ISA_NEON2X AND EMBREE_STATIC_LIB)) + DISABLE_STACK_PROTECTOR_FOR_INTERSECTORS(${EMBREE_LIBRARY_FILES_AVX2}) + ADD_LIBRARY(embree_avx2 STATIC ${EMBREE_LIBRARY_FILES_AVX2}) + TARGET_LINK_LIBRARIES(embree_avx2 PRIVATE tasking) diff --git a/doc/python_api/sphinx_doc_gen.py b/doc/python_api/sphinx_doc_gen.py index 17ffdb8e244..f5e0369cede 100644 --- a/doc/python_api/sphinx_doc_gen.py +++ b/doc/python_api/sphinx_doc_gen.py @@ -1529,7 +1529,8 @@ def pyrna2sphinx(basepath): else: fw(".. class:: %s\n\n" % struct_id) - fw(" %s\n\n" % struct.description) + write_indented_lines(" ", fw, struct.description, False) + fw("\n") # Properties sorted in alphabetical order. sorted_struct_properties = struct.properties[:] diff --git a/extern/audaspace/bindings/C/AUD_Special.cpp b/extern/audaspace/bindings/C/AUD_Special.cpp index 1ce25dcd41c..a5ecb7a6dc0 100644 --- a/extern/audaspace/bindings/C/AUD_Special.cpp +++ b/extern/audaspace/bindings/C/AUD_Special.cpp @@ -270,7 +270,7 @@ AUD_API int AUD_readSound(AUD_Sound* sound, float* buffer, int length, int sampl return length; } -AUD_API const char* AUD_mixdown(AUD_Sound* sound, unsigned int start, unsigned int length, unsigned int buffersize, const char* filename, AUD_DeviceSpecs specs, AUD_Container format, AUD_Codec codec, unsigned int bitrate, void(*callback)(float, void*), void* data) +AUD_API int AUD_mixdown(AUD_Sound* sound, unsigned int start, unsigned int length, unsigned int buffersize, const char* filename, AUD_DeviceSpecs specs, AUD_Container format, AUD_Codec codec, unsigned int bitrate, void(*callback)(float, void*), void* data, char* error, size_t errorsize) { try { @@ -282,15 +282,20 @@ AUD_API const char* AUD_mixdown(AUD_Sound* sound, unsigned int start, unsigned i std::shared_ptr<IWriter> writer = FileWriter::createWriter(filename, convCToDSpec(specs), static_cast<Container>(format), static_cast<Codec>(codec), bitrate); FileWriter::writeReader(reader, writer, length, buffersize, callback, data); - return nullptr; + return true; } catch(Exception& e) { - return e.getMessage().c_str(); + if(error && errorsize) + { + std::strncpy(error, e.getMessage().c_str(), errorsize); + error[errorsize - 1] = '\0'; + } + return false; } } -AUD_API const char* AUD_mixdown_per_channel(AUD_Sound* sound, unsigned int start, unsigned int length, unsigned int buffersize, const char* filename, AUD_DeviceSpecs specs, AUD_Container format, AUD_Codec codec, unsigned int bitrate, void(*callback)(float, void*), void* data) +AUD_API int AUD_mixdown_per_channel(AUD_Sound* sound, unsigned int start, unsigned int length, unsigned int buffersize, const char* filename, AUD_DeviceSpecs specs, AUD_Container format, AUD_Codec codec, unsigned int bitrate, void(*callback)(float, void*), void* data, char* error, size_t errorsize) { try { @@ -328,11 +333,16 @@ AUD_API const char* AUD_mixdown_per_channel(AUD_Sound* sound, unsigned int start reader->seek(start); FileWriter::writeReader(reader, writers, length, buffersize, callback, data); - return nullptr; + return true; } catch(Exception& e) { - return e.getMessage().c_str(); + if(error && errorsize) + { + std::strncpy(error, e.getMessage().c_str(), errorsize); + error[errorsize - 1] = '\0'; + } + return false; } } diff --git a/extern/audaspace/bindings/C/AUD_Special.h b/extern/audaspace/bindings/C/AUD_Special.h index 2f5d13c6fd9..1d181d33f87 100644 --- a/extern/audaspace/bindings/C/AUD_Special.h +++ b/extern/audaspace/bindings/C/AUD_Special.h @@ -70,13 +70,15 @@ extern AUD_API int AUD_readSound(AUD_Sound* sound, float* buffer, int length, in * \param bitrate The bitrate for encoding. * \param callback A callback function that is called periodically during mixdown, reporting progress if length > 0. Can be NULL. * \param data Pass through parameter that is passed to the callback. - * \return An error message or NULL in case of success. + * \param error String buffer to copy the error message to in case of failure. + * \param errorsize The size of the error buffer. + * \return Whether or not the operation succeeded. */ -extern AUD_API const char* AUD_mixdown(AUD_Sound* sound, unsigned int start, unsigned int length, +extern AUD_API int AUD_mixdown(AUD_Sound* sound, unsigned int start, unsigned int length, unsigned int buffersize, const char* filename, AUD_DeviceSpecs specs, AUD_Container format, AUD_Codec codec, unsigned int bitrate, - void(*callback)(float, void*), void* data); + void(*callback)(float, void*), void* data, char* error, size_t errorsize); /** * Mixes a sound down into multiple files. @@ -91,13 +93,15 @@ extern AUD_API const char* AUD_mixdown(AUD_Sound* sound, unsigned int start, uns * \param bitrate The bitrate for encoding. * \param callback A callback function that is called periodically during mixdown, reporting progress if length > 0. Can be NULL. * \param data Pass through parameter that is passed to the callback. - * \return An error message or NULL in case of success. + * \param error String buffer to copy the error message to in case of failure. + * \param errorsize The size of the error buffer. + * \return Whether or not the operation succeeded. */ -extern AUD_API const char* AUD_mixdown_per_channel(AUD_Sound* sound, unsigned int start, unsigned int length, +extern AUD_API int AUD_mixdown_per_channel(AUD_Sound* sound, unsigned int start, unsigned int length, unsigned int buffersize, const char* filename, AUD_DeviceSpecs specs, AUD_Container format, AUD_Codec codec, unsigned int bitrate, - void(*callback)(float, void*), void* data); + void(*callback)(float, void*), void* data, char* error, size_t errorsize); /** * Opens a read device and prepares it for mixdown of the sound scene. diff --git a/intern/cycles/blender/CMakeLists.txt b/intern/cycles/blender/CMakeLists.txt index 4919b99cfe0..63d89221d20 100644 --- a/intern/cycles/blender/CMakeLists.txt +++ b/intern/cycles/blender/CMakeLists.txt @@ -128,10 +128,6 @@ if(WITH_OPENIMAGEDENOISE) ) endif() -if(WITH_EXPERIMENTAL_FEATURES) - add_definitions(-DWITH_NEW_CURVES_TYPE) -endif() - blender_add_lib(bf_intern_cycles "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") add_dependencies(bf_intern_cycles bf_rna) diff --git a/intern/cycles/blender/addon/presets.py b/intern/cycles/blender/addon/presets.py index cc6d574da99..e1f08c07eaf 100644 --- a/intern/cycles/blender/addon/presets.py +++ b/intern/cycles/blender/addon/presets.py @@ -84,10 +84,36 @@ class AddPresetViewportSampling(AddPresetBase, Operator): preset_subdir = "cycles/viewport_sampling" +class AddPresetPerformance(AddPresetBase, Operator): + '''Add an Performance Preset''' + bl_idname = "render.cycles_performance_preset_add" + bl_label = "Add Performance Preset" + preset_menu = "CYCLES_PT_performance_presets" + + preset_defines = [ + "render = bpy.context.scene.render" + "cycles = bpy.context.scene.cycles" + ] + + preset_values = [ + "render.threads_mode", + "render.use_persistent_data", + "cycles.debug_use_spatial_splits", + "cycles.debug_use_compact_bvh", + "cycles.debug_use_hair_bvh", + "cycles.debug_bvh_time_steps", + "cycles.use_auto_tile", + "cycles.tile_size", + ] + + preset_subdir = "cycles/performance" + + classes = ( AddPresetIntegrator, AddPresetSampling, AddPresetViewportSampling, + AddPresetPerformance, ) diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index e88b65b5119..2c926893f9d 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -693,7 +693,7 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): debug_use_compact_bvh: BoolProperty( name="Use Compact BVH", description="Use compact BVH structure (uses less ram but renders slower)", - default=True, + default=False, ) debug_bvh_time_steps: IntProperty( name="BVH Time Steps", @@ -1560,7 +1560,7 @@ class CyclesPreferences(bpy.types.AddonPreferences): if sys.platform.startswith("win"): col.label(text="and Windows driver version 101.1660 or newer", icon='BLANK1') elif sys.platform.startswith("linux"): - col.label(text="and Linux driver version xx.xx.28000 or newer", icon='BLANK1') + col.label(text="and Linux driver version xx.xx.23570 or newer", icon='BLANK1') elif device_type == 'METAL': col.label(text="Requires Apple Silicon with macOS 12.2 or newer", icon='BLANK1') col.label(text="or AMD with macOS 12.3 or newer", icon='BLANK1') diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index 77da3f36685..0fead409866 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -43,6 +43,12 @@ class CYCLES_PT_integrator_presets(CyclesPresetPanel): preset_add_operator = "render.cycles_integrator_preset_add" +class CYCLES_PT_performance_presets(CyclesPresetPanel): + bl_label = "Performance Presets" + preset_subdir = "cycles/performance" + preset_add_operator = "render.cycles_performance_preset_add" + + class CyclesButtonsPanel: bl_space_type = "PROPERTIES" bl_region_type = "WINDOW" @@ -624,6 +630,9 @@ class CYCLES_RENDER_PT_performance(CyclesButtonsPanel, Panel): bl_label = "Performance" bl_options = {'DEFAULT_CLOSED'} + def draw_header_preset(self, context): + CYCLES_PT_performance_presets.draw_panel_header(self.layout) + def draw(self, context): pass @@ -943,6 +952,8 @@ class CYCLES_CAMERA_PT_dof(CyclesButtonsPanel, Panel): col = split.column() col.prop(dof, "focus_object", text="Focus Object") + if dof.focus_object and dof.focus_object.type == 'ARMATURE': + col.prop_search(dof, "focus_subtarget", dof.focus_object.data, "bones", text="Focus Bone") sub = col.row() sub.active = dof.focus_object is None @@ -1202,7 +1213,7 @@ class CYCLES_OBJECT_PT_lightgroup(CyclesButtonsPanel, Panel): sub.prop_search(ob, "lightgroup", view_layer, "lightgroups", text="Light Group", results_are_suggestions=True) sub = row.column(align=True) - sub.active = bool(ob.lightgroup) and not any(lg.name == ob.lightgroup for lg in view_layer.lightgroups) + sub.enabled = bool(ob.lightgroup) and not any(lg.name == ob.lightgroup for lg in view_layer.lightgroups) sub.operator("scene.view_layer_add_lightgroup", icon='ADD', text="").name = ob.lightgroup @@ -1640,7 +1651,7 @@ class CYCLES_WORLD_PT_settings_light_group(CyclesButtonsPanel, Panel): ) sub = row.column(align=True) - sub.active = bool(world.lightgroup) and not any(lg.name == world.lightgroup for lg in view_layer.lightgroups) + sub.enabled = bool(world.lightgroup) and not any(lg.name == world.lightgroup for lg in view_layer.lightgroups) sub.operator("scene.view_layer_add_lightgroup", icon='ADD', text="").name = world.lightgroup @@ -2269,6 +2280,7 @@ classes = ( CYCLES_PT_sampling_presets, CYCLES_PT_viewport_sampling_presets, CYCLES_PT_integrator_presets, + CYCLES_PT_performance_presets, CYCLES_RENDER_PT_sampling, CYCLES_RENDER_PT_sampling_viewport, CYCLES_RENDER_PT_sampling_viewport_denoise, diff --git a/intern/cycles/blender/camera.cpp b/intern/cycles/blender/camera.cpp index 2ab5f02a337..6926c833096 100644 --- a/intern/cycles/blender/camera.cpp +++ b/intern/cycles/blender/camera.cpp @@ -143,11 +143,20 @@ static float blender_camera_focal_distance(BL::RenderEngine &b_engine, if (!b_dof_object) return b_camera.dof().focus_distance(); + Transform dofmat = get_transform(b_dof_object.matrix_world()); + + string focus_subtarget = b_camera.dof().focus_subtarget(); + if (b_dof_object.pose() && !focus_subtarget.empty()) { + BL::PoseBone b_bone = b_dof_object.pose().bones[focus_subtarget]; + if (b_bone) { + dofmat = dofmat * get_transform(b_bone.matrix()); + } + } + /* for dof object, return distance along camera Z direction */ BL::Array<float, 16> b_ob_matrix; b_engine.camera_model_matrix(b_ob, bcam->use_spherical_stereo, b_ob_matrix); Transform obmat = transform_clear_scale(get_transform(b_ob_matrix)); - Transform dofmat = get_transform(b_dof_object.matrix_world()); float3 view_dir = normalize(transform_get_column(&obmat, 2)); float3 dof_dir = transform_get_column(&obmat, 3) - transform_get_column(&dofmat, 3); return fabsf(dot(view_dir, dof_dir)); diff --git a/intern/cycles/blender/curves.cpp b/intern/cycles/blender/curves.cpp index 10012720bd8..c4154bce022 100644 --- a/intern/cycles/blender/curves.cpp +++ b/intern/cycles/blender/curves.cpp @@ -613,8 +613,6 @@ void BlenderSync::sync_particle_hair( } } -#ifdef WITH_NEW_CURVES_TYPE - static std::optional<BL::FloatAttribute> find_curves_radius_attribute(BL::Curves b_curves) { for (BL::Attribute &b_attribute : b_curves.attributes) { @@ -632,6 +630,25 @@ static std::optional<BL::FloatAttribute> find_curves_radius_attribute(BL::Curves return std::nullopt; } +static BL::FloatVectorAttribute find_curves_position_attribute(BL::Curves b_curves) +{ + for (BL::Attribute &b_attribute : b_curves.attributes) { + if (b_attribute.name() != "position") { + continue; + } + if (b_attribute.domain() != BL::Attribute::domain_POINT) { + continue; + } + if (b_attribute.data_type() != BL::Attribute::data_type_FLOAT_VECTOR) { + continue; + } + return BL::FloatVectorAttribute{b_attribute}; + } + /* The position attribute must exist. */ + assert(false); + return BL::FloatVectorAttribute{b_curves.attributes[0]}; +} + template<typename TypeInCycles, typename GetValueAtIndex> static void fill_generic_attribute(BL::Curves &b_curves, TypeInCycles *data, @@ -795,16 +812,16 @@ static void attr_create_generic(Scene *scene, } } -static float4 hair_point_as_float4(BL::Curves b_curves, +static float4 hair_point_as_float4(BL::FloatVectorAttribute b_attr_position, std::optional<BL::FloatAttribute> b_attr_radius, const int index) { - float4 mP = float3_to_float4(get_float3(b_curves.position_data[index].vector())); + float4 mP = float3_to_float4(get_float3(b_attr_position.data[index].vector())); mP.w = b_attr_radius ? b_attr_radius->data[index].value() : 0.0f; return mP; } -static float4 interpolate_hair_points(BL::Curves b_curves, +static float4 interpolate_hair_points(BL::FloatVectorAttribute b_attr_position, std::optional<BL::FloatAttribute> b_attr_radius, const int first_point_index, const int num_points, @@ -814,8 +831,8 @@ static float4 interpolate_hair_points(BL::Curves b_curves, 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_curves, b_attr_radius, first_point_index + point_a), - hair_point_as_float4(b_curves, b_attr_radius, first_point_index + point_b), + return lerp(hair_point_as_float4(b_attr_position, b_attr_radius, first_point_index + point_a), + hair_point_as_float4(b_attr_position, b_attr_radius, first_point_index + point_b), t); } @@ -848,6 +865,7 @@ static void export_hair_curves(Scene *scene, hair->reserve_curves(num_curves, num_keys); + BL::FloatVectorAttribute b_attr_position = find_curves_position_attribute(b_curves); std::optional<BL::FloatAttribute> b_attr_radius = find_curves_radius_attribute(b_curves); /* Export curves and points. */ @@ -866,7 +884,7 @@ static void export_hair_curves(Scene *scene, /* Position and radius. */ for (int i = 0; i < num_points; i++) { - const float3 co = get_float3(b_curves.position_data[first_point_index + i].vector()); + const float3 co = get_float3(b_attr_position.data[first_point_index + i].vector()); const float radius = b_attr_radius ? b_attr_radius->data[first_point_index + i].value() : 0.005f; hair->add_curve_key(co, radius); @@ -923,6 +941,7 @@ static void export_hair_curves_motion(Hair *hair, BL::Curves b_curves, int motio int num_motion_keys = 0; int curve_index = 0; + BL::FloatVectorAttribute b_attr_position = find_curves_position_attribute(b_curves); std::optional<BL::FloatAttribute> b_attr_radius = find_curves_radius_attribute(b_curves); for (int i = 0; i < num_curves; i++) { @@ -938,7 +957,7 @@ static void export_hair_curves_motion(Hair *hair, BL::Curves b_curves, int motio int point_index = first_point_index + i; if (point_index < num_keys) { - mP[num_motion_keys] = hair_point_as_float4(b_curves, b_attr_radius, point_index); + mP[num_motion_keys] = hair_point_as_float4(b_attr_position, b_attr_radius, point_index); num_motion_keys++; if (!have_motion) { @@ -958,7 +977,7 @@ static void export_hair_curves_motion(Hair *hair, BL::Curves b_curves, int motio for (int i = 0; i < curve.num_keys; i++) { const float step = i * step_size; mP[num_motion_keys] = interpolate_hair_points( - b_curves, b_attr_radius, first_point_index, num_points, step); + b_attr_position, b_attr_radius, first_point_index, num_points, step); num_motion_keys++; } have_motion = true; @@ -990,15 +1009,6 @@ void BlenderSync::sync_hair(Hair *hair, BObjectInfo &b_ob_info, bool motion, int export_hair_curves(scene, hair, b_curves, need_motion, motion_scale); } } -#else -void BlenderSync::sync_hair(Hair *hair, BObjectInfo &b_ob_info, bool motion, int motion_step) -{ - (void)hair; - (void)b_ob_info; - (void)motion; - (void)motion_step; -} -#endif void BlenderSync::sync_hair(BL::Depsgraph b_depsgraph, BObjectInfo &b_ob_info, Hair *hair) { @@ -1010,14 +1020,11 @@ void BlenderSync::sync_hair(BL::Depsgraph b_depsgraph, BObjectInfo &b_ob_info, H new_hair.set_used_shaders(used_shaders); if (view_layer.use_hair) { -#ifdef WITH_NEW_CURVES_TYPE if (b_ob_info.object_data.is_a(&RNA_Curves)) { /* Hair object. */ sync_hair(&new_hair, b_ob_info, false); } - else -#endif - { + else { /* Particle hair. */ bool need_undeformed = new_hair.need_attribute(scene, ATTR_STD_GENERATED); BL::Mesh b_mesh = object_to_mesh( @@ -1064,15 +1071,12 @@ void BlenderSync::sync_hair_motion(BL::Depsgraph b_depsgraph, /* Export deformed coordinates. */ if (ccl::BKE_object_is_deform_modified(b_ob_info, b_scene, preview)) { -#ifdef WITH_NEW_CURVES_TYPE if (b_ob_info.object_data.is_a(&RNA_Curves)) { /* Hair object. */ sync_hair(hair, b_ob_info, true, motion_step); return; } - else -#endif - { + else { /* Particle hair. */ BL::Mesh b_mesh = object_to_mesh( b_data, b_ob_info, b_depsgraph, false, Mesh::SUBDIVISION_NONE); diff --git a/intern/cycles/blender/geometry.cpp b/intern/cycles/blender/geometry.cpp index 215860f59e6..fc03ca6e489 100644 --- a/intern/cycles/blender/geometry.cpp +++ b/intern/cycles/blender/geometry.cpp @@ -18,11 +18,7 @@ CCL_NAMESPACE_BEGIN static Geometry::Type determine_geom_type(BObjectInfo &b_ob_info, bool use_particle_hair) { -#ifdef WITH_NEW_CURVES_TYPE if (b_ob_info.object_data.is_a(&RNA_Curves) || use_particle_hair) { -#else - if (use_particle_hair) { -#endif return Geometry::HAIR; } @@ -217,11 +213,7 @@ void BlenderSync::sync_geometry_motion(BL::Depsgraph &b_depsgraph, if (progress.get_cancel()) return; -#ifdef WITH_NEW_CURVES_TYPE if (b_ob_info.object_data.is_a(&RNA_Curves) || use_particle_hair) { -#else - if (use_particle_hair) { -#endif Hair *hair = static_cast<Hair *>(geom); sync_hair_motion(b_depsgraph, b_ob_info, hair, motion_step); } diff --git a/intern/cycles/blender/pointcloud.cpp b/intern/cycles/blender/pointcloud.cpp index 0312ad87a70..b4e90859877 100644 --- a/intern/cycles/blender/pointcloud.cpp +++ b/intern/cycles/blender/pointcloud.cpp @@ -1,8 +1,10 @@ /* SPDX-License-Identifier: Apache-2.0 * Copyright 2011-2022 Blender Foundation */ -#include "scene/pointcloud.h" +#include <optional> + #include "scene/attribute.h" +#include "scene/pointcloud.h" #include "scene/scene.h" #include "blender/sync.h" @@ -138,6 +140,36 @@ static void copy_attributes(PointCloud *pointcloud, } } +static std::optional<BL::FloatAttribute> find_radius_attribute(BL::PointCloud b_pointcloud) +{ + for (BL::Attribute &b_attribute : b_pointcloud.attributes) { + if (b_attribute.name() != "radius") { + continue; + } + if (b_attribute.data_type() != BL::Attribute::data_type_FLOAT) { + continue; + } + return BL::FloatAttribute{b_attribute}; + } + return std::nullopt; +} + +static BL::FloatVectorAttribute find_position_attribute(BL::PointCloud b_pointcloud) +{ + for (BL::Attribute &b_attribute : b_pointcloud.attributes) { + if (b_attribute.name() != "position") { + continue; + } + if (b_attribute.data_type() != BL::Attribute::data_type_FLOAT_VECTOR) { + continue; + } + return BL::FloatVectorAttribute{b_attribute}; + } + /* The position attribute must exist. */ + assert(false); + return BL::FloatVectorAttribute{b_pointcloud.attributes[0]}; +} + static void export_pointcloud(Scene *scene, PointCloud *pointcloud, BL::PointCloud b_pointcloud, @@ -156,18 +188,18 @@ static void export_pointcloud(Scene *scene, const int num_points = b_pointcloud.points.length(); pointcloud->reserve(num_points); + BL::FloatVectorAttribute b_attr_position = find_position_attribute(b_pointcloud); + std::optional<BL::FloatAttribute> b_attr_radius = find_radius_attribute(b_pointcloud); + /* Export points. */ - BL::PointCloud::points_iterator b_point_iter; - for (b_pointcloud.points.begin(b_point_iter); b_point_iter != b_pointcloud.points.end(); - ++b_point_iter) { - BL::Point b_point = *b_point_iter; - const float3 co = get_float3(b_point.co()); - const float radius = b_point.radius(); + for (int i = 0; i < num_points; i++) { + const float3 co = get_float3(b_attr_position.data[i].vector()); + const float radius = b_attr_radius ? b_attr_radius->data[i].value() : 0.0f; pointcloud->add_point(co, radius); /* Random number per point. */ if (attr_random != NULL) { - attr_random->add(hash_uint2_to_float(b_point.index(), 0)); + attr_random->add(hash_uint2_to_float(i, 0)); } } @@ -195,14 +227,15 @@ static void export_pointcloud_motion(PointCloud *pointcloud, int num_motion_points = 0; const array<float3> &pointcloud_points = pointcloud->get_points(); - BL::PointCloud::points_iterator b_point_iter; - for (b_pointcloud.points.begin(b_point_iter); b_point_iter != b_pointcloud.points.end(); - ++b_point_iter) { - BL::Point b_point = *b_point_iter; + BL::FloatVectorAttribute b_attr_position = find_position_attribute(b_pointcloud); + std::optional<BL::FloatAttribute> b_attr_radius = find_radius_attribute(b_pointcloud); + for (int i = 0; i < num_points; i++) { if (num_motion_points < num_points) { - float3 P = get_float3(b_point.co()); - P.w = b_point.radius(); + const float3 co = get_float3(b_attr_position.data[i].vector()); + const float radius = b_attr_radius ? b_attr_radius->data[i].value() : 0.0f; + float3 P = co; + P.w = radius; mP[num_motion_points] = P; have_motion = have_motion || (P != pointcloud_points[num_motion_points]); num_motion_points++; diff --git a/intern/cycles/bvh/params.h b/intern/cycles/bvh/params.h index 41d851ee687..648350d03b0 100644 --- a/intern/cycles/bvh/params.h +++ b/intern/cycles/bvh/params.h @@ -129,7 +129,7 @@ class BVHParams { top_level = false; bvh_layout = BVH_LAYOUT_BVH2; - use_compact_structure = true; + use_compact_structure = false; use_unaligned_nodes = false; num_motion_curve_steps = 0; diff --git a/intern/cycles/device/cpu/device_impl.cpp b/intern/cycles/device/cpu/device_impl.cpp index d4f0532aa5e..1e4b9baa0c0 100644 --- a/intern/cycles/device/cpu/device_impl.cpp +++ b/intern/cycles/device/cpu/device_impl.cpp @@ -197,7 +197,7 @@ void CPUDevice::const_copy_to(const char *name, void *host, size_t size) // Update scene handle (since it is different for each device on multi devices) KernelData *const data = (KernelData *)host; - data->bvh.scene = embree_scene; + data->device_bvh = embree_scene; } #endif kernel_const_copy(&kernel_globals, name, host, size); diff --git a/intern/cycles/device/device.h b/intern/cycles/device/device.h index 340be85e853..cdb13ca0a97 100644 --- a/intern/cycles/device/device.h +++ b/intern/cycles/device/device.h @@ -29,6 +29,7 @@ class DeviceQueue; class Progress; class CPUKernels; class CPUKernelThreadGlobals; +class Scene; /* Device Types */ @@ -186,6 +187,11 @@ class Device { return 0; } + /* Called after kernel texture setup, and prior to integrator state setup. */ + virtual void optimize_for_scene(Scene * /*scene*/) + { + } + virtual bool is_resident(device_ptr /*key*/, Device *sub_device) { /* Memory is always resident if this is not a multi device, regardless of whether the pointer diff --git a/intern/cycles/device/metal/device_impl.h b/intern/cycles/device/metal/device_impl.h index 4aea8d697a5..99e60d3a788 100644 --- a/intern/cycles/device/metal/device_impl.h +++ b/intern/cycles/device/metal/device_impl.h @@ -75,7 +75,8 @@ class MetalDevice : public Device { std::vector<id<MTLTexture>> texture_slot_map; bool use_metalrt = false; - bool use_function_specialisation = false; + MetalPipelineType kernel_specialization_level = PSO_GENERIC; + std::atomic_bool async_compile_and_load = false; virtual BVHLayoutMask get_bvh_layout_mask() const override; @@ -91,9 +92,7 @@ class MetalDevice : public Device { bool use_adaptive_compilation(); - string get_source(const uint kernel_features); - - string compile_kernel(const uint kernel_features, const char *name); + void make_source(MetalPipelineType pso_type, const uint kernel_features); virtual bool load_kernels(const uint kernel_features) override; @@ -111,7 +110,9 @@ class MetalDevice : public Device { virtual void build_bvh(BVH *bvh, Progress &progress, bool refit) override; - id<MTLLibrary> compile(string const &source); + virtual void optimize_for_scene(Scene *scene) override; + + bool compile_and_load(MetalPipelineType pso_type); /* ------------------------------------------------------------------ */ /* low-level memory management */ diff --git a/intern/cycles/device/metal/device_impl.mm b/intern/cycles/device/metal/device_impl.mm index 87c83242240..d1250b83d22 100644 --- a/intern/cycles/device/metal/device_impl.mm +++ b/intern/cycles/device/metal/device_impl.mm @@ -6,6 +6,8 @@ # include "device/metal/device_impl.h" # include "device/metal/device.h" +# include "scene/scene.h" + # include "util/debug.h" # include "util/md5.h" # include "util/path.h" @@ -78,6 +80,10 @@ MetalDevice::MetalDevice(const DeviceInfo &info, Stats &stats, Profiler &profile case METAL_GPU_APPLE: { max_threads_per_threadgroup = 512; use_metalrt = info.use_metalrt; + + /* Specialize the intersection kernels on Apple GPUs by default as these can be built very + * quickly. */ + kernel_specialization_level = PSO_SPECIALIZED_INTERSECT; break; } } @@ -90,6 +96,13 @@ MetalDevice::MetalDevice(const DeviceInfo &info, Stats &stats, Profiler &profile capture_enabled = true; } + if (auto envstr = getenv("CYCLES_METAL_SPECIALIZATION_LEVEL")) { + kernel_specialization_level = (MetalPipelineType)atoi(envstr); + } + metal_printf("kernel_specialization_level = %s\n", + kernel_type_as_string( + (MetalPipelineType)min((int)kernel_specialization_level, (int)PSO_NUM - 1))); + MTLArgumentDescriptor *arg_desc_params = [[MTLArgumentDescriptor alloc] init]; arg_desc_params.dataType = MTLDataTypePointer; arg_desc_params.access = MTLArgumentAccessReadOnly; @@ -209,61 +222,86 @@ bool MetalDevice::use_adaptive_compilation() return DebugFlags().metal.adaptive_compile; } -string MetalDevice::get_source(const uint kernel_features) +void MetalDevice::make_source(MetalPipelineType pso_type, const uint kernel_features) { - string build_options; - + string global_defines; if (use_adaptive_compilation()) { - build_options += " -D__KERNEL_FEATURES__=" + to_string(kernel_features); + global_defines += "#define __KERNEL_FEATURES__ " + to_string(kernel_features) + "\n"; } if (use_metalrt) { - build_options += "-D__METALRT__ "; + global_defines += "#define __METALRT__\n"; if (motion_blur) { - build_options += "-D__METALRT_MOTION__ "; + global_defines += "#define __METALRT_MOTION__\n"; } } # ifdef WITH_CYCLES_DEBUG - build_options += "-D__KERNEL_DEBUG__ "; + global_defines += "#define __KERNEL_DEBUG__\n"; # endif switch (device_vendor) { default: break; case METAL_GPU_INTEL: - build_options += "-D__KERNEL_METAL_INTEL__ "; + global_defines += "#define __KERNEL_METAL_INTEL__\n"; break; case METAL_GPU_AMD: - build_options += "-D__KERNEL_METAL_AMD__ "; + global_defines += "#define __KERNEL_METAL_AMD__\n"; break; case METAL_GPU_APPLE: - build_options += "-D__KERNEL_METAL_APPLE__ "; + global_defines += "#define __KERNEL_METAL_APPLE__\n"; break; } - /* reformat -D defines list into compilable form */ - vector<string> components; - string_replace(build_options, "-D", ""); - string_split(components, build_options, " "); + string &source = this->source[pso_type]; + source = "\n#include \"kernel/device/metal/kernel.metal\"\n"; + source = path_source_replace_includes(source, path_get("source")); - string globalDefines; - for (const string &component : components) { - vector<string> assignments; - string_split(assignments, component, "="); - if (assignments.size() == 2) - globalDefines += string_printf( - "#define %s %s\n", assignments[0].c_str(), assignments[1].c_str()); - else - globalDefines += string_printf("#define %s\n", assignments[0].c_str()); + /* Perform any required specialization on the source. + * With Metal function constants we can generate a single variant of the kernel source which can + * be repeatedly respecialized. + */ + string baked_constants; + + /* Replace specific KernelData "dot" dereferences with a Metal function_constant identifier of + * the same character length. Build a string of all active constant values which is then hashed + * in order to identify the PSO. + */ + if (pso_type != PSO_GENERIC) { + const double starttime = time_dt(); + +# define KERNEL_STRUCT_BEGIN(name, parent) \ + string_replace_same_length(source, "kernel_data." #parent ".", "kernel_data_" #parent "_"); + + /* Add constants to md5 so that 'get_best_pipeline' is able to return a suitable match. */ +# define KERNEL_STRUCT_MEMBER(parent, _type, name) \ + baked_constants += string(#parent "." #name "=") + \ + to_string(_type(launch_params.data.parent.name)) + "\n"; + +# include "kernel/data_template.h" + + /* Opt in to all of available specializations. This can be made more granular for the + * PSO_SPECIALIZED_INTERSECT case in order to minimize the number of specialization requests, + * but the overhead should be negligible as these are very quick to (re)build and aren't + * serialized to disk via MTLBinaryArchives. + */ + global_defines += "#define __KERNEL_USE_DATA_CONSTANTS__\n"; + + metal_printf("KernelData patching took %.1f ms\n", (time_dt() - starttime) * 1000.0); } - string source = globalDefines + "\n#include \"kernel/device/metal/kernel.metal\"\n"; - source = path_source_replace_includes(source, path_get("source")); - - metal_printf("Global defines:\n%s\n", globalDefines.c_str()); + source = global_defines + source; + metal_printf("================\n%s================\n\%s================\n", + global_defines.c_str(), + baked_constants.c_str()); - return source; + /* Generate an MD5 from the source and include any baked constants. This is used when caching + * PSOs. */ + MD5Hash md5; + md5.append(baked_constants); + md5.append(source); + source_md5[pso_type] = md5.get_hex(); } bool MetalDevice::load_kernels(const uint _kernel_features) @@ -279,28 +317,22 @@ bool MetalDevice::load_kernels(const uint _kernel_features) * active, but may still need to be rendered without motion blur if that isn't active as well. */ motion_blur = kernel_features & KERNEL_FEATURE_OBJECT_MOTION; - source[PSO_GENERIC] = get_source(kernel_features); - - const double starttime = time_dt(); - - mtlLibrary[PSO_GENERIC] = compile(source[PSO_GENERIC]); - - metal_printf("Front-end compilation finished in %.1f seconds (generic)\n", - time_dt() - starttime); - - MD5Hash md5; - md5.append(source[PSO_GENERIC]); - source_md5[PSO_GENERIC] = md5.get_hex(); - - bool result = MetalDeviceKernels::load(this, false); + bool result = compile_and_load(PSO_GENERIC); reserve_local_memory(kernel_features); - return result; } -id<MTLLibrary> MetalDevice::compile(string const &source) +bool MetalDevice::compile_and_load(MetalPipelineType pso_type) { + make_source(pso_type, kernel_features); + + if (!MetalDeviceKernels::should_load_kernels(this, pso_type)) { + /* We already have a full set of matching pipelines which are cached or queued. */ + metal_printf("%s kernels already requested\n", kernel_type_as_string(pso_type)); + return true; + } + MTLCompileOptions *options = [[MTLCompileOptions alloc] init]; options.fastMathEnabled = YES; @@ -308,19 +340,30 @@ id<MTLLibrary> MetalDevice::compile(string const &source) options.languageVersion = MTLLanguageVersion2_4; } + if (getenv("CYCLES_METAL_PROFILING") || getenv("CYCLES_METAL_DEBUG")) { + path_write_text(path_cache_get(string_printf("%s.metal", kernel_type_as_string(pso_type))), + source[pso_type]); + } + + const double starttime = time_dt(); + NSError *error = NULL; - id<MTLLibrary> mtlLibrary = [mtlDevice newLibraryWithSource:@(source.c_str()) - options:options - error:&error]; + mtlLibrary[pso_type] = [mtlDevice newLibraryWithSource:@(source[pso_type].c_str()) + options:options + error:&error]; - if (!mtlLibrary) { + if (!mtlLibrary[pso_type]) { NSString *err = [error localizedDescription]; set_error(string_printf("Failed to compile library:\n%s", [err UTF8String])); } + metal_printf("Front-end compilation finished in %.1f seconds (%s)\n", + time_dt() - starttime, + kernel_type_as_string(pso_type)); + [options release]; - return mtlLibrary; + return MetalDeviceKernels::load(this, pso_type); } void MetalDevice::reserve_local_memory(const uint kernel_features) @@ -627,6 +670,58 @@ device_ptr MetalDevice::mem_alloc_sub_ptr(device_memory &mem, size_t offset, siz return 0; } +void MetalDevice::optimize_for_scene(Scene *scene) +{ + MetalPipelineType specialization_level = kernel_specialization_level; + + if (specialization_level < PSO_SPECIALIZED_INTERSECT) { + return; + } + + /* PSO_SPECIALIZED_INTERSECT kernels are fast to specialize, so we always load them + * synchronously. */ + compile_and_load(PSO_SPECIALIZED_INTERSECT); + + if (specialization_level < PSO_SPECIALIZED_SHADE) { + return; + } + if (!scene->params.background) { + /* Don't load PSO_SPECIALIZED_SHADE kernels during viewport rendering as they are slower to + * build. */ + return; + } + + /* PSO_SPECIALIZED_SHADE kernels are slower to specialize, so we load them asynchronously, and + * only if there isn't an existing load in flight. + */ + auto specialize_shade_fn = ^() { + compile_and_load(PSO_SPECIALIZED_SHADE); + async_compile_and_load = false; + }; + + bool async_specialize_shade = true; + + /* Block if a per-kernel profiling is enabled (ensure steady rendering rate). */ + if (getenv("CYCLES_METAL_PROFILING") != nullptr) { + async_specialize_shade = false; + } + + if (async_specialize_shade) { + if (!async_compile_and_load) { + async_compile_and_load = true; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), + specialize_shade_fn); + } + else { + metal_printf( + "Async PSO_SPECIALIZED_SHADE load request already in progress - dropping request\n"); + } + } + else { + specialize_shade_fn(); + } +} + void MetalDevice::const_copy_to(const char *name, void *host, size_t size) { if (strcmp(name, "data") == 0) { @@ -652,7 +747,7 @@ void MetalDevice::const_copy_to(const char *name, void *host, size_t size) /* Update data storage pointers in launch parameters. */ if (strcmp(name, "integrator_state") == 0) { /* IntegratorStateGPU is contiguous pointers */ - const size_t pointer_block_size = sizeof(IntegratorStateGPU); + const size_t pointer_block_size = offsetof(IntegratorStateGPU, sort_partition_divisor); update_launch_pointers( offsetof(KernelParamsMetal, integrator_state), host, size, pointer_block_size); } diff --git a/intern/cycles/device/metal/kernel.h b/intern/cycles/device/metal/kernel.h index 69b2a686ecc..11393f8b7e1 100644 --- a/intern/cycles/device/metal/kernel.h +++ b/intern/cycles/device/metal/kernel.h @@ -31,7 +31,7 @@ enum { enum { METALRT_TABLE_DEFAULT, METALRT_TABLE_SHADOW, METALRT_TABLE_LOCAL, METALRT_TABLE_NUM }; /* Pipeline State Object types */ -enum { +enum MetalPipelineType { /* A kernel that can be used with all scenes, supporting all features. * It is slow to compile, but only needs to be compiled once and is then * cached for future render sessions. This allows a render to get underway @@ -39,28 +39,33 @@ enum { */ PSO_GENERIC, - /* A kernel that is relatively quick to compile, but is specialized for the - * scene being rendered. It only contains the functionality and even baked in - * constants for values that means it needs to be recompiled whenever a - * dependent setting is changed. The render performance of this kernel is - * significantly faster though, and justifies the extra compile time. + /* A intersection kernel that is very quick to specialize and results in faster intersection + * kernel performance. It uses Metal function constants to replace several KernelData variables + * with fixed constants. + */ + PSO_SPECIALIZED_INTERSECT, + + /* A shading kernel that is slow to specialize, but results in faster shading kernel performance + * rendered. It uses Metal function constants to replace several KernelData variables with fixed + * constants and short-circuit all unused SVM node case handlers. */ - /* METAL_WIP: This isn't used and will require more changes to enable. */ - PSO_SPECIALISED, + PSO_SPECIALIZED_SHADE, PSO_NUM }; -const char *kernel_type_as_string(int kernel_type); +const char *kernel_type_as_string(MetalPipelineType pso_type); struct MetalKernelPipeline { void compile(); id<MTLLibrary> mtlLibrary = nil; - bool scene_specialized; + MetalPipelineType pso_type; string source_md5; + size_t usage_count = 0; + KernelData kernel_data_; bool use_metalrt; bool metalrt_hair; bool metalrt_hair_thick; @@ -75,6 +80,8 @@ struct MetalKernelPipeline { id<MTLComputePipelineState> pipeline = nil; int num_threads_per_block = 0; + bool should_use_binary_archive() const; + string error_str; API_AVAILABLE(macos(11.0)) @@ -85,7 +92,8 @@ struct MetalKernelPipeline { /* Cache of Metal kernels for each DeviceKernel. */ namespace MetalDeviceKernels { -bool load(MetalDevice *device, bool scene_specialized); +bool should_load_kernels(MetalDevice *device, MetalPipelineType pso_type); +bool load(MetalDevice *device, MetalPipelineType pso_type); const MetalKernelPipeline *get_best_pipeline(const MetalDevice *device, DeviceKernel kernel); } /* namespace MetalDeviceKernels */ diff --git a/intern/cycles/device/metal/kernel.mm b/intern/cycles/device/metal/kernel.mm index fec4cd80466..385cb412b06 100644 --- a/intern/cycles/device/metal/kernel.mm +++ b/intern/cycles/device/metal/kernel.mm @@ -5,6 +5,7 @@ # include "device/metal/kernel.h" # include "device/metal/device_impl.h" +# include "kernel/device/metal/function_constants.h" # include "util/md5.h" # include "util/path.h" # include "util/tbb.h" @@ -16,13 +17,15 @@ CCL_NAMESPACE_BEGIN /* limit to 2 MTLCompiler instances */ int max_mtlcompiler_threads = 2; -const char *kernel_type_as_string(int kernel_type) +const char *kernel_type_as_string(MetalPipelineType pso_type) { - switch (kernel_type) { + switch (pso_type) { case PSO_GENERIC: return "PSO_GENERIC"; - case PSO_SPECIALISED: - return "PSO_SPECIALISED"; + case PSO_SPECIALIZED_INTERSECT: + return "PSO_SPECIALIZED_INTERSECT"; + case PSO_SPECIALIZED_SHADE: + return "PSO_SPECIALIZED_SHADE"; default: assert(0); } @@ -50,7 +53,11 @@ struct ShaderCache { /* Non-blocking request for a kernel, optionally specialized to the scene being rendered by * device. */ - void load_kernel(DeviceKernel kernel, MetalDevice *device, bool scene_specialized); + void load_kernel(DeviceKernel kernel, MetalDevice *device, MetalPipelineType pso_type); + + bool should_load_kernel(DeviceKernel device_kernel, + MetalDevice *device, + MetalPipelineType pso_type); void wait_for_all(); @@ -139,31 +146,34 @@ void ShaderCache::compile_thread_func(int thread_index) } } -void ShaderCache::load_kernel(DeviceKernel device_kernel, - MetalDevice *device, - bool scene_specialized) +bool ShaderCache::should_load_kernel(DeviceKernel device_kernel, + MetalDevice *device, + MetalPipelineType pso_type) { - { - /* create compiler threads on first run */ - thread_scoped_lock lock(cache_mutex); - if (compile_threads.empty()) { - running = true; - for (int i = 0; i < max_mtlcompiler_threads; i++) { - compile_threads.push_back(std::thread([&] { compile_thread_func(i); })); - } - } + if (device_kernel == DEVICE_KERNEL_INTEGRATOR_MEGAKERNEL) { + /* Skip megakernel. */ + return false; } - if (device_kernel == DEVICE_KERNEL_INTEGRATOR_MEGAKERNEL) { - /* skip megakernel */ - return; + if (device_kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE) { + if ((device->kernel_features & KERNEL_FEATURE_NODE_RAYTRACE) == 0) { + /* Skip shade_surface_raytrace kernel if the scene doesn't require it. */ + return false; + } } - if (scene_specialized) { + if (pso_type != PSO_GENERIC) { /* Only specialize kernels where it can make an impact. */ if (device_kernel < DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST || device_kernel > DEVICE_KERNEL_INTEGRATOR_MEGAKERNEL) { - return; + return false; + } + + /* Only specialize shading / intersection kernels as requested. */ + bool is_shade_kernel = (device_kernel >= DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND); + bool is_shade_pso = (pso_type == PSO_SPECIALIZED_SHADE); + if (is_shade_pso != is_shade_kernel) { + return false; } } @@ -171,35 +181,45 @@ void ShaderCache::load_kernel(DeviceKernel device_kernel, /* check whether the kernel has already been requested / cached */ thread_scoped_lock lock(cache_mutex); for (auto &pipeline : pipelines[device_kernel]) { - if (scene_specialized) { - if (pipeline->source_md5 == device->source_md5[PSO_SPECIALISED]) { - /* we already requested a pipeline that is specialized for this kernel data */ - metal_printf("Specialized kernel already requested (%s)\n", - device_kernel_as_string(device_kernel)); - return; - } + if (pipeline->source_md5 == device->source_md5[pso_type]) { + return false; } - else { - if (pipeline->source_md5 == device->source_md5[PSO_GENERIC]) { - /* we already requested a generic pipeline for this kernel */ - metal_printf("Generic kernel already requested (%s)\n", - device_kernel_as_string(device_kernel)); - return; - } + } + } + + return true; +} + +void ShaderCache::load_kernel(DeviceKernel device_kernel, + MetalDevice *device, + MetalPipelineType pso_type) +{ + { + /* create compiler threads on first run */ + thread_scoped_lock lock(cache_mutex); + if (compile_threads.empty()) { + running = true; + for (int i = 0; i < max_mtlcompiler_threads; i++) { + compile_threads.push_back(std::thread([&] { compile_thread_func(i); })); } } } + if (!should_load_kernel(device_kernel, device, pso_type)) { + return; + } + incomplete_requests++; PipelineRequest request; request.pipeline = new MetalKernelPipeline; - request.pipeline->scene_specialized = scene_specialized; + memcpy(&request.pipeline->kernel_data_, + &device->launch_params.data, + sizeof(request.pipeline->kernel_data_)); + request.pipeline->pso_type = pso_type; request.pipeline->mtlDevice = mtlDevice; - request.pipeline->source_md5 = - device->source_md5[scene_specialized ? PSO_SPECIALISED : PSO_GENERIC]; - request.pipeline->mtlLibrary = - device->mtlLibrary[scene_specialized ? PSO_SPECIALISED : PSO_GENERIC]; + request.pipeline->source_md5 = device->source_md5[pso_type]; + request.pipeline->mtlLibrary = device->mtlLibrary[pso_type]; request.pipeline->device_kernel = device_kernel; request.pipeline->threads_per_threadgroup = device->max_threads_per_threadgroup; @@ -214,7 +234,24 @@ void ShaderCache::load_kernel(DeviceKernel device_kernel, { thread_scoped_lock lock(cache_mutex); - pipelines[device_kernel].push_back(unique_ptr<MetalKernelPipeline>(request.pipeline)); + auto &collection = pipelines[device_kernel]; + + /* Cache up to 3 kernel variants with the same pso_type, purging oldest first. */ + int max_entries_of_same_pso_type = 3; + for (int i = (int)collection.size() - 1; i >= 0; i--) { + if (collection[i]->pso_type == pso_type) { + max_entries_of_same_pso_type -= 1; + if (max_entries_of_same_pso_type == 0) { + metal_printf("Purging oldest %s:%s kernel from ShaderCache\n", + kernel_type_as_string(pso_type), + device_kernel_as_string(device_kernel)); + collection.erase(collection.begin() + i); + break; + } + } + } + + collection.push_back(unique_ptr<MetalKernelPipeline>(request.pipeline)); request_queue.push_back(request); } cond_var.notify_one(); @@ -248,8 +285,9 @@ MetalKernelPipeline *ShaderCache::get_best_pipeline(DeviceKernel kernel, const M continue; } - if (pipeline->scene_specialized) { - if (pipeline->source_md5 == device->source_md5[PSO_SPECIALISED]) { + if (pipeline->pso_type != PSO_GENERIC) { + if (pipeline->source_md5 == device->source_md5[PSO_SPECIALIZED_INTERSECT] || + pipeline->source_md5 == device->source_md5[PSO_SPECIALIZED_SHADE]) { best_pipeline = pipeline.get(); } } @@ -258,13 +296,65 @@ MetalKernelPipeline *ShaderCache::get_best_pipeline(DeviceKernel kernel, const M } } + if (best_pipeline->usage_count == 0 && best_pipeline->pso_type != PSO_GENERIC) { + metal_printf("Swapping in %s version of %s\n", + kernel_type_as_string(best_pipeline->pso_type), + device_kernel_as_string(kernel)); + } + best_pipeline->usage_count += 1; + return best_pipeline; } -void MetalKernelPipeline::compile() +bool MetalKernelPipeline::should_use_binary_archive() const { - int pso_type = scene_specialized ? PSO_SPECIALISED : PSO_GENERIC; + if (auto str = getenv("CYCLES_METAL_DISABLE_BINARY_ARCHIVES")) { + if (atoi(str) != 0) { + /* Don't archive if we have opted out by env var. */ + return false; + } + } + + if (pso_type == PSO_GENERIC) { + /* Archive the generic kernels. */ + return true; + } + + if (device_kernel >= DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND && + device_kernel <= DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW) { + /* Archive all shade kernels - they take a long time to compile. */ + return true; + } + + /* The remaining kernels are all fast to compile. They may get cached by the system shader cache, + * but will be quick to regenerate if not. */ + return false; +} + +static MTLFunctionConstantValues *GetConstantValues(KernelData const *data = nullptr) +{ + MTLFunctionConstantValues *constant_values = [MTLFunctionConstantValues new]; + + MTLDataType MTLDataType_int = MTLDataTypeInt; + MTLDataType MTLDataType_float = MTLDataTypeFloat; + MTLDataType MTLDataType_float4 = MTLDataTypeFloat4; + KernelData zero_data = {0}; + if (!data) { + data = &zero_data; + } +# define KERNEL_STRUCT_MEMBER(parent, _type, name) \ + [constant_values setConstantValue:&data->parent.name \ + type:MTLDataType_##_type \ + atIndex:KernelData_##parent##_##name]; + +# include "kernel/data_template.h" + + return constant_values; +} + +void MetalKernelPipeline::compile() +{ const std::string function_name = std::string("cycles_metal_") + device_kernel_as_string(device_kernel); @@ -281,6 +371,17 @@ void MetalKernelPipeline::compile() if (@available(macOS 11.0, *)) { MTLFunctionDescriptor *func_desc = [MTLIntersectionFunctionDescriptor functionDescriptor]; func_desc.name = entryPoint; + + if (pso_type == PSO_SPECIALIZED_SHADE) { + func_desc.constantValues = GetConstantValues(&kernel_data_); + } + else if (pso_type == PSO_SPECIALIZED_INTERSECT) { + func_desc.constantValues = GetConstantValues(&kernel_data_); + } + else { + func_desc.constantValues = GetConstantValues(); + } + function = [mtlLibrary newFunctionWithDescriptor:func_desc error:&error]; } @@ -427,10 +528,7 @@ void MetalKernelPipeline::compile() MTLPipelineOption pipelineOptions = MTLPipelineOptionNone; - bool use_binary_archive = true; - if (auto str = getenv("CYCLES_METAL_DISABLE_BINARY_ARCHIVES")) { - use_binary_archive = (atoi(str) == 0); - } + bool use_binary_archive = should_use_binary_archive(); id<MTLBinaryArchive> archive = nil; string metalbin_path; @@ -608,19 +706,32 @@ void MetalKernelPipeline::compile() } } -bool MetalDeviceKernels::load(MetalDevice *device, bool scene_specialized) +bool MetalDeviceKernels::load(MetalDevice *device, MetalPipelineType pso_type) { + const double starttime = time_dt(); auto shader_cache = get_shader_cache(device->mtlDevice); for (int i = 0; i < DEVICE_KERNEL_NUM; i++) { - shader_cache->load_kernel((DeviceKernel)i, device, scene_specialized); + shader_cache->load_kernel((DeviceKernel)i, device, pso_type); } - if (!scene_specialized || getenv("CYCLES_METAL_PROFILING")) { - shader_cache->wait_for_all(); - } + shader_cache->wait_for_all(); + metal_printf("Back-end compilation finished in %.1f seconds (%s)\n", + time_dt() - starttime, + kernel_type_as_string(pso_type)); return true; } +bool MetalDeviceKernels::should_load_kernels(MetalDevice *device, MetalPipelineType pso_type) +{ + auto shader_cache = get_shader_cache(device->mtlDevice); + for (int i = 0; i < DEVICE_KERNEL_NUM; i++) { + if (shader_cache->should_load_kernel((DeviceKernel)i, device, pso_type)) { + return true; + } + } + return false; +} + const MetalKernelPipeline *MetalDeviceKernels::get_best_pipeline(const MetalDevice *device, DeviceKernel kernel) { diff --git a/intern/cycles/device/metal/queue.h b/intern/cycles/device/metal/queue.h index b0bd487c86d..fc32740f3e1 100644 --- a/intern/cycles/device/metal/queue.h +++ b/intern/cycles/device/metal/queue.h @@ -24,6 +24,7 @@ class MetalDeviceQueue : public DeviceQueue { virtual int num_concurrent_states(const size_t) const override; virtual int num_concurrent_busy_states() const override; + virtual int num_sort_partition_elements() const override; virtual void init_execution() override; diff --git a/intern/cycles/device/metal/queue.mm b/intern/cycles/device/metal/queue.mm index 03e60b6bb6e..5ac63a16c61 100644 --- a/intern/cycles/device/metal/queue.mm +++ b/intern/cycles/device/metal/queue.mm @@ -293,6 +293,11 @@ int MetalDeviceQueue::num_concurrent_busy_states() const return result; } +int MetalDeviceQueue::num_sort_partition_elements() const +{ + return MetalInfo::optimal_sort_partition_elements(metal_device_->mtlDevice); +} + void MetalDeviceQueue::init_execution() { /* Synchronize all textures and memory copies before executing task. */ @@ -359,7 +364,7 @@ bool MetalDeviceQueue::enqueue(DeviceKernel kernel, /* Prepare any non-pointer (i.e. plain-old-data) KernelParamsMetal data */ /* The plain-old-data is contiguous, continuing to the end of KernelParamsMetal */ size_t plain_old_launch_data_offset = offsetof(KernelParamsMetal, integrator_state) + - sizeof(IntegratorStateGPU); + offsetof(IntegratorStateGPU, sort_partition_divisor); size_t plain_old_launch_data_size = sizeof(KernelParamsMetal) - plain_old_launch_data_offset; memcpy(init_arg_buffer + globals_offsets + plain_old_launch_data_offset, (uint8_t *)&metal_device_->launch_params + plain_old_launch_data_offset, @@ -416,7 +421,7 @@ bool MetalDeviceQueue::enqueue(DeviceKernel kernel, /* this relies on IntegratorStateGPU layout being contiguous device_ptrs */ const size_t pointer_block_end = offsetof(KernelParamsMetal, integrator_state) + - sizeof(IntegratorStateGPU); + offsetof(IntegratorStateGPU, sort_partition_divisor); for (size_t offset = 0; offset < pointer_block_end; offset += sizeof(device_ptr)) { int pointer_index = int(offset / sizeof(device_ptr)); MetalDevice::MetalMem *mmem = *( diff --git a/intern/cycles/device/metal/util.h b/intern/cycles/device/metal/util.h index fd32d8a260f..a988d01d361 100644 --- a/intern/cycles/device/metal/util.h +++ b/intern/cycles/device/metal/util.h @@ -37,6 +37,7 @@ struct MetalInfo { static int get_apple_gpu_core_count(id<MTLDevice> device); static MetalGPUVendor get_device_vendor(id<MTLDevice> device); static AppleGPUArchitecture get_apple_gpu_architecture(id<MTLDevice> device); + static int optimal_sort_partition_elements(id<MTLDevice> device); static string get_device_name(id<MTLDevice> device); }; diff --git a/intern/cycles/device/metal/util.mm b/intern/cycles/device/metal/util.mm index a7a5b596b8f..65c67c400fe 100644 --- a/intern/cycles/device/metal/util.mm +++ b/intern/cycles/device/metal/util.mm @@ -72,6 +72,21 @@ MetalGPUVendor MetalInfo::get_device_vendor(id<MTLDevice> device) return METAL_GPU_UNKNOWN; } +int MetalInfo::optimal_sort_partition_elements(id<MTLDevice> device) +{ + if (auto str = getenv("CYCLES_METAL_SORT_PARTITION_ELEMENTS")) { + return atoi(str); + } + + /* On M1 and M2 GPUs, we see better cache utilization if we partition the active indices before + * sorting each partition by material. Partitioning into chunks of 65536 elements results in an + * overall render time speedup of up to 15%. */ + if (get_device_vendor(device) == METAL_GPU_APPLE) { + return 65536; + } + return 0; +} + vector<id<MTLDevice>> const &MetalInfo::get_usable_devices() { static vector<id<MTLDevice>> usable_devices; diff --git a/intern/cycles/device/optix/device_impl.cpp b/intern/cycles/device/optix/device_impl.cpp index e7dcc29a2da..11c0d1bf8a0 100644 --- a/intern/cycles/device/optix/device_impl.cpp +++ b/intern/cycles/device/optix/device_impl.cpp @@ -2047,7 +2047,7 @@ void OptiXDevice::const_copy_to(const char *name, void *host, size_t size) /* Update traversable handle (since it is different for each device on multi devices). */ KernelData *const data = (KernelData *)host; - *(OptixTraversableHandle *)&data->bvh.scene = tlas_handle; + *(OptixTraversableHandle *)&data->device_bvh = tlas_handle; update_launch_params(offsetof(KernelParamsOptiX, data), host, size); return; diff --git a/intern/cycles/device/queue.h b/intern/cycles/device/queue.h index 14a5db3a204..808431af401 100644 --- a/intern/cycles/device/queue.h +++ b/intern/cycles/device/queue.h @@ -105,6 +105,13 @@ class DeviceQueue { * value. */ virtual int num_concurrent_busy_states() const = 0; + /* Number of elements in a partition of sorted shaders, that improves memory locality of + * integrator state fetch at the cost of decreased coherence for shader kernel execution. */ + virtual int num_sort_partition_elements() const + { + return 65536; + } + /* Initialize execution of kernels on this queue. * * Will, for example, load all data required by the kernels from Device to global or path state. diff --git a/intern/cycles/integrator/path_trace.cpp b/intern/cycles/integrator/path_trace.cpp index 6912bf928cd..ed278821b46 100644 --- a/intern/cycles/integrator/path_trace.cpp +++ b/intern/cycles/integrator/path_trace.cpp @@ -373,7 +373,7 @@ void PathTrace::path_trace(RenderWork &render_work) work_balance_infos_[i].time_spent += work_time; work_balance_infos_[i].occupancy = statistics.occupancy; - VLOG_WORK << "Rendered " << num_samples << " samples in " << work_time << " seconds (" + VLOG_INFO << "Rendered " << num_samples << " samples in " << work_time << " seconds (" << work_time / num_samples << " seconds per sample), occupancy: " << statistics.occupancy; }); diff --git a/intern/cycles/integrator/path_trace_work_gpu.cpp b/intern/cycles/integrator/path_trace_work_gpu.cpp index e262c252ce3..fa313f6460a 100644 --- a/intern/cycles/integrator/path_trace_work_gpu.cpp +++ b/intern/cycles/integrator/path_trace_work_gpu.cpp @@ -181,27 +181,45 @@ void PathTraceWorkGPU::alloc_integrator_queue() void PathTraceWorkGPU::alloc_integrator_sorting() { + /* Compute sort partitions, to balance between memory locality and coherence. + * Sort partitioning becomes less effective when more shaders are in the wavefront. In lieu of a + * more sophisticated heuristic we simply disable sort partitioning if the shader count is high. + */ + num_sort_partitions_ = 1; + if (device_scene_->data.max_shaders < 300) { + const int num_elements = queue_->num_sort_partition_elements(); + if (num_elements) { + num_sort_partitions_ = max(max_num_paths_ / num_elements, 1); + } + } + + integrator_state_gpu_.sort_partition_divisor = (int)divide_up(max_num_paths_, + num_sort_partitions_); + /* Allocate arrays for shader sorting. */ - const int max_shaders = device_scene_->data.max_shaders; - if (integrator_shader_sort_counter_.size() < max_shaders) { - integrator_shader_sort_counter_.alloc(max_shaders); + const int sort_buckets = device_scene_->data.max_shaders * num_sort_partitions_; + if (integrator_shader_sort_counter_.size() < sort_buckets) { + integrator_shader_sort_counter_.alloc(sort_buckets); integrator_shader_sort_counter_.zero_to_device(); + integrator_state_gpu_.sort_key_counter[DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE] = + (int *)integrator_shader_sort_counter_.device_pointer; - integrator_shader_raytrace_sort_counter_.alloc(max_shaders); - integrator_shader_raytrace_sort_counter_.zero_to_device(); + if (device_scene_->data.kernel_features & KERNEL_FEATURE_NODE_RAYTRACE) { + integrator_shader_raytrace_sort_counter_.alloc(sort_buckets); + integrator_shader_raytrace_sort_counter_.zero_to_device(); + integrator_state_gpu_.sort_key_counter[DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE] = + (int *)integrator_shader_raytrace_sort_counter_.device_pointer; + } - integrator_shader_mnee_sort_counter_.alloc(max_shaders); - integrator_shader_mnee_sort_counter_.zero_to_device(); + if (device_scene_->data.kernel_features & KERNEL_FEATURE_MNEE) { + integrator_shader_mnee_sort_counter_.alloc(sort_buckets); + integrator_shader_mnee_sort_counter_.zero_to_device(); + integrator_state_gpu_.sort_key_counter[DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_MNEE] = + (int *)integrator_shader_mnee_sort_counter_.device_pointer; + } - integrator_shader_sort_prefix_sum_.alloc(max_shaders); + integrator_shader_sort_prefix_sum_.alloc(sort_buckets); integrator_shader_sort_prefix_sum_.zero_to_device(); - - integrator_state_gpu_.sort_key_counter[DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE] = - (int *)integrator_shader_sort_counter_.device_pointer; - integrator_state_gpu_.sort_key_counter[DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE] = - (int *)integrator_shader_raytrace_sort_counter_.device_pointer; - integrator_state_gpu_.sort_key_counter[DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_MNEE] = - (int *)integrator_shader_mnee_sort_counter_.device_pointer; } } @@ -333,8 +351,12 @@ void PathTraceWorkGPU::enqueue_reset() queue_->enqueue(DEVICE_KERNEL_INTEGRATOR_RESET, max_num_paths_, args); queue_->zero_to_device(integrator_queue_counter_); queue_->zero_to_device(integrator_shader_sort_counter_); - queue_->zero_to_device(integrator_shader_raytrace_sort_counter_); - queue_->zero_to_device(integrator_shader_mnee_sort_counter_); + if (device_scene_->data.kernel_features & KERNEL_FEATURE_NODE_RAYTRACE) { + queue_->zero_to_device(integrator_shader_raytrace_sort_counter_); + } + if (device_scene_->data.kernel_features & KERNEL_FEATURE_MNEE) { + queue_->zero_to_device(integrator_shader_mnee_sort_counter_); + } /* Tiles enqueue need to know number of active paths, which is based on this counter. Zero the * counter on the host side because `zero_to_device()` is not doing it. */ @@ -486,9 +508,9 @@ void PathTraceWorkGPU::compute_sorted_queued_paths(DeviceKernel kernel, /* Compute prefix sum of number of active paths with each shader. */ { const int work_size = 1; - int max_shaders = device_scene_->data.max_shaders; + int sort_buckets = device_scene_->data.max_shaders * num_sort_partitions_; - DeviceKernelArguments args(&d_counter, &d_prefix_sum, &max_shaders); + DeviceKernelArguments args(&d_counter, &d_prefix_sum, &sort_buckets); queue_->enqueue(DEVICE_KERNEL_PREFIX_SUM, work_size, args); } diff --git a/intern/cycles/integrator/path_trace_work_gpu.h b/intern/cycles/integrator/path_trace_work_gpu.h index 4c10a221a30..a805258d1b5 100644 --- a/intern/cycles/integrator/path_trace_work_gpu.h +++ b/intern/cycles/integrator/path_trace_work_gpu.h @@ -156,6 +156,9 @@ class PathTraceWorkGPU : public PathTraceWork { bool interop_use_checked_ = false; bool interop_use_ = false; + /* Number of partitions to sort state indices into prior to material sort. */ + int num_sort_partitions_; + /* Maximum number of concurrent integrator states. */ int max_num_paths_; diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt index b893ff6ef24..21a78722c0d 100644 --- a/intern/cycles/kernel/CMakeLists.txt +++ b/intern/cycles/kernel/CMakeLists.txt @@ -79,6 +79,7 @@ set(SRC_KERNEL_DEVICE_METAL_HEADERS device/metal/compat.h device/metal/context_begin.h device/metal/context_end.h + device/metal/function_constants.h device/metal/globals.h ) @@ -154,6 +155,7 @@ set(SRC_KERNEL_SVM_HEADERS svm/math_util.h svm/mix.h svm/musgrave.h + svm/node_types_template.h svm/noise.h svm/noisetex.h svm/normal.h @@ -282,6 +284,7 @@ set(SRC_KERNEL_UTIL_HEADERS set(SRC_KERNEL_TYPES_HEADERS data_arrays.h + data_template.h tables.h types.h ) @@ -844,10 +847,9 @@ if(WITH_CYCLES_DEVICE_ONEAPI) else() list(APPEND sycl_compiler_flags -fPIC) - # avoid getting __FAST_MATH__ to be defined for the graphics compiler on CentOS 7 until the compile-time issue it triggers gets fixed. - if(WITH_CYCLES_ONEAPI_BINARIES) - list(APPEND sycl_compiler_flags -fhonor-nans) - endif() + # We avoid getting __FAST_MATH__ to be defined when building on CentOS 7 until the compilation crash + # it triggers at either AoT or JIT stages gets fixed. + list(APPEND sycl_compiler_flags -fhonor-nans) # add $ORIGIN to cycles_kernel_oneapi.so rpath so libsycl.so and # libpi_level_zero.so can be placed next to it and get found. diff --git a/intern/cycles/kernel/bvh/bvh.h b/intern/cycles/kernel/bvh/bvh.h index a1d0e307170..9972de86c47 100644 --- a/intern/cycles/kernel/bvh/bvh.h +++ b/intern/cycles/kernel/bvh/bvh.h @@ -172,11 +172,11 @@ ccl_device_intersect bool scene_intersect(KernelGlobals kg, ray_flags |= OPTIX_RAY_FLAG_TERMINATE_ON_FIRST_HIT; } - optixTrace(scene_intersect_valid(ray) ? kernel_data.bvh.scene : 0, + optixTrace(scene_intersect_valid(ray) ? kernel_data.device_bvh : 0, ray->P, ray->D, - 0.0f, - ray->t, + ray->tmin, + ray->tmax, ray->time, ray_mask, ray_flags, @@ -203,28 +203,28 @@ ccl_device_intersect bool scene_intersect(KernelGlobals kg, #elif defined(__METALRT__) if (!scene_intersect_valid(ray)) { - isect->t = ray->t; + isect->t = ray->tmax; isect->type = PRIMITIVE_NONE; return false; } # if defined(__KERNEL_DEBUG__) if (is_null_instance_acceleration_structure(metal_ancillaries->accel_struct)) { - isect->t = ray->t; + isect->t = ray->tmax; isect->type = PRIMITIVE_NONE; kernel_assert(!"Invalid metal_ancillaries->accel_struct pointer"); return false; } if (is_null_intersection_function_table(metal_ancillaries->ift_default)) { - isect->t = ray->t; + isect->t = ray->tmax; isect->type = PRIMITIVE_NONE; kernel_assert(!"Invalid ift_default"); return false; } # endif - metal::raytracing::ray r(ray->P, ray->D, 0.0f, ray->t); + metal::raytracing::ray r(ray->P, ray->D, ray->tmin, ray->tmax); metalrt_intersector_type metalrt_intersect; if (!kernel_data.bvh.have_curves) { @@ -263,7 +263,7 @@ ccl_device_intersect bool scene_intersect(KernelGlobals kg, # endif if (intersection.type == intersection_type::none) { - isect->t = ray->t; + isect->t = ray->tmax; isect->type = PRIMITIVE_NONE; return false; @@ -295,14 +295,14 @@ ccl_device_intersect bool scene_intersect(KernelGlobals kg, } # ifdef __EMBREE__ - if (kernel_data.bvh.scene) { - isect->t = ray->t; + if (kernel_data.device_bvh) { + isect->t = ray->tmax; CCLIntersectContext ctx(kg, CCLIntersectContext::RAY_REGULAR); IntersectContext rtc_ctx(&ctx); RTCRayHit ray_hit; ctx.ray = ray; kernel_embree_setup_rayhit(*ray, ray_hit, visibility); - rtcIntersect1(kernel_data.bvh.scene, &rtc_ctx.context, &ray_hit); + rtcIntersect1(kernel_data.device_bvh, &rtc_ctx.context, &ray_hit); if (ray_hit.hit.geomID != RTC_INVALID_GEOMETRY_ID && ray_hit.hit.primID != RTC_INVALID_GEOMETRY_ID) { kernel_embree_convert_hit(kg, &ray_hit.ray, &ray_hit.hit, isect); @@ -357,11 +357,11 @@ ccl_device_intersect bool scene_intersect_local(KernelGlobals kg, if (local_isect) { local_isect->num_hits = 0; /* Initialize hit count to zero. */ } - optixTrace(scene_intersect_valid(ray) ? kernel_data.bvh.scene : 0, + optixTrace(scene_intersect_valid(ray) ? kernel_data.device_bvh : 0, ray->P, ray->D, - 0.0f, - ray->t, + ray->tmin, + ray->tmax, ray->time, 0xFF, /* Need to always call into __anyhit__kernel_optix_local_hit. */ @@ -405,7 +405,7 @@ ccl_device_intersect bool scene_intersect_local(KernelGlobals kg, } # endif - metal::raytracing::ray r(ray->P, ray->D, 0.0f, ray->t); + metal::raytracing::ray r(ray->P, ray->D, ray->tmin, ray->tmax); metalrt_intersector_type metalrt_intersect; metalrt_intersect.force_opacity(metal::raytracing::forced_opacity::non_opaque); @@ -451,7 +451,7 @@ ccl_device_intersect bool scene_intersect_local(KernelGlobals kg, } # ifdef __EMBREE__ - if (kernel_data.bvh.scene) { + if (kernel_data.device_bvh) { const bool has_bvh = !(kernel_data_fetch(object_flag, local_object) & SD_OBJECT_TRANSFORM_APPLIED); CCLIntersectContext ctx( @@ -470,13 +470,13 @@ ccl_device_intersect bool scene_intersect_local(KernelGlobals kg, /* If this object has its own BVH, use it. */ if (has_bvh) { - RTCGeometry geom = rtcGetGeometry(kernel_data.bvh.scene, local_object * 2); + RTCGeometry geom = rtcGetGeometry(kernel_data.device_bvh, local_object * 2); if (geom) { float3 P = ray->P; float3 dir = ray->D; float3 idir = ray->D; Transform ob_itfm; - rtc_ray.tfar = ray->t * + rtc_ray.tfar = ray->tmax * bvh_instance_motion_push(kg, local_object, ray, &P, &dir, &idir, &ob_itfm); /* bvh_instance_motion_push() returns the inverse transform but * it's not needed here. */ @@ -496,7 +496,7 @@ ccl_device_intersect bool scene_intersect_local(KernelGlobals kg, } } else { - rtcOccluded1(kernel_data.bvh.scene, &rtc_ctx.context, &rtc_ray); + rtcOccluded1(kernel_data.device_bvh, &rtc_ctx.context, &rtc_ray); } /* rtcOccluded1 sets tfar to -inf if a hit was found. */ @@ -539,11 +539,11 @@ ccl_device_intersect bool scene_intersect_shadow_all(KernelGlobals kg, ray_mask = 0xFF; } - optixTrace(scene_intersect_valid(ray) ? kernel_data.bvh.scene : 0, + optixTrace(scene_intersect_valid(ray) ? kernel_data.device_bvh : 0, ray->P, ray->D, - 0.0f, - ray->t, + ray->tmin, + ray->tmax, ray->time, ray_mask, /* Need to always call into __anyhit__kernel_optix_shadow_all_hit. */ @@ -582,7 +582,7 @@ ccl_device_intersect bool scene_intersect_shadow_all(KernelGlobals kg, } # endif - metal::raytracing::ray r(ray->P, ray->D, 0.0f, ray->t); + metal::raytracing::ray r(ray->P, ray->D, ray->tmin, ray->tmax); metalrt_intersector_type metalrt_intersect; metalrt_intersect.force_opacity(metal::raytracing::forced_opacity::non_opaque); @@ -633,7 +633,7 @@ ccl_device_intersect bool scene_intersect_shadow_all(KernelGlobals kg, } # ifdef __EMBREE__ - if (kernel_data.bvh.scene) { + if (kernel_data.device_bvh) { CCLIntersectContext ctx(kg, CCLIntersectContext::RAY_SHADOW_ALL); Intersection *isect_array = (Intersection *)state->shadow_isect; ctx.isect_s = isect_array; @@ -642,7 +642,7 @@ ccl_device_intersect bool scene_intersect_shadow_all(KernelGlobals kg, IntersectContext rtc_ctx(&ctx); RTCRay rtc_ray; kernel_embree_setup_ray(*ray, rtc_ray, visibility); - rtcOccluded1(kernel_data.bvh.scene, &rtc_ctx.context, &rtc_ray); + rtcOccluded1(kernel_data.device_bvh, &rtc_ctx.context, &rtc_ray); *num_recorded_hits = ctx.num_recorded_hits; *throughput = ctx.throughput; @@ -698,11 +698,11 @@ ccl_device_intersect bool scene_intersect_volume(KernelGlobals kg, ray_mask = 0xFF; } - optixTrace(scene_intersect_valid(ray) ? kernel_data.bvh.scene : 0, + optixTrace(scene_intersect_valid(ray) ? kernel_data.device_bvh : 0, ray->P, ray->D, - 0.0f, - ray->t, + ray->tmin, + ray->tmax, ray->time, ray_mask, /* Need to always call into __anyhit__kernel_optix_volume_test. */ @@ -744,7 +744,7 @@ ccl_device_intersect bool scene_intersect_volume(KernelGlobals kg, } # endif - metal::raytracing::ray r(ray->P, ray->D, 0.0f, ray->t); + metal::raytracing::ray r(ray->P, ray->D, ray->tmin, ray->tmax); metalrt_intersector_type metalrt_intersect; metalrt_intersect.force_opacity(metal::raytracing::forced_opacity::non_opaque); @@ -825,7 +825,7 @@ ccl_device_intersect uint scene_intersect_volume_all(KernelGlobals kg, } # ifdef __EMBREE__ - if (kernel_data.bvh.scene) { + if (kernel_data.device_bvh) { CCLIntersectContext ctx(kg, CCLIntersectContext::RAY_VOLUME_ALL); ctx.isect_s = isect; ctx.max_hits = max_hits; @@ -834,7 +834,7 @@ ccl_device_intersect uint scene_intersect_volume_all(KernelGlobals kg, IntersectContext rtc_ctx(&ctx); RTCRay rtc_ray; kernel_embree_setup_ray(*ray, rtc_ray, visibility); - rtcOccluded1(kernel_data.bvh.scene, &rtc_ctx.context, &rtc_ray); + rtcOccluded1(kernel_data.device_bvh, &rtc_ctx.context, &rtc_ray); return ctx.num_hits; } # endif /* __EMBREE__ */ diff --git a/intern/cycles/kernel/bvh/embree.h b/intern/cycles/kernel/bvh/embree.h index 1c6b9bc1e62..fecbccac2f8 100644 --- a/intern/cycles/kernel/bvh/embree.h +++ b/intern/cycles/kernel/bvh/embree.h @@ -83,8 +83,8 @@ ccl_device_inline void kernel_embree_setup_ray(const Ray &ray, rtc_ray.dir_x = ray.D.x; rtc_ray.dir_y = ray.D.y; rtc_ray.dir_z = ray.D.z; - rtc_ray.tnear = 0.0f; - rtc_ray.tfar = ray.t; + rtc_ray.tnear = ray.tmin; + rtc_ray.tfar = ray.tmax; rtc_ray.time = ray.time; rtc_ray.mask = visibility; } @@ -107,7 +107,7 @@ ccl_device_inline bool kernel_embree_is_self_intersection(const KernelGlobals kg const int oID = hit->instID[0] / 2; if ((ray->self.object == oID) || (ray->self.light_object == oID)) { RTCScene inst_scene = (RTCScene)rtcGetGeometryUserData( - rtcGetGeometry(kernel_data.bvh.scene, hit->instID[0])); + rtcGetGeometry(kernel_data.device_bvh, hit->instID[0])); const int pID = hit->primID + (intptr_t)rtcGetGeometryUserData(rtcGetGeometry(inst_scene, hit->geomID)); status = intersection_skip_self_shadow(ray->self, oID, pID); @@ -117,7 +117,7 @@ ccl_device_inline bool kernel_embree_is_self_intersection(const KernelGlobals kg const int oID = hit->geomID / 2; if ((ray->self.object == oID) || (ray->self.light_object == oID)) { const int pID = hit->primID + (intptr_t)rtcGetGeometryUserData( - rtcGetGeometry(kernel_data.bvh.scene, hit->geomID)); + rtcGetGeometry(kernel_data.device_bvh, hit->geomID)); status = intersection_skip_self_shadow(ray->self, oID, pID); } } @@ -133,14 +133,14 @@ ccl_device_inline void kernel_embree_convert_hit(KernelGlobals kg, isect->t = ray->tfar; if (hit->instID[0] != RTC_INVALID_GEOMETRY_ID) { RTCScene inst_scene = (RTCScene)rtcGetGeometryUserData( - rtcGetGeometry(kernel_data.bvh.scene, hit->instID[0])); + rtcGetGeometry(kernel_data.device_bvh, hit->instID[0])); isect->prim = hit->primID + (intptr_t)rtcGetGeometryUserData(rtcGetGeometry(inst_scene, hit->geomID)); isect->object = hit->instID[0] / 2; } else { isect->prim = hit->primID + (intptr_t)rtcGetGeometryUserData( - rtcGetGeometry(kernel_data.bvh.scene, hit->geomID)); + rtcGetGeometry(kernel_data.device_bvh, hit->geomID)); isect->object = hit->geomID / 2; } @@ -166,7 +166,7 @@ ccl_device_inline void kernel_embree_convert_sss_hit( isect->v = hit->u; isect->t = ray->tfar; RTCScene inst_scene = (RTCScene)rtcGetGeometryUserData( - rtcGetGeometry(kernel_data.bvh.scene, object * 2)); + rtcGetGeometry(kernel_data.device_bvh, object * 2)); isect->prim = hit->primID + (intptr_t)rtcGetGeometryUserData(rtcGetGeometry(inst_scene, hit->geomID)); isect->object = object; diff --git a/intern/cycles/kernel/bvh/local.h b/intern/cycles/kernel/bvh/local.h index 3b6b30ea93d..017a241ef4a 100644 --- a/intern/cycles/kernel/bvh/local.h +++ b/intern/cycles/kernel/bvh/local.h @@ -47,8 +47,9 @@ ccl_device_inline float3 P = ray->P; float3 dir = bvh_clamp_direction(ray->D); float3 idir = bvh_inverse_direction(dir); + float tmin = ray->tmin; int object = OBJECT_NONE; - float isect_t = ray->t; + float isect_t = ray->tmax; if (local_isect != NULL) { local_isect->num_hits = 0; @@ -59,10 +60,13 @@ ccl_device_inline if (!(object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { #if BVH_FEATURE(BVH_MOTION) Transform ob_itfm; - isect_t *= bvh_instance_motion_push(kg, local_object, ray, &P, &dir, &idir, &ob_itfm); + const float t_world_to_instance = bvh_instance_motion_push( + kg, local_object, ray, &P, &dir, &idir, &ob_itfm); #else - isect_t *= bvh_instance_push(kg, local_object, ray, &P, &dir, &idir); + const float t_world_to_instance = bvh_instance_push(kg, local_object, ray, &P, &dir, &idir); #endif + isect_t *= t_world_to_instance; + tmin *= t_world_to_instance; object = local_object; } @@ -81,6 +85,7 @@ ccl_device_inline dir, #endif idir, + tmin, isect_t, node_addr, PATH_RAY_ALL_VISIBILITY, @@ -155,6 +160,7 @@ ccl_device_inline local_object, prim, prim_addr, + tmin, isect_t, lcg_state, max_hits)) { @@ -191,6 +197,7 @@ ccl_device_inline local_object, prim, prim_addr, + tmin, isect_t, lcg_state, max_hits)) { diff --git a/intern/cycles/kernel/bvh/nodes.h b/intern/cycles/kernel/bvh/nodes.h index c19dea9223b..e02841fad16 100644 --- a/intern/cycles/kernel/bvh/nodes.h +++ b/intern/cycles/kernel/bvh/nodes.h @@ -18,7 +18,8 @@ ccl_device_forceinline Transform bvh_unaligned_node_fetch_space(KernelGlobals kg ccl_device_forceinline int bvh_aligned_node_intersect(KernelGlobals kg, const float3 P, const float3 idir, - const float t, + const float tmin, + const float tmax, const int node_addr, const uint visibility, float dist[2]) @@ -39,8 +40,8 @@ ccl_device_forceinline int bvh_aligned_node_intersect(KernelGlobals kg, float c0hiy = (node1.z - P.y) * idir.y; float c0loz = (node2.x - P.z) * idir.z; float c0hiz = (node2.z - P.z) * idir.z; - float c0min = max4(0.0f, min(c0lox, c0hix), min(c0loy, c0hiy), min(c0loz, c0hiz)); - float c0max = min4(t, max(c0lox, c0hix), max(c0loy, c0hiy), max(c0loz, c0hiz)); + float c0min = max4(tmin, min(c0lox, c0hix), min(c0loy, c0hiy), min(c0loz, c0hiz)); + float c0max = min4(tmax, max(c0lox, c0hix), max(c0loy, c0hiy), max(c0loz, c0hiz)); float c1lox = (node0.y - P.x) * idir.x; float c1hix = (node0.w - P.x) * idir.x; @@ -48,8 +49,8 @@ ccl_device_forceinline int bvh_aligned_node_intersect(KernelGlobals kg, float c1hiy = (node1.w - P.y) * idir.y; float c1loz = (node2.y - P.z) * idir.z; float c1hiz = (node2.w - P.z) * idir.z; - float c1min = max4(0.0f, min(c1lox, c1hix), min(c1loy, c1hiy), min(c1loz, c1hiz)); - float c1max = min4(t, max(c1lox, c1hix), max(c1loy, c1hiy), max(c1loz, c1hiz)); + float c1min = max4(tmin, min(c1lox, c1hix), min(c1loy, c1hiy), min(c1loz, c1hiz)); + float c1max = min4(tmax, max(c1lox, c1hix), max(c1loy, c1hiy), max(c1loz, c1hiz)); dist[0] = c0min; dist[1] = c1min; @@ -66,7 +67,8 @@ ccl_device_forceinline int bvh_aligned_node_intersect(KernelGlobals kg, ccl_device_forceinline bool bvh_unaligned_node_intersect_child(KernelGlobals kg, const float3 P, const float3 dir, - const float t, + const float tmin, + const float tmax, int node_addr, int child, float dist[2]) @@ -83,8 +85,8 @@ ccl_device_forceinline bool bvh_unaligned_node_intersect_child(KernelGlobals kg, const float far_x = max(lower_xyz.x, upper_xyz.x); const float far_y = max(lower_xyz.y, upper_xyz.y); const float far_z = max(lower_xyz.z, upper_xyz.z); - const float tnear = max4(0.0f, near_x, near_y, near_z); - const float tfar = min4(t, far_x, far_y, far_z); + const float tnear = max4(tmin, near_x, near_y, near_z); + const float tfar = min4(tmax, far_x, far_y, far_z); *dist = tnear; return tnear <= tfar; } @@ -93,7 +95,8 @@ ccl_device_forceinline int bvh_unaligned_node_intersect(KernelGlobals kg, const float3 P, const float3 dir, const float3 idir, - const float t, + const float tmin, + const float tmax, const int node_addr, const uint visibility, float dist[2]) @@ -102,7 +105,7 @@ ccl_device_forceinline int bvh_unaligned_node_intersect(KernelGlobals kg, #ifdef __VISIBILITY_FLAG__ float4 cnodes = kernel_data_fetch(bvh_nodes, node_addr + 0); #endif - if (bvh_unaligned_node_intersect_child(kg, P, dir, t, node_addr, 0, &dist[0])) { + if (bvh_unaligned_node_intersect_child(kg, P, dir, tmin, tmax, node_addr, 0, &dist[0])) { #ifdef __VISIBILITY_FLAG__ if ((__float_as_uint(cnodes.x) & visibility)) #endif @@ -110,7 +113,7 @@ ccl_device_forceinline int bvh_unaligned_node_intersect(KernelGlobals kg, mask |= 1; } } - if (bvh_unaligned_node_intersect_child(kg, P, dir, t, node_addr, 1, &dist[1])) { + if (bvh_unaligned_node_intersect_child(kg, P, dir, tmin, tmax, node_addr, 1, &dist[1])) { #ifdef __VISIBILITY_FLAG__ if ((__float_as_uint(cnodes.y) & visibility)) #endif @@ -125,16 +128,17 @@ ccl_device_forceinline int bvh_node_intersect(KernelGlobals kg, const float3 P, const float3 dir, const float3 idir, - const float t, + const float tmin, + const float tmax, const int node_addr, const uint visibility, float dist[2]) { float4 node = kernel_data_fetch(bvh_nodes, node_addr); if (__float_as_uint(node.x) & PATH_RAY_NODE_UNALIGNED) { - return bvh_unaligned_node_intersect(kg, P, dir, idir, t, node_addr, visibility, dist); + return bvh_unaligned_node_intersect(kg, P, dir, idir, tmin, tmax, node_addr, visibility, dist); } else { - return bvh_aligned_node_intersect(kg, P, idir, t, node_addr, visibility, dist); + return bvh_aligned_node_intersect(kg, P, idir, tmin, tmax, node_addr, visibility, dist); } } diff --git a/intern/cycles/kernel/bvh/shadow_all.h b/intern/cycles/kernel/bvh/shadow_all.h index e86fe867eac..db3c91569aa 100644 --- a/intern/cycles/kernel/bvh/shadow_all.h +++ b/intern/cycles/kernel/bvh/shadow_all.h @@ -49,6 +49,7 @@ ccl_device_inline float3 P = ray->P; float3 dir = bvh_clamp_direction(ray->D); float3 idir = bvh_inverse_direction(dir); + float tmin = ray->tmin; int object = OBJECT_NONE; uint num_hits = 0; @@ -59,12 +60,12 @@ ccl_device_inline /* Max distance in world space. May be dynamically reduced when max number of * recorded hits is exceeded and we no longer need to find hits beyond the max * distance found. */ - float t_max_world = ray->t; + float t_max_world = ray->tmax; /* Current maximum distance to the intersection. * Is calculated as a ray length, transformed to an object space when entering * instance node. */ - float t_max_current = ray->t; + float t_max_current = ray->tmax; /* Conversion from world to local space for the current instance if any, 1.0 * otherwise. */ @@ -88,6 +89,7 @@ ccl_device_inline dir, #endif idir, + tmin, t_max_current, node_addr, visibility, @@ -156,8 +158,16 @@ ccl_device_inline switch (type & PRIMITIVE_ALL) { case PRIMITIVE_TRIANGLE: { - hit = triangle_intersect( - kg, &isect, P, dir, t_max_current, visibility, prim_object, prim, prim_addr); + hit = triangle_intersect(kg, + &isect, + P, + dir, + tmin, + t_max_current, + visibility, + prim_object, + prim, + prim_addr); break; } #if BVH_FEATURE(BVH_MOTION) @@ -166,6 +176,7 @@ ccl_device_inline &isect, P, dir, + tmin, t_max_current, ray->time, visibility, @@ -189,8 +200,16 @@ ccl_device_inline } const int curve_type = kernel_data_fetch(prim_type, prim_addr); - hit = curve_intersect( - kg, &isect, P, dir, t_max_current, prim_object, prim, ray->time, curve_type); + hit = curve_intersect(kg, + &isect, + P, + dir, + tmin, + t_max_current, + prim_object, + prim, + ray->time, + curve_type); break; } @@ -207,8 +226,16 @@ ccl_device_inline } const int point_type = kernel_data_fetch(prim_type, prim_addr); - hit = point_intersect( - kg, &isect, P, dir, t_max_current, prim_object, prim, ray->time, point_type); + hit = point_intersect(kg, + &isect, + P, + dir, + tmin, + t_max_current, + prim_object, + prim, + ray->time, + point_type); break; } #endif /* BVH_FEATURE(BVH_POINTCLOUD) */ @@ -302,6 +329,7 @@ ccl_device_inline /* Convert intersection to object space. */ t_max_current *= t_world_to_instance; + tmin *= t_world_to_instance; ++stack_ptr; kernel_assert(stack_ptr < BVH_STACK_SIZE); @@ -323,7 +351,8 @@ ccl_device_inline #endif /* Restore world space ray length. */ - t_max_current = ray->t; + tmin = ray->tmin; + t_max_current = ray->tmax; object = OBJECT_NONE; t_world_to_instance = 1.0f; diff --git a/intern/cycles/kernel/bvh/traversal.h b/intern/cycles/kernel/bvh/traversal.h index 784fbf4fd11..0ff38bf02de 100644 --- a/intern/cycles/kernel/bvh/traversal.h +++ b/intern/cycles/kernel/bvh/traversal.h @@ -43,13 +43,14 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg, float3 P = ray->P; float3 dir = bvh_clamp_direction(ray->D); float3 idir = bvh_inverse_direction(dir); + float tmin = ray->tmin; int object = OBJECT_NONE; #if BVH_FEATURE(BVH_MOTION) Transform ob_itfm; #endif - isect->t = ray->t; + isect->t = ray->tmax; isect->u = 0.0f; isect->v = 0.0f; isect->prim = PRIM_NONE; @@ -71,6 +72,7 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg, dir, #endif idir, + tmin, isect->t, node_addr, visibility, @@ -133,8 +135,16 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg, switch (type & PRIMITIVE_ALL) { case PRIMITIVE_TRIANGLE: { - if (triangle_intersect( - kg, isect, P, dir, isect->t, visibility, prim_object, prim, prim_addr)) { + if (triangle_intersect(kg, + isect, + P, + dir, + tmin, + isect->t, + visibility, + prim_object, + prim, + prim_addr)) { /* shadow ray early termination */ if (visibility & PATH_RAY_SHADOW_OPAQUE) return true; @@ -147,6 +157,7 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg, isect, P, dir, + tmin, isect->t, ray->time, visibility, @@ -174,7 +185,7 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg, const int curve_type = kernel_data_fetch(prim_type, prim_addr); const bool hit = curve_intersect( - kg, isect, P, dir, isect->t, prim_object, prim, ray->time, curve_type); + kg, isect, P, dir, tmin, isect->t, prim_object, prim, ray->time, curve_type); if (hit) { /* shadow ray early termination */ if (visibility & PATH_RAY_SHADOW_OPAQUE) @@ -195,7 +206,7 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg, const int point_type = kernel_data_fetch(prim_type, prim_addr); const bool hit = point_intersect( - kg, isect, P, dir, isect->t, prim_object, prim, ray->time, point_type); + kg, isect, P, dir, tmin, isect->t, prim_object, prim, ray->time, point_type); if (hit) { /* shadow ray early termination */ if (visibility & PATH_RAY_SHADOW_OPAQUE) @@ -212,11 +223,15 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg, object = kernel_data_fetch(prim_object, -prim_addr - 1); #if BVH_FEATURE(BVH_MOTION) - isect->t *= bvh_instance_motion_push(kg, object, ray, &P, &dir, &idir, &ob_itfm); + const float t_world_to_instance = bvh_instance_motion_push( + kg, object, ray, &P, &dir, &idir, &ob_itfm); #else - isect->t *= bvh_instance_push(kg, object, ray, &P, &dir, &idir); + const float t_world_to_instance = bvh_instance_push(kg, object, ray, &P, &dir, &idir); #endif + isect->t *= t_world_to_instance; + tmin *= t_world_to_instance; + ++stack_ptr; kernel_assert(stack_ptr < BVH_STACK_SIZE); traversal_stack[stack_ptr] = ENTRYPOINT_SENTINEL; @@ -235,6 +250,7 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg, #else isect->t = bvh_instance_pop(kg, object, ray, &P, &dir, &idir, isect->t); #endif + tmin = ray->tmin; object = OBJECT_NONE; node_addr = traversal_stack[stack_ptr]; diff --git a/intern/cycles/kernel/bvh/util.h b/intern/cycles/kernel/bvh/util.h index 572e023db25..1795ae4c790 100644 --- a/intern/cycles/kernel/bvh/util.h +++ b/intern/cycles/kernel/bvh/util.h @@ -5,6 +5,19 @@ CCL_NAMESPACE_BEGIN +/* Offset intersection distance by the smallest possible amount, to skip + * intersections at this distance. This works in cases where the ray start + * position is unchanged and only tmin is updated, since for self + * intersection we'll be comparing against the exact same distances. */ +ccl_device_forceinline float intersection_t_offset(const float t) +{ + /* This is a simplified version of nextafterf(t, FLT_MAX), only dealing with + * non-negative and finite t. */ + kernel_assert(t >= 0.0f && isfinite_safe(t)); + const uint32_t bits = (t == 0.0f) ? 1 : __float_as_uint(t) + 1; + return __uint_as_float(bits); +} + #if defined(__KERNEL_CPU__) ccl_device int intersections_compare(const void *a, const void *b) { diff --git a/intern/cycles/kernel/bvh/volume.h b/intern/cycles/kernel/bvh/volume.h index 9715712a8f2..bd4e508ecac 100644 --- a/intern/cycles/kernel/bvh/volume.h +++ b/intern/cycles/kernel/bvh/volume.h @@ -46,13 +46,14 @@ ccl_device_inline float3 P = ray->P; float3 dir = bvh_clamp_direction(ray->D); float3 idir = bvh_inverse_direction(dir); + float tmin = ray->tmin; int object = OBJECT_NONE; #if BVH_FEATURE(BVH_MOTION) Transform ob_itfm; #endif - isect->t = ray->t; + isect->t = ray->tmax; isect->u = 0.0f; isect->v = 0.0f; isect->prim = PRIM_NONE; @@ -73,6 +74,7 @@ ccl_device_inline dir, #endif idir, + tmin, isect->t, node_addr, visibility, @@ -140,7 +142,7 @@ ccl_device_inline continue; } triangle_intersect( - kg, isect, P, dir, isect->t, visibility, prim_object, prim, prim_addr); + kg, isect, P, dir, tmin, isect->t, visibility, prim_object, prim, prim_addr); } break; } @@ -165,6 +167,7 @@ ccl_device_inline isect, P, dir, + tmin, isect->t, ray->time, visibility, @@ -186,11 +189,15 @@ ccl_device_inline int object_flag = kernel_data_fetch(object_flag, object); if (object_flag & SD_OBJECT_HAS_VOLUME) { #if BVH_FEATURE(BVH_MOTION) - isect->t *= bvh_instance_motion_push(kg, object, ray, &P, &dir, &idir, &ob_itfm); + const float t_world_to_instance = bvh_instance_motion_push( + kg, object, ray, &P, &dir, &idir, &ob_itfm); #else - isect->t *= bvh_instance_push(kg, object, ray, &P, &dir, &idir); + const float t_world_to_instance = bvh_instance_push(kg, object, ray, &P, &dir, &idir); #endif + isect->t *= t_world_to_instance; + tmin *= t_world_to_instance; + ++stack_ptr; kernel_assert(stack_ptr < BVH_STACK_SIZE); traversal_stack[stack_ptr] = ENTRYPOINT_SENTINEL; @@ -217,6 +224,8 @@ ccl_device_inline isect->t = bvh_instance_pop(kg, object, ray, &P, &dir, &idir, isect->t); #endif + tmin = ray->tmin; + object = OBJECT_NONE; node_addr = traversal_stack[stack_ptr]; --stack_ptr; diff --git a/intern/cycles/kernel/bvh/volume_all.h b/intern/cycles/kernel/bvh/volume_all.h index d06ea8fe557..c6eeb07a14d 100644 --- a/intern/cycles/kernel/bvh/volume_all.h +++ b/intern/cycles/kernel/bvh/volume_all.h @@ -44,12 +44,12 @@ ccl_device_inline int node_addr = kernel_data.bvh.root; /* ray parameters in registers */ - const float tmax = ray->t; float3 P = ray->P; float3 dir = bvh_clamp_direction(ray->D); float3 idir = bvh_inverse_direction(dir); + float tmin = ray->tmin; int object = OBJECT_NONE; - float isect_t = tmax; + float isect_t = ray->tmax; #if BVH_FEATURE(BVH_MOTION) Transform ob_itfm; @@ -58,7 +58,7 @@ ccl_device_inline int num_hits_in_instance = 0; uint num_hits = 0; - isect_array->t = tmax; + isect_array->t = ray->tmax; /* traversal loop */ do { @@ -75,6 +75,7 @@ ccl_device_inline dir, #endif idir, + tmin, isect_t, node_addr, visibility, @@ -141,8 +142,16 @@ ccl_device_inline if ((object_flag & SD_OBJECT_HAS_VOLUME) == 0) { continue; } - hit = triangle_intersect( - kg, isect_array, P, dir, isect_t, visibility, prim_object, prim, prim_addr); + hit = triangle_intersect(kg, + isect_array, + P, + dir, + tmin, + isect_t, + visibility, + prim_object, + prim, + prim_addr); if (hit) { /* Move on to next entry in intersections array. */ isect_array++; @@ -189,6 +198,7 @@ ccl_device_inline isect_array, P, dir, + tmin, isect_t, ray->time, visibility, @@ -232,11 +242,15 @@ ccl_device_inline int object_flag = kernel_data_fetch(object_flag, object); if (object_flag & SD_OBJECT_HAS_VOLUME) { #if BVH_FEATURE(BVH_MOTION) - isect_t *= bvh_instance_motion_push(kg, object, ray, &P, &dir, &idir, &ob_itfm); + const float t_world_to_instance = bvh_instance_motion_push( + kg, object, ray, &P, &dir, &idir, &ob_itfm); #else - isect_t *= bvh_instance_push(kg, object, ray, &P, &dir, &idir); + const float t_world_to_instance = bvh_instance_push(kg, object, ray, &P, &dir, &idir); #endif + isect_t *= t_world_to_instance; + tmin *= t_world_to_instance; + num_hits_in_instance = 0; isect_array->t = isect_t; @@ -280,7 +294,8 @@ ccl_device_inline #endif } - isect_t = tmax; + tmin = ray->tmin; + isect_t = ray->tmax; isect_array->t = isect_t; object = OBJECT_NONE; diff --git a/intern/cycles/kernel/camera/camera.h b/intern/cycles/kernel/camera/camera.h index 25960a94ddb..926ccf7b86f 100644 --- a/intern/cycles/kernel/camera/camera.h +++ b/intern/cycles/kernel/camera/camera.h @@ -165,9 +165,11 @@ ccl_device void camera_sample_perspective(KernelGlobals kg, float nearclip = kernel_data.cam.nearclip * z_inv; ray->P += nearclip * ray->D; ray->dP += nearclip * ray->dD; - ray->t = kernel_data.cam.cliplength * z_inv; + ray->tmin = 0.0f; + ray->tmax = kernel_data.cam.cliplength * z_inv; #else - ray->t = FLT_MAX; + ray->tmin = 0.0f; + ray->tmax = FLT_MAX; #endif } @@ -231,9 +233,11 @@ ccl_device void camera_sample_orthographic(KernelGlobals kg, #ifdef __CAMERA_CLIPPING__ /* clipping */ - ray->t = kernel_data.cam.cliplength; + ray->tmin = 0.0f; + ray->tmax = kernel_data.cam.cliplength; #else - ray->t = FLT_MAX; + ray->tmin = 0.0f; + ray->tmax = FLT_MAX; #endif } @@ -258,7 +262,7 @@ ccl_device_inline void camera_sample_panorama(ccl_constant KernelCamera *cam, /* indicates ray should not receive any light, outside of the lens */ if (is_zero(D)) { - ray->t = 0.0f; + ray->tmax = 0.0f; return; } @@ -349,9 +353,11 @@ ccl_device_inline void camera_sample_panorama(ccl_constant KernelCamera *cam, float nearclip = cam->nearclip; ray->P += nearclip * ray->D; ray->dP += nearclip * ray->dD; - ray->t = cam->cliplength; + ray->tmin = 0.0f; + ray->tmax = cam->cliplength; #else - ray->t = FLT_MAX; + ray->tmin = 0.0f; + ray->tmax = FLT_MAX; #endif } @@ -368,7 +374,7 @@ ccl_device_inline void camera_sample(KernelGlobals kg, ccl_private Ray *ray) { /* pixel filter */ - int filter_table_offset = kernel_data.film.filter_table_offset; + int filter_table_offset = kernel_data.tables.filter_table_offset; float raster_x = x + lookup_table_read(kg, filter_u, filter_table_offset, FILTER_TABLE_SIZE); float raster_y = y + lookup_table_read(kg, filter_v, filter_table_offset, FILTER_TABLE_SIZE); diff --git a/intern/cycles/kernel/data_template.h b/intern/cycles/kernel/data_template.h new file mode 100644 index 00000000000..b06ac62a5d8 --- /dev/null +++ b/intern/cycles/kernel/data_template.h @@ -0,0 +1,206 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2011-2022 Blender Foundation */ + +#ifndef KERNEL_STRUCT_BEGIN +# define KERNEL_STRUCT_BEGIN(name, parent) +#endif +#ifndef KERNEL_STRUCT_END +# define KERNEL_STRUCT_END(name) +#endif +#ifndef KERNEL_STRUCT_MEMBER +# define KERNEL_STRUCT_MEMBER(parent, type, name) +#endif + +/* Background. */ + +KERNEL_STRUCT_BEGIN(KernelBackground, background) +/* xyz store direction, w the angle. float4 instead of float3 is used + * to ensure consistent padding/alignment across devices. */ +KERNEL_STRUCT_MEMBER(background, float4, sun) +/* Only shader index. */ +KERNEL_STRUCT_MEMBER(background, int, surface_shader) +KERNEL_STRUCT_MEMBER(background, int, volume_shader) +KERNEL_STRUCT_MEMBER(background, float, volume_step_size) +KERNEL_STRUCT_MEMBER(background, int, transparent) +KERNEL_STRUCT_MEMBER(background, float, transparent_roughness_squared_threshold) +/* Portal sampling. */ +KERNEL_STRUCT_MEMBER(background, float, portal_weight) +KERNEL_STRUCT_MEMBER(background, int, num_portals) +KERNEL_STRUCT_MEMBER(background, int, portal_offset) +/* Sun sampling. */ +KERNEL_STRUCT_MEMBER(background, float, sun_weight) +/* Importance map sampling. */ +KERNEL_STRUCT_MEMBER(background, float, map_weight) +KERNEL_STRUCT_MEMBER(background, int, map_res_x) +KERNEL_STRUCT_MEMBER(background, int, map_res_y) +/* Multiple importance sampling. */ +KERNEL_STRUCT_MEMBER(background, int, use_mis) +/* Lightgroup. */ +KERNEL_STRUCT_MEMBER(background, int, lightgroup) +/* Padding. */ +KERNEL_STRUCT_MEMBER(background, int, pad1) +KERNEL_STRUCT_MEMBER(background, int, pad2) +KERNEL_STRUCT_MEMBER(background, int, pad3) +KERNEL_STRUCT_END(KernelBackground) + +/* BVH: own BVH2 if no native device acceleration struct used. */ + +KERNEL_STRUCT_BEGIN(KernelBVH, bvh) +KERNEL_STRUCT_MEMBER(bvh, int, root) +KERNEL_STRUCT_MEMBER(bvh, int, have_motion) +KERNEL_STRUCT_MEMBER(bvh, int, have_curves) +KERNEL_STRUCT_MEMBER(bvh, int, bvh_layout) +KERNEL_STRUCT_MEMBER(bvh, int, use_bvh_steps) +KERNEL_STRUCT_MEMBER(bvh, int, curve_subdivisions) +KERNEL_STRUCT_MEMBER(bvh, int, pad1) +KERNEL_STRUCT_MEMBER(bvh, int, pad2) +KERNEL_STRUCT_END(KernelBVH) + +/* Film. */ + +KERNEL_STRUCT_BEGIN(KernelFilm, film) +/* XYZ to rendering color space transform. float4 instead of float3 to + * ensure consistent padding/alignment across devices. */ +KERNEL_STRUCT_MEMBER(film, float4, xyz_to_r) +KERNEL_STRUCT_MEMBER(film, float4, xyz_to_g) +KERNEL_STRUCT_MEMBER(film, float4, xyz_to_b) +KERNEL_STRUCT_MEMBER(film, float4, rgb_to_y) +/* Rec709 to rendering color space. */ +KERNEL_STRUCT_MEMBER(film, float4, rec709_to_r) +KERNEL_STRUCT_MEMBER(film, float4, rec709_to_g) +KERNEL_STRUCT_MEMBER(film, float4, rec709_to_b) +KERNEL_STRUCT_MEMBER(film, int, is_rec709) +/* Exposuse. */ +KERNEL_STRUCT_MEMBER(film, float, exposure) +/* Passed used. */ +KERNEL_STRUCT_MEMBER(film, int, pass_flag) +KERNEL_STRUCT_MEMBER(film, int, light_pass_flag) +/* Pass offsets. */ +KERNEL_STRUCT_MEMBER(film, int, pass_stride) +KERNEL_STRUCT_MEMBER(film, int, pass_combined) +KERNEL_STRUCT_MEMBER(film, int, pass_depth) +KERNEL_STRUCT_MEMBER(film, int, pass_position) +KERNEL_STRUCT_MEMBER(film, int, pass_normal) +KERNEL_STRUCT_MEMBER(film, int, pass_roughness) +KERNEL_STRUCT_MEMBER(film, int, pass_motion) +KERNEL_STRUCT_MEMBER(film, int, pass_motion_weight) +KERNEL_STRUCT_MEMBER(film, int, pass_uv) +KERNEL_STRUCT_MEMBER(film, int, pass_object_id) +KERNEL_STRUCT_MEMBER(film, int, pass_material_id) +KERNEL_STRUCT_MEMBER(film, int, pass_diffuse_color) +KERNEL_STRUCT_MEMBER(film, int, pass_glossy_color) +KERNEL_STRUCT_MEMBER(film, int, pass_transmission_color) +KERNEL_STRUCT_MEMBER(film, int, pass_diffuse_indirect) +KERNEL_STRUCT_MEMBER(film, int, pass_glossy_indirect) +KERNEL_STRUCT_MEMBER(film, int, pass_transmission_indirect) +KERNEL_STRUCT_MEMBER(film, int, pass_volume_indirect) +KERNEL_STRUCT_MEMBER(film, int, pass_diffuse_direct) +KERNEL_STRUCT_MEMBER(film, int, pass_glossy_direct) +KERNEL_STRUCT_MEMBER(film, int, pass_transmission_direct) +KERNEL_STRUCT_MEMBER(film, int, pass_volume_direct) +KERNEL_STRUCT_MEMBER(film, int, pass_emission) +KERNEL_STRUCT_MEMBER(film, int, pass_background) +KERNEL_STRUCT_MEMBER(film, int, pass_ao) +KERNEL_STRUCT_MEMBER(film, float, pass_alpha_threshold) +KERNEL_STRUCT_MEMBER(film, int, pass_shadow) +KERNEL_STRUCT_MEMBER(film, float, pass_shadow_scale) +KERNEL_STRUCT_MEMBER(film, int, pass_shadow_catcher) +KERNEL_STRUCT_MEMBER(film, int, pass_shadow_catcher_sample_count) +KERNEL_STRUCT_MEMBER(film, int, pass_shadow_catcher_matte) +/* Cryptomatte. */ +KERNEL_STRUCT_MEMBER(film, int, cryptomatte_passes) +KERNEL_STRUCT_MEMBER(film, int, cryptomatte_depth) +KERNEL_STRUCT_MEMBER(film, int, pass_cryptomatte) +/* Adaptive sampling. */ +KERNEL_STRUCT_MEMBER(film, int, pass_adaptive_aux_buffer) +KERNEL_STRUCT_MEMBER(film, int, pass_sample_count) +/* Mist. */ +KERNEL_STRUCT_MEMBER(film, int, pass_mist) +KERNEL_STRUCT_MEMBER(film, float, mist_start) +KERNEL_STRUCT_MEMBER(film, float, mist_inv_depth) +KERNEL_STRUCT_MEMBER(film, float, mist_falloff) +/* Denoising. */ +KERNEL_STRUCT_MEMBER(film, int, pass_denoising_normal) +KERNEL_STRUCT_MEMBER(film, int, pass_denoising_albedo) +KERNEL_STRUCT_MEMBER(film, int, pass_denoising_depth) +/* AOVs. */ +KERNEL_STRUCT_MEMBER(film, int, pass_aov_color) +KERNEL_STRUCT_MEMBER(film, int, pass_aov_value) +/* Light groups. */ +KERNEL_STRUCT_MEMBER(film, int, pass_lightgroup) +/* Baking. */ +KERNEL_STRUCT_MEMBER(film, int, pass_bake_primitive) +KERNEL_STRUCT_MEMBER(film, int, pass_bake_differential) +/* Shadow catcher. */ +KERNEL_STRUCT_MEMBER(film, int, use_approximate_shadow_catcher) +/* Padding. */ +KERNEL_STRUCT_MEMBER(film, int, pad1) +KERNEL_STRUCT_MEMBER(film, int, pad2) +KERNEL_STRUCT_END(KernelFilm) + +/* Integrator. */ + +KERNEL_STRUCT_BEGIN(KernelIntegrator, integrator) +/* Emission. */ +KERNEL_STRUCT_MEMBER(integrator, int, use_direct_light) +KERNEL_STRUCT_MEMBER(integrator, int, num_distribution) +KERNEL_STRUCT_MEMBER(integrator, int, num_all_lights) +KERNEL_STRUCT_MEMBER(integrator, float, pdf_triangles) +KERNEL_STRUCT_MEMBER(integrator, float, pdf_lights) +KERNEL_STRUCT_MEMBER(integrator, float, light_inv_rr_threshold) +/* Bounces. */ +KERNEL_STRUCT_MEMBER(integrator, int, min_bounce) +KERNEL_STRUCT_MEMBER(integrator, int, max_bounce) +KERNEL_STRUCT_MEMBER(integrator, int, max_diffuse_bounce) +KERNEL_STRUCT_MEMBER(integrator, int, max_glossy_bounce) +KERNEL_STRUCT_MEMBER(integrator, int, max_transmission_bounce) +KERNEL_STRUCT_MEMBER(integrator, int, max_volume_bounce) +/* AO bounces. */ +KERNEL_STRUCT_MEMBER(integrator, int, ao_bounces) +KERNEL_STRUCT_MEMBER(integrator, float, ao_bounces_distance) +KERNEL_STRUCT_MEMBER(integrator, float, ao_bounces_factor) +KERNEL_STRUCT_MEMBER(integrator, float, ao_additive_factor) +/* Transparency. */ +KERNEL_STRUCT_MEMBER(integrator, int, transparent_min_bounce) +KERNEL_STRUCT_MEMBER(integrator, int, transparent_max_bounce) +KERNEL_STRUCT_MEMBER(integrator, int, transparent_shadows) +/* Caustics. */ +KERNEL_STRUCT_MEMBER(integrator, int, caustics_reflective) +KERNEL_STRUCT_MEMBER(integrator, int, caustics_refractive) +KERNEL_STRUCT_MEMBER(integrator, float, filter_glossy) +/* Seed. */ +KERNEL_STRUCT_MEMBER(integrator, int, seed) +/* Clamp. */ +KERNEL_STRUCT_MEMBER(integrator, float, sample_clamp_direct) +KERNEL_STRUCT_MEMBER(integrator, float, sample_clamp_indirect) +/* MIS. */ +KERNEL_STRUCT_MEMBER(integrator, int, use_lamp_mis) +/* Caustics. */ +KERNEL_STRUCT_MEMBER(integrator, int, use_caustics) +/* Sampling pattern. */ +KERNEL_STRUCT_MEMBER(integrator, int, sampling_pattern) +KERNEL_STRUCT_MEMBER(integrator, float, scrambling_distance) +/* Volume render. */ +KERNEL_STRUCT_MEMBER(integrator, int, use_volumes) +KERNEL_STRUCT_MEMBER(integrator, int, volume_max_steps) +KERNEL_STRUCT_MEMBER(integrator, float, volume_step_rate) +/* Shadow catcher. */ +KERNEL_STRUCT_MEMBER(integrator, int, has_shadow_catcher) +/* Closure filter. */ +KERNEL_STRUCT_MEMBER(integrator, int, filter_closures) +/* MIS debugging. */ +KERNEL_STRUCT_MEMBER(integrator, int, direct_light_sampling_type) +/* Padding */ +KERNEL_STRUCT_MEMBER(integrator, int, pad1) +KERNEL_STRUCT_END(KernelIntegrator) + +/* SVM. For shader specialization. */ + +KERNEL_STRUCT_BEGIN(KernelSVMUsage, svm_usage) +#define SHADER_NODE_TYPE(type) KERNEL_STRUCT_MEMBER(svm_usage, int, type) +#include "kernel/svm/node_types_template.h" +KERNEL_STRUCT_END(KernelSVMUsage) + +#undef KERNEL_STRUCT_BEGIN +#undef KERNEL_STRUCT_MEMBER +#undef KERNEL_STRUCT_END diff --git a/intern/cycles/kernel/device/gpu/kernel.h b/intern/cycles/kernel/device/gpu/kernel.h index b9a44ccad02..e1ab802aa80 100644 --- a/intern/cycles/kernel/device/gpu/kernel.h +++ b/intern/cycles/kernel/device/gpu/kernel.h @@ -246,7 +246,7 @@ ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) ccl_gpu_kernel_postfix #if defined(__KERNEL_METAL_APPLE__) && defined(__METALRT__) -constant int __dummy_constant [[function_constant(0)]]; +constant int __dummy_constant [[function_constant(Kernel_DummyConstant)]]; #endif ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) diff --git a/intern/cycles/kernel/device/metal/function_constants.h b/intern/cycles/kernel/device/metal/function_constants.h new file mode 100644 index 00000000000..3adf390c7f6 --- /dev/null +++ b/intern/cycles/kernel/device/metal/function_constants.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2021-2022 Blender Foundation */ + +enum { + Kernel_DummyConstant, +#define KERNEL_STRUCT_MEMBER(parent, type, name) KernelData_##parent##_##name, +#include "kernel/data_template.h" +}; + +#ifdef __KERNEL_METAL__ +# define KERNEL_STRUCT_MEMBER(parent, type, name) \ + constant type kernel_data_##parent##_##name \ + [[function_constant(KernelData_##parent##_##name)]]; +# include "kernel/data_template.h" +#endif diff --git a/intern/cycles/kernel/device/metal/kernel.metal b/intern/cycles/kernel/device/metal/kernel.metal index 3c31dc3354c..764c26dbe8f 100644 --- a/intern/cycles/kernel/device/metal/kernel.metal +++ b/intern/cycles/kernel/device/metal/kernel.metal @@ -5,6 +5,7 @@ #include "kernel/device/metal/compat.h" #include "kernel/device/metal/globals.h" +#include "kernel/device/metal/function_constants.h" #include "kernel/device/gpu/kernel.h" /* MetalRT intersection handlers */ @@ -409,6 +410,7 @@ void metalrt_intersection_curve(constant KernelParamsMetal &launch_params_metal, const float3 ray_origin, const float3 ray_direction, float time, + const float ray_tmin, const float ray_tmax, thread BoundingBoxIntersectionResult &result) { @@ -433,7 +435,7 @@ void metalrt_intersection_curve(constant KernelParamsMetal &launch_params_metal, isect.t *= len; MetalKernelContext context(launch_params_metal); - if (context.curve_intersect(NULL, &isect, P, dir, isect.t, object, prim, time, type)) { + if (context.curve_intersect(NULL, &isect, P, dir, ray_tmin, isect.t, object, prim, time, type)) { result = metalrt_visibility_test<BoundingBoxIntersectionResult, METALRT_HIT_BOUNDING_BOX>( launch_params_metal, payload, object, prim, isect.u); if (result.accept) { @@ -455,6 +457,7 @@ void metalrt_intersection_curve_shadow(constant KernelParamsMetal &launch_params const float3 ray_origin, const float3 ray_direction, float time, + const float ray_tmin, const float ray_tmax, thread BoundingBoxIntersectionResult &result) { @@ -474,7 +477,7 @@ void metalrt_intersection_curve_shadow(constant KernelParamsMetal &launch_params isect.t *= len; MetalKernelContext context(launch_params_metal); - if (context.curve_intersect(NULL, &isect, P, dir, isect.t, object, prim, time, type)) { + if (context.curve_intersect(NULL, &isect, P, dir, ray_tmin, isect.t, object, prim, time, type)) { result.continue_search = metalrt_shadow_all_hit<METALRT_HIT_BOUNDING_BOX>( launch_params_metal, payload, object, prim, float2(isect.u, isect.v), ray_tmax); result.accept = !result.continue_search; @@ -493,6 +496,7 @@ __intersection__curve_ribbon(constant KernelParamsMetal &launch_params_metal [[b const uint primitive_id [[primitive_id]], const float3 ray_origin [[origin]], const float3 ray_direction [[direction]], + const float ray_tmin [[min_distance]], const float ray_tmax [[max_distance]]) { uint prim = primitive_id + kernel_data_fetch(object_prim_offset, object); @@ -510,7 +514,7 @@ __intersection__curve_ribbon(constant KernelParamsMetal &launch_params_metal [[b # else 0.0f, # endif - ray_tmax, result); + ray_tmin, ray_tmax, result); } return result; @@ -524,6 +528,7 @@ __intersection__curve_ribbon_shadow(constant KernelParamsMetal &launch_params_me const uint primitive_id [[primitive_id]], const float3 ray_origin [[origin]], const float3 ray_direction [[direction]], + const float ray_tmin [[min_distance]], const float ray_tmax [[max_distance]]) { uint prim = primitive_id + kernel_data_fetch(object_prim_offset, object); @@ -541,7 +546,7 @@ __intersection__curve_ribbon_shadow(constant KernelParamsMetal &launch_params_me # else 0.0f, # endif - ray_tmax, result); + ray_tmin, ray_tmax, result); } return result; @@ -555,6 +560,7 @@ __intersection__curve_all(constant KernelParamsMetal &launch_params_metal [[buff const uint primitive_id [[primitive_id]], const float3 ray_origin [[origin]], const float3 ray_direction [[direction]], + const float ray_tmin [[min_distance]], const float ray_tmax [[max_distance]]) { uint prim = primitive_id + kernel_data_fetch(object_prim_offset, object); @@ -570,7 +576,7 @@ __intersection__curve_all(constant KernelParamsMetal &launch_params_metal [[buff # else 0.0f, # endif - ray_tmax, result); + ray_tmin, ray_tmax, result); return result; } @@ -583,6 +589,7 @@ __intersection__curve_all_shadow(constant KernelParamsMetal &launch_params_metal const uint primitive_id [[primitive_id]], const float3 ray_origin [[origin]], const float3 ray_direction [[direction]], + const float ray_tmin [[min_distance]], const float ray_tmax [[max_distance]]) { uint prim = primitive_id + kernel_data_fetch(object_prim_offset, object); @@ -599,7 +606,7 @@ __intersection__curve_all_shadow(constant KernelParamsMetal &launch_params_metal # else 0.0f, # endif - ray_tmax, result); + ray_tmin, ray_tmax, result); return result; } @@ -615,6 +622,7 @@ void metalrt_intersection_point(constant KernelParamsMetal &launch_params_metal, const float3 ray_origin, const float3 ray_direction, float time, + const float ray_tmin, const float ray_tmax, thread BoundingBoxIntersectionResult &result) { @@ -639,7 +647,7 @@ void metalrt_intersection_point(constant KernelParamsMetal &launch_params_metal, isect.t *= len; MetalKernelContext context(launch_params_metal); - if (context.point_intersect(NULL, &isect, P, dir, isect.t, object, prim, time, type)) { + if (context.point_intersect(NULL, &isect, P, dir, ray_tmin, isect.t, object, prim, time, type)) { result = metalrt_visibility_test<BoundingBoxIntersectionResult, METALRT_HIT_BOUNDING_BOX>( launch_params_metal, payload, object, prim, isect.u); if (result.accept) { @@ -661,6 +669,7 @@ void metalrt_intersection_point_shadow(constant KernelParamsMetal &launch_params const float3 ray_origin, const float3 ray_direction, float time, + const float ray_tmin, const float ray_tmax, thread BoundingBoxIntersectionResult &result) { @@ -680,7 +689,7 @@ void metalrt_intersection_point_shadow(constant KernelParamsMetal &launch_params isect.t *= len; MetalKernelContext context(launch_params_metal); - if (context.point_intersect(NULL, &isect, P, dir, isect.t, object, prim, time, type)) { + if (context.point_intersect(NULL, &isect, P, dir, ray_tmin, isect.t, object, prim, time, type)) { result.continue_search = metalrt_shadow_all_hit<METALRT_HIT_BOUNDING_BOX>( launch_params_metal, payload, object, prim, float2(isect.u, isect.v), ray_tmax); result.accept = !result.continue_search; @@ -699,6 +708,7 @@ __intersection__point(constant KernelParamsMetal &launch_params_metal [[buffer(1 const uint primitive_id [[primitive_id]], const float3 ray_origin [[origin]], const float3 ray_direction [[direction]], + const float ray_tmin [[min_distance]], const float ray_tmax [[max_distance]]) { const uint prim = primitive_id + kernel_data_fetch(object_prim_offset, object); @@ -715,7 +725,7 @@ __intersection__point(constant KernelParamsMetal &launch_params_metal [[buffer(1 # else 0.0f, # endif - ray_tmax, result); + ray_tmin, ray_tmax, result); return result; } @@ -728,6 +738,7 @@ __intersection__point_shadow(constant KernelParamsMetal &launch_params_metal [[b const uint primitive_id [[primitive_id]], const float3 ray_origin [[origin]], const float3 ray_direction [[direction]], + const float ray_tmin [[min_distance]], const float ray_tmax [[max_distance]]) { const uint prim = primitive_id + kernel_data_fetch(object_prim_offset, object); @@ -744,7 +755,7 @@ __intersection__point_shadow(constant KernelParamsMetal &launch_params_metal [[b # else 0.0f, # endif - ray_tmax, result); + ray_tmin, ray_tmax, result); return result; } diff --git a/intern/cycles/kernel/device/oneapi/kernel.cpp b/intern/cycles/kernel/device/oneapi/kernel.cpp index 82910d72105..300e201600c 100644 --- a/intern/cycles/kernel/device/oneapi/kernel.cpp +++ b/intern/cycles/kernel/device/oneapi/kernel.cpp @@ -670,10 +670,7 @@ bool oneapi_enqueue_kernel(KernelContext *kernel_context, } static const int lowest_supported_driver_version_win = 1011660; -/* TODO: once Linux JIT compilation crash from CentOS generated spv is fixed, adjust version below. - * Until then, set CYCLES_ONEAPI_ALL_DEVICES environment variable to avoid getting it filtered out. - */ -static const int lowest_supported_driver_version_neo = 28000; +static const int lowest_supported_driver_version_neo = 23570; static int parse_driver_build_version(const sycl::device &device) { diff --git a/intern/cycles/kernel/device/optix/kernel.cu b/intern/cycles/kernel/device/optix/kernel.cu index 949bf41d171..510f7cca5d6 100644 --- a/intern/cycles/kernel/device/optix/kernel.cu +++ b/intern/cycles/kernel/device/optix/kernel.cu @@ -51,32 +51,36 @@ ccl_device_forceinline int get_object_id() extern "C" __global__ void __raygen__kernel_optix_integrator_intersect_closest() { const int global_index = optixGetLaunchIndex().x; - const int path_index = (kernel_params.path_index_array) ? kernel_params.path_index_array[global_index] : - global_index; + const int path_index = (kernel_params.path_index_array) ? + kernel_params.path_index_array[global_index] : + global_index; integrator_intersect_closest(nullptr, path_index, kernel_params.render_buffer); } extern "C" __global__ void __raygen__kernel_optix_integrator_intersect_shadow() { const int global_index = optixGetLaunchIndex().x; - const int path_index = (kernel_params.path_index_array) ? kernel_params.path_index_array[global_index] : - global_index; + const int path_index = (kernel_params.path_index_array) ? + kernel_params.path_index_array[global_index] : + global_index; integrator_intersect_shadow(nullptr, path_index); } extern "C" __global__ void __raygen__kernel_optix_integrator_intersect_subsurface() { const int global_index = optixGetLaunchIndex().x; - const int path_index = (kernel_params.path_index_array) ? kernel_params.path_index_array[global_index] : - global_index; + const int path_index = (kernel_params.path_index_array) ? + kernel_params.path_index_array[global_index] : + global_index; integrator_intersect_subsurface(nullptr, path_index); } extern "C" __global__ void __raygen__kernel_optix_integrator_intersect_volume_stack() { const int global_index = optixGetLaunchIndex().x; - const int path_index = (kernel_params.path_index_array) ? kernel_params.path_index_array[global_index] : - global_index; + const int path_index = (kernel_params.path_index_array) ? + kernel_params.path_index_array[global_index] : + global_index; integrator_intersect_volume_stack(nullptr, path_index); } @@ -408,6 +412,7 @@ ccl_device_inline void optix_intersection_curve(const int prim, const int type) float3 P = optixGetObjectRayOrigin(); float3 dir = optixGetObjectRayDirection(); + float tmin = optixGetRayTmin(); /* The direction is not normalized by default, but the curve intersection routine expects that */ float len; @@ -425,7 +430,7 @@ ccl_device_inline void optix_intersection_curve(const int prim, const int type) if (isect.t != FLT_MAX) isect.t *= len; - if (curve_intersect(NULL, &isect, P, dir, isect.t, object, prim, time, type)) { + if (curve_intersect(NULL, &isect, P, dir, tmin, isect.t, object, prim, time, type)) { static_assert(PRIMITIVE_ALL < 128, "Values >= 128 are reserved for OptiX internal use"); optixReportIntersection(isect.t / len, type & PRIMITIVE_ALL, @@ -462,6 +467,7 @@ extern "C" __global__ void __intersection__point() float3 P = optixGetObjectRayOrigin(); float3 dir = optixGetObjectRayDirection(); + float tmin = optixGetRayTmin(); /* The direction is not normalized by default, the point intersection routine expects that. */ float len; @@ -480,7 +486,7 @@ extern "C" __global__ void __intersection__point() isect.t *= len; } - if (point_intersect(NULL, &isect, P, dir, isect.t, object, prim, time, type)) { + if (point_intersect(NULL, &isect, P, dir, tmin, isect.t, object, prim, time, type)) { static_assert(PRIMITIVE_ALL < 128, "Values >= 128 are reserved for OptiX internal use"); optixReportIntersection(isect.t / len, type & PRIMITIVE_ALL); } diff --git a/intern/cycles/kernel/geom/curve_intersect.h b/intern/cycles/kernel/geom/curve_intersect.h index 001bec01749..9770105dd81 100644 --- a/intern/cycles/kernel/geom/curve_intersect.h +++ b/intern/cycles/kernel/geom/curve_intersect.h @@ -156,7 +156,8 @@ ccl_device_inline float2 half_plane_intersect(const float3 P, const float3 N, co } ccl_device bool curve_intersect_iterative(const float3 ray_dir, - ccl_private float *ray_tfar, + const float ray_tmin, + ccl_private float *ray_tmax, const float dt, const float4 curve[4], float u, @@ -220,7 +221,7 @@ ccl_device bool curve_intersect_iterative(const float3 ray_dir, if (fabsf(f) < f_err && fabsf(g) < g_err) { t += dt; - if (!(0.0f <= t && t <= *ray_tfar)) { + if (!(t >= ray_tmin && t <= *ray_tmax)) { return false; /* Rejects NaNs */ } if (!(u >= 0.0f && u <= 1.0f)) { @@ -237,7 +238,7 @@ ccl_device bool curve_intersect_iterative(const float3 ray_dir, } /* Record intersection. */ - *ray_tfar = t; + *ray_tmax = t; isect->t = t; isect->u = u; isect->v = 0.0f; @@ -250,7 +251,8 @@ ccl_device bool curve_intersect_iterative(const float3 ray_dir, ccl_device bool curve_intersect_recursive(const float3 ray_orig, const float3 ray_dir, - float ray_tfar, + const float ray_tmin, + float ray_tmax, float4 curve[4], ccl_private Intersection *isect) { @@ -331,7 +333,7 @@ ccl_device bool curve_intersect_recursive(const float3 ray_orig, } /* Intersect with cap-planes. */ - float2 tp = make_float2(-dt, ray_tfar - dt); + float2 tp = make_float2(ray_tmin - dt, ray_tmax - dt); tp = make_float2(max(tp.x, tc_outer.x), min(tp.y, tc_outer.y)); const float2 h0 = half_plane_intersect( float4_to_float3(P0), float4_to_float3(dP0du), ray_dir); @@ -394,19 +396,20 @@ ccl_device bool curve_intersect_recursive(const float3 ray_orig, CURVE_NUM_BEZIER_SUBDIVISIONS; if (depth >= termDepth) { found |= curve_intersect_iterative( - ray_dir, &ray_tfar, dt, curve, u_outer0, tp0.x, use_backfacing, isect); + ray_dir, ray_tmin, &ray_tmax, dt, curve, u_outer0, tp0.x, use_backfacing, isect); } else { recurse = true; } } - if (valid1 && (tp1.x + dt <= ray_tfar)) { + const float t1 = tp1.x + dt; + if (valid1 && (t1 >= ray_tmin && t1 <= ray_tmax)) { const int termDepth = unstable1 ? CURVE_NUM_BEZIER_SUBDIVISIONS_UNSTABLE : CURVE_NUM_BEZIER_SUBDIVISIONS; if (depth >= termDepth) { found |= curve_intersect_iterative( - ray_dir, &ray_tfar, dt, curve, u_outer1, tp1.y, use_backfacing, isect); + ray_dir, ray_tmin, &ray_tmax, dt, curve, u_outer1, tp1.y, use_backfacing, isect); } else { recurse = true; @@ -456,7 +459,8 @@ ccl_device_inline bool cylinder_culling_test(const float2 p1, const float2 p2, c * v0,v1,v3 and v2,v3,v1. The edge v1,v2 decides which of the two * triangles gets intersected. */ -ccl_device_inline bool ribbon_intersect_quad(const float ray_tfar, +ccl_device_inline bool ribbon_intersect_quad(const float ray_tmin, + const float ray_tmax, const float3 quad_v0, const float3 quad_v1, const float3 quad_v2, @@ -497,7 +501,7 @@ ccl_device_inline bool ribbon_intersect_quad(const float ray_tfar, /* Perform depth test? */ const float t = rcpDen * dot(v0, Ng); - if (!(0.0f <= t && t <= ray_tfar)) { + if (!(t >= ray_tmin && t <= ray_tmax)) { return false; } @@ -534,7 +538,8 @@ ccl_device_inline float4 ribbon_to_ray_space(const float3 ray_space[3], ccl_device_inline bool ribbon_intersect(const float3 ray_org, const float3 ray_dir, - float ray_tfar, + const float ray_tmin, + float ray_tmax, const int N, float4 curve[4], ccl_private Intersection *isect) @@ -582,7 +587,7 @@ ccl_device_inline bool ribbon_intersect(const float3 ray_org, /* Intersect quad. */ float vu, vv, vt; - bool valid0 = ribbon_intersect_quad(ray_tfar, lp0, lp1, up1, up0, &vu, &vv, &vt); + bool valid0 = ribbon_intersect_quad(ray_tmin, ray_tmax, lp0, lp1, up1, up0, &vu, &vv, &vt); if (valid0) { /* ignore self intersections */ @@ -596,7 +601,7 @@ ccl_device_inline bool ribbon_intersect(const float3 ray_org, vv = 2.0f * vv - 1.0f; /* Record intersection. */ - ray_tfar = vt; + ray_tmax = vt; isect->t = vt; isect->u = u + vu * step_size; isect->v = vv; @@ -616,6 +621,7 @@ ccl_device_forceinline bool curve_intersect(KernelGlobals kg, ccl_private Intersection *isect, const float3 P, const float3 dir, + const float tmin, const float tmax, int object, int prim, @@ -645,7 +651,7 @@ ccl_device_forceinline bool curve_intersect(KernelGlobals kg, if (type & PRIMITIVE_CURVE_RIBBON) { /* todo: adaptive number of subdivisions could help performance here. */ const int subdivisions = kernel_data.bvh.curve_subdivisions; - if (ribbon_intersect(P, dir, tmax, subdivisions, curve, isect)) { + if (ribbon_intersect(P, dir, tmin, tmax, subdivisions, curve, isect)) { isect->prim = prim; isect->object = object; isect->type = type; @@ -655,7 +661,7 @@ ccl_device_forceinline bool curve_intersect(KernelGlobals kg, return false; } else { - if (curve_intersect_recursive(P, dir, tmax, curve, isect)) { + if (curve_intersect_recursive(P, dir, tmin, tmax, curve, isect)) { isect->prim = prim; isect->object = object; isect->type = type; diff --git a/intern/cycles/kernel/geom/motion_triangle_intersect.h b/intern/cycles/kernel/geom/motion_triangle_intersect.h index 6eea5096567..b59c5c43c20 100644 --- a/intern/cycles/kernel/geom/motion_triangle_intersect.h +++ b/intern/cycles/kernel/geom/motion_triangle_intersect.h @@ -46,6 +46,7 @@ ccl_device_inline bool motion_triangle_intersect(KernelGlobals kg, ccl_private Intersection *isect, float3 P, float3 dir, + float tmin, float tmax, float time, uint visibility, @@ -58,7 +59,7 @@ ccl_device_inline bool motion_triangle_intersect(KernelGlobals kg, motion_triangle_vertices(kg, object, prim, time, verts); /* Ray-triangle intersection, unoptimized. */ float t, u, v; - if (ray_triangle_intersect(P, dir, tmax, verts[0], verts[1], verts[2], &u, &v, &t)) { + if (ray_triangle_intersect(P, dir, tmin, tmax, verts[0], verts[1], verts[2], &u, &v, &t)) { #ifdef __VISIBILITY_FLAG__ /* Visibility flag test. we do it here under the assumption * that most triangles are culled by node flags. @@ -92,6 +93,7 @@ ccl_device_inline bool motion_triangle_intersect_local(KernelGlobals kg, int object, int prim, int prim_addr, + float tmin, float tmax, ccl_private uint *lcg_state, int max_hits) @@ -101,7 +103,7 @@ ccl_device_inline bool motion_triangle_intersect_local(KernelGlobals kg, motion_triangle_vertices(kg, object, prim, time, verts); /* Ray-triangle intersection, unoptimized. */ float t, u, v; - if (!ray_triangle_intersect(P, dir, tmax, verts[0], verts[1], verts[2], &u, &v, &t)) { + if (!ray_triangle_intersect(P, dir, tmin, tmax, verts[0], verts[1], verts[2], &u, &v, &t)) { return false; } diff --git a/intern/cycles/kernel/geom/point_intersect.h b/intern/cycles/kernel/geom/point_intersect.h index dfd9d9a015b..ee5a564947b 100644 --- a/intern/cycles/kernel/geom/point_intersect.h +++ b/intern/cycles/kernel/geom/point_intersect.h @@ -9,8 +9,12 @@ CCL_NAMESPACE_BEGIN #ifdef __POINTCLOUD__ -ccl_device_forceinline bool point_intersect_test( - const float4 point, const float3 P, const float3 dir, const float tmax, ccl_private float *t) +ccl_device_forceinline bool point_intersect_test(const float4 point, + const float3 P, + const float3 dir, + const float tmin, + const float tmax, + ccl_private float *t) { const float3 center = float4_to_float3(point); const float radius = point.w; @@ -28,12 +32,12 @@ ccl_device_forceinline bool point_intersect_test( const float td = sqrt((r2 - l2) * rd2); const float t_front = projC0 - td; - const bool valid_front = (0.0f <= t_front) & (t_front <= tmax); + const bool valid_front = (tmin <= t_front) & (t_front <= tmax); /* Always back-face culling for now. */ # if 0 const float t_back = projC0 + td; - const bool valid_back = (0.0f <= t_back) & (t_back <= tmax); + const bool valid_back = (tmin <= t_back) & (t_back <= tmax); /* check if there is a first hit */ const bool valid_first = valid_front | valid_back; @@ -56,6 +60,7 @@ ccl_device_forceinline bool point_intersect(KernelGlobals kg, ccl_private Intersection *isect, const float3 P, const float3 dir, + const float tmin, const float tmax, const int object, const int prim, @@ -65,7 +70,7 @@ ccl_device_forceinline bool point_intersect(KernelGlobals kg, const float4 point = (type & PRIMITIVE_MOTION) ? motion_point(kg, object, prim, time) : kernel_data_fetch(points, prim); - if (!point_intersect_test(point, P, dir, tmax, &isect->t)) { + if (!point_intersect_test(point, P, dir, tmin, tmax, &isect->t)) { return false; } diff --git a/intern/cycles/kernel/geom/shader_data.h b/intern/cycles/kernel/geom/shader_data.h index e5dbeac5e66..99b9289cb4a 100644 --- a/intern/cycles/kernel/geom/shader_data.h +++ b/intern/cycles/kernel/geom/shader_data.h @@ -407,7 +407,7 @@ ccl_device_inline void shader_setup_from_volume(KernelGlobals kg, { /* vectors */ - sd->P = ray->P; + sd->P = ray->P + ray->D * ray->tmin; sd->N = -ray->D; sd->Ng = -ray->D; sd->I = -ray->D; @@ -441,7 +441,6 @@ ccl_device_inline void shader_setup_from_volume(KernelGlobals kg, /* for NDC coordinates */ sd->ray_P = ray->P; - sd->ray_dP = ray->dP; } #endif /* __VOLUME__ */ diff --git a/intern/cycles/kernel/geom/triangle_intersect.h b/intern/cycles/kernel/geom/triangle_intersect.h index 0c76de9ccc7..f968e537cfa 100644 --- a/intern/cycles/kernel/geom/triangle_intersect.h +++ b/intern/cycles/kernel/geom/triangle_intersect.h @@ -17,6 +17,7 @@ ccl_device_inline bool triangle_intersect(KernelGlobals kg, ccl_private Intersection *isect, float3 P, float3 dir, + float tmin, float tmax, uint visibility, int object, @@ -28,7 +29,7 @@ ccl_device_inline bool triangle_intersect(KernelGlobals kg, tri_b = kernel_data_fetch(tri_verts, tri_vindex + 1), tri_c = kernel_data_fetch(tri_verts, tri_vindex + 2); float t, u, v; - if (ray_triangle_intersect(P, dir, tmax, tri_a, tri_b, tri_c, &u, &v, &t)) { + if (ray_triangle_intersect(P, dir, tmin, tmax, tri_a, tri_b, tri_c, &u, &v, &t)) { #ifdef __VISIBILITY_FLAG__ /* Visibility flag test. we do it here under the assumption * that most triangles are culled by node flags. @@ -62,6 +63,7 @@ ccl_device_inline bool triangle_intersect_local(KernelGlobals kg, int object, int prim, int prim_addr, + float tmin, float tmax, ccl_private uint *lcg_state, int max_hits) @@ -71,7 +73,7 @@ ccl_device_inline bool triangle_intersect_local(KernelGlobals kg, tri_b = kernel_data_fetch(tri_verts, tri_vindex + 1), tri_c = kernel_data_fetch(tri_verts, tri_vindex + 2); float t, u, v; - if (!ray_triangle_intersect(P, dir, tmax, tri_a, tri_b, tri_c, &u, &v, &t)) { + if (!ray_triangle_intersect(P, dir, tmin, tmax, tri_a, tri_b, tri_c, &u, &v, &t)) { return false; } diff --git a/intern/cycles/kernel/integrator/init_from_bake.h b/intern/cycles/kernel/integrator/init_from_bake.h index c63684d58e6..bf3f41b52b9 100644 --- a/intern/cycles/kernel/integrator/init_from_bake.h +++ b/intern/cycles/kernel/integrator/init_from_bake.h @@ -174,14 +174,15 @@ ccl_device bool integrator_init_from_bake(KernelGlobals kg, Ray ray ccl_optional_struct_init; ray.P = zero_float3(); ray.D = normalize(P); - ray.t = FLT_MAX; + ray.tmin = 0.0f; + ray.tmax = FLT_MAX; ray.time = 0.5f; ray.dP = differential_zero_compact(); ray.dD = differential_zero_compact(); integrator_state_write_ray(kg, state, &ray); /* Setup next kernel to execute. */ - INTEGRATOR_PATH_INIT(DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND); + integrator_path_init(kg, state, DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND); } else { /* Surface baking. */ @@ -210,7 +211,8 @@ ccl_device bool integrator_init_from_bake(KernelGlobals kg, Ray ray ccl_optional_struct_init; ray.P = P + N; ray.D = -N; - ray.t = FLT_MAX; + ray.tmin = 0.0f; + ray.tmax = FLT_MAX; ray.time = 0.5f; /* Setup differentials. */ @@ -247,13 +249,15 @@ ccl_device bool integrator_init_from_bake(KernelGlobals kg, const bool use_raytrace_kernel = (shader_flags & SD_HAS_RAYTRACE); if (use_caustics) { - INTEGRATOR_PATH_INIT_SORTED(DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_MNEE, shader_index); + integrator_path_init_sorted( + kg, state, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_MNEE, shader_index); } else if (use_raytrace_kernel) { - INTEGRATOR_PATH_INIT_SORTED(DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE, shader_index); + integrator_path_init_sorted( + kg, state, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE, shader_index); } else { - INTEGRATOR_PATH_INIT_SORTED(DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE, shader_index); + integrator_path_init_sorted(kg, state, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE, shader_index); } } diff --git a/intern/cycles/kernel/integrator/init_from_camera.h b/intern/cycles/kernel/integrator/init_from_camera.h index 9fe27cdda9a..e89ab3991c7 100644 --- a/intern/cycles/kernel/integrator/init_from_camera.h +++ b/intern/cycles/kernel/integrator/init_from_camera.h @@ -86,7 +86,7 @@ ccl_device bool integrator_init_from_camera(KernelGlobals kg, /* Generate camera ray. */ Ray ray; integrate_camera_sample(kg, sample, x, y, rng_hash, &ray); - if (ray.t == 0.0f) { + if (ray.tmax == 0.0f) { return true; } @@ -100,10 +100,10 @@ ccl_device bool integrator_init_from_camera(KernelGlobals kg, /* Continue with intersect_closest kernel, optionally initializing volume * stack before that if the camera may be inside a volume. */ if (kernel_data.cam.is_inside_volume) { - INTEGRATOR_PATH_INIT(DEVICE_KERNEL_INTEGRATOR_INTERSECT_VOLUME_STACK); + integrator_path_init(kg, state, DEVICE_KERNEL_INTEGRATOR_INTERSECT_VOLUME_STACK); } else { - INTEGRATOR_PATH_INIT(DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST); + integrator_path_init(kg, state, DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST); } return true; diff --git a/intern/cycles/kernel/integrator/intersect_closest.h b/intern/cycles/kernel/integrator/intersect_closest.h index 621aa05f46b..60299f2cb2f 100644 --- a/intern/cycles/kernel/integrator/intersect_closest.h +++ b/intern/cycles/kernel/integrator/intersect_closest.h @@ -109,14 +109,14 @@ ccl_device_forceinline void integrator_split_shadow_catcher( /* If using background pass, schedule background shading kernel so that we have a background * to alpha-over on. The background kernel will then continue the path afterwards. */ INTEGRATOR_STATE_WRITE(state, path, flag) |= PATH_RAY_SHADOW_CATCHER_BACKGROUND; - INTEGRATOR_PATH_INIT(DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND); + integrator_path_init(kg, state, DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND); return; } if (!integrator_state_volume_stack_is_empty(kg, state)) { /* Volume stack is not empty. Re-init the volume stack to exclude any non-shadow catcher * objects from it, and then continue shading volume and shadow catcher surface after. */ - INTEGRATOR_PATH_INIT(DEVICE_KERNEL_INTEGRATOR_INTERSECT_VOLUME_STACK); + integrator_path_init(kg, state, DEVICE_KERNEL_INTEGRATOR_INTERSECT_VOLUME_STACK); return; } @@ -128,18 +128,19 @@ ccl_device_forceinline void integrator_split_shadow_catcher( const bool use_raytrace_kernel = (flags & SD_HAS_RAYTRACE); if (use_caustics) { - INTEGRATOR_PATH_INIT_SORTED(DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_MNEE, shader); + integrator_path_init_sorted(kg, state, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_MNEE, shader); } else if (use_raytrace_kernel) { - INTEGRATOR_PATH_INIT_SORTED(DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE, shader); + integrator_path_init_sorted( + kg, state, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE, shader); } else { - INTEGRATOR_PATH_INIT_SORTED(DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE, shader); + integrator_path_init_sorted(kg, state, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE, shader); } } /* Schedule next kernel to be executed after updating volume stack for shadow catcher. */ -template<uint32_t current_kernel> +template<DeviceKernel current_kernel> ccl_device_forceinline void integrator_intersect_next_kernel_after_shadow_catcher_volume( KernelGlobals kg, IntegratorState state) { @@ -156,20 +157,21 @@ ccl_device_forceinline void integrator_intersect_next_kernel_after_shadow_catche const bool use_raytrace_kernel = (flags & SD_HAS_RAYTRACE); if (use_caustics) { - INTEGRATOR_PATH_NEXT_SORTED( - current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_MNEE, shader); + integrator_path_next_sorted( + kg, state, current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_MNEE, shader); } else if (use_raytrace_kernel) { - INTEGRATOR_PATH_NEXT_SORTED( - current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE, shader); + integrator_path_next_sorted( + kg, state, current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE, shader); } else { - INTEGRATOR_PATH_NEXT_SORTED(current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE, shader); + integrator_path_next_sorted( + kg, state, current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE, shader); } } /* Schedule next kernel to be executed after executing background shader for shadow catcher. */ -template<uint32_t current_kernel> +template<DeviceKernel current_kernel> ccl_device_forceinline void integrator_intersect_next_kernel_after_shadow_catcher_background( KernelGlobals kg, IntegratorState state) { @@ -177,7 +179,8 @@ ccl_device_forceinline void integrator_intersect_next_kernel_after_shadow_catche if (!integrator_state_volume_stack_is_empty(kg, state)) { /* Volume stack is not empty. Re-init the volume stack to exclude any non-shadow catcher * objects from it, and then continue shading volume and shadow catcher surface after. */ - INTEGRATOR_PATH_NEXT(current_kernel, DEVICE_KERNEL_INTEGRATOR_INTERSECT_VOLUME_STACK); + integrator_path_next( + kg, state, current_kernel, DEVICE_KERNEL_INTEGRATOR_INTERSECT_VOLUME_STACK); return; } @@ -190,7 +193,7 @@ ccl_device_forceinline void integrator_intersect_next_kernel_after_shadow_catche * * Note that current_kernel is a template value since making this a variable * leads to poor performance with CUDA atomics. */ -template<uint32_t current_kernel> +template<DeviceKernel current_kernel> ccl_device_forceinline void integrator_intersect_next_kernel( KernelGlobals kg, IntegratorState state, @@ -206,10 +209,10 @@ ccl_device_forceinline void integrator_intersect_next_kernel( const int flags = (hit_surface) ? kernel_data_fetch(shaders, shader).flags : 0; if (!integrator_intersect_terminate(kg, state, flags)) { - INTEGRATOR_PATH_NEXT(current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_VOLUME); + integrator_path_next(kg, state, current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_VOLUME); } else { - INTEGRATOR_PATH_TERMINATE(current_kernel); + integrator_path_terminate(kg, state, current_kernel); } return; } @@ -218,7 +221,7 @@ ccl_device_forceinline void integrator_intersect_next_kernel( if (hit) { /* Hit a surface, continue with light or surface kernel. */ if (isect->type & PRIMITIVE_LAMP) { - INTEGRATOR_PATH_NEXT(current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_LIGHT); + integrator_path_next(kg, state, current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_LIGHT); } else { /* Hit a surface, continue with surface kernel unless terminated. */ @@ -231,16 +234,16 @@ ccl_device_forceinline void integrator_intersect_next_kernel( (object_flags & SD_OBJECT_CAUSTICS); const bool use_raytrace_kernel = (flags & SD_HAS_RAYTRACE); if (use_caustics) { - INTEGRATOR_PATH_NEXT_SORTED( - current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_MNEE, shader); + integrator_path_next_sorted( + kg, state, current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_MNEE, shader); } else if (use_raytrace_kernel) { - INTEGRATOR_PATH_NEXT_SORTED( - current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE, shader); + integrator_path_next_sorted( + kg, state, current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE, shader); } else { - INTEGRATOR_PATH_NEXT_SORTED( - current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE, shader); + integrator_path_next_sorted( + kg, state, current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE, shader); } #ifdef __SHADOW_CATCHER__ @@ -249,13 +252,13 @@ ccl_device_forceinline void integrator_intersect_next_kernel( #endif } else { - INTEGRATOR_PATH_TERMINATE(current_kernel); + integrator_path_terminate(kg, state, current_kernel); } } } else { /* Nothing hit, continue with background kernel. */ - INTEGRATOR_PATH_NEXT(current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND); + integrator_path_next(kg, state, current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND); } } @@ -263,7 +266,7 @@ ccl_device_forceinline void integrator_intersect_next_kernel( * * The logic here matches integrator_intersect_next_kernel, except that * volume shading and termination testing have already been done. */ -template<uint32_t current_kernel> +template<DeviceKernel current_kernel> ccl_device_forceinline void integrator_intersect_next_kernel_after_volume( KernelGlobals kg, IntegratorState state, @@ -273,7 +276,7 @@ ccl_device_forceinline void integrator_intersect_next_kernel_after_volume( if (isect->prim != PRIM_NONE) { /* Hit a surface, continue with light or surface kernel. */ if (isect->type & PRIMITIVE_LAMP) { - INTEGRATOR_PATH_NEXT(current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_LIGHT); + integrator_path_next(kg, state, current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_LIGHT); return; } else { @@ -286,16 +289,16 @@ ccl_device_forceinline void integrator_intersect_next_kernel_after_volume( const bool use_raytrace_kernel = (flags & SD_HAS_RAYTRACE); if (use_caustics) { - INTEGRATOR_PATH_NEXT_SORTED( - current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_MNEE, shader); + integrator_path_next_sorted( + kg, state, current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_MNEE, shader); } else if (use_raytrace_kernel) { - INTEGRATOR_PATH_NEXT_SORTED( - current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE, shader); + integrator_path_next_sorted( + kg, state, current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE, shader); } else { - INTEGRATOR_PATH_NEXT_SORTED( - current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE, shader); + integrator_path_next_sorted( + kg, state, current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE, shader); } #ifdef __SHADOW_CATCHER__ @@ -307,7 +310,7 @@ ccl_device_forceinline void integrator_intersect_next_kernel_after_volume( } else { /* Nothing hit, continue with background kernel. */ - INTEGRATOR_PATH_NEXT(current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND); + integrator_path_next(kg, state, current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND); return; } } @@ -321,7 +324,7 @@ ccl_device void integrator_intersect_closest(KernelGlobals kg, /* Read ray from integrator state into local memory. */ Ray ray ccl_optional_struct_init; integrator_state_read_ray(kg, state, &ray); - kernel_assert(ray.t != 0.0f); + kernel_assert(ray.tmax != 0.0f); const uint visibility = path_state_ray_visibility(state); const int last_isect_prim = INTEGRATOR_STATE(state, isect, prim); @@ -329,12 +332,12 @@ ccl_device void integrator_intersect_closest(KernelGlobals kg, /* Trick to use short AO rays to approximate indirect light at the end of the path. */ if (path_state_ao_bounce(kg, state)) { - ray.t = kernel_data.integrator.ao_bounces_distance; + ray.tmax = kernel_data.integrator.ao_bounces_distance; if (last_isect_object != OBJECT_NONE) { const float object_ao_distance = kernel_data_fetch(objects, last_isect_object).ao_distance; if (object_ao_distance != 0.0f) { - ray.t = object_ao_distance; + ray.tmax = object_ao_distance; } } } diff --git a/intern/cycles/kernel/integrator/intersect_shadow.h b/intern/cycles/kernel/integrator/intersect_shadow.h index 3e746998225..1b48b360858 100644 --- a/intern/cycles/kernel/integrator/intersect_shadow.h +++ b/intern/cycles/kernel/integrator/intersect_shadow.h @@ -162,7 +162,7 @@ ccl_device void integrator_intersect_shadow(KernelGlobals kg, IntegratorShadowSt if (opaque_hit) { /* Hit an opaque surface, shadow path ends here. */ - INTEGRATOR_SHADOW_PATH_TERMINATE(DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW); + integrator_shadow_path_terminate(kg, state, DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW); return; } else { @@ -171,7 +171,9 @@ ccl_device void integrator_intersect_shadow(KernelGlobals kg, IntegratorShadowSt * * TODO: could also write to render buffer directly if no transparent shadows? * Could save a kernel execution for the common case. */ - INTEGRATOR_SHADOW_PATH_NEXT(DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW, + integrator_shadow_path_next(kg, + state, + DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW, DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW); return; } diff --git a/intern/cycles/kernel/integrator/intersect_subsurface.h b/intern/cycles/kernel/integrator/intersect_subsurface.h index 0a2c4ad680d..f439d6905a0 100644 --- a/intern/cycles/kernel/integrator/intersect_subsurface.h +++ b/intern/cycles/kernel/integrator/intersect_subsurface.h @@ -17,7 +17,7 @@ ccl_device void integrator_intersect_subsurface(KernelGlobals kg, IntegratorStat } #endif - INTEGRATOR_PATH_TERMINATE(DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE); + integrator_path_terminate(kg, state, DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE); } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/integrator/intersect_volume_stack.h b/intern/cycles/kernel/integrator/intersect_volume_stack.h index 49ef01dc870..9ba4a0a3964 100644 --- a/intern/cycles/kernel/integrator/intersect_volume_stack.h +++ b/intern/cycles/kernel/integrator/intersect_volume_stack.h @@ -24,7 +24,8 @@ ccl_device void integrator_volume_stack_update_for_subsurface(KernelGlobals kg, Ray volume_ray ccl_optional_struct_init; volume_ray.P = from_P; - volume_ray.D = normalize_len(to_P - from_P, &volume_ray.t); + volume_ray.D = normalize_len(to_P - from_P, &volume_ray.tmax); + volume_ray.tmin = 0.0f; volume_ray.self.object = INTEGRATOR_STATE(state, isect, object); volume_ray.self.prim = INTEGRATOR_STATE(state, isect, prim); volume_ray.self.light_object = OBJECT_NONE; @@ -58,12 +59,9 @@ ccl_device void integrator_volume_stack_update_for_subsurface(KernelGlobals kg, volume_stack_enter_exit(kg, state, stack_sd); /* Move ray forward. */ - volume_ray.P = stack_sd->P; + volume_ray.tmin = intersection_t_offset(isect.t); volume_ray.self.object = isect.object; volume_ray.self.prim = isect.prim; - if (volume_ray.t != FLT_MAX) { - volume_ray.D = normalize_len(to_P - volume_ray.P, &volume_ray.t); - } ++step; } #endif @@ -82,7 +80,8 @@ ccl_device void integrator_volume_stack_init(KernelGlobals kg, IntegratorState s /* Trace ray in random direction. Any direction works, Z up is a guess to get the * fewest hits. */ volume_ray.D = make_float3(0.0f, 0.0f, 1.0f); - volume_ray.t = FLT_MAX; + volume_ray.tmin = 0.0f; + volume_ray.tmax = FLT_MAX; volume_ray.self.object = OBJECT_NONE; volume_ray.self.prim = PRIM_NONE; volume_ray.self.light_object = OBJECT_NONE; @@ -199,7 +198,7 @@ ccl_device void integrator_volume_stack_init(KernelGlobals kg, IntegratorState s } /* Move ray forward. */ - volume_ray.P = stack_sd->P; + volume_ray.tmin = intersection_t_offset(isect.t); volume_ray.self.object = isect.object; volume_ray.self.prim = isect.prim; ++step; @@ -222,7 +221,9 @@ ccl_device void integrator_intersect_volume_stack(KernelGlobals kg, IntegratorSt } else { /* Volume stack init for camera rays, continue with intersection of camera ray. */ - INTEGRATOR_PATH_NEXT(DEVICE_KERNEL_INTEGRATOR_INTERSECT_VOLUME_STACK, + integrator_path_next(kg, + state, + DEVICE_KERNEL_INTEGRATOR_INTERSECT_VOLUME_STACK, DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST); } } diff --git a/intern/cycles/kernel/integrator/mnee.h b/intern/cycles/kernel/integrator/mnee.h index 67505b9b612..7a6f866b1a0 100644 --- a/intern/cycles/kernel/integrator/mnee.h +++ b/intern/cycles/kernel/integrator/mnee.h @@ -137,8 +137,14 @@ ccl_device_forceinline void mnee_update_light_sample(KernelGlobals kg, } } else if (ls->type == LIGHT_AREA) { + float invarea = fabsf(klight->area.invarea); ls->D = normalize_len(ls->P - P, &ls->t); - ls->pdf = fabsf(klight->area.invarea); + ls->pdf = invarea; + if (klight->area.tan_spread > 0.f) { + ls->eval_fac = 0.25f * invarea; + ls->eval_fac *= light_spread_attenuation( + ls->D, ls->Ng, klight->area.tan_spread, klight->area.normalize_spread); + } } ls->pdf *= kernel_data.integrator.pdf_lights; @@ -436,6 +442,7 @@ ccl_device_forceinline bool mnee_newton_solver(KernelGlobals kg, projection_ray.self.light_prim = PRIM_NONE; projection_ray.dP = differential_make_compact(sd->dP); projection_ray.dD = differential_zero_compact(); + projection_ray.tmin = 0.0f; projection_ray.time = sd->time; Intersection projection_isect; @@ -499,8 +506,8 @@ ccl_device_forceinline bool mnee_newton_solver(KernelGlobals kg, projection_ray.self.prim = pv.prim; projection_ray.P = pv.p; } - projection_ray.D = normalize_len(tentative_p - projection_ray.P, &projection_ray.t); - projection_ray.t *= MNEE_PROJECTION_DISTANCE_MULTIPLIER; + projection_ray.D = normalize_len(tentative_p - projection_ray.P, &projection_ray.tmax); + projection_ray.tmax *= MNEE_PROJECTION_DISTANCE_MULTIPLIER; bool projection_success = false; for (int isect_count = 0; isect_count < MNEE_MAX_INTERSECTION_COUNT; isect_count++) { @@ -519,8 +526,7 @@ ccl_device_forceinline bool mnee_newton_solver(KernelGlobals kg, projection_ray.self.object = projection_isect.object; projection_ray.self.prim = projection_isect.prim; - projection_ray.P += projection_isect.t * projection_ray.D; - projection_ray.t -= projection_isect.t; + projection_ray.tmin = intersection_t_offset(projection_isect.t); } if (!projection_success) { reduce_stepsize = true; @@ -852,6 +858,7 @@ ccl_device_forceinline bool mnee_path_contribution(KernelGlobals kg, Ray probe_ray; probe_ray.self.light_object = ls->object; probe_ray.self.light_prim = ls->prim; + probe_ray.tmin = 0.0f; probe_ray.dP = differential_make_compact(sd->dP); probe_ray.dD = differential_zero_compact(); probe_ray.time = sd->time; @@ -867,13 +874,13 @@ ccl_device_forceinline bool mnee_path_contribution(KernelGlobals kg, ccl_private const ManifoldVertex &v = vertices[vi]; /* Check visibility. */ - probe_ray.D = normalize_len(v.p - probe_ray.P, &probe_ray.t); + probe_ray.D = normalize_len(v.p - probe_ray.P, &probe_ray.tmax); if (scene_intersect(kg, &probe_ray, PATH_RAY_TRANSMIT, &probe_isect)) { int hit_object = (probe_isect.object == OBJECT_NONE) ? kernel_data_fetch(prim_object, probe_isect.prim) : probe_isect.object; /* Test whether the ray hit the appropriate object at its intended location. */ - if (hit_object != v.object || fabsf(probe_ray.t - probe_isect.t) > MNEE_MIN_DISTANCE) + if (hit_object != v.object || fabsf(probe_ray.tmax - probe_isect.t) > MNEE_MIN_DISTANCE) return false; } probe_ray.self.object = v.object; @@ -952,15 +959,16 @@ ccl_device_forceinline int kernel_path_mnee_sample(KernelGlobals kg, probe_ray.self.light_object = ls->object; probe_ray.self.light_prim = ls->prim; probe_ray.P = sd->P; + probe_ray.tmin = 0.0f; if (ls->t == FLT_MAX) { /* Distant / env light. */ probe_ray.D = ls->D; - probe_ray.t = ls->t; + probe_ray.tmax = ls->t; } else { /* Other lights, avoid self-intersection. */ probe_ray.D = ls->P - probe_ray.P; - probe_ray.D = normalize_len(probe_ray.D, &probe_ray.t); + probe_ray.D = normalize_len(probe_ray.D, &probe_ray.tmax); } probe_ray.dP = differential_make_compact(sd->dP); probe_ray.dD = differential_zero_compact(); @@ -1042,9 +1050,7 @@ ccl_device_forceinline int kernel_path_mnee_sample(KernelGlobals kg, probe_ray.self.object = probe_isect.object; probe_ray.self.prim = probe_isect.prim; - probe_ray.P += probe_isect.t * probe_ray.D; - if (ls->t != FLT_MAX) - probe_ray.t -= probe_isect.t; + probe_ray.tmin = intersection_t_offset(probe_isect.t); }; /* Mark the manifold walk invalid to keep mollification on by default. */ diff --git a/intern/cycles/kernel/integrator/path_state.h b/intern/cycles/kernel/integrator/path_state.h index 1a085506a70..912c380cdb6 100644 --- a/intern/cycles/kernel/integrator/path_state.h +++ b/intern/cycles/kernel/integrator/path_state.h @@ -52,7 +52,6 @@ ccl_device_inline void path_state_init_integrator(KernelGlobals kg, INTEGRATOR_STATE_WRITE(state, path, flag) = PATH_RAY_CAMERA | PATH_RAY_MIS_SKIP | PATH_RAY_TRANSPARENT_BACKGROUND; INTEGRATOR_STATE_WRITE(state, path, mis_ray_pdf) = 0.0f; - INTEGRATOR_STATE_WRITE(state, path, mis_ray_t) = 0.0f; INTEGRATOR_STATE_WRITE(state, path, min_ray_pdf) = FLT_MAX; INTEGRATOR_STATE_WRITE(state, path, continuation_probability) = 1.0f; INTEGRATOR_STATE_WRITE(state, path, throughput) = make_float3(1.0f, 1.0f, 1.0f); diff --git a/intern/cycles/kernel/integrator/shade_background.h b/intern/cycles/kernel/integrator/shade_background.h index 4791a963ae6..a7edfffd175 100644 --- a/intern/cycles/kernel/integrator/shade_background.h +++ b/intern/cycles/kernel/integrator/shade_background.h @@ -62,11 +62,10 @@ ccl_device float3 integrator_eval_background_shader(KernelGlobals kg, const float3 ray_P = INTEGRATOR_STATE(state, ray, P); const float3 ray_D = INTEGRATOR_STATE(state, ray, D); const float mis_ray_pdf = INTEGRATOR_STATE(state, path, mis_ray_pdf); - const float mis_ray_t = INTEGRATOR_STATE(state, path, mis_ray_t); /* multiple importance sampling, get background light pdf for ray * direction, and compute weight with respect to BSDF pdf */ - const float pdf = background_light_pdf(kg, ray_P - ray_D * mis_ray_t, ray_D); + const float pdf = background_light_pdf(kg, ray_P, ray_D); const float mis_weight = light_sample_mis_weight_forward(kg, mis_ray_pdf, pdf); L *= mis_weight; } @@ -213,7 +212,7 @@ ccl_device void integrator_shade_background(KernelGlobals kg, } #endif - INTEGRATOR_PATH_TERMINATE(DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND); + integrator_path_terminate(kg, state, DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND); } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/integrator/shade_light.h b/intern/cycles/kernel/integrator/shade_light.h index be926c78439..910e3383f51 100644 --- a/intern/cycles/kernel/integrator/shade_light.h +++ b/intern/cycles/kernel/integrator/shade_light.h @@ -22,19 +22,8 @@ ccl_device_inline void integrate_light(KernelGlobals kg, const float3 ray_D = INTEGRATOR_STATE(state, ray, D); const float ray_time = INTEGRATOR_STATE(state, ray, time); - /* Advance ray beyond light. */ - /* TODO: can we make this more numerically robust to avoid reintersecting the - * same light in some cases? Ray should not intersect surface anymore as the - * object and prim ids will prevent self intersection. */ - const float3 new_ray_P = ray_P + ray_D * isect.t; - INTEGRATOR_STATE_WRITE(state, ray, P) = new_ray_P; - INTEGRATOR_STATE_WRITE(state, ray, t) -= isect.t; - - /* Set position to where the BSDF was sampled, for correct MIS PDF. */ - const float mis_ray_t = INTEGRATOR_STATE(state, path, mis_ray_t); - ray_P -= ray_D * mis_ray_t; - isect.t += mis_ray_t; - INTEGRATOR_STATE_WRITE(state, path, mis_ray_t) = isect.t; + /* Advance ray to new start distance. */ + INTEGRATOR_STATE_WRITE(state, ray, tmin) = intersection_t_offset(isect.t); LightSample ls ccl_optional_struct_init; const bool use_light_sample = light_sample_from_intersection(kg, &isect, ray_P, ray_D, &ls); @@ -99,11 +88,13 @@ ccl_device void integrator_shade_light(KernelGlobals kg, INTEGRATOR_STATE_WRITE(state, path, transparent_bounce) = transparent_bounce; if (transparent_bounce >= kernel_data.integrator.transparent_max_bounce) { - INTEGRATOR_PATH_TERMINATE(DEVICE_KERNEL_INTEGRATOR_SHADE_LIGHT); + integrator_path_terminate(kg, state, DEVICE_KERNEL_INTEGRATOR_SHADE_LIGHT); return; } else { - INTEGRATOR_PATH_NEXT(DEVICE_KERNEL_INTEGRATOR_SHADE_LIGHT, + integrator_path_next(kg, + state, + DEVICE_KERNEL_INTEGRATOR_SHADE_LIGHT, DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST); return; } diff --git a/intern/cycles/kernel/integrator/shade_shadow.h b/intern/cycles/kernel/integrator/shade_shadow.h index 2b929b7b62e..4b002a47bee 100644 --- a/intern/cycles/kernel/integrator/shade_shadow.h +++ b/intern/cycles/kernel/integrator/shade_shadow.h @@ -75,13 +75,9 @@ ccl_device_inline void integrate_transparent_volume_shadow(KernelGlobals kg, ray.self.light_object = OBJECT_NONE; ray.self.light_prim = PRIM_NONE; /* Modify ray position and length to match current segment. */ - const float start_t = (hit == 0) ? 0.0f : - INTEGRATOR_STATE_ARRAY(state, shadow_isect, hit - 1, t); - const float end_t = (hit < num_recorded_hits) ? - INTEGRATOR_STATE_ARRAY(state, shadow_isect, hit, t) : - ray.t; - ray.P += start_t * ray.D; - ray.t = end_t - start_t; + ray.tmin = (hit == 0) ? ray.tmin : INTEGRATOR_STATE_ARRAY(state, shadow_isect, hit - 1, t); + ray.tmax = (hit < num_recorded_hits) ? INTEGRATOR_STATE_ARRAY(state, shadow_isect, hit, t) : + ray.tmax; shader_setup_from_volume(kg, shadow_sd, &ray); @@ -137,10 +133,7 @@ ccl_device_inline bool integrate_transparent_shadow(KernelGlobals kg, /* There are more hits that we could not recorded due to memory usage, * adjust ray to intersect again from the last hit. */ const float last_hit_t = INTEGRATOR_STATE_ARRAY(state, shadow_isect, num_recorded_hits - 1, t); - const float3 ray_P = INTEGRATOR_STATE(state, shadow_ray, P); - const float3 ray_D = INTEGRATOR_STATE(state, shadow_ray, D); - INTEGRATOR_STATE_WRITE(state, shadow_ray, P) = ray_P + last_hit_t * ray_D; - INTEGRATOR_STATE_WRITE(state, shadow_ray, t) -= last_hit_t; + INTEGRATOR_STATE_WRITE(state, shadow_ray, tmin) = intersection_t_offset(last_hit_t); } return false; @@ -158,20 +151,22 @@ ccl_device void integrator_shade_shadow(KernelGlobals kg, /* Evaluate transparent shadows. */ const bool opaque = integrate_transparent_shadow(kg, state, num_hits); if (opaque) { - INTEGRATOR_SHADOW_PATH_TERMINATE(DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW); + integrator_shadow_path_terminate(kg, state, DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW); return; } #endif if (shadow_intersections_has_remaining(num_hits)) { /* More intersections to find, continue shadow ray. */ - INTEGRATOR_SHADOW_PATH_NEXT(DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW, + integrator_shadow_path_next(kg, + state, + DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW, DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW); return; } else { kernel_accum_light(kg, state, render_buffer); - INTEGRATOR_SHADOW_PATH_TERMINATE(DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW); + integrator_shadow_path_terminate(kg, state, DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW); return; } } diff --git a/intern/cycles/kernel/integrator/shade_surface.h b/intern/cycles/kernel/integrator/shade_surface.h index 57b88b806a4..1514b3956ad 100644 --- a/intern/cycles/kernel/integrator/shade_surface.h +++ b/intern/cycles/kernel/integrator/shade_surface.h @@ -77,7 +77,7 @@ ccl_device_forceinline void integrate_surface_emission(KernelGlobals kg, # endif { const float bsdf_pdf = INTEGRATOR_STATE(state, path, mis_ray_pdf); - const float t = sd->ray_length + INTEGRATOR_STATE(state, path, mis_ray_t); + const float t = sd->ray_length; /* Multiple importance sampling, get triangle light pdf, * and compute weight with respect to BSDF pdf. */ @@ -190,8 +190,8 @@ ccl_device_forceinline void integrate_surface_direct_light(KernelGlobals kg, const bool is_light = light_sample_is_light(&ls); /* Branch off shadow kernel. */ - INTEGRATOR_SHADOW_PATH_INIT( - shadow_state, state, DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW, shadow); + IntegratorShadowState shadow_state = integrator_shadow_path_init( + kg, state, DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW, false); /* Copy volume stack and enter/exit volume. */ integrator_state_copy_volume_stack_to_shadow(kg, shadow_state, state); @@ -323,16 +323,21 @@ ccl_device_forceinline int integrate_surface_bsdf_bssrdf_bounce( return LABEL_NONE; } - /* Setup ray. Note that clipping works through transparent bounces. */ - INTEGRATOR_STATE_WRITE(state, ray, P) = sd->P; - INTEGRATOR_STATE_WRITE(state, ray, D) = normalize(bsdf_omega_in); - INTEGRATOR_STATE_WRITE(state, ray, t) = (label & LABEL_TRANSPARENT) ? - INTEGRATOR_STATE(state, ray, t) - sd->ray_length : - FLT_MAX; + if (label & LABEL_TRANSPARENT) { + /* Only need to modify start distance for transparent. */ + INTEGRATOR_STATE_WRITE(state, ray, tmin) = intersection_t_offset(sd->ray_length); + } + else { + /* Setup ray with changed origin and direction. */ + INTEGRATOR_STATE_WRITE(state, ray, P) = sd->P; + INTEGRATOR_STATE_WRITE(state, ray, D) = normalize(bsdf_omega_in); + INTEGRATOR_STATE_WRITE(state, ray, tmin) = 0.0f; + INTEGRATOR_STATE_WRITE(state, ray, tmax) = FLT_MAX; #ifdef __RAY_DIFFERENTIALS__ - INTEGRATOR_STATE_WRITE(state, ray, dP) = differential_make_compact(sd->dP); - INTEGRATOR_STATE_WRITE(state, ray, dD) = differential_make_compact(bsdf_domega_in); + INTEGRATOR_STATE_WRITE(state, ray, dP) = differential_make_compact(sd->dP); + INTEGRATOR_STATE_WRITE(state, ray, dD) = differential_make_compact(bsdf_domega_in); #endif + } /* Update throughput. */ float3 throughput = INTEGRATOR_STATE(state, path, throughput); @@ -349,12 +354,8 @@ ccl_device_forceinline int integrate_surface_bsdf_bssrdf_bounce( } /* Update path state */ - if (label & LABEL_TRANSPARENT) { - INTEGRATOR_STATE_WRITE(state, path, mis_ray_t) += sd->ray_length; - } - else { + if (!(label & LABEL_TRANSPARENT)) { INTEGRATOR_STATE_WRITE(state, path, mis_ray_pdf) = bsdf_pdf; - INTEGRATOR_STATE_WRITE(state, path, mis_ray_t) = 0.0f; INTEGRATOR_STATE_WRITE(state, path, min_ray_pdf) = fminf( bsdf_pdf, INTEGRATOR_STATE(state, path, min_ray_pdf)); } @@ -371,17 +372,8 @@ ccl_device_forceinline int integrate_surface_volume_only_bounce(IntegratorState return LABEL_NONE; } - /* Setup ray position, direction stays unchanged. */ - INTEGRATOR_STATE_WRITE(state, ray, P) = sd->P; - - /* Clipping works through transparent. */ - INTEGRATOR_STATE_WRITE(state, ray, t) -= sd->ray_length; - -# ifdef __RAY_DIFFERENTIALS__ - INTEGRATOR_STATE_WRITE(state, ray, dP) = differential_make_compact(sd->dP); -# endif - - INTEGRATOR_STATE_WRITE(state, path, mis_ray_t) += sd->ray_length; + /* Only modify start distance. */ + INTEGRATOR_STATE_WRITE(state, ray, tmin) = intersection_t_offset(sd->ray_length); return LABEL_TRANSMIT | LABEL_TRANSPARENT; } @@ -432,7 +424,8 @@ ccl_device_forceinline void integrate_surface_ao(KernelGlobals kg, Ray ray ccl_optional_struct_init; ray.P = shadow_ray_offset(kg, sd, ao_D, &skip_self); ray.D = ao_D; - ray.t = kernel_data.integrator.ao_bounces_distance; + ray.tmin = 0.0f; + ray.tmax = kernel_data.integrator.ao_bounces_distance; ray.time = sd->time; ray.self.object = (skip_self) ? sd->object : OBJECT_NONE; ray.self.prim = (skip_self) ? sd->prim : PRIM_NONE; @@ -442,7 +435,8 @@ ccl_device_forceinline void integrate_surface_ao(KernelGlobals kg, ray.dD = differential_zero_compact(); /* Branch off shadow kernel. */ - INTEGRATOR_SHADOW_PATH_INIT(shadow_state, state, DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW, ao); + IntegratorShadowState shadow_state = integrator_shadow_path_init( + kg, state, DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW, true); /* Copy volume stack and enter/exit volume. */ integrator_state_copy_volume_stack_to_shadow(kg, shadow_state, state); @@ -604,22 +598,23 @@ ccl_device bool integrate_surface(KernelGlobals kg, } template<uint node_feature_mask = KERNEL_FEATURE_NODE_MASK_SURFACE & ~KERNEL_FEATURE_NODE_RAYTRACE, - int current_kernel = DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE> + DeviceKernel current_kernel = DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE> ccl_device_forceinline void integrator_shade_surface(KernelGlobals kg, IntegratorState state, ccl_global float *ccl_restrict render_buffer) { if (integrate_surface<node_feature_mask>(kg, state, render_buffer)) { if (INTEGRATOR_STATE(state, path, flag) & PATH_RAY_SUBSURFACE) { - INTEGRATOR_PATH_NEXT(current_kernel, DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE); + integrator_path_next( + kg, state, current_kernel, DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE); } else { - kernel_assert(INTEGRATOR_STATE(state, ray, t) != 0.0f); - INTEGRATOR_PATH_NEXT(current_kernel, DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST); + kernel_assert(INTEGRATOR_STATE(state, ray, tmax) != 0.0f); + integrator_path_next(kg, state, current_kernel, DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST); } } else { - INTEGRATOR_PATH_TERMINATE(current_kernel); + integrator_path_terminate(kg, state, current_kernel); } } diff --git a/intern/cycles/kernel/integrator/shade_volume.h b/intern/cycles/kernel/integrator/shade_volume.h index 6cf80f4ddc5..4aab097a7d8 100644 --- a/intern/cycles/kernel/integrator/shade_volume.h +++ b/intern/cycles/kernel/integrator/shade_volume.h @@ -114,7 +114,8 @@ ccl_device_inline bool volume_shader_sample(KernelGlobals kg, ccl_device_forceinline void volume_step_init(KernelGlobals kg, ccl_private const RNGState *rng_state, const float object_step_size, - float t, + const float tmin, + const float tmax, ccl_private float *step_size, ccl_private float *step_shade_offset, ccl_private float *steps_offset, @@ -122,7 +123,7 @@ ccl_device_forceinline void volume_step_init(KernelGlobals kg, { if (object_step_size == FLT_MAX) { /* Homogeneous volume. */ - *step_size = t; + *step_size = tmax - tmin; *step_shade_offset = 0.0f; *steps_offset = 1.0f; *max_steps = 1; @@ -130,6 +131,7 @@ ccl_device_forceinline void volume_step_init(KernelGlobals kg, else { /* Heterogeneous volume. */ *max_steps = kernel_data.integrator.volume_max_steps; + const float t = tmax - tmin; float step = min(object_step_size, t); /* compute exact steps in advance for malloc */ @@ -165,7 +167,7 @@ ccl_device void volume_shadow_homogeneous(KernelGlobals kg, IntegratorState stat float3 sigma_t = zero_float3(); if (shadow_volume_shader_sample(kg, state, sd, &sigma_t)) { - *throughput *= volume_color_transmittance(sigma_t, ray->t); + *throughput *= volume_color_transmittance(sigma_t, ray->tmax - ray->tmin); } } # endif @@ -194,7 +196,8 @@ ccl_device void volume_shadow_heterogeneous(KernelGlobals kg, volume_step_init(kg, &rng_state, object_step_size, - ray->t, + ray->tmin, + ray->tmax, &step_size, &step_shade_offset, &unused, @@ -202,13 +205,13 @@ ccl_device void volume_shadow_heterogeneous(KernelGlobals kg, const float steps_offset = 1.0f; /* compute extinction at the start */ - float t = 0.0f; + float t = ray->tmin; float3 sum = zero_float3(); for (int i = 0; i < max_steps; i++) { /* advance to new position */ - float new_t = min(ray->t, (i + steps_offset) * step_size); + float new_t = min(ray->tmax, ray->tmin + (i + steps_offset) * step_size); float dt = new_t - t; float3 new_P = ray->P + ray->D * (t + dt * step_shade_offset); @@ -233,7 +236,7 @@ ccl_device void volume_shadow_heterogeneous(KernelGlobals kg, /* stop if at the end of the volume */ t = new_t; - if (t == ray->t) { + if (t == ray->tmax) { /* Update throughput in case we haven't done it above */ tp = *throughput * exp(sum); break; @@ -257,15 +260,16 @@ ccl_device float volume_equiangular_sample(ccl_private const Ray *ccl_restrict r const float xi, ccl_private float *pdf) { - const float t = ray->t; + const float tmin = ray->tmin; + const float tmax = ray->tmax; const float delta = dot((light_P - ray->P), ray->D); const float D = safe_sqrtf(len_squared(light_P - ray->P) - delta * delta); if (UNLIKELY(D == 0.0f)) { *pdf = 0.0f; return 0.0f; } - const float theta_a = -atan2f(delta, D); - const float theta_b = atan2f(t - delta, D); + const float theta_a = atan2f(tmin - delta, D); + const float theta_b = atan2f(tmax - delta, D); const float t_ = D * tanf((xi * theta_b) + (1 - xi) * theta_a); if (UNLIKELY(theta_b == theta_a)) { *pdf = 0.0f; @@ -273,7 +277,7 @@ ccl_device float volume_equiangular_sample(ccl_private const Ray *ccl_restrict r } *pdf = D / ((theta_b - theta_a) * (D * D + t_ * t_)); - return min(t, delta + t_); /* min is only for float precision errors */ + return clamp(delta + t_, tmin, tmax); /* clamp is only for float precision errors */ } ccl_device float volume_equiangular_pdf(ccl_private const Ray *ccl_restrict ray, @@ -286,11 +290,12 @@ ccl_device float volume_equiangular_pdf(ccl_private const Ray *ccl_restrict ray, return 0.0f; } - const float t = ray->t; + const float tmin = ray->tmin; + const float tmax = ray->tmax; const float t_ = sample_t - delta; - const float theta_a = -atan2f(delta, D); - const float theta_b = atan2f(t - delta, D); + const float theta_a = atan2f(tmin - delta, D); + const float theta_b = atan2f(tmax - delta, D); if (UNLIKELY(theta_b == theta_a)) { return 0.0f; } @@ -310,11 +315,12 @@ ccl_device float volume_equiangular_cdf(ccl_private const Ray *ccl_restrict ray, return 0.0f; } - const float t = ray->t; + const float tmin = ray->tmin; + const float tmax = ray->tmax; const float t_ = sample_t - delta; - const float theta_a = -atan2f(delta, D); - const float theta_b = atan2f(t - delta, D); + const float theta_a = atan2f(tmin - delta, D); + const float theta_b = atan2f(tmax - delta, D); if (UNLIKELY(theta_b == theta_a)) { return 0.0f; } @@ -390,8 +396,8 @@ ccl_device float3 volume_emission_integrate(ccl_private VolumeShaderCoefficients typedef struct VolumeIntegrateState { /* Volume segment extents. */ - float start_t; - float end_t; + float tmin; + float tmax; /* If volume is absorption-only up to this point, and no probabilistic * scattering or termination has been used yet. */ @@ -426,9 +432,9 @@ ccl_device_forceinline void volume_integrate_step_scattering( /* Equiangular sampling for direct lighting. */ if (vstate.direct_sample_method == VOLUME_SAMPLE_EQUIANGULAR && !result.direct_scatter) { - if (result.direct_t >= vstate.start_t && result.direct_t <= vstate.end_t && + if (result.direct_t >= vstate.tmin && result.direct_t <= vstate.tmax && vstate.equiangular_pdf > VOLUME_SAMPLE_PDF_CUTOFF) { - const float new_dt = result.direct_t - vstate.start_t; + const float new_dt = result.direct_t - vstate.tmin; const float3 new_transmittance = volume_color_transmittance(coeff.sigma_t, new_dt); result.direct_scatter = true; @@ -458,7 +464,7 @@ ccl_device_forceinline void volume_integrate_step_scattering( /* compute sampling distance */ const float sample_sigma_t = volume_channel_get(coeff.sigma_t, channel); const float new_dt = -logf(1.0f - vstate.rscatter) / sample_sigma_t; - const float new_t = vstate.start_t + new_dt; + const float new_t = vstate.tmin + new_dt; /* transmittance and pdf */ const float3 new_transmittance = volume_color_transmittance(coeff.sigma_t, new_dt); @@ -528,7 +534,8 @@ ccl_device_forceinline void volume_integrate_heterogeneous( volume_step_init(kg, rng_state, object_step_size, - ray->t, + ray->tmin, + ray->tmax, &step_size, &step_shade_offset, &steps_offset, @@ -536,8 +543,8 @@ ccl_device_forceinline void volume_integrate_heterogeneous( /* Initialize volume integration state. */ VolumeIntegrateState vstate ccl_optional_struct_init; - vstate.start_t = 0.0f; - vstate.end_t = 0.0f; + vstate.tmin = ray->tmin; + vstate.tmax = ray->tmin; vstate.absorption_only = true; vstate.rscatter = path_state_rng_1D(kg, rng_state, PRNG_SCATTER_DISTANCE); vstate.rphase = path_state_rng_1D(kg, rng_state, PRNG_PHASE_CHANNEL); @@ -578,8 +585,8 @@ ccl_device_forceinline void volume_integrate_heterogeneous( for (int i = 0; i < max_steps; i++) { /* Advance to new position */ - vstate.end_t = min(ray->t, (i + steps_offset) * step_size); - const float shade_t = vstate.start_t + (vstate.end_t - vstate.start_t) * step_shade_offset; + vstate.tmax = min(ray->tmax, ray->tmin + (i + steps_offset) * step_size); + const float shade_t = vstate.tmin + (vstate.tmax - vstate.tmin) * step_shade_offset; sd->P = ray->P + ray->D * shade_t; /* compute segment */ @@ -588,7 +595,7 @@ ccl_device_forceinline void volume_integrate_heterogeneous( const int closure_flag = sd->flag; /* Evaluate transmittance over segment. */ - const float dt = (vstate.end_t - vstate.start_t); + const float dt = (vstate.tmax - vstate.tmin); const float3 transmittance = (closure_flag & SD_EXTINCTION) ? volume_color_transmittance(coeff.sigma_t, dt) : one_float3(); @@ -645,8 +652,8 @@ ccl_device_forceinline void volume_integrate_heterogeneous( } /* Stop if at the end of the volume. */ - vstate.start_t = vstate.end_t; - if (vstate.start_t == ray->t) { + vstate.tmin = vstate.tmax; + if (vstate.tmin == ray->tmax) { break; } } @@ -774,8 +781,8 @@ ccl_device_forceinline void integrate_volume_direct_light( const bool is_light = light_sample_is_light(ls); /* Branch off shadow kernel. */ - INTEGRATOR_SHADOW_PATH_INIT( - shadow_state, state, DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW, shadow); + IntegratorShadowState shadow_state = integrator_shadow_path_init( + kg, state, DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW, false); /* Write shadow ray and associated state to global memory. */ integrator_state_write_shadow_ray(kg, shadow_state, &ray); @@ -880,7 +887,8 @@ ccl_device_forceinline bool integrate_volume_phase_scatter( /* Setup ray. */ INTEGRATOR_STATE_WRITE(state, ray, P) = sd->P; INTEGRATOR_STATE_WRITE(state, ray, D) = normalize(phase_omega_in); - INTEGRATOR_STATE_WRITE(state, ray, t) = FLT_MAX; + INTEGRATOR_STATE_WRITE(state, ray, tmin) = 0.0f; + INTEGRATOR_STATE_WRITE(state, ray, tmax) = FLT_MAX; # ifdef __RAY_DIFFERENTIALS__ INTEGRATOR_STATE_WRITE(state, ray, dP) = differential_make_compact(sd->dP); INTEGRATOR_STATE_WRITE(state, ray, dD) = differential_make_compact(phase_domega_in); @@ -901,7 +909,6 @@ ccl_device_forceinline bool integrate_volume_phase_scatter( /* Update path state */ INTEGRATOR_STATE_WRITE(state, path, mis_ray_pdf) = phase_pdf; - INTEGRATOR_STATE_WRITE(state, path, mis_ray_t) = 0.0f; INTEGRATOR_STATE_WRITE(state, path, min_ray_pdf) = fminf( phase_pdf, INTEGRATOR_STATE(state, path, min_ray_pdf)); @@ -1021,7 +1028,7 @@ ccl_device void integrator_shade_volume(KernelGlobals kg, integrator_state_read_isect(kg, state, &isect); /* Set ray length to current segment. */ - ray.t = (isect.prim != PRIM_NONE) ? isect.t : FLT_MAX; + ray.tmax = (isect.prim != PRIM_NONE) ? isect.t : FLT_MAX; /* Clean volume stack for background rays. */ if (isect.prim == PRIM_NONE) { @@ -1032,13 +1039,15 @@ ccl_device void integrator_shade_volume(KernelGlobals kg, if (event == VOLUME_PATH_SCATTERED) { /* Queue intersect_closest kernel. */ - INTEGRATOR_PATH_NEXT(DEVICE_KERNEL_INTEGRATOR_SHADE_VOLUME, + integrator_path_next(kg, + state, + DEVICE_KERNEL_INTEGRATOR_SHADE_VOLUME, DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST); return; } else if (event == VOLUME_PATH_MISSED) { /* End path. */ - INTEGRATOR_PATH_TERMINATE(DEVICE_KERNEL_INTEGRATOR_SHADE_VOLUME); + integrator_path_terminate(kg, state, DEVICE_KERNEL_INTEGRATOR_SHADE_VOLUME); return; } else { diff --git a/intern/cycles/kernel/integrator/shadow_catcher.h b/intern/cycles/kernel/integrator/shadow_catcher.h index 42d44580f80..ff63625aceb 100644 --- a/intern/cycles/kernel/integrator/shadow_catcher.h +++ b/intern/cycles/kernel/integrator/shadow_catcher.h @@ -50,7 +50,7 @@ ccl_device_inline bool kernel_shadow_catcher_is_path_split_bounce(KernelGlobals ccl_device_inline bool kernel_shadow_catcher_path_can_split(KernelGlobals kg, ConstIntegratorState state) { - if (INTEGRATOR_PATH_IS_TERMINATED) { + if (integrator_path_is_terminated(state)) { return false; } diff --git a/intern/cycles/kernel/integrator/shadow_state_template.h b/intern/cycles/kernel/integrator/shadow_state_template.h index eaee65ada40..c340467606d 100644 --- a/intern/cycles/kernel/integrator/shadow_state_template.h +++ b/intern/cycles/kernel/integrator/shadow_state_template.h @@ -47,7 +47,8 @@ KERNEL_STRUCT_END(shadow_path) KERNEL_STRUCT_BEGIN(shadow_ray) KERNEL_STRUCT_MEMBER(shadow_ray, packed_float3, P, KERNEL_FEATURE_PATH_TRACING) KERNEL_STRUCT_MEMBER(shadow_ray, packed_float3, D, KERNEL_FEATURE_PATH_TRACING) -KERNEL_STRUCT_MEMBER(shadow_ray, float, t, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_MEMBER(shadow_ray, float, tmin, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_MEMBER(shadow_ray, float, tmax, KERNEL_FEATURE_PATH_TRACING) KERNEL_STRUCT_MEMBER(shadow_ray, float, time, KERNEL_FEATURE_PATH_TRACING) KERNEL_STRUCT_MEMBER(shadow_ray, float, dP, KERNEL_FEATURE_PATH_TRACING) KERNEL_STRUCT_MEMBER(shadow_ray, int, object, KERNEL_FEATURE_PATH_TRACING) diff --git a/intern/cycles/kernel/integrator/state.h b/intern/cycles/kernel/integrator/state.h index d6fef27f344..d10d31e930e 100644 --- a/intern/cycles/kernel/integrator/state.h +++ b/intern/cycles/kernel/integrator/state.h @@ -127,6 +127,9 @@ typedef struct IntegratorStateGPU { /* Index of main path which will be used by a next shadow catcher split. */ ccl_global int *next_main_path_index; + + /* Divisor used to partition active indices by locality when sorting by material. */ + uint sort_partition_divisor; } IntegratorStateGPU; /* Abstraction diff --git a/intern/cycles/kernel/integrator/state_flow.h b/intern/cycles/kernel/integrator/state_flow.h index fed74d49434..4b03c665e17 100644 --- a/intern/cycles/kernel/integrator/state_flow.h +++ b/intern/cycles/kernel/integrator/state_flow.h @@ -10,125 +10,196 @@ CCL_NAMESPACE_BEGIN /* Control Flow * - * Utilities for control flow between kernels. The implementation may differ per device - * or even be handled on the host side. To abstract such differences, experiment with - * different implementations and for debugging, this is abstracted using macros. + * Utilities for control flow between kernels. The implementation is different between CPU and + * GPU devices. For the latter part of the logic is handled on the host side with wavefronts. * * There is a main path for regular path tracing camera for path tracing. Shadows for next * event estimation branch off from this into their own path, that may be computed in - * parallel while the main path continues. + * parallel while the main path continues. Additionally, shading kernels are sorted using + * a key for coherence. * * Each kernel on the main path must call one of these functions. These may not be called * multiple times from the same kernel. * - * INTEGRATOR_PATH_INIT(next_kernel) - * INTEGRATOR_PATH_NEXT(current_kernel, next_kernel) - * INTEGRATOR_PATH_TERMINATE(current_kernel) + * integrator_path_init(kg, state, next_kernel) + * integrator_path_next(kg, state, current_kernel, next_kernel) + * integrator_path_terminate(kg, state, current_kernel) * * For the shadow path similar functions are used, and again each shadow kernel must call * one of them, and only once. */ -#define INTEGRATOR_PATH_IS_TERMINATED (INTEGRATOR_STATE(state, path, queued_kernel) == 0) -#define INTEGRATOR_SHADOW_PATH_IS_TERMINATED \ - (INTEGRATOR_STATE(state, shadow_path, queued_kernel) == 0) +ccl_device_forceinline bool integrator_path_is_terminated(ConstIntegratorState state) +{ + return INTEGRATOR_STATE(state, path, queued_kernel) == 0; +} + +ccl_device_forceinline bool integrator_shadow_path_is_terminated(ConstIntegratorShadowState state) +{ + return INTEGRATOR_STATE(state, shadow_path, queued_kernel) == 0; +} #ifdef __KERNEL_GPU__ -# define INTEGRATOR_PATH_INIT(next_kernel) \ - atomic_fetch_and_add_uint32(&kernel_integrator_state.queue_counter->num_queued[next_kernel], \ - 1); \ - INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = next_kernel; -# define INTEGRATOR_PATH_NEXT(current_kernel, next_kernel) \ - atomic_fetch_and_sub_uint32( \ - &kernel_integrator_state.queue_counter->num_queued[current_kernel], 1); \ - atomic_fetch_and_add_uint32(&kernel_integrator_state.queue_counter->num_queued[next_kernel], \ - 1); \ - INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = next_kernel; -# define INTEGRATOR_PATH_TERMINATE(current_kernel) \ - atomic_fetch_and_sub_uint32( \ - &kernel_integrator_state.queue_counter->num_queued[current_kernel], 1); \ - INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = 0; - -# define INTEGRATOR_SHADOW_PATH_INIT(shadow_state, state, next_kernel, shadow_type) \ - IntegratorShadowState shadow_state = atomic_fetch_and_add_uint32( \ - &kernel_integrator_state.next_shadow_path_index[0], 1); \ - atomic_fetch_and_add_uint32(&kernel_integrator_state.queue_counter->num_queued[next_kernel], \ - 1); \ - INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, queued_kernel) = next_kernel; -# define INTEGRATOR_SHADOW_PATH_NEXT(current_kernel, next_kernel) \ - atomic_fetch_and_sub_uint32( \ - &kernel_integrator_state.queue_counter->num_queued[current_kernel], 1); \ - atomic_fetch_and_add_uint32(&kernel_integrator_state.queue_counter->num_queued[next_kernel], \ - 1); \ - INTEGRATOR_STATE_WRITE(state, shadow_path, queued_kernel) = next_kernel; -# define INTEGRATOR_SHADOW_PATH_TERMINATE(current_kernel) \ - atomic_fetch_and_sub_uint32( \ - &kernel_integrator_state.queue_counter->num_queued[current_kernel], 1); \ - INTEGRATOR_STATE_WRITE(state, shadow_path, queued_kernel) = 0; - -# define INTEGRATOR_PATH_INIT_SORTED(next_kernel, key) \ - { \ - const int key_ = key; \ - atomic_fetch_and_add_uint32( \ - &kernel_integrator_state.queue_counter->num_queued[next_kernel], 1); \ - INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = next_kernel; \ - INTEGRATOR_STATE_WRITE(state, path, shader_sort_key) = key_; \ - atomic_fetch_and_add_uint32(&kernel_integrator_state.sort_key_counter[next_kernel][key_], \ - 1); \ - } -# define INTEGRATOR_PATH_NEXT_SORTED(current_kernel, next_kernel, key) \ - { \ - const int key_ = key; \ - atomic_fetch_and_sub_uint32( \ - &kernel_integrator_state.queue_counter->num_queued[current_kernel], 1); \ - atomic_fetch_and_add_uint32( \ - &kernel_integrator_state.queue_counter->num_queued[next_kernel], 1); \ - INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = next_kernel; \ - INTEGRATOR_STATE_WRITE(state, path, shader_sort_key) = key_; \ - atomic_fetch_and_add_uint32(&kernel_integrator_state.sort_key_counter[next_kernel][key_], \ - 1); \ - } +ccl_device_forceinline void integrator_path_init(KernelGlobals kg, + IntegratorState state, + const DeviceKernel next_kernel) +{ + atomic_fetch_and_add_uint32(&kernel_integrator_state.queue_counter->num_queued[next_kernel], 1); + INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = next_kernel; +} + +ccl_device_forceinline void integrator_path_next(KernelGlobals kg, + IntegratorState state, + const DeviceKernel current_kernel, + const DeviceKernel next_kernel) +{ + atomic_fetch_and_sub_uint32(&kernel_integrator_state.queue_counter->num_queued[current_kernel], + 1); + atomic_fetch_and_add_uint32(&kernel_integrator_state.queue_counter->num_queued[next_kernel], 1); + INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = next_kernel; +} + +ccl_device_forceinline void integrator_path_terminate(KernelGlobals kg, + IntegratorState state, + const DeviceKernel current_kernel) +{ + atomic_fetch_and_sub_uint32(&kernel_integrator_state.queue_counter->num_queued[current_kernel], + 1); + INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = 0; +} + +ccl_device_forceinline IntegratorShadowState integrator_shadow_path_init( + KernelGlobals kg, IntegratorState state, const DeviceKernel next_kernel, const bool is_ao) +{ + IntegratorShadowState shadow_state = atomic_fetch_and_add_uint32( + &kernel_integrator_state.next_shadow_path_index[0], 1); + atomic_fetch_and_add_uint32(&kernel_integrator_state.queue_counter->num_queued[next_kernel], 1); + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, queued_kernel) = next_kernel; + return shadow_state; +} + +ccl_device_forceinline void integrator_shadow_path_next(KernelGlobals kg, + IntegratorShadowState state, + const DeviceKernel current_kernel, + const DeviceKernel next_kernel) +{ + atomic_fetch_and_sub_uint32(&kernel_integrator_state.queue_counter->num_queued[current_kernel], + 1); + atomic_fetch_and_add_uint32(&kernel_integrator_state.queue_counter->num_queued[next_kernel], 1); + INTEGRATOR_STATE_WRITE(state, shadow_path, queued_kernel) = next_kernel; +} + +ccl_device_forceinline void integrator_shadow_path_terminate(KernelGlobals kg, + IntegratorShadowState state, + const DeviceKernel current_kernel) +{ + atomic_fetch_and_sub_uint32(&kernel_integrator_state.queue_counter->num_queued[current_kernel], + 1); + INTEGRATOR_STATE_WRITE(state, shadow_path, queued_kernel) = 0; +} + +/* Sort first by truncated state index (for good locality), then by key (for good coherence). */ +# define INTEGRATOR_SORT_KEY(key, state) \ + (key + kernel_data.max_shaders * (state / kernel_integrator_state.sort_partition_divisor)) + +ccl_device_forceinline void integrator_path_init_sorted(KernelGlobals kg, + IntegratorState state, + const DeviceKernel next_kernel, + const uint32_t key) +{ + const int key_ = INTEGRATOR_SORT_KEY(key, state); + atomic_fetch_and_add_uint32(&kernel_integrator_state.queue_counter->num_queued[next_kernel], 1); + INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = next_kernel; + INTEGRATOR_STATE_WRITE(state, path, shader_sort_key) = key_; + atomic_fetch_and_add_uint32(&kernel_integrator_state.sort_key_counter[next_kernel][key_], 1); +} + +ccl_device_forceinline void integrator_path_next_sorted(KernelGlobals kg, + IntegratorState state, + const DeviceKernel current_kernel, + const DeviceKernel next_kernel, + const uint32_t key) +{ + const int key_ = INTEGRATOR_SORT_KEY(key, state); + atomic_fetch_and_sub_uint32(&kernel_integrator_state.queue_counter->num_queued[current_kernel], + 1); + atomic_fetch_and_add_uint32(&kernel_integrator_state.queue_counter->num_queued[next_kernel], 1); + INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = next_kernel; + INTEGRATOR_STATE_WRITE(state, path, shader_sort_key) = key_; + atomic_fetch_and_add_uint32(&kernel_integrator_state.sort_key_counter[next_kernel][key_], 1); +} #else -# define INTEGRATOR_PATH_INIT(next_kernel) \ - INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = next_kernel; -# define INTEGRATOR_PATH_INIT_SORTED(next_kernel, key) \ - { \ - INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = next_kernel; \ - (void)key; \ - } -# define INTEGRATOR_PATH_NEXT(current_kernel, next_kernel) \ - { \ - INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = next_kernel; \ - (void)current_kernel; \ - } -# define INTEGRATOR_PATH_TERMINATE(current_kernel) \ - { \ - INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = 0; \ - (void)current_kernel; \ - } -# define INTEGRATOR_PATH_NEXT_SORTED(current_kernel, next_kernel, key) \ - { \ - INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = next_kernel; \ - (void)key; \ - (void)current_kernel; \ - } - -# define INTEGRATOR_SHADOW_PATH_INIT(shadow_state, state, next_kernel, shadow_type) \ - IntegratorShadowState shadow_state = &state->shadow_type; \ - INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, queued_kernel) = next_kernel; -# define INTEGRATOR_SHADOW_PATH_NEXT(current_kernel, next_kernel) \ - { \ - INTEGRATOR_STATE_WRITE(state, shadow_path, queued_kernel) = next_kernel; \ - (void)current_kernel; \ - } -# define INTEGRATOR_SHADOW_PATH_TERMINATE(current_kernel) \ - { \ - INTEGRATOR_STATE_WRITE(state, shadow_path, queued_kernel) = 0; \ - (void)current_kernel; \ - } +ccl_device_forceinline void integrator_path_init(KernelGlobals kg, + IntegratorState state, + const DeviceKernel next_kernel) +{ + INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = next_kernel; +} + +ccl_device_forceinline void integrator_path_init_sorted(KernelGlobals kg, + IntegratorState state, + const DeviceKernel next_kernel, + const uint32_t key) +{ + INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = next_kernel; + (void)key; +} + +ccl_device_forceinline void integrator_path_next(KernelGlobals kg, + IntegratorState state, + const DeviceKernel current_kernel, + const DeviceKernel next_kernel) +{ + INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = next_kernel; + (void)current_kernel; +} + +ccl_device_forceinline void integrator_path_terminate(KernelGlobals kg, + IntegratorState state, + const DeviceKernel current_kernel) +{ + INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = 0; + (void)current_kernel; +} + +ccl_device_forceinline void integrator_path_next_sorted(KernelGlobals kg, + IntegratorState state, + const DeviceKernel current_kernel, + const DeviceKernel next_kernel, + const uint32_t key) +{ + INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = next_kernel; + (void)key; + (void)current_kernel; +} + +ccl_device_forceinline IntegratorShadowState integrator_shadow_path_init( + KernelGlobals kg, IntegratorState state, const DeviceKernel next_kernel, const bool is_ao) +{ + IntegratorShadowState shadow_state = (is_ao) ? &state->ao : &state->shadow; + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, queued_kernel) = next_kernel; + return shadow_state; +} + +ccl_device_forceinline void integrator_shadow_path_next(KernelGlobals kg, + IntegratorShadowState state, + const DeviceKernel current_kernel, + const DeviceKernel next_kernel) +{ + INTEGRATOR_STATE_WRITE(state, shadow_path, queued_kernel) = next_kernel; + (void)current_kernel; +} + +ccl_device_forceinline void integrator_shadow_path_terminate(KernelGlobals kg, + IntegratorShadowState state, + const DeviceKernel current_kernel) +{ + INTEGRATOR_STATE_WRITE(state, shadow_path, queued_kernel) = 0; + (void)current_kernel; +} #endif diff --git a/intern/cycles/kernel/integrator/state_template.h b/intern/cycles/kernel/integrator/state_template.h index e7e6db037b0..5c2af131945 100644 --- a/intern/cycles/kernel/integrator/state_template.h +++ b/intern/cycles/kernel/integrator/state_template.h @@ -37,11 +37,10 @@ KERNEL_STRUCT_MEMBER(path, uint32_t, flag, KERNEL_FEATURE_PATH_TRACING) /* enum PathRayMNEE */ KERNEL_STRUCT_MEMBER(path, uint8_t, mnee, KERNEL_FEATURE_PATH_TRACING) /* Multiple importance sampling - * The PDF of BSDF sampling at the last scatter point, and distance to the - * last scatter point minus the last ray segment. This distance lets us - * compute the complete distance through transparent surfaces and volumes. */ + * The PDF of BSDF sampling at the last scatter point, which is at ray distance + * zero and distance. Note that transparency and volume attenuation increase + * the ray tmin but keep P unmodified so that this works. */ KERNEL_STRUCT_MEMBER(path, float, mis_ray_pdf, KERNEL_FEATURE_PATH_TRACING) -KERNEL_STRUCT_MEMBER(path, float, mis_ray_t, KERNEL_FEATURE_PATH_TRACING) /* Filter glossy. */ KERNEL_STRUCT_MEMBER(path, float, min_ray_pdf, KERNEL_FEATURE_PATH_TRACING) /* Continuation probability for path termination. */ @@ -63,7 +62,8 @@ KERNEL_STRUCT_END(path) KERNEL_STRUCT_BEGIN(ray) KERNEL_STRUCT_MEMBER(ray, packed_float3, P, KERNEL_FEATURE_PATH_TRACING) KERNEL_STRUCT_MEMBER(ray, packed_float3, D, KERNEL_FEATURE_PATH_TRACING) -KERNEL_STRUCT_MEMBER(ray, float, t, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_MEMBER(ray, float, tmin, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_MEMBER(ray, float, tmax, KERNEL_FEATURE_PATH_TRACING) KERNEL_STRUCT_MEMBER(ray, float, time, KERNEL_FEATURE_PATH_TRACING) KERNEL_STRUCT_MEMBER(ray, float, dP, KERNEL_FEATURE_PATH_TRACING) KERNEL_STRUCT_MEMBER(ray, float, dD, KERNEL_FEATURE_PATH_TRACING) diff --git a/intern/cycles/kernel/integrator/state_util.h b/intern/cycles/kernel/integrator/state_util.h index 280db2d1aac..8dd58ad6bcd 100644 --- a/intern/cycles/kernel/integrator/state_util.h +++ b/intern/cycles/kernel/integrator/state_util.h @@ -17,7 +17,8 @@ ccl_device_forceinline void integrator_state_write_ray(KernelGlobals kg, { INTEGRATOR_STATE_WRITE(state, ray, P) = ray->P; INTEGRATOR_STATE_WRITE(state, ray, D) = ray->D; - INTEGRATOR_STATE_WRITE(state, ray, t) = ray->t; + INTEGRATOR_STATE_WRITE(state, ray, tmin) = ray->tmin; + INTEGRATOR_STATE_WRITE(state, ray, tmax) = ray->tmax; INTEGRATOR_STATE_WRITE(state, ray, time) = ray->time; INTEGRATOR_STATE_WRITE(state, ray, dP) = ray->dP; INTEGRATOR_STATE_WRITE(state, ray, dD) = ray->dD; @@ -29,7 +30,8 @@ ccl_device_forceinline void integrator_state_read_ray(KernelGlobals kg, { ray->P = INTEGRATOR_STATE(state, ray, P); ray->D = INTEGRATOR_STATE(state, ray, D); - ray->t = INTEGRATOR_STATE(state, ray, t); + ray->tmin = INTEGRATOR_STATE(state, ray, tmin); + ray->tmax = INTEGRATOR_STATE(state, ray, tmax); ray->time = INTEGRATOR_STATE(state, ray, time); ray->dP = INTEGRATOR_STATE(state, ray, dP); ray->dD = INTEGRATOR_STATE(state, ray, dD); @@ -42,7 +44,8 @@ ccl_device_forceinline void integrator_state_write_shadow_ray( { INTEGRATOR_STATE_WRITE(state, shadow_ray, P) = ray->P; INTEGRATOR_STATE_WRITE(state, shadow_ray, D) = ray->D; - INTEGRATOR_STATE_WRITE(state, shadow_ray, t) = ray->t; + INTEGRATOR_STATE_WRITE(state, shadow_ray, tmin) = ray->tmin; + INTEGRATOR_STATE_WRITE(state, shadow_ray, tmax) = ray->tmax; INTEGRATOR_STATE_WRITE(state, shadow_ray, time) = ray->time; INTEGRATOR_STATE_WRITE(state, shadow_ray, dP) = ray->dP; } @@ -53,7 +56,8 @@ ccl_device_forceinline void integrator_state_read_shadow_ray(KernelGlobals kg, { ray->P = INTEGRATOR_STATE(state, shadow_ray, P); ray->D = INTEGRATOR_STATE(state, shadow_ray, D); - ray->t = INTEGRATOR_STATE(state, shadow_ray, t); + ray->tmin = INTEGRATOR_STATE(state, shadow_ray, tmin); + ray->tmax = INTEGRATOR_STATE(state, shadow_ray, tmax); ray->time = INTEGRATOR_STATE(state, shadow_ray, time); ray->dP = INTEGRATOR_STATE(state, shadow_ray, dP); ray->dD = differential_zero_compact(); diff --git a/intern/cycles/kernel/integrator/subsurface.h b/intern/cycles/kernel/integrator/subsurface.h index 1e6fcf4aff0..2f96f215d8a 100644 --- a/intern/cycles/kernel/integrator/subsurface.h +++ b/intern/cycles/kernel/integrator/subsurface.h @@ -38,7 +38,8 @@ ccl_device int subsurface_bounce(KernelGlobals kg, /* Setup ray into surface. */ INTEGRATOR_STATE_WRITE(state, ray, P) = sd->P; INTEGRATOR_STATE_WRITE(state, ray, D) = bssrdf->N; - INTEGRATOR_STATE_WRITE(state, ray, t) = FLT_MAX; + INTEGRATOR_STATE_WRITE(state, ray, tmin) = 0.0f; + INTEGRATOR_STATE_WRITE(state, ray, tmax) = FLT_MAX; INTEGRATOR_STATE_WRITE(state, ray, dP) = differential_make_compact(sd->dP); INTEGRATOR_STATE_WRITE(state, ray, dD) = differential_zero_compact(); @@ -160,7 +161,7 @@ ccl_device_inline bool subsurface_scatter(KernelGlobals kg, IntegratorState stat /* Pretend ray is coming from the outside towards the exit point. This ensures * correct front/back facing normals. * TODO: find a more elegant solution? */ - ray.P += ray.D * ray.t * 2.0f; + ray.P += ray.D * ray.tmax * 2.0f; ray.D = -ray.D; integrator_state_write_isect(kg, state, &ss_isect.hits[0]); @@ -177,17 +178,23 @@ ccl_device_inline bool subsurface_scatter(KernelGlobals kg, IntegratorState stat const bool use_raytrace_kernel = (shader_flags & SD_HAS_RAYTRACE); if (use_caustics) { - INTEGRATOR_PATH_NEXT_SORTED(DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE, + integrator_path_next_sorted(kg, + state, + DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_MNEE, shader); } else if (use_raytrace_kernel) { - INTEGRATOR_PATH_NEXT_SORTED(DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE, + integrator_path_next_sorted(kg, + state, + DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE, shader); } else { - INTEGRATOR_PATH_NEXT_SORTED(DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE, + integrator_path_next_sorted(kg, + state, + DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE, shader); } diff --git a/intern/cycles/kernel/integrator/subsurface_disk.h b/intern/cycles/kernel/integrator/subsurface_disk.h index ae857c50493..2836934f6dd 100644 --- a/intern/cycles/kernel/integrator/subsurface_disk.h +++ b/intern/cycles/kernel/integrator/subsurface_disk.h @@ -82,7 +82,8 @@ ccl_device_inline bool subsurface_disk(KernelGlobals kg, /* Create ray. */ ray.P = P + disk_N * disk_height + disk_P; ray.D = -disk_N; - ray.t = 2.0f * disk_height; + ray.tmin = 0.0f; + ray.tmax = 2.0f * disk_height; ray.dP = ray_dP; ray.dD = differential_zero_compact(); ray.time = time; @@ -188,7 +189,8 @@ ccl_device_inline bool subsurface_disk(KernelGlobals kg, ray.P = ray.P + ray.D * ss_isect.hits[hit].t; ray.D = ss_isect.Ng[hit]; - ray.t = 1.0f; + ray.tmin = 0.0f; + ray.tmax = 1.0f; return true; } diff --git a/intern/cycles/kernel/integrator/subsurface_random_walk.h b/intern/cycles/kernel/integrator/subsurface_random_walk.h index 8094bf7159e..c1691030817 100644 --- a/intern/cycles/kernel/integrator/subsurface_random_walk.h +++ b/intern/cycles/kernel/integrator/subsurface_random_walk.h @@ -195,7 +195,8 @@ ccl_device_inline bool subsurface_random_walk(KernelGlobals kg, /* Setup ray. */ ray.P = P; ray.D = D; - ray.t = FLT_MAX; + ray.tmin = 0.0f; + ray.tmax = FLT_MAX; ray.time = time; ray.dP = ray_dP; ray.dD = differential_zero_compact(); @@ -370,10 +371,10 @@ ccl_device_inline bool subsurface_random_walk(KernelGlobals kg, * chance of connecting to it. * TODO: Maybe use less than 10 times the mean free path? */ if (bounce == 0) { - ray.t = max(t, 10.0f / (reduce_min(sigma_t))); + ray.tmax = max(t, 10.0f / (reduce_min(sigma_t))); } else { - ray.t = t; + ray.tmax = t; /* After the first bounce the object can intersect the same surface again */ ray.self.object = OBJECT_NONE; ray.self.prim = PRIM_NONE; @@ -384,12 +385,12 @@ ccl_device_inline bool subsurface_random_walk(KernelGlobals kg, if (hit) { #ifdef __KERNEL_GPU_RAYTRACING__ /* t is always in world space with OptiX and MetalRT. */ - ray.t = ss_isect.hits[0].t; + ray.tmax = ss_isect.hits[0].t; #else /* Compute world space distance to surface hit. */ float3 D = transform_direction(&ob_itfm, ray.D); D = normalize(D) * ss_isect.hits[0].t; - ray.t = len(transform_direction(&ob_tfm, D)); + ray.tmax = len(transform_direction(&ob_tfm, D)); #endif } @@ -397,16 +398,16 @@ ccl_device_inline bool subsurface_random_walk(KernelGlobals kg, /* Check if we hit the opposite side. */ if (hit) { have_opposite_interface = true; - opposite_distance = dot(ray.P + ray.t * ray.D - P, -N); + opposite_distance = dot(ray.P + ray.tmax * ray.D - P, -N); } /* Apart from the opposite side check, we were supposed to only trace up to distance t, * so check if there would have been a hit in that case. */ - hit = ray.t < t; + hit = ray.tmax < t; } /* Use the distance to the exit point for the throughput update if we found one. */ if (hit) { - t = ray.t; + t = ray.tmax; } /* Advance to new scatter location. */ diff --git a/intern/cycles/kernel/light/light.h b/intern/cycles/kernel/light/light.h index 1e7a333d013..b939489bb18 100644 --- a/intern/cycles/kernel/light/light.h +++ b/intern/cycles/kernel/light/light.h @@ -270,31 +270,26 @@ ccl_device bool lights_intersect(KernelGlobals kg, if (type == LIGHT_SPOT) { /* Spot/Disk light. */ - const float mis_ray_t = INTEGRATOR_STATE(state, path, mis_ray_t); - const float3 ray_P = ray->P - ray->D * mis_ray_t; - const float3 lightP = make_float3(klight->co[0], klight->co[1], klight->co[2]); const float radius = klight->spot.radius; if (radius == 0.0f) { continue; } /* disk oriented normal */ - const float3 lightN = normalize(ray_P - lightP); + const float3 lightN = normalize(ray->P - lightP); /* One sided. */ if (dot(ray->D, lightN) >= 0.0f) { continue; } float3 P; - if (!ray_disk_intersect(ray->P, ray->D, ray->t, lightP, lightN, radius, &P, &t)) { + if (!ray_disk_intersect( + ray->P, ray->D, ray->tmin, ray->tmax, lightP, lightN, radius, &P, &t)) { continue; } } else if (type == LIGHT_POINT) { /* Sphere light (aka, aligned disk light). */ - const float mis_ray_t = INTEGRATOR_STATE(state, path, mis_ray_t); - const float3 ray_P = ray->P - ray->D * mis_ray_t; - const float3 lightP = make_float3(klight->co[0], klight->co[1], klight->co[2]); const float radius = klight->spot.radius; if (radius == 0.0f) { @@ -302,9 +297,10 @@ ccl_device bool lights_intersect(KernelGlobals kg, } /* disk oriented normal */ - const float3 lightN = normalize(ray_P - lightP); + const float3 lightN = normalize(ray->P - lightP); float3 P; - if (!ray_disk_intersect(ray->P, ray->D, ray->t, lightP, lightN, radius, &P, &t)) { + if (!ray_disk_intersect( + ray->P, ray->D, ray->tmin, ray->tmax, lightP, lightN, radius, &P, &t)) { continue; } } @@ -330,8 +326,19 @@ ccl_device bool lights_intersect(KernelGlobals kg, const float3 light_P = make_float3(klight->co[0], klight->co[1], klight->co[2]); float3 P; - if (!ray_quad_intersect( - ray->P, ray->D, 0.0f, ray->t, light_P, axisu, axisv, Ng, &P, &t, &u, &v, is_round)) { + if (!ray_quad_intersect(ray->P, + ray->D, + ray->tmin, + ray->tmax, + light_P, + axisu, + axisv, + Ng, + &P, + &t, + &u, + &v, + is_round)) { continue; } } @@ -775,7 +782,8 @@ ccl_device_forceinline void triangle_light_sample(KernelGlobals kg, ls->D = z * B + safe_sqrtf(1.0f - z * z) * safe_normalize(C_ - dot(C_, B) * B); /* calculate intersection with the planar triangle */ - if (!ray_triangle_intersect(P, ls->D, FLT_MAX, V[0], V[1], V[2], &ls->u, &ls->v, &ls->t)) { + if (!ray_triangle_intersect( + P, ls->D, 0.0f, FLT_MAX, V[0], V[1], V[2], &ls->u, &ls->v, &ls->t)) { ls->pdf = 0.0f; return; } diff --git a/intern/cycles/kernel/light/sample.h b/intern/cycles/kernel/light/sample.h index 5cf7dce683a..210bb1b35c2 100644 --- a/intern/cycles/kernel/light/sample.h +++ b/intern/cycles/kernel/light/sample.h @@ -227,23 +227,24 @@ ccl_device_inline void shadow_ray_setup(ccl_private const ShaderData *ccl_restri if (ls->shader & SHADER_CAST_SHADOW) { /* setup ray */ ray->P = P; + ray->tmin = 0.0f; if (ls->t == FLT_MAX) { /* distant light */ ray->D = ls->D; - ray->t = ls->t; + ray->tmax = ls->t; } else { /* other lights, avoid self-intersection */ ray->D = ls->P - P; - ray->D = normalize_len(ray->D, &ray->t); + ray->D = normalize_len(ray->D, &ray->tmax); } } else { /* signal to not cast shadow ray */ ray->P = zero_float3(); ray->D = zero_float3(); - ray->t = 0.0f; + ray->tmax = 0.0f; } ray->dP = differential_make_compact(sd->dP); diff --git a/intern/cycles/kernel/osl/services.cpp b/intern/cycles/kernel/osl/services.cpp index 78c23b858c4..6b7981b7f3a 100644 --- a/intern/cycles/kernel/osl/services.cpp +++ b/intern/cycles/kernel/osl/services.cpp @@ -1094,10 +1094,8 @@ bool OSLRenderServices::get_background_attribute(const KernelGlobalsCPU *kg, ndc[0] = camera_world_to_ndc(kg, sd, sd->ray_P); if (derivatives) { - ndc[1] = camera_world_to_ndc(kg, sd, sd->ray_P + make_float3(sd->ray_dP, 0.0f, 0.0f)) - - ndc[0]; - ndc[2] = camera_world_to_ndc(kg, sd, sd->ray_P + make_float3(0.0f, sd->ray_dP, 0.0f)) - - ndc[0]; + ndc[1] = zero_float3(); + ndc[2] = zero_float3(); } } else { @@ -1671,7 +1669,8 @@ bool OSLRenderServices::trace(TraceOpt &options, ray.P = TO_FLOAT3(P); ray.D = TO_FLOAT3(R); - ray.t = (options.maxdist == 1.0e30f) ? FLT_MAX : options.maxdist - options.mindist; + ray.tmin = 0.0f; + ray.tmax = (options.maxdist == 1.0e30f) ? FLT_MAX : options.maxdist - options.mindist; ray.time = sd->time; ray.self.object = OBJECT_NONE; ray.self.prim = PRIM_NONE; diff --git a/intern/cycles/kernel/svm/ao.h b/intern/cycles/kernel/svm/ao.h index e66c535824c..c57c68d6230 100644 --- a/intern/cycles/kernel/svm/ao.h +++ b/intern/cycles/kernel/svm/ao.h @@ -59,7 +59,8 @@ ccl_device float svm_ao( Ray ray; ray.P = sd->P; ray.D = D.x * T + D.y * B + D.z * N; - ray.t = max_dist; + ray.tmin = 0.0f; + ray.tmax = max_dist; ray.time = sd->time; ray.self.object = sd->object; ray.self.prim = sd->prim; diff --git a/intern/cycles/kernel/svm/bevel.h b/intern/cycles/kernel/svm/bevel.h index 790437d8e82..4617a056a52 100644 --- a/intern/cycles/kernel/svm/bevel.h +++ b/intern/cycles/kernel/svm/bevel.h @@ -179,7 +179,8 @@ ccl_device float3 svm_bevel( Ray ray ccl_optional_struct_init; ray.P = sd->P + disk_N * disk_height + disk_P; ray.D = -disk_N; - ray.t = 2.0f * disk_height; + ray.tmin = 0.0f; + ray.tmax = 2.0f * disk_height; ray.dP = differential_zero_compact(); ray.dD = differential_zero_compact(); ray.time = sd->time; diff --git a/intern/cycles/kernel/svm/node_types_template.h b/intern/cycles/kernel/svm/node_types_template.h new file mode 100644 index 00000000000..39d279be4cb --- /dev/null +++ b/intern/cycles/kernel/svm/node_types_template.h @@ -0,0 +1,110 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2011-2022 Blender Foundation */ + +#ifndef SHADER_NODE_TYPE +# define SHADER_NODE_TYPE(name) +#endif + +/* NOTE: for best OpenCL performance, item definition in the enum must + * match the switch case order in `svm.h`. */ + +SHADER_NODE_TYPE(NODE_END) +SHADER_NODE_TYPE(NODE_SHADER_JUMP) +SHADER_NODE_TYPE(NODE_CLOSURE_BSDF) +SHADER_NODE_TYPE(NODE_CLOSURE_EMISSION) +SHADER_NODE_TYPE(NODE_CLOSURE_BACKGROUND) +SHADER_NODE_TYPE(NODE_CLOSURE_SET_WEIGHT) +SHADER_NODE_TYPE(NODE_CLOSURE_WEIGHT) +SHADER_NODE_TYPE(NODE_EMISSION_WEIGHT) +SHADER_NODE_TYPE(NODE_MIX_CLOSURE) +SHADER_NODE_TYPE(NODE_JUMP_IF_ZERO) +SHADER_NODE_TYPE(NODE_JUMP_IF_ONE) +SHADER_NODE_TYPE(NODE_GEOMETRY) +SHADER_NODE_TYPE(NODE_CONVERT) +SHADER_NODE_TYPE(NODE_TEX_COORD) +SHADER_NODE_TYPE(NODE_VALUE_F) +SHADER_NODE_TYPE(NODE_VALUE_V) +SHADER_NODE_TYPE(NODE_ATTR) +SHADER_NODE_TYPE(NODE_VERTEX_COLOR) +SHADER_NODE_TYPE(NODE_GEOMETRY_BUMP_DX) +SHADER_NODE_TYPE(NODE_GEOMETRY_BUMP_DY) +SHADER_NODE_TYPE(NODE_SET_DISPLACEMENT) +SHADER_NODE_TYPE(NODE_DISPLACEMENT) +SHADER_NODE_TYPE(NODE_VECTOR_DISPLACEMENT) +SHADER_NODE_TYPE(NODE_TEX_IMAGE) +SHADER_NODE_TYPE(NODE_TEX_IMAGE_BOX) +SHADER_NODE_TYPE(NODE_TEX_NOISE) +SHADER_NODE_TYPE(NODE_SET_BUMP) +SHADER_NODE_TYPE(NODE_ATTR_BUMP_DX) +SHADER_NODE_TYPE(NODE_ATTR_BUMP_DY) +SHADER_NODE_TYPE(NODE_VERTEX_COLOR_BUMP_DX) +SHADER_NODE_TYPE(NODE_VERTEX_COLOR_BUMP_DY) +SHADER_NODE_TYPE(NODE_TEX_COORD_BUMP_DX) +SHADER_NODE_TYPE(NODE_TEX_COORD_BUMP_DY) +SHADER_NODE_TYPE(NODE_CLOSURE_SET_NORMAL) +SHADER_NODE_TYPE(NODE_ENTER_BUMP_EVAL) +SHADER_NODE_TYPE(NODE_LEAVE_BUMP_EVAL) +SHADER_NODE_TYPE(NODE_HSV) +SHADER_NODE_TYPE(NODE_CLOSURE_HOLDOUT) +SHADER_NODE_TYPE(NODE_FRESNEL) +SHADER_NODE_TYPE(NODE_LAYER_WEIGHT) +SHADER_NODE_TYPE(NODE_CLOSURE_VOLUME) +SHADER_NODE_TYPE(NODE_PRINCIPLED_VOLUME) +SHADER_NODE_TYPE(NODE_MATH) +SHADER_NODE_TYPE(NODE_VECTOR_MATH) +SHADER_NODE_TYPE(NODE_RGB_RAMP) +SHADER_NODE_TYPE(NODE_GAMMA) +SHADER_NODE_TYPE(NODE_BRIGHTCONTRAST) +SHADER_NODE_TYPE(NODE_LIGHT_PATH) +SHADER_NODE_TYPE(NODE_OBJECT_INFO) +SHADER_NODE_TYPE(NODE_PARTICLE_INFO) +SHADER_NODE_TYPE(NODE_HAIR_INFO) +SHADER_NODE_TYPE(NODE_POINT_INFO) +SHADER_NODE_TYPE(NODE_TEXTURE_MAPPING) +SHADER_NODE_TYPE(NODE_MAPPING) +SHADER_NODE_TYPE(NODE_MIN_MAX) +SHADER_NODE_TYPE(NODE_CAMERA) +SHADER_NODE_TYPE(NODE_TEX_ENVIRONMENT) +SHADER_NODE_TYPE(NODE_TEX_SKY) +SHADER_NODE_TYPE(NODE_TEX_GRADIENT) +SHADER_NODE_TYPE(NODE_TEX_VORONOI) +SHADER_NODE_TYPE(NODE_TEX_MUSGRAVE) +SHADER_NODE_TYPE(NODE_TEX_WAVE) +SHADER_NODE_TYPE(NODE_TEX_MAGIC) +SHADER_NODE_TYPE(NODE_TEX_CHECKER) +SHADER_NODE_TYPE(NODE_TEX_BRICK) +SHADER_NODE_TYPE(NODE_TEX_WHITE_NOISE) +SHADER_NODE_TYPE(NODE_NORMAL) +SHADER_NODE_TYPE(NODE_LIGHT_FALLOFF) +SHADER_NODE_TYPE(NODE_IES) +SHADER_NODE_TYPE(NODE_CURVES) +SHADER_NODE_TYPE(NODE_TANGENT) +SHADER_NODE_TYPE(NODE_NORMAL_MAP) +SHADER_NODE_TYPE(NODE_INVERT) +SHADER_NODE_TYPE(NODE_MIX) +SHADER_NODE_TYPE(NODE_SEPARATE_COLOR) +SHADER_NODE_TYPE(NODE_COMBINE_COLOR) +SHADER_NODE_TYPE(NODE_SEPARATE_VECTOR) +SHADER_NODE_TYPE(NODE_COMBINE_VECTOR) +SHADER_NODE_TYPE(NODE_SEPARATE_HSV) +SHADER_NODE_TYPE(NODE_COMBINE_HSV) +SHADER_NODE_TYPE(NODE_VECTOR_ROTATE) +SHADER_NODE_TYPE(NODE_VECTOR_TRANSFORM) +SHADER_NODE_TYPE(NODE_WIREFRAME) +SHADER_NODE_TYPE(NODE_WAVELENGTH) +SHADER_NODE_TYPE(NODE_BLACKBODY) +SHADER_NODE_TYPE(NODE_MAP_RANGE) +SHADER_NODE_TYPE(NODE_VECTOR_MAP_RANGE) +SHADER_NODE_TYPE(NODE_CLAMP) +SHADER_NODE_TYPE(NODE_BEVEL) +SHADER_NODE_TYPE(NODE_AMBIENT_OCCLUSION) +SHADER_NODE_TYPE(NODE_TEX_VOXEL) +SHADER_NODE_TYPE(NODE_AOV_START) +SHADER_NODE_TYPE(NODE_AOV_COLOR) +SHADER_NODE_TYPE(NODE_AOV_VALUE) +SHADER_NODE_TYPE(NODE_FLOAT_CURVE) + +/* Padding for struct alignment. */ +SHADER_NODE_TYPE(NODE_PAD1) + +#undef SHADER_NODE_TYPE diff --git a/intern/cycles/kernel/svm/svm.h b/intern/cycles/kernel/svm/svm.h index 8fd41ec8531..9d6d3e9222c 100644 --- a/intern/cycles/kernel/svm/svm.h +++ b/intern/cycles/kernel/svm/svm.h @@ -204,6 +204,15 @@ CCL_NAMESPACE_END CCL_NAMESPACE_BEGIN +#ifdef __KERNEL_USE_DATA_CONSTANTS__ +# define SVM_CASE(node) \ + case node: \ + if (!kernel_data_svm_usage_##node) \ + break; +#else +# define SVM_CASE(node) case node: +#endif + /* Main Interpreter Loop */ template<uint node_feature_mask, ShaderType type, typename ConstIntegratorGenericState> ccl_device void svm_eval_nodes(KernelGlobals kg, @@ -219,9 +228,10 @@ ccl_device void svm_eval_nodes(KernelGlobals kg, uint4 node = read_node(kg, &offset); switch (node.x) { - case NODE_END: - return; - case NODE_SHADER_JUMP: { + SVM_CASE(NODE_END) + return; + SVM_CASE(NODE_SHADER_JUMP) + { if (type == SHADER_TYPE_SURFACE) offset = node.y; else if (type == SHADER_TYPE_VOLUME) @@ -232,351 +242,349 @@ ccl_device void svm_eval_nodes(KernelGlobals kg, return; break; } - case NODE_CLOSURE_BSDF: - offset = svm_node_closure_bsdf<node_feature_mask, type>( - kg, sd, stack, node, path_flag, offset); - break; - case NODE_CLOSURE_EMISSION: - IF_KERNEL_NODES_FEATURE(EMISSION) - { - svm_node_closure_emission(sd, stack, node); - } - break; - case NODE_CLOSURE_BACKGROUND: - IF_KERNEL_NODES_FEATURE(EMISSION) - { - svm_node_closure_background(sd, stack, node); - } - break; - case NODE_CLOSURE_SET_WEIGHT: - svm_node_closure_set_weight(sd, node.y, node.z, node.w); - break; - case NODE_CLOSURE_WEIGHT: - svm_node_closure_weight(sd, stack, node.y); - break; - case NODE_EMISSION_WEIGHT: - IF_KERNEL_NODES_FEATURE(EMISSION) - { - svm_node_emission_weight(kg, sd, stack, node); - } - break; - case NODE_MIX_CLOSURE: - svm_node_mix_closure(sd, stack, node); - break; - case NODE_JUMP_IF_ZERO: - if (stack_load_float(stack, node.z) <= 0.0f) - offset += node.y; - break; - case NODE_JUMP_IF_ONE: - if (stack_load_float(stack, node.z) >= 1.0f) - offset += node.y; - break; - case NODE_GEOMETRY: - svm_node_geometry(kg, sd, stack, node.y, node.z); - break; - case NODE_CONVERT: - svm_node_convert(kg, sd, stack, node.y, node.z, node.w); - break; - case NODE_TEX_COORD: - offset = svm_node_tex_coord(kg, sd, path_flag, stack, node, offset); - break; - case NODE_VALUE_F: - svm_node_value_f(kg, sd, stack, node.y, node.z); - break; - case NODE_VALUE_V: - offset = svm_node_value_v(kg, sd, stack, node.y, offset); - break; - case NODE_ATTR: - svm_node_attr<node_feature_mask>(kg, sd, stack, node); - break; - case NODE_VERTEX_COLOR: - svm_node_vertex_color(kg, sd, stack, node.y, node.z, node.w); - break; - case NODE_GEOMETRY_BUMP_DX: - IF_KERNEL_NODES_FEATURE(BUMP) - { - svm_node_geometry_bump_dx(kg, sd, stack, node.y, node.z); - } - break; - case NODE_GEOMETRY_BUMP_DY: - IF_KERNEL_NODES_FEATURE(BUMP) - { - svm_node_geometry_bump_dy(kg, sd, stack, node.y, node.z); - } - break; - case NODE_SET_DISPLACEMENT: - svm_node_set_displacement<node_feature_mask>(kg, sd, stack, node.y); - break; - case NODE_DISPLACEMENT: - svm_node_displacement<node_feature_mask>(kg, sd, stack, node); - break; - case NODE_VECTOR_DISPLACEMENT: - offset = svm_node_vector_displacement<node_feature_mask>(kg, sd, stack, node, offset); - break; - case NODE_TEX_IMAGE: - offset = svm_node_tex_image(kg, sd, stack, node, offset); - break; - case NODE_TEX_IMAGE_BOX: - svm_node_tex_image_box(kg, sd, stack, node); - break; - case NODE_TEX_NOISE: - offset = svm_node_tex_noise(kg, sd, stack, node.y, node.z, node.w, offset); - break; - case NODE_SET_BUMP: - svm_node_set_bump<node_feature_mask>(kg, sd, stack, node); - break; - case NODE_ATTR_BUMP_DX: - IF_KERNEL_NODES_FEATURE(BUMP) - { - svm_node_attr_bump_dx(kg, sd, stack, node); - } - break; - case NODE_ATTR_BUMP_DY: - IF_KERNEL_NODES_FEATURE(BUMP) - { - svm_node_attr_bump_dy(kg, sd, stack, node); - } - break; - case NODE_VERTEX_COLOR_BUMP_DX: - IF_KERNEL_NODES_FEATURE(BUMP) - { - svm_node_vertex_color_bump_dx(kg, sd, stack, node.y, node.z, node.w); - } - break; - case NODE_VERTEX_COLOR_BUMP_DY: - IF_KERNEL_NODES_FEATURE(BUMP) - { - svm_node_vertex_color_bump_dy(kg, sd, stack, node.y, node.z, node.w); - } - break; - case NODE_TEX_COORD_BUMP_DX: - IF_KERNEL_NODES_FEATURE(BUMP) - { - offset = svm_node_tex_coord_bump_dx(kg, sd, path_flag, stack, node, offset); - } - break; - case NODE_TEX_COORD_BUMP_DY: - IF_KERNEL_NODES_FEATURE(BUMP) - { - offset = svm_node_tex_coord_bump_dy(kg, sd, path_flag, stack, node, offset); - } - break; - case NODE_CLOSURE_SET_NORMAL: - IF_KERNEL_NODES_FEATURE(BUMP) - { - svm_node_set_normal(kg, sd, stack, node.y, node.z); - } - break; - case NODE_ENTER_BUMP_EVAL: - IF_KERNEL_NODES_FEATURE(BUMP_STATE) - { - svm_node_enter_bump_eval(kg, sd, stack, node.y); - } - break; - case NODE_LEAVE_BUMP_EVAL: - IF_KERNEL_NODES_FEATURE(BUMP_STATE) - { - svm_node_leave_bump_eval(kg, sd, stack, node.y); - } - break; - case NODE_HSV: - svm_node_hsv(kg, sd, stack, node); - break; - - case NODE_CLOSURE_HOLDOUT: - svm_node_closure_holdout(sd, stack, node); - break; - case NODE_FRESNEL: - svm_node_fresnel(sd, stack, node.y, node.z, node.w); - break; - case NODE_LAYER_WEIGHT: - svm_node_layer_weight(sd, stack, node); - break; - case NODE_CLOSURE_VOLUME: - IF_KERNEL_NODES_FEATURE(VOLUME) - { - svm_node_closure_volume<type>(kg, sd, stack, node); - } - break; - case NODE_PRINCIPLED_VOLUME: - IF_KERNEL_NODES_FEATURE(VOLUME) - { - offset = svm_node_principled_volume<type>(kg, sd, stack, node, path_flag, offset); - } - break; - case NODE_MATH: - svm_node_math(kg, sd, stack, node.y, node.z, node.w); - break; - case NODE_VECTOR_MATH: - offset = svm_node_vector_math(kg, sd, stack, node.y, node.z, node.w, offset); - break; - case NODE_RGB_RAMP: - offset = svm_node_rgb_ramp(kg, sd, stack, node, offset); - break; - case NODE_GAMMA: - svm_node_gamma(sd, stack, node.y, node.z, node.w); - break; - case NODE_BRIGHTCONTRAST: - svm_node_brightness(sd, stack, node.y, node.z, node.w); - break; - case NODE_LIGHT_PATH: - svm_node_light_path<node_feature_mask>(kg, state, sd, stack, node.y, node.z, path_flag); - break; - case NODE_OBJECT_INFO: - svm_node_object_info(kg, sd, stack, node.y, node.z); - break; - case NODE_PARTICLE_INFO: - svm_node_particle_info(kg, sd, stack, node.y, node.z); - break; + SVM_CASE(NODE_CLOSURE_BSDF) + offset = svm_node_closure_bsdf<node_feature_mask, type>( + kg, sd, stack, node, path_flag, offset); + break; + SVM_CASE(NODE_CLOSURE_EMISSION) + IF_KERNEL_NODES_FEATURE(EMISSION) + { + svm_node_closure_emission(sd, stack, node); + } + break; + SVM_CASE(NODE_CLOSURE_BACKGROUND) + IF_KERNEL_NODES_FEATURE(EMISSION) + { + svm_node_closure_background(sd, stack, node); + } + break; + SVM_CASE(NODE_CLOSURE_SET_WEIGHT) + svm_node_closure_set_weight(sd, node.y, node.z, node.w); + break; + SVM_CASE(NODE_CLOSURE_WEIGHT) + svm_node_closure_weight(sd, stack, node.y); + break; + SVM_CASE(NODE_EMISSION_WEIGHT) + IF_KERNEL_NODES_FEATURE(EMISSION) + { + svm_node_emission_weight(kg, sd, stack, node); + } + break; + SVM_CASE(NODE_MIX_CLOSURE) + svm_node_mix_closure(sd, stack, node); + break; + SVM_CASE(NODE_JUMP_IF_ZERO) + if (stack_load_float(stack, node.z) <= 0.0f) + offset += node.y; + break; + SVM_CASE(NODE_JUMP_IF_ONE) + if (stack_load_float(stack, node.z) >= 1.0f) + offset += node.y; + break; + SVM_CASE(NODE_GEOMETRY) + svm_node_geometry(kg, sd, stack, node.y, node.z); + break; + SVM_CASE(NODE_CONVERT) + svm_node_convert(kg, sd, stack, node.y, node.z, node.w); + break; + SVM_CASE(NODE_TEX_COORD) + offset = svm_node_tex_coord(kg, sd, path_flag, stack, node, offset); + break; + SVM_CASE(NODE_VALUE_F) + svm_node_value_f(kg, sd, stack, node.y, node.z); + break; + SVM_CASE(NODE_VALUE_V) + offset = svm_node_value_v(kg, sd, stack, node.y, offset); + break; + SVM_CASE(NODE_ATTR) + svm_node_attr<node_feature_mask>(kg, sd, stack, node); + break; + SVM_CASE(NODE_VERTEX_COLOR) + svm_node_vertex_color(kg, sd, stack, node.y, node.z, node.w); + break; + SVM_CASE(NODE_GEOMETRY_BUMP_DX) + IF_KERNEL_NODES_FEATURE(BUMP) + { + svm_node_geometry_bump_dx(kg, sd, stack, node.y, node.z); + } + break; + SVM_CASE(NODE_GEOMETRY_BUMP_DY) + IF_KERNEL_NODES_FEATURE(BUMP) + { + svm_node_geometry_bump_dy(kg, sd, stack, node.y, node.z); + } + break; + SVM_CASE(NODE_SET_DISPLACEMENT) + svm_node_set_displacement<node_feature_mask>(kg, sd, stack, node.y); + break; + SVM_CASE(NODE_DISPLACEMENT) + svm_node_displacement<node_feature_mask>(kg, sd, stack, node); + break; + SVM_CASE(NODE_VECTOR_DISPLACEMENT) + offset = svm_node_vector_displacement<node_feature_mask>(kg, sd, stack, node, offset); + break; + SVM_CASE(NODE_TEX_IMAGE) + offset = svm_node_tex_image(kg, sd, stack, node, offset); + break; + SVM_CASE(NODE_TEX_IMAGE_BOX) + svm_node_tex_image_box(kg, sd, stack, node); + break; + SVM_CASE(NODE_TEX_NOISE) + offset = svm_node_tex_noise(kg, sd, stack, node.y, node.z, node.w, offset); + break; + SVM_CASE(NODE_SET_BUMP) + svm_node_set_bump<node_feature_mask>(kg, sd, stack, node); + break; + SVM_CASE(NODE_ATTR_BUMP_DX) + IF_KERNEL_NODES_FEATURE(BUMP) + { + svm_node_attr_bump_dx(kg, sd, stack, node); + } + break; + SVM_CASE(NODE_ATTR_BUMP_DY) + IF_KERNEL_NODES_FEATURE(BUMP) + { + svm_node_attr_bump_dy(kg, sd, stack, node); + } + break; + SVM_CASE(NODE_VERTEX_COLOR_BUMP_DX) + IF_KERNEL_NODES_FEATURE(BUMP) + { + svm_node_vertex_color_bump_dx(kg, sd, stack, node.y, node.z, node.w); + } + break; + SVM_CASE(NODE_VERTEX_COLOR_BUMP_DY) + IF_KERNEL_NODES_FEATURE(BUMP) + { + svm_node_vertex_color_bump_dy(kg, sd, stack, node.y, node.z, node.w); + } + break; + SVM_CASE(NODE_TEX_COORD_BUMP_DX) + IF_KERNEL_NODES_FEATURE(BUMP) + { + offset = svm_node_tex_coord_bump_dx(kg, sd, path_flag, stack, node, offset); + } + break; + SVM_CASE(NODE_TEX_COORD_BUMP_DY) + IF_KERNEL_NODES_FEATURE(BUMP) + { + offset = svm_node_tex_coord_bump_dy(kg, sd, path_flag, stack, node, offset); + } + break; + SVM_CASE(NODE_CLOSURE_SET_NORMAL) + IF_KERNEL_NODES_FEATURE(BUMP) + { + svm_node_set_normal(kg, sd, stack, node.y, node.z); + } + break; + SVM_CASE(NODE_ENTER_BUMP_EVAL) + IF_KERNEL_NODES_FEATURE(BUMP_STATE) + { + svm_node_enter_bump_eval(kg, sd, stack, node.y); + } + break; + SVM_CASE(NODE_LEAVE_BUMP_EVAL) + IF_KERNEL_NODES_FEATURE(BUMP_STATE) + { + svm_node_leave_bump_eval(kg, sd, stack, node.y); + } + break; + SVM_CASE(NODE_HSV) + svm_node_hsv(kg, sd, stack, node); + break; + SVM_CASE(NODE_CLOSURE_HOLDOUT) + svm_node_closure_holdout(sd, stack, node); + break; + SVM_CASE(NODE_FRESNEL) + svm_node_fresnel(sd, stack, node.y, node.z, node.w); + break; + SVM_CASE(NODE_LAYER_WEIGHT) + svm_node_layer_weight(sd, stack, node); + break; + SVM_CASE(NODE_CLOSURE_VOLUME) + IF_KERNEL_NODES_FEATURE(VOLUME) + { + svm_node_closure_volume<type>(kg, sd, stack, node); + } + break; + SVM_CASE(NODE_PRINCIPLED_VOLUME) + IF_KERNEL_NODES_FEATURE(VOLUME) + { + offset = svm_node_principled_volume<type>(kg, sd, stack, node, path_flag, offset); + } + break; + SVM_CASE(NODE_MATH) + svm_node_math(kg, sd, stack, node.y, node.z, node.w); + break; + SVM_CASE(NODE_VECTOR_MATH) + offset = svm_node_vector_math(kg, sd, stack, node.y, node.z, node.w, offset); + break; + SVM_CASE(NODE_RGB_RAMP) + offset = svm_node_rgb_ramp(kg, sd, stack, node, offset); + break; + SVM_CASE(NODE_GAMMA) + svm_node_gamma(sd, stack, node.y, node.z, node.w); + break; + SVM_CASE(NODE_BRIGHTCONTRAST) + svm_node_brightness(sd, stack, node.y, node.z, node.w); + break; + SVM_CASE(NODE_LIGHT_PATH) + svm_node_light_path<node_feature_mask>(kg, state, sd, stack, node.y, node.z, path_flag); + break; + SVM_CASE(NODE_OBJECT_INFO) + svm_node_object_info(kg, sd, stack, node.y, node.z); + break; + SVM_CASE(NODE_PARTICLE_INFO) + svm_node_particle_info(kg, sd, stack, node.y, node.z); + break; #if defined(__HAIR__) - case NODE_HAIR_INFO: - svm_node_hair_info(kg, sd, stack, node.y, node.z); - break; + SVM_CASE(NODE_HAIR_INFO) + svm_node_hair_info(kg, sd, stack, node.y, node.z); + break; #endif #if defined(__POINTCLOUD__) - case NODE_POINT_INFO: - svm_node_point_info(kg, sd, stack, node.y, node.z); - break; + SVM_CASE(NODE_POINT_INFO) + svm_node_point_info(kg, sd, stack, node.y, node.z); + break; #endif - case NODE_TEXTURE_MAPPING: - offset = svm_node_texture_mapping(kg, sd, stack, node.y, node.z, offset); - break; - case NODE_MAPPING: - svm_node_mapping(kg, sd, stack, node.y, node.z, node.w); - break; - case NODE_MIN_MAX: - offset = svm_node_min_max(kg, sd, stack, node.y, node.z, offset); - break; - case NODE_CAMERA: - svm_node_camera(kg, sd, stack, node.y, node.z, node.w); - break; - case NODE_TEX_ENVIRONMENT: - svm_node_tex_environment(kg, sd, stack, node); - break; - case NODE_TEX_SKY: - offset = svm_node_tex_sky(kg, sd, stack, node, offset); - break; - case NODE_TEX_GRADIENT: - svm_node_tex_gradient(sd, stack, node); - break; - case NODE_TEX_VORONOI: - offset = svm_node_tex_voronoi<node_feature_mask>( - kg, sd, stack, node.y, node.z, node.w, offset); - break; - case NODE_TEX_MUSGRAVE: - offset = svm_node_tex_musgrave(kg, sd, stack, node.y, node.z, node.w, offset); - break; - case NODE_TEX_WAVE: - offset = svm_node_tex_wave(kg, sd, stack, node, offset); - break; - case NODE_TEX_MAGIC: - offset = svm_node_tex_magic(kg, sd, stack, node, offset); - break; - case NODE_TEX_CHECKER: - svm_node_tex_checker(kg, sd, stack, node); - break; - case NODE_TEX_BRICK: - offset = svm_node_tex_brick(kg, sd, stack, node, offset); - break; - case NODE_TEX_WHITE_NOISE: - svm_node_tex_white_noise(kg, sd, stack, node.y, node.z, node.w); - break; - case NODE_NORMAL: - offset = svm_node_normal(kg, sd, stack, node.y, node.z, node.w, offset); - break; - case NODE_LIGHT_FALLOFF: - svm_node_light_falloff(sd, stack, node); - break; - case NODE_IES: - svm_node_ies(kg, sd, stack, node); - break; - case NODE_RGB_CURVES: - case NODE_VECTOR_CURVES: - offset = svm_node_curves(kg, sd, stack, node, offset); - break; - case NODE_FLOAT_CURVE: - offset = svm_node_curve(kg, sd, stack, node, offset); - break; - case NODE_TANGENT: - svm_node_tangent(kg, sd, stack, node); - break; - case NODE_NORMAL_MAP: - svm_node_normal_map(kg, sd, stack, node); - break; - case NODE_INVERT: - svm_node_invert(sd, stack, node.y, node.z, node.w); - break; - case NODE_MIX: - offset = svm_node_mix(kg, sd, stack, node.y, node.z, node.w, offset); - break; - case NODE_SEPARATE_COLOR: - svm_node_separate_color(kg, sd, stack, node.y, node.z, node.w); - break; - case NODE_COMBINE_COLOR: - svm_node_combine_color(kg, sd, stack, node.y, node.z, node.w); - break; - case NODE_SEPARATE_VECTOR: - svm_node_separate_vector(sd, stack, node.y, node.z, node.w); - break; - case NODE_COMBINE_VECTOR: - svm_node_combine_vector(sd, stack, node.y, node.z, node.w); - break; - case NODE_SEPARATE_HSV: - offset = svm_node_separate_hsv(kg, sd, stack, node.y, node.z, node.w, offset); - break; - case NODE_COMBINE_HSV: - offset = svm_node_combine_hsv(kg, sd, stack, node.y, node.z, node.w, offset); - break; - case NODE_VECTOR_ROTATE: - svm_node_vector_rotate(sd, stack, node.y, node.z, node.w); - break; - case NODE_VECTOR_TRANSFORM: - svm_node_vector_transform(kg, sd, stack, node); - break; - case NODE_WIREFRAME: - svm_node_wireframe(kg, sd, stack, node); - break; - case NODE_WAVELENGTH: - svm_node_wavelength(kg, sd, stack, node.y, node.z); - break; - case NODE_BLACKBODY: - svm_node_blackbody(kg, sd, stack, node.y, node.z); - break; - case NODE_MAP_RANGE: - offset = svm_node_map_range(kg, sd, stack, node.y, node.z, node.w, offset); - break; - case NODE_VECTOR_MAP_RANGE: - offset = svm_node_vector_map_range(kg, sd, stack, node.y, node.z, node.w, offset); - break; - case NODE_CLAMP: - offset = svm_node_clamp(kg, sd, stack, node.y, node.z, node.w, offset); - break; + SVM_CASE(NODE_TEXTURE_MAPPING) + offset = svm_node_texture_mapping(kg, sd, stack, node.y, node.z, offset); + break; + SVM_CASE(NODE_MAPPING) + svm_node_mapping(kg, sd, stack, node.y, node.z, node.w); + break; + SVM_CASE(NODE_MIN_MAX) + offset = svm_node_min_max(kg, sd, stack, node.y, node.z, offset); + break; + SVM_CASE(NODE_CAMERA) + svm_node_camera(kg, sd, stack, node.y, node.z, node.w); + break; + SVM_CASE(NODE_TEX_ENVIRONMENT) + svm_node_tex_environment(kg, sd, stack, node); + break; + SVM_CASE(NODE_TEX_SKY) + offset = svm_node_tex_sky(kg, sd, stack, node, offset); + break; + SVM_CASE(NODE_TEX_GRADIENT) + svm_node_tex_gradient(sd, stack, node); + break; + SVM_CASE(NODE_TEX_VORONOI) + offset = svm_node_tex_voronoi<node_feature_mask>( + kg, sd, stack, node.y, node.z, node.w, offset); + break; + SVM_CASE(NODE_TEX_MUSGRAVE) + offset = svm_node_tex_musgrave(kg, sd, stack, node.y, node.z, node.w, offset); + break; + SVM_CASE(NODE_TEX_WAVE) + offset = svm_node_tex_wave(kg, sd, stack, node, offset); + break; + SVM_CASE(NODE_TEX_MAGIC) + offset = svm_node_tex_magic(kg, sd, stack, node, offset); + break; + SVM_CASE(NODE_TEX_CHECKER) + svm_node_tex_checker(kg, sd, stack, node); + break; + SVM_CASE(NODE_TEX_BRICK) + offset = svm_node_tex_brick(kg, sd, stack, node, offset); + break; + SVM_CASE(NODE_TEX_WHITE_NOISE) + svm_node_tex_white_noise(kg, sd, stack, node.y, node.z, node.w); + break; + SVM_CASE(NODE_NORMAL) + offset = svm_node_normal(kg, sd, stack, node.y, node.z, node.w, offset); + break; + SVM_CASE(NODE_LIGHT_FALLOFF) + svm_node_light_falloff(sd, stack, node); + break; + SVM_CASE(NODE_IES) + svm_node_ies(kg, sd, stack, node); + break; + SVM_CASE(NODE_CURVES) + offset = svm_node_curves(kg, sd, stack, node, offset); + break; + SVM_CASE(NODE_FLOAT_CURVE) + offset = svm_node_curve(kg, sd, stack, node, offset); + break; + SVM_CASE(NODE_TANGENT) + svm_node_tangent(kg, sd, stack, node); + break; + SVM_CASE(NODE_NORMAL_MAP) + svm_node_normal_map(kg, sd, stack, node); + break; + SVM_CASE(NODE_INVERT) + svm_node_invert(sd, stack, node.y, node.z, node.w); + break; + SVM_CASE(NODE_MIX) + offset = svm_node_mix(kg, sd, stack, node.y, node.z, node.w, offset); + break; + SVM_CASE(NODE_SEPARATE_COLOR) + svm_node_separate_color(kg, sd, stack, node.y, node.z, node.w); + break; + SVM_CASE(NODE_COMBINE_COLOR) + svm_node_combine_color(kg, sd, stack, node.y, node.z, node.w); + break; + SVM_CASE(NODE_SEPARATE_VECTOR) + svm_node_separate_vector(sd, stack, node.y, node.z, node.w); + break; + SVM_CASE(NODE_COMBINE_VECTOR) + svm_node_combine_vector(sd, stack, node.y, node.z, node.w); + break; + SVM_CASE(NODE_SEPARATE_HSV) + offset = svm_node_separate_hsv(kg, sd, stack, node.y, node.z, node.w, offset); + break; + SVM_CASE(NODE_COMBINE_HSV) + offset = svm_node_combine_hsv(kg, sd, stack, node.y, node.z, node.w, offset); + break; + SVM_CASE(NODE_VECTOR_ROTATE) + svm_node_vector_rotate(sd, stack, node.y, node.z, node.w); + break; + SVM_CASE(NODE_VECTOR_TRANSFORM) + svm_node_vector_transform(kg, sd, stack, node); + break; + SVM_CASE(NODE_WIREFRAME) + svm_node_wireframe(kg, sd, stack, node); + break; + SVM_CASE(NODE_WAVELENGTH) + svm_node_wavelength(kg, sd, stack, node.y, node.z); + break; + SVM_CASE(NODE_BLACKBODY) + svm_node_blackbody(kg, sd, stack, node.y, node.z); + break; + SVM_CASE(NODE_MAP_RANGE) + offset = svm_node_map_range(kg, sd, stack, node.y, node.z, node.w, offset); + break; + SVM_CASE(NODE_VECTOR_MAP_RANGE) + offset = svm_node_vector_map_range(kg, sd, stack, node.y, node.z, node.w, offset); + break; + SVM_CASE(NODE_CLAMP) + offset = svm_node_clamp(kg, sd, stack, node.y, node.z, node.w, offset); + break; #ifdef __SHADER_RAYTRACE__ - case NODE_BEVEL: - svm_node_bevel<node_feature_mask>(kg, state, sd, stack, node); - break; - case NODE_AMBIENT_OCCLUSION: - svm_node_ao<node_feature_mask>(kg, state, sd, stack, node); - break; + SVM_CASE(NODE_BEVEL) + svm_node_bevel<node_feature_mask>(kg, state, sd, stack, node); + break; + SVM_CASE(NODE_AMBIENT_OCCLUSION) + svm_node_ao<node_feature_mask>(kg, state, sd, stack, node); + break; #endif - case NODE_TEX_VOXEL: - IF_KERNEL_NODES_FEATURE(VOLUME) - { - offset = svm_node_tex_voxel(kg, sd, stack, node, offset); - } - break; - case NODE_AOV_START: - if (!svm_node_aov_check(path_flag, render_buffer)) { - return; - } - break; - case NODE_AOV_COLOR: - svm_node_aov_color<node_feature_mask>(kg, state, sd, stack, node, render_buffer); - break; - case NODE_AOV_VALUE: - svm_node_aov_value<node_feature_mask>(kg, state, sd, stack, node, render_buffer); - break; + SVM_CASE(NODE_TEX_VOXEL) + IF_KERNEL_NODES_FEATURE(VOLUME) + { + offset = svm_node_tex_voxel(kg, sd, stack, node, offset); + } + break; + SVM_CASE(NODE_AOV_START) + if (!svm_node_aov_check(path_flag, render_buffer)) { + return; + } + break; + SVM_CASE(NODE_AOV_COLOR) + svm_node_aov_color<node_feature_mask>(kg, state, sd, stack, node, render_buffer); + break; + SVM_CASE(NODE_AOV_VALUE) + svm_node_aov_value<node_feature_mask>(kg, state, sd, stack, node, render_buffer); + break; default: kernel_assert(!"Unknown node type was passed to the SVM machine"); return; diff --git a/intern/cycles/kernel/svm/tex_coord.h b/intern/cycles/kernel/svm/tex_coord.h index d9138796c45..2a0130e11d4 100644 --- a/intern/cycles/kernel/svm/tex_coord.h +++ b/intern/cycles/kernel/svm/tex_coord.h @@ -138,7 +138,7 @@ ccl_device_noinline int svm_node_tex_coord_bump_dx(KernelGlobals kg, case NODE_TEXCO_WINDOW: { if ((path_flag & PATH_RAY_CAMERA) && sd->object == OBJECT_NONE && kernel_data.cam.type == CAMERA_ORTHOGRAPHIC) - data = camera_world_to_ndc(kg, sd, sd->ray_P + make_float3(sd->ray_dP, 0.0f, 0.0f)); + data = camera_world_to_ndc(kg, sd, sd->ray_P); else data = camera_world_to_ndc(kg, sd, sd->P + sd->dP.dx); data.z = 0.0f; @@ -223,7 +223,7 @@ ccl_device_noinline int svm_node_tex_coord_bump_dy(KernelGlobals kg, case NODE_TEXCO_WINDOW: { if ((path_flag & PATH_RAY_CAMERA) && sd->object == OBJECT_NONE && kernel_data.cam.type == CAMERA_ORTHOGRAPHIC) - data = camera_world_to_ndc(kg, sd, sd->ray_P + make_float3(0.0f, sd->ray_dP, 0.0f)); + data = camera_world_to_ndc(kg, sd, sd->ray_P); else data = camera_world_to_ndc(kg, sd, sd->P + sd->dP.dy); data.z = 0.0f; diff --git a/intern/cycles/kernel/svm/types.h b/intern/cycles/kernel/svm/types.h index 82109ec4c4f..12d0ec141e6 100644 --- a/intern/cycles/kernel/svm/types.h +++ b/intern/cycles/kernel/svm/types.h @@ -17,104 +17,9 @@ CCL_NAMESPACE_BEGIN /* Nodes */ typedef enum ShaderNodeType { - NODE_END = 0, - NODE_SHADER_JUMP, - NODE_CLOSURE_BSDF, - NODE_CLOSURE_EMISSION, - NODE_CLOSURE_BACKGROUND, - NODE_CLOSURE_SET_WEIGHT, - NODE_CLOSURE_WEIGHT, - NODE_EMISSION_WEIGHT, - NODE_MIX_CLOSURE, - NODE_JUMP_IF_ZERO, - NODE_JUMP_IF_ONE, - NODE_GEOMETRY, - NODE_CONVERT, - NODE_TEX_COORD, - NODE_VALUE_F, - NODE_VALUE_V, - NODE_ATTR, - NODE_VERTEX_COLOR, - NODE_GEOMETRY_BUMP_DX, - NODE_GEOMETRY_BUMP_DY, - NODE_SET_DISPLACEMENT, - NODE_DISPLACEMENT, - NODE_VECTOR_DISPLACEMENT, - NODE_TEX_IMAGE, - NODE_TEX_IMAGE_BOX, - NODE_TEX_NOISE, - NODE_SET_BUMP, - NODE_ATTR_BUMP_DX, - NODE_ATTR_BUMP_DY, - NODE_VERTEX_COLOR_BUMP_DX, - NODE_VERTEX_COLOR_BUMP_DY, - NODE_TEX_COORD_BUMP_DX, - NODE_TEX_COORD_BUMP_DY, - NODE_CLOSURE_SET_NORMAL, - NODE_ENTER_BUMP_EVAL, - NODE_LEAVE_BUMP_EVAL, - NODE_HSV, - NODE_CLOSURE_HOLDOUT, - NODE_FRESNEL, - NODE_LAYER_WEIGHT, - NODE_CLOSURE_VOLUME, - NODE_PRINCIPLED_VOLUME, - NODE_MATH, - NODE_VECTOR_MATH, - NODE_RGB_RAMP, - NODE_GAMMA, - NODE_BRIGHTCONTRAST, - NODE_LIGHT_PATH, - NODE_OBJECT_INFO, - NODE_PARTICLE_INFO, - NODE_HAIR_INFO, - NODE_POINT_INFO, - NODE_TEXTURE_MAPPING, - NODE_MAPPING, - NODE_MIN_MAX, - NODE_CAMERA, - NODE_TEX_ENVIRONMENT, - NODE_TEX_SKY, - NODE_TEX_GRADIENT, - NODE_TEX_VORONOI, - NODE_TEX_MUSGRAVE, - NODE_TEX_WAVE, - NODE_TEX_MAGIC, - NODE_TEX_CHECKER, - NODE_TEX_BRICK, - NODE_TEX_WHITE_NOISE, - NODE_NORMAL, - NODE_LIGHT_FALLOFF, - NODE_IES, - NODE_RGB_CURVES, - NODE_VECTOR_CURVES, - NODE_TANGENT, - NODE_NORMAL_MAP, - NODE_INVERT, - NODE_MIX, - NODE_SEPARATE_COLOR, - NODE_COMBINE_COLOR, - NODE_SEPARATE_VECTOR, - NODE_COMBINE_VECTOR, - NODE_SEPARATE_HSV, - NODE_COMBINE_HSV, - NODE_VECTOR_ROTATE, - NODE_VECTOR_TRANSFORM, - NODE_WIREFRAME, - NODE_WAVELENGTH, - NODE_BLACKBODY, - NODE_MAP_RANGE, - NODE_VECTOR_MAP_RANGE, - NODE_CLAMP, - NODE_BEVEL, - NODE_AMBIENT_OCCLUSION, - NODE_TEX_VOXEL, - NODE_AOV_START, - NODE_AOV_COLOR, - NODE_AOV_VALUE, - NODE_FLOAT_CURVE, - /* NOTE: for best OpenCL performance, item definition in the enum must - * match the switch case order in `svm.h`. */ +#define SHADER_NODE_TYPE(name) name, +#include "node_types_template.h" + NODE_NUM } ShaderNodeType; typedef enum NodeAttributeOutputType { diff --git a/intern/cycles/kernel/types.h b/intern/cycles/kernel/types.h index f2e61d25002..05320deed19 100644 --- a/intern/cycles/kernel/types.h +++ b/intern/cycles/kernel/types.h @@ -535,7 +535,8 @@ typedef struct RaySelfPrimitives { typedef struct Ray { float3 P; /* origin */ float3 D; /* direction */ - float t; /* length of the ray */ + float tmin; /* start distance */ + float tmax; /* end distance */ float time; /* time (for motion blur) */ RaySelfPrimitives self; @@ -1072,94 +1073,6 @@ typedef struct KernelCamera { } KernelCamera; static_assert_align(KernelCamera, 16); -typedef struct KernelFilm { - float exposure; - int pass_flag; - - int light_pass_flag; - int pass_stride; - - int pass_combined; - int pass_depth; - int pass_position; - int pass_normal; - int pass_roughness; - int pass_motion; - - int pass_motion_weight; - int pass_uv; - int pass_object_id; - int pass_material_id; - - int pass_diffuse_color; - int pass_glossy_color; - int pass_transmission_color; - - int pass_diffuse_indirect; - int pass_glossy_indirect; - int pass_transmission_indirect; - int pass_volume_indirect; - - int pass_diffuse_direct; - int pass_glossy_direct; - int pass_transmission_direct; - int pass_volume_direct; - - int pass_emission; - int pass_background; - int pass_ao; - float pass_alpha_threshold; - - int pass_shadow; - float pass_shadow_scale; - - int pass_shadow_catcher; - int pass_shadow_catcher_sample_count; - int pass_shadow_catcher_matte; - - int filter_table_offset; - - int cryptomatte_passes; - int cryptomatte_depth; - int pass_cryptomatte; - - int pass_adaptive_aux_buffer; - int pass_sample_count; - - int pass_mist; - float mist_start; - float mist_inv_depth; - float mist_falloff; - - int pass_denoising_normal; - int pass_denoising_albedo; - int pass_denoising_depth; - - int pass_aov_color; - int pass_aov_value; - int pass_lightgroup; - - /* XYZ to rendering color space transform. float4 instead of float3 to - * ensure consistent padding/alignment across devices. */ - float4 xyz_to_r; - float4 xyz_to_g; - float4 xyz_to_b; - float4 rgb_to_y; - /* Rec709 to rendering color space. */ - float4 rec709_to_r; - float4 rec709_to_g; - float4 rec709_to_b; - int is_rec709; - - int pass_bake_primitive; - int pass_bake_differential; - - int use_approximate_shadow_catcher; - - int pad1; -} KernelFilm; -static_assert_align(KernelFilm, 16); - typedef struct KernelFilmConvert { int pass_offset; int pass_stride; @@ -1201,108 +1114,6 @@ typedef struct KernelFilmConvert { } KernelFilmConvert; static_assert_align(KernelFilmConvert, 16); -typedef struct KernelBackground { - /* only shader index */ - int surface_shader; - int volume_shader; - float volume_step_size; - int transparent; - float transparent_roughness_squared_threshold; - - /* portal sampling */ - float portal_weight; - int num_portals; - int portal_offset; - - /* sun sampling */ - float sun_weight; - /* xyz store direction, w the angle. float4 instead of float3 is used - * to ensure consistent padding/alignment across devices. */ - float4 sun; - - /* map sampling */ - float map_weight; - int map_res_x; - int map_res_y; - - int use_mis; - - int lightgroup; - - /* Padding */ - int pad1, pad2; -} KernelBackground; -static_assert_align(KernelBackground, 16); - -typedef struct KernelIntegrator { - /* emission */ - int use_direct_light; - int num_distribution; - int num_all_lights; - float pdf_triangles; - float pdf_lights; - float light_inv_rr_threshold; - - /* bounces */ - int min_bounce; - int max_bounce; - - int max_diffuse_bounce; - int max_glossy_bounce; - int max_transmission_bounce; - int max_volume_bounce; - - /* AO bounces */ - int ao_bounces; - float ao_bounces_distance; - float ao_bounces_factor; - float ao_additive_factor; - - /* transparent */ - int transparent_min_bounce; - int transparent_max_bounce; - int transparent_shadows; - - /* caustics */ - int caustics_reflective; - int caustics_refractive; - float filter_glossy; - - /* seed */ - int seed; - - /* clamp */ - float sample_clamp_direct; - float sample_clamp_indirect; - - /* mis */ - int use_lamp_mis; - - /* caustics */ - int use_caustics; - - /* sampler */ - int sampling_pattern; - - /* volume render */ - int use_volumes; - int volume_max_steps; - float volume_step_rate; - - int has_shadow_catcher; - float scrambling_distance; - - /* Closure filter. */ - int filter_closures; - - /* MIS debugging. */ - int direct_light_sampling_type; - - /* padding */ - int pad1; -} KernelIntegrator; -static_assert_align(KernelIntegrator, 16); - typedef enum KernelBVHLayout { BVH_LAYOUT_NONE = 0, @@ -1320,36 +1131,25 @@ typedef enum KernelBVHLayout { BVH_LAYOUT_ALL = BVH_LAYOUT_BVH2 | BVH_LAYOUT_EMBREE | BVH_LAYOUT_OPTIX | BVH_LAYOUT_METAL, } KernelBVHLayout; -typedef struct KernelBVH { - /* Own BVH */ - int root; - int have_motion; - int have_curves; - int bvh_layout; - int use_bvh_steps; - int curve_subdivisions; +/* Specialized struct that can become constants in dynamic compilation. */ +#define KERNEL_STRUCT_BEGIN(name, parent) struct name { +#define KERNEL_STRUCT_END(name) \ + } \ + ; \ + static_assert_align(name, 16); - /* Custom BVH */ -#ifdef __KERNEL_OPTIX__ - OptixTraversableHandle scene; -#elif defined __METALRT__ - metalrt_as_type scene; +#ifdef __KERNEL_USE_DATA_CONSTANTS__ +# define KERNEL_STRUCT_MEMBER(parent, type, name) type __unused_##name; #else -# ifdef __EMBREE__ - RTCScene scene; -# ifndef __KERNEL_64_BIT__ - int pad2; -# endif -# else - int scene, pad2; -# endif +# define KERNEL_STRUCT_MEMBER(parent, type, name) type name; #endif -} KernelBVH; -static_assert_align(KernelBVH, 16); + +#include "kernel/data_template.h" typedef struct KernelTables { int beckmann_offset; - int pad1, pad2, pad3; + int filter_table_offset; + int pad1, pad2; } KernelTables; static_assert_align(KernelTables, 16); @@ -1362,18 +1162,37 @@ typedef struct KernelBake { static_assert_align(KernelBake, 16); typedef struct KernelData { + /* Features and limits. */ uint kernel_features; uint max_closures; uint max_shaders; uint volume_stack_size; + /* Always dynamic data mambers. */ KernelCamera cam; - KernelFilm film; - KernelBackground background; - KernelIntegrator integrator; - KernelBVH bvh; - KernelTables tables; KernelBake bake; + KernelTables tables; + + /* Potentially specialized data members. */ +#define KERNEL_STRUCT_BEGIN(name, parent) name parent; +#include "kernel/data_template.h" + + /* Device specific BVH. */ +#ifdef __KERNEL_OPTIX__ + OptixTraversableHandle device_bvh; +#elif defined __METALRT__ + metalrt_as_type device_bvh; +#else +# ifdef __EMBREE__ + RTCScene device_bvh; +# ifndef __KERNEL_64_BIT__ + int pad1; +# endif +# else + int device_bvh, pad1; +# endif +#endif + int pad2, pad3; } KernelData; static_assert_align(KernelData, 16); diff --git a/intern/cycles/scene/film.cpp b/intern/cycles/scene/film.cpp index 8239ee84b82..a6a8f90a449 100644 --- a/intern/cycles/scene/film.cpp +++ b/intern/cycles/scene/film.cpp @@ -394,7 +394,7 @@ void Film::device_update(Device *device, DeviceScene *dscene, Scene *scene) vector<float> table = filter_table(filter_type, filter_width); scene->lookup_tables->remove_table(&filter_table_offset_); filter_table_offset_ = scene->lookup_tables->add_table(dscene, table); - kfilm->filter_table_offset = (int)filter_table_offset_; + dscene->data.tables.filter_table_offset = (int)filter_table_offset_; /* mist pass parameters */ kfilm->mist_start = mist_start; diff --git a/intern/cycles/scene/geometry.cpp b/intern/cycles/scene/geometry.cpp index bdc8839e277..67ff118692e 100644 --- a/intern/cycles/scene/geometry.cpp +++ b/intern/cycles/scene/geometry.cpp @@ -1362,7 +1362,7 @@ void GeometryManager::device_update_bvh(Device *device, dscene->data.bvh.use_bvh_steps = (scene->params.num_bvh_time_steps != 0); dscene->data.bvh.curve_subdivisions = scene->params.curve_subdivisions(); /* The scene handle is set in 'CPUDevice::const_copy_to' and 'OptiXDevice::const_copy_to' */ - dscene->data.bvh.scene = 0; + dscene->data.device_bvh = 0; } /* Set of flags used to help determining what data has been modified or needs reallocation, so we diff --git a/intern/cycles/scene/scene.cpp b/intern/cycles/scene/scene.cpp index eedb2a4fa3a..18cd665ac74 100644 --- a/intern/cycles/scene/scene.cpp +++ b/intern/cycles/scene/scene.cpp @@ -369,6 +369,8 @@ void Scene::device_update(Device *device_, Progress &progress) device->const_copy_to("data", &dscene.data, sizeof(dscene.data)); } + device->optimize_for_scene(this); + if (print_stats) { size_t mem_used = util_guarded_get_mem_used(); size_t mem_peak = util_guarded_get_mem_peak(); diff --git a/intern/cycles/scene/scene.h b/intern/cycles/scene/scene.h index d04c6a27f11..d1004bb7b66 100644 --- a/intern/cycles/scene/scene.h +++ b/intern/cycles/scene/scene.h @@ -82,7 +82,7 @@ class DeviceScene { device_vector<uint> patches; - /* pointcloud */ + /* point-cloud */ device_vector<float4> points; device_vector<uint> points_shader; @@ -124,7 +124,7 @@ class DeviceScene { /* integrator */ device_vector<float> sample_pattern_lut; - /* ies lights */ + /* IES lights */ device_vector<float> ies_lights; KernelData data; diff --git a/intern/cycles/scene/shader_nodes.cpp b/intern/cycles/scene/shader_nodes.cpp index f93a1a5231a..bedb0fe2902 100644 --- a/intern/cycles/scene/shader_nodes.cpp +++ b/intern/cycles/scene/shader_nodes.cpp @@ -6671,7 +6671,7 @@ void CurvesNode::compile(SVMCompiler &compiler, ShaderInput *fac_in = input("Fac"); - compiler.add_node(type, + compiler.add_node(ShaderNodeType(type), compiler.encode_uchar4(compiler.stack_assign(fac_in), compiler.stack_assign(value_in), compiler.stack_assign(value_out), @@ -6736,7 +6736,7 @@ void RGBCurvesNode::constant_fold(const ConstantFolder &folder) void RGBCurvesNode::compile(SVMCompiler &compiler) { - CurvesNode::compile(compiler, NODE_RGB_CURVES, input("Color"), output("Color")); + CurvesNode::compile(compiler, NODE_CURVES, input("Color"), output("Color")); } void RGBCurvesNode::compile(OSLCompiler &compiler) @@ -6774,7 +6774,7 @@ void VectorCurvesNode::constant_fold(const ConstantFolder &folder) void VectorCurvesNode::compile(SVMCompiler &compiler) { - CurvesNode::compile(compiler, NODE_VECTOR_CURVES, input("Vector"), output("Vector")); + CurvesNode::compile(compiler, NODE_CURVES, input("Vector"), output("Vector")); } void VectorCurvesNode::compile(OSLCompiler &compiler) diff --git a/intern/cycles/scene/svm.cpp b/intern/cycles/scene/svm.cpp index 4bc5a1b9cc2..ede3f87e7e3 100644 --- a/intern/cycles/scene/svm.cpp +++ b/intern/cycles/scene/svm.cpp @@ -44,8 +44,6 @@ void SVMShaderManager::device_update_shader(Scene *scene, } assert(shader->graph); - svm_nodes->push_back_slow(make_int4(NODE_SHADER_JUMP, 0, 0, 0)); - SVMCompiler::Summary summary; SVMCompiler compiler(scene); compiler.background = (shader == scene->background->get_shader(scene)); @@ -170,6 +168,9 @@ SVMCompiler::SVMCompiler(Scene *scene) : scene(scene) background = false; mix_weight_offset = SVM_STACK_INVALID; compile_failed = false; + + /* This struct has one entry for every node, in order of ShaderNodeType definition. */ + svm_node_types_used = (std::atomic_int *)&scene->dscene.data.svm_usage; } int SVMCompiler::stack_size(SocketType::Type type) @@ -378,11 +379,13 @@ void SVMCompiler::add_node(int a, int b, int c, int d) void SVMCompiler::add_node(ShaderNodeType type, int a, int b, int c) { + svm_node_types_used[type] = true; current_svm_nodes.push_back_slow(make_int4(type, a, b, c)); } void SVMCompiler::add_node(ShaderNodeType type, const float3 &f) { + svm_node_types_used[type] = true; current_svm_nodes.push_back_slow( make_int4(type, __float_as_int(f.x), __float_as_int(f.y), __float_as_int(f.z))); } @@ -663,6 +666,7 @@ void SVMCompiler::generate_multi_closure(ShaderNode *root_node, /* Add instruction to skip closure and its dependencies if mix * weight is zero. */ + svm_node_types_used[NODE_JUMP_IF_ONE] = true; current_svm_nodes.push_back_slow(make_int4(NODE_JUMP_IF_ONE, 0, stack_assign(facin), 0)); int node_jump_skip_index = current_svm_nodes.size() - 1; @@ -678,6 +682,7 @@ void SVMCompiler::generate_multi_closure(ShaderNode *root_node, /* Add instruction to skip closure and its dependencies if mix * weight is zero. */ + svm_node_types_used[NODE_JUMP_IF_ZERO] = true; current_svm_nodes.push_back_slow(make_int4(NODE_JUMP_IF_ZERO, 0, stack_assign(facin), 0)); int node_jump_skip_index = current_svm_nodes.size() - 1; @@ -844,6 +849,9 @@ void SVMCompiler::compile_type(Shader *shader, ShaderGraph *graph, ShaderType ty void SVMCompiler::compile(Shader *shader, array<int4> &svm_nodes, int index, Summary *summary) { + svm_node_types_used[NODE_SHADER_JUMP] = true; + svm_nodes.push_back_slow(make_int4(NODE_SHADER_JUMP, 0, 0, 0)); + /* copy graph for shader with bump mapping */ ShaderNode *output = shader->graph->output(); int start_num_svm_nodes = svm_nodes.size(); diff --git a/intern/cycles/scene/svm.h b/intern/cycles/scene/svm.h index 19746616207..f72375e7f87 100644 --- a/intern/cycles/scene/svm.h +++ b/intern/cycles/scene/svm.h @@ -211,6 +211,7 @@ class SVMCompiler { /* compile */ void compile_type(Shader *shader, ShaderGraph *graph, ShaderType type); + std::atomic_int *svm_node_types_used; array<int4> current_svm_nodes; ShaderType current_type; Shader *current_shader; diff --git a/intern/cycles/util/math_intersect.h b/intern/cycles/util/math_intersect.h index b0de0b25a45..c5b1cd51030 100644 --- a/intern/cycles/util/math_intersect.h +++ b/intern/cycles/util/math_intersect.h @@ -10,7 +10,8 @@ CCL_NAMESPACE_BEGIN ccl_device bool ray_sphere_intersect(float3 ray_P, float3 ray_D, - float ray_t, + float ray_tmin, + float ray_tmax, float3 sphere_P, float sphere_radius, ccl_private float3 *isect_P, @@ -33,7 +34,7 @@ ccl_device bool ray_sphere_intersect(float3 ray_P, return false; } const float t = tp - sqrtf(radiussq - dsq); /* pythagoras */ - if (t < ray_t) { + if (t > ray_tmin && t < ray_tmax) { *isect_t = t; *isect_P = ray_P + ray_D * t; return true; @@ -44,7 +45,8 @@ ccl_device bool ray_sphere_intersect(float3 ray_P, ccl_device bool ray_aligned_disk_intersect(float3 ray_P, float3 ray_D, - float ray_t, + float ray_tmin, + float ray_tmax, float3 disk_P, float disk_radius, ccl_private float3 *isect_P, @@ -59,7 +61,7 @@ ccl_device bool ray_aligned_disk_intersect(float3 ray_P, } /* Compute t to intersection point. */ const float t = -disk_t / div; - if (t < 0.0f || t > ray_t) { + if (!(t > ray_tmin && t < ray_tmax)) { return false; } /* Test if within radius. */ @@ -74,7 +76,8 @@ ccl_device bool ray_aligned_disk_intersect(float3 ray_P, ccl_device bool ray_disk_intersect(float3 ray_P, float3 ray_D, - float ray_t, + float ray_tmin, + float ray_tmax, float3 disk_P, float3 disk_N, float disk_radius, @@ -92,7 +95,8 @@ ccl_device bool ray_disk_intersect(float3 ray_P, } float3 P = ray_P + t * ray_D; float3 T = P - disk_P; - if (dot(T, T) < sqr(disk_radius) /*&& t > 0.f*/ && t <= ray_t) { + + if (dot(T, T) < sqr(disk_radius) && (t > ray_tmin && t < ray_tmax)) { *isect_P = ray_P + t * ray_D; *isect_t = t; return true; @@ -103,7 +107,8 @@ ccl_device bool ray_disk_intersect(float3 ray_P, ccl_device_forceinline bool ray_triangle_intersect(float3 ray_P, float3 ray_dir, - float ray_t, + float ray_tmin, + float ray_tmax, const float3 tri_a, const float3 tri_b, const float3 tri_c, @@ -149,16 +154,14 @@ ccl_device_forceinline bool ray_triangle_intersect(float3 ray_P, /* Perform depth test. */ const float T = dot3(v0, Ng); - const int sign_den = (__float_as_int(den) & 0x80000000); - const float sign_T = xor_signmask(T, sign_den); - if ((sign_T < 0.0f) || (sign_T > ray_t * xor_signmask(den, sign_den))) { + const float t = T / den; + if (!(t >= ray_tmin && t <= ray_tmax)) { return false; } - const float inv_den = 1.0f / den; - *isect_u = U * inv_den; - *isect_v = V * inv_den; - *isect_t = T * inv_den; + *isect_u = U / den; + *isect_v = V / den; + *isect_t = t; return true; #undef dot3 @@ -171,8 +174,8 @@ ccl_device_forceinline bool ray_triangle_intersect(float3 ray_P, */ ccl_device bool ray_quad_intersect(float3 ray_P, float3 ray_D, - float ray_mint, - float ray_maxt, + float ray_tmin, + float ray_tmax, float3 quad_P, float3 quad_u, float3 quad_v, @@ -185,7 +188,7 @@ ccl_device bool ray_quad_intersect(float3 ray_P, { /* Perform intersection test. */ float t = -(dot(ray_P, quad_n) - dot(quad_P, quad_n)) / dot(ray_D, quad_n); - if (t < ray_mint || t > ray_maxt) { + if (!(t > ray_tmin && t < ray_tmax)) { return false; } const float3 hit = ray_P + t * ray_D; diff --git a/intern/cycles/util/string.cpp b/intern/cycles/util/string.cpp index 66ff866ee10..0c318cea44a 100644 --- a/intern/cycles/util/string.cpp +++ b/intern/cycles/util/string.cpp @@ -136,6 +136,19 @@ void string_replace(string &haystack, const string &needle, const string &other) } } +void string_replace_same_length(string &haystack, const string &needle, const string &other) +{ + assert(needle.size() == other.size()); + size_t pos = 0; + while (pos != string::npos) { + pos = haystack.find(needle, pos); + if (pos != string::npos) { + memcpy(haystack.data() + pos, other.data(), other.size()); + pos += other.size(); + } + } +} + string string_remove_trademark(const string &s) { string result = s; @@ -164,6 +177,11 @@ string to_string(const char *str) return string(str); } +string to_string(const float4 &v) +{ + return string_printf("%f,%f,%f,%f", v.x, v.y, v.z, v.w); +} + string string_to_lower(const string &s) { string r = s; diff --git a/intern/cycles/util/string.h b/intern/cycles/util/string.h index a74feee1750..ecbe9e106c6 100644 --- a/intern/cycles/util/string.h +++ b/intern/cycles/util/string.h @@ -38,12 +38,14 @@ void string_split(vector<string> &tokens, const string &separators = "\t ", bool skip_empty_tokens = true); void string_replace(string &haystack, const string &needle, const string &other); +void string_replace_same_length(string &haystack, const string &needle, const string &other); bool string_startswith(string_view s, string_view start); bool string_endswith(string_view s, string_view end); string string_strip(const string &s); string string_remove_trademark(const string &s); string string_from_bool(const bool var); string to_string(const char *str); +string to_string(const float4 &v); string string_to_lower(const string &s); /* Wide char strings are only used on Windows to deal with non-ASCII diff --git a/intern/ghost/GHOST_Types.h b/intern/ghost/GHOST_Types.h index 2fc84349eb9..495fb739978 100644 --- a/intern/ghost/GHOST_Types.h +++ b/intern/ghost/GHOST_Types.h @@ -547,21 +547,15 @@ typedef struct { /** The key code. */ GHOST_TKey key; - /* ascii / utf8: both should always be set when possible, - * - ascii may be '\0' however if the user presses a non ascii key - * - unicode may not be set if the system has no unicode support - * - * These values are intended to be used as follows. - * For text input use unicode when available, fallback to ascii. - * For areas where unicode is not needed, number input for example, always - * use ascii, unicode is ignored - campbell. - */ - /** The ascii code for the key event ('\0' if none). */ - char ascii; /** The unicode character. if the length is 6, not NULL terminated if all 6 are set. */ char utf8_buf[6]; - /** Generated by auto-repeat. */ + /** + * Enabled when the key is held (auto-repeat). + * In this case press events are sent without a corresponding release/up event. + * + * All back-ends must set this variable for correct behavior regarding repeatable keys. + */ char is_repeat; } GHOST_TEventKeyData; diff --git a/intern/ghost/intern/GHOST_EventKey.h b/intern/ghost/intern/GHOST_EventKey.h index 1c3156c27d2..f85a674be9f 100644 --- a/intern/ghost/intern/GHOST_EventKey.h +++ b/intern/ghost/intern/GHOST_EventKey.h @@ -22,13 +22,13 @@ class GHOST_EventKey : public GHOST_Event { * \param msec: The time this event was generated. * \param type: The type of key event. * \param key: The key code of the key. + * \param is_repeat: Enabled for key repeat events (only for press events). */ GHOST_EventKey( uint64_t msec, GHOST_TEventType type, GHOST_IWindow *window, GHOST_TKey key, bool is_repeat) : GHOST_Event(msec, type, window) { m_keyEventData.key = key; - m_keyEventData.ascii = '\0'; m_keyEventData.utf8_buf[0] = '\0'; m_keyEventData.is_repeat = is_repeat; m_data = &m_keyEventData; @@ -39,23 +39,24 @@ class GHOST_EventKey : public GHOST_Event { * \param msec: The time this event was generated. * \param type: The type of key event. * \param key: The key code of the key. - * \param ascii: The ascii code for the key event. + * \param is_repeat: Enabled for key repeat events (only for press events). + * \param utf8_buf: The text associated with this key event (only for press events). */ GHOST_EventKey(uint64_t msec, GHOST_TEventType type, GHOST_IWindow *window, GHOST_TKey key, - char ascii, - const char utf8_buf[6], - bool is_repeat) + bool is_repeat, + const char utf8_buf[6]) : GHOST_Event(msec, type, window) { m_keyEventData.key = key; - m_keyEventData.ascii = ascii; - if (utf8_buf) + if (utf8_buf) { memcpy(m_keyEventData.utf8_buf, utf8_buf, sizeof(m_keyEventData.utf8_buf)); - else + } + else { m_keyEventData.utf8_buf[0] = '\0'; + } m_keyEventData.is_repeat = is_repeat; m_data = &m_keyEventData; } diff --git a/intern/ghost/intern/GHOST_SystemCocoa.mm b/intern/ghost/intern/GHOST_SystemCocoa.mm index 0a905f5093e..c247ef3daa0 100644 --- a/intern/ghost/intern/GHOST_SystemCocoa.mm +++ b/intern/ghost/intern/GHOST_SystemCocoa.mm @@ -1779,7 +1779,6 @@ GHOST_TSuccess GHOST_SystemCocoa::handleKeyEvent(void *eventPtr) NSString *characters; NSData *convertedCharacters; GHOST_TKey keyCode; - unsigned char ascii; NSString *charsIgnoringModifiers; window = m_windowManager->getWindowAssociatedWithOSWindow((void *)[event window]); @@ -1789,7 +1788,6 @@ GHOST_TSuccess GHOST_SystemCocoa::handleKeyEvent(void *eventPtr) } char utf8_buf[6] = {'\0'}; - ascii = 0; switch ([event type]) { @@ -1809,7 +1807,6 @@ GHOST_TSuccess GHOST_SystemCocoa::handleKeyEvent(void *eventPtr) kUCKeyActionUp); } - /* handling both unicode or ascii */ characters = [event characters]; if ([characters length] > 0) { convertedCharacters = [characters dataUsingEncoding:NSUTF8StringEncoding]; @@ -1835,41 +1832,31 @@ GHOST_TSuccess GHOST_SystemCocoa::handleKeyEvent(void *eventPtr) if ((keyCode == GHOST_kKeyQ) && (m_modifierMask & NSEventModifierFlagCommand)) break; // Cmd-Q is directly handled by Cocoa - /* ascii is a subset of unicode */ - if (utf8_buf[0] && !utf8_buf[1]) { - ascii = utf8_buf[0]; - } - if ([event type] == NSEventTypeKeyDown) { pushEvent(new GHOST_EventKey([event timestamp] * 1000, GHOST_kEventKeyDown, window, keyCode, - ascii, - utf8_buf, - [event isARepeat])); + [event isARepeat], + utf8_buf)); #if 0 - printf("Key down rawCode=0x%x charsIgnoringModifiers=%c keyCode=%u ascii=%i %c utf8=%s\n", + printf("Key down rawCode=0x%x charsIgnoringModifiers=%c keyCode=%u utf8=%s\n", [event keyCode], [charsIgnoringModifiers length] > 0 ? [charsIgnoringModifiers characterAtIndex:0] : ' ', keyCode, - ascii, - ascii, utf8_buf); #endif } else { pushEvent(new GHOST_EventKey( - [event timestamp] * 1000, GHOST_kEventKeyUp, window, keyCode, 0, NULL, false)); + [event timestamp] * 1000, GHOST_kEventKeyUp, window, keyCode, false, NULL)); #if 0 - printf("Key up rawCode=0x%x charsIgnoringModifiers=%c keyCode=%u ascii=%i %c utf8=%s\n", + printf("Key up rawCode=0x%x charsIgnoringModifiers=%c keyCode=%u utf8=%s\n", [event keyCode], [charsIgnoringModifiers length] > 0 ? [charsIgnoringModifiers characterAtIndex:0] : ' ', keyCode, - ascii, - ascii, utf8_buf); #endif } diff --git a/intern/ghost/intern/GHOST_SystemSDL.cpp b/intern/ghost/intern/GHOST_SystemSDL.cpp index a29bfd9489d..d912b57f049 100644 --- a/intern/ghost/intern/GHOST_SystemSDL.cpp +++ b/intern/ghost/intern/GHOST_SystemSDL.cpp @@ -279,6 +279,141 @@ static GHOST_TKey convertSDLKey(SDL_Scancode key) } #undef GXMAP +static char convert_keyboard_event_to_ascii(const SDL_KeyboardEvent &sdl_sub_evt) +{ + SDL_Keycode sym = sdl_sub_evt.keysym.sym; + if (sym > 127) { + switch (sym) { + case SDLK_KP_DIVIDE: + sym = '/'; + break; + case SDLK_KP_MULTIPLY: + sym = '*'; + break; + case SDLK_KP_MINUS: + sym = '-'; + break; + case SDLK_KP_PLUS: + sym = '+'; + break; + case SDLK_KP_1: + sym = '1'; + break; + case SDLK_KP_2: + sym = '2'; + break; + case SDLK_KP_3: + sym = '3'; + break; + case SDLK_KP_4: + sym = '4'; + break; + case SDLK_KP_5: + sym = '5'; + break; + case SDLK_KP_6: + sym = '6'; + break; + case SDLK_KP_7: + sym = '7'; + break; + case SDLK_KP_8: + sym = '8'; + break; + case SDLK_KP_9: + sym = '9'; + break; + case SDLK_KP_0: + sym = '0'; + break; + case SDLK_KP_PERIOD: + sym = '.'; + break; + default: + sym = 0; + break; + } + } + else { + if (sdl_sub_evt.keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT)) { + /* Weak US keyboard assumptions. */ + if (sym >= 'a' && sym <= ('a' + 32)) { + sym -= 32; + } + else { + switch (sym) { + case '`': + sym = '~'; + break; + case '1': + sym = '!'; + break; + case '2': + sym = '@'; + break; + case '3': + sym = '#'; + break; + case '4': + sym = '$'; + break; + case '5': + sym = '%'; + break; + case '6': + sym = '^'; + break; + case '7': + sym = '&'; + break; + case '8': + sym = '*'; + break; + case '9': + sym = '('; + break; + case '0': + sym = ')'; + break; + case '-': + sym = '_'; + break; + case '=': + sym = '+'; + break; + case '[': + sym = '{'; + break; + case ']': + sym = '}'; + break; + case '\\': + sym = '|'; + break; + case ';': + sym = ':'; + break; + case '\'': + sym = '"'; + break; + case ',': + sym = '<'; + break; + case '.': + sym = '>'; + break; + case '/': + sym = '?'; + break; + default: + break; + } + } + } + } + return (char)sym; +} + /** * Events don't always have valid windows, * but GHOST needs a window _always_. fallback to the GL window. @@ -454,9 +589,9 @@ void GHOST_SystemSDL::processEvent(SDL_Event *sdl_event) case SDL_KEYDOWN: case SDL_KEYUP: { SDL_KeyboardEvent &sdl_sub_evt = sdl_event->key; - SDL_Keycode sym = sdl_sub_evt.keysym.sym; GHOST_TEventType type = (sdl_sub_evt.state == SDL_PRESSED) ? GHOST_kEventKeyDown : GHOST_kEventKeyUp; + const bool is_repeat = sdl_sub_evt.repeat != 0; GHOST_WindowSDL *window = findGhostWindow( SDL_GetWindowFromID_fallback(sdl_sub_evt.windowID)); @@ -465,138 +600,15 @@ void GHOST_SystemSDL::processEvent(SDL_Event *sdl_event) GHOST_TKey gkey = convertSDLKey(sdl_sub_evt.keysym.scancode); /* NOTE: the `sdl_sub_evt.keysym.sym` is truncated, * for unicode support ghost has to be modified. */ - // printf("%d\n", sym); - if (sym > 127) { - switch (sym) { - case SDLK_KP_DIVIDE: - sym = '/'; - break; - case SDLK_KP_MULTIPLY: - sym = '*'; - break; - case SDLK_KP_MINUS: - sym = '-'; - break; - case SDLK_KP_PLUS: - sym = '+'; - break; - case SDLK_KP_1: - sym = '1'; - break; - case SDLK_KP_2: - sym = '2'; - break; - case SDLK_KP_3: - sym = '3'; - break; - case SDLK_KP_4: - sym = '4'; - break; - case SDLK_KP_5: - sym = '5'; - break; - case SDLK_KP_6: - sym = '6'; - break; - case SDLK_KP_7: - sym = '7'; - break; - case SDLK_KP_8: - sym = '8'; - break; - case SDLK_KP_9: - sym = '9'; - break; - case SDLK_KP_0: - sym = '0'; - break; - case SDLK_KP_PERIOD: - sym = '.'; - break; - default: - sym = 0; - break; - } - } - else { - if (sdl_sub_evt.keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT)) { - /* lame US keyboard assumptions */ - if (sym >= 'a' && sym <= ('a' + 32)) { - sym -= 32; - } - else { - switch (sym) { - case '`': - sym = '~'; - break; - case '1': - sym = '!'; - break; - case '2': - sym = '@'; - break; - case '3': - sym = '#'; - break; - case '4': - sym = '$'; - break; - case '5': - sym = '%'; - break; - case '6': - sym = '^'; - break; - case '7': - sym = '&'; - break; - case '8': - sym = '*'; - break; - case '9': - sym = '('; - break; - case '0': - sym = ')'; - break; - case '-': - sym = '_'; - break; - case '=': - sym = '+'; - break; - case '[': - sym = '{'; - break; - case ']': - sym = '}'; - break; - case '\\': - sym = '|'; - break; - case ';': - sym = ':'; - break; - case '\'': - sym = '"'; - break; - case ',': - sym = '<'; - break; - case '.': - sym = '>'; - break; - case '/': - sym = '?'; - break; - default: - break; - } - } - } + + /* TODO(@campbellbarton): support full unicode, SDL supports this but it needs to be + * explicitly enabled via #SDL_StartTextInput which GHOST would have to wrap. */ + char utf8_buf[sizeof(GHOST_TEventKeyData::utf8_buf)] = {'\0'}; + if (type == GHOST_kEventKeyDown) { + utf8_buf[0] = convert_keyboard_event_to_ascii(sdl_sub_evt); } - g_event = new GHOST_EventKey(getMilliSeconds(), type, window, gkey, sym, nullptr, false); + g_event = new GHOST_EventKey(getMilliSeconds(), type, window, gkey, is_repeat, utf8_buf); break; } } diff --git a/intern/ghost/intern/GHOST_SystemWayland.cpp b/intern/ghost/intern/GHOST_SystemWayland.cpp index 08aa640c5cd..d7520f1243f 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.cpp +++ b/intern/ghost/intern/GHOST_SystemWayland.cpp @@ -128,11 +128,6 @@ static bool use_gnome_confine_hack = false; */ #define EVDEV_OFFSET 8 -struct buffer_t { - void *data = nullptr; - size_t size = 0; -}; - struct cursor_t { bool visible = false; /** @@ -147,13 +142,11 @@ struct cursor_t { struct wl_buffer *wl_buffer = nullptr; struct wl_cursor_image wl_image = {0}; struct wl_cursor_theme *wl_theme = nullptr; - struct buffer_t *file_buffer = nullptr; + void *custom_data = nullptr; + size_t custom_data_size = 0; int size = 0; std::string theme_name; - /** Outputs on which the cursor is visible. */ - std::unordered_set<const output_t *> outputs; - int theme_scale = 1; int custom_scale = 1; }; @@ -235,6 +228,11 @@ struct input_state_pointer_t { */ wl_fixed_t xy[2] = {0, 0}; + /** Outputs on which the cursor is visible. */ + std::unordered_set<const output_t *> outputs; + + int theme_scale = 1; + /** The serial of the last used pointer or tablet. */ uint32_t serial = 0; @@ -400,6 +398,19 @@ static input_state_pointer_t *input_state_pointer_active(input_t *input) return nullptr; } +static input_state_pointer_t *input_state_pointer_from_cursor_surface(input_t *input, + const wl_surface *wl_surface) +{ + if (ghost_wl_surface_own_cursor_pointer(wl_surface)) { + return &input->pointer; + } + if (ghost_wl_surface_own_cursor_tablet(wl_surface)) { + return &input->tablet; + } + GHOST_ASSERT(0, "Surface found without pointer/tablet tag"); + return nullptr; +} + static void display_destroy(display_t *d) { if (d->data_device_manager) { @@ -449,11 +460,12 @@ static void display_destroy(display_t *d) if (input->data_device) { wl_data_device_release(input->data_device); } + + if (input->cursor.custom_data) { + munmap(input->cursor.custom_data, input->cursor.custom_data_size); + } + if (input->wl_pointer) { - if (input->cursor.file_buffer) { - munmap(input->cursor.file_buffer->data, input->cursor.file_buffer->size); - delete input->cursor.file_buffer; - } if (input->cursor.wl_surface) { wl_surface_destroy(input->cursor.wl_surface); } @@ -747,7 +759,83 @@ static const std::vector<std::string> mime_send = { "text/plain", }; -#undef LOG +static int memfd_create_sealed(const char *name) +{ +#ifdef HAVE_MEMFD_CREATE + const int fd = memfd_create(name, MFD_CLOEXEC | MFD_ALLOW_SEALING); + if (fd >= 0) { + fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL); + } + return fd; +#else /* HAVE_MEMFD_CREATE */ + char *path = getenv("XDG_RUNTIME_DIR"); + if (!path) { + errno = ENOENT; + return -1; + } + char *tmpname; + asprintf(&tmpname, "%s/%s-XXXXXX", path, name); + const int fd = mkostemp(tmpname, O_CLOEXEC); + if (fd >= 0) { + unlink(tmpname); + } + free(tmpname); + return fd; +#endif /* !HAVE_MEMFD_CREATE */ +} + +static size_t ghost_wl_shm_format_as_size(enum wl_shm_format format) +{ + switch (format) { + case WL_SHM_FORMAT_ARGB8888: { + return 4; + } + default: { + /* Support other formats as needed. */ + GHOST_ASSERT(0, "Unexpected format passed in!"); + return 4; + } + } +} + +/** + * Return a #wl_buffer, ready to have it's data filled in or NULL in case of failure. + * The caller is responsible for calling `unmap(buffer_data, buffer_size)`. + * + * \param r_buffer_data: The buffer to be filled. + * \param r_buffer_data_size: The size of `r_buffer_data` in bytes. + */ +static wl_buffer *ghost_wl_buffer_create_for_image(struct wl_shm *shm, + const int32_t size_xy[2], + enum wl_shm_format format, + void **r_buffer_data, + size_t *r_buffer_data_size) +{ + const int fd = memfd_create_sealed("ghost-wl-buffer"); + wl_buffer *buffer = nullptr; + if (fd >= 0) { + const int32_t buffer_stride = size_xy[0] * ghost_wl_shm_format_as_size(format); + const int32_t buffer_size = buffer_stride * size_xy[1]; + if (posix_fallocate(fd, 0, buffer_size) == 0) { + void *buffer_data = mmap(nullptr, buffer_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (buffer_data != MAP_FAILED) { + struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, buffer_size); + buffer = wl_shm_pool_create_buffer(pool, 0, UNPACK2(size_xy), buffer_stride, format); + wl_shm_pool_destroy(pool); + if (buffer) { + *r_buffer_data = buffer_data; + *r_buffer_data_size = (size_t)buffer_size; + } + else { + /* Highly unlikely. */ + munmap(buffer_data, buffer_size); + } + } + } + close(fd); + } + return buffer; +} /** \} */ @@ -1285,19 +1373,22 @@ static const struct wl_buffer_listener cursor_buffer_listener = { static CLG_LogRef LOG_WL_CURSOR_SURFACE = {"ghost.wl.handle.cursor_surface"}; #define LOG (&LOG_WL_CURSOR_SURFACE) -static bool update_cursor_scale(cursor_t &cursor, wl_shm *shm) +static bool update_cursor_scale(cursor_t &cursor, + wl_shm *shm, + input_state_pointer_t *input_state, + wl_surface *cursor_surface) { int scale = 0; - for (const output_t *output : cursor.outputs) { + for (const output_t *output : input_state->outputs) { if (output->scale > scale) { scale = output->scale; } } - if (scale > 0 && cursor.theme_scale != scale) { - cursor.theme_scale = scale; + if (scale > 0 && input_state->theme_scale != scale) { + input_state->theme_scale = scale; if (!cursor.is_custom) { - wl_surface_set_buffer_scale(cursor.wl_surface, scale); + wl_surface_set_buffer_scale(cursor_surface, scale); } wl_cursor_theme_destroy(cursor.wl_theme); cursor.wl_theme = wl_cursor_theme_load(cursor.theme_name.c_str(), scale * cursor.size, shm); @@ -1307,7 +1398,7 @@ static bool update_cursor_scale(cursor_t &cursor, wl_shm *shm) } static void cursor_surface_handle_enter(void *data, - struct wl_surface * /*wl_surface*/, + struct wl_surface *wl_surface, struct wl_output *output) { if (!ghost_wl_output_own(output)) { @@ -1317,13 +1408,14 @@ static void cursor_surface_handle_enter(void *data, CLOG_INFO(LOG, 2, "handle_enter"); input_t *input = static_cast<input_t *>(data); + input_state_pointer_t *input_state = input_state_pointer_from_cursor_surface(input, wl_surface); const output_t *reg_output = ghost_wl_output_user_data(output); - input->cursor.outputs.insert(reg_output); - update_cursor_scale(input->cursor, input->system->shm()); + input_state->outputs.insert(reg_output); + update_cursor_scale(input->cursor, input->system->shm(), input_state, wl_surface); } static void cursor_surface_handle_leave(void *data, - struct wl_surface * /*wl_surface*/, + struct wl_surface *wl_surface, struct wl_output *output) { if (!(output && ghost_wl_output_own(output))) { @@ -1333,9 +1425,10 @@ static void cursor_surface_handle_leave(void *data, CLOG_INFO(LOG, 2, "handle_leave"); input_t *input = static_cast<input_t *>(data); + input_state_pointer_t *input_state = input_state_pointer_from_cursor_surface(input, wl_surface); const output_t *reg_output = ghost_wl_output_user_data(output); - input->cursor.outputs.erase(reg_output); - update_cursor_scale(input->cursor, input->system->shm()); + input_state->outputs.erase(reg_output); + update_cursor_scale(input->cursor, input->system->shm(), input_state, wl_surface); } static const struct wl_surface_listener cursor_surface_listener = { @@ -1879,6 +1972,8 @@ static void tablet_seat_handle_tool_added(void *data, /* Every tool has it's own cursor surface. */ tool_input->cursor_surface = wl_compositor_create_surface(input->system->compositor()); + ghost_wl_surface_tag_cursor_tablet(tool_input->cursor_surface); + wl_surface_add_listener(tool_input->cursor_surface, &cursor_surface_listener, (void *)input); zwp_tablet_tool_v2_add_listener(id, &tablet_tool_listner, tool_input); @@ -2162,8 +2257,8 @@ static void keyboard_handle_key(void *data, if (wl_surface *focus_surface = input->keyboard.wl_surface) { GHOST_IWindow *win = ghost_wl_surface_user_data(focus_surface); - input->system->pushEvent(new GHOST_EventKey( - input->system->getMilliSeconds(), etype, win, gkey, '\0', utf8_buf, false)); + input->system->pushEvent( + new GHOST_EventKey(input->system->getMilliSeconds(), etype, win, gkey, false, utf8_buf)); } /* An existing payload means the key repeat timer is reset and will be added again. */ @@ -2195,9 +2290,8 @@ static void keyboard_handle_key(void *data, GHOST_kEventKeyDown, win, payload->key_data.gkey, - '\0', - utf8_buf, - true)); + true, + utf8_buf)); } }; input->key_repeat.timer = input->system->installTimer( @@ -2282,13 +2376,14 @@ static void seat_handle_capabilities(void *data, input->cursor.wl_surface = wl_compositor_create_surface(input->system->compositor()); input->cursor.visible = true; input->cursor.wl_buffer = nullptr; - input->cursor.file_buffer = new buffer_t; if (!get_cursor_settings(input->cursor.theme_name, input->cursor.size)) { input->cursor.theme_name = std::string(); input->cursor.size = default_cursor_size; } wl_pointer_add_listener(input->wl_pointer, &pointer_listener, data); + wl_surface_add_listener(input->cursor.wl_surface, &cursor_surface_listener, data); + ghost_wl_surface_tag_cursor_pointer(input->cursor.wl_surface); } if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) { @@ -2502,7 +2597,17 @@ static void output_handle_done(void *data, struct wl_output * /*wl_output*/) static void output_handle_scale(void *data, struct wl_output * /*wl_output*/, const int32_t factor) { + CLOG_INFO(LOG, 2, "scale"); static_cast<output_t *>(data)->scale = factor; + + if (window_manager) { + for (GHOST_IWindow *iwin : window_manager->getWindows()) { + GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(iwin); + win->outputs_changed_update_scale(); + /* TODO(@campbellbarton): support refreshing the UI when the DPI changes. + * There are glitches when resizing the monitor which would be nice to solve. */ + } + } } static const struct wl_output_listener output_listener = { @@ -3139,19 +3244,30 @@ GHOST_IWindow *GHOST_SystemWayland::createWindow(const char *title, static void cursor_buffer_show(const input_t *input) { const cursor_t *c = &input->cursor; - const int scale = c->is_custom ? c->custom_scale : c->theme_scale; - const int32_t hotspot_x = int32_t(c->wl_image.hotspot_x) / scale; - const int32_t hotspot_y = int32_t(c->wl_image.hotspot_y) / scale; - wl_pointer_set_cursor( - input->wl_pointer, input->pointer.serial, c->wl_surface, hotspot_x, hotspot_y); - for (struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2 : input->tablet_tools) { - tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>( - zwp_tablet_tool_v2_get_user_data(zwp_tablet_tool_v2)); - zwp_tablet_tool_v2_set_cursor(zwp_tablet_tool_v2, - input->tablet.serial, - tool_input->cursor_surface, - hotspot_x, - hotspot_y); + + if (input->wl_pointer) { + const int scale = c->is_custom ? c->custom_scale : input->pointer.theme_scale; + const int32_t hotspot_x = int32_t(c->wl_image.hotspot_x) / scale; + const int32_t hotspot_y = int32_t(c->wl_image.hotspot_y) / scale; + if (input->wl_pointer) { + wl_pointer_set_cursor( + input->wl_pointer, input->pointer.serial, c->wl_surface, hotspot_x, hotspot_y); + } + } + + if (!input->tablet_tools.empty()) { + const int scale = c->is_custom ? c->custom_scale : input->tablet.theme_scale; + const int32_t hotspot_x = int32_t(c->wl_image.hotspot_x) / scale; + const int32_t hotspot_y = int32_t(c->wl_image.hotspot_y) / scale; + for (struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2 : input->tablet_tools) { + tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>( + zwp_tablet_tool_v2_get_user_data(zwp_tablet_tool_v2)); + zwp_tablet_tool_v2_set_cursor(zwp_tablet_tool_v2, + input->tablet.serial, + tool_input->cursor_surface, + hotspot_x, + hotspot_y); + } } } @@ -3169,52 +3285,77 @@ static void cursor_buffer_hide(const input_t *input) } } -static void cursor_buffer_set(const input_t *input, wl_buffer *buffer) +/** + * Needed to ensure the cursor size is always a multiple of scale. + */ +static int cursor_buffer_compatible_scale_from_image(const struct wl_cursor_image *wl_image, + int scale) { - const cursor_t *c = &input->cursor; - const int scale = c->is_custom ? c->custom_scale : c->theme_scale; - - const bool visible = (c->visible && c->is_hardware); - - const int32_t image_size_x = int32_t(c->wl_image.width); - const int32_t image_size_y = int32_t(c->wl_image.height); + const int32_t image_size_x = int32_t(wl_image->width); + const int32_t image_size_y = int32_t(wl_image->height); + while (scale > 1) { + if ((image_size_x % scale) == 0 && (image_size_y % scale) == 0) { + break; + } + scale -= 1; + } + return scale; +} - /* This is a requirement of WAYLAND, when this isn't the case, - * it causes Blender's window to close intermittently. */ +static void cursor_buffer_set_surface_impl(const input_t *input, + wl_buffer *buffer, + struct wl_surface *wl_surface, + const int scale) +{ + const wl_cursor_image *wl_image = &input->cursor.wl_image; + const int32_t image_size_x = int32_t(wl_image->width); + const int32_t image_size_y = int32_t(wl_image->height); GHOST_ASSERT((image_size_x % scale) == 0 && (image_size_y % scale) == 0, "The size must be a multiple of the scale!"); - const int32_t hotspot_x = int32_t(c->wl_image.hotspot_x) / scale; - const int32_t hotspot_y = int32_t(c->wl_image.hotspot_y) / scale; + wl_surface_set_buffer_scale(wl_surface, scale); + wl_surface_attach(wl_surface, buffer, 0, 0); + wl_surface_damage(wl_surface, 0, 0, image_size_x, image_size_y); + wl_surface_commit(wl_surface); +} - wl_surface_set_buffer_scale(c->wl_surface, scale); - wl_surface_attach(c->wl_surface, buffer, 0, 0); - wl_surface_damage(c->wl_surface, 0, 0, image_size_x, image_size_y); - wl_surface_commit(c->wl_surface); +static void cursor_buffer_set(const input_t *input, wl_buffer *buffer) +{ + const cursor_t *c = &input->cursor; + const wl_cursor_image *wl_image = &input->cursor.wl_image; + const bool visible = (c->visible && c->is_hardware); - wl_pointer_set_cursor(input->wl_pointer, - input->pointer.serial, - visible ? c->wl_surface : nullptr, - hotspot_x, - hotspot_y); + /* This is a requirement of WAYLAND, when this isn't the case, + * it causes Blender's window to close intermittently. */ + if (input->wl_pointer) { + const int scale = cursor_buffer_compatible_scale_from_image( + wl_image, c->is_custom ? c->custom_scale : input->pointer.theme_scale); + const int32_t hotspot_x = int32_t(wl_image->hotspot_x) / scale; + const int32_t hotspot_y = int32_t(wl_image->hotspot_y) / scale; + cursor_buffer_set_surface_impl(input, buffer, c->wl_surface, scale); + wl_pointer_set_cursor(input->wl_pointer, + input->pointer.serial, + visible ? c->wl_surface : nullptr, + hotspot_x, + hotspot_y); + } /* Set the cursor for all tablet tools as well. */ - for (struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2 : input->tablet_tools) { - tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>( - zwp_tablet_tool_v2_get_user_data(zwp_tablet_tool_v2)); - - /* FIXME: for some reason cursor scale is applied twice (when the scale isn't 1x), - * this happens both in gnome-shell & KDE. Setting the surface scale here doesn't help. */ - wl_surface_set_buffer_scale(tool_input->cursor_surface, scale); - wl_surface_attach(tool_input->cursor_surface, buffer, 0, 0); - wl_surface_damage(tool_input->cursor_surface, 0, 0, image_size_x, image_size_y); - wl_surface_commit(tool_input->cursor_surface); - - zwp_tablet_tool_v2_set_cursor(zwp_tablet_tool_v2, - input->tablet.serial, - visible ? tool_input->cursor_surface : nullptr, - hotspot_x, - hotspot_y); + if (!input->tablet_tools.empty()) { + const int scale = cursor_buffer_compatible_scale_from_image( + wl_image, c->is_custom ? c->custom_scale : input->tablet.theme_scale); + const int32_t hotspot_x = int32_t(wl_image->hotspot_x) / scale; + const int32_t hotspot_y = int32_t(wl_image->hotspot_y) / scale; + for (struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2 : input->tablet_tools) { + tablet_tool_input_t *tool_input = static_cast<tablet_tool_input_t *>( + zwp_tablet_tool_v2_get_user_data(zwp_tablet_tool_v2)); + cursor_buffer_set_surface_impl(input, buffer, tool_input->cursor_surface, scale); + zwp_tablet_tool_v2_set_cursor(zwp_tablet_tool_v2, + input->tablet.serial, + visible ? tool_input->cursor_surface : nullptr, + hotspot_x, + hotspot_y); + } } } @@ -3347,55 +3488,19 @@ GHOST_TSuccess GHOST_SystemWayland::setCustomCursorShape(uint8_t *bitmap, cursor_t *cursor = &d->inputs[0]->cursor; - static const int32_t stride = sizex * 4; /* ARGB */ - cursor->file_buffer->size = (size_t)stride * sizey; - -#ifdef HAVE_MEMFD_CREATE - const int fd = memfd_create("blender-cursor-custom", MFD_CLOEXEC | MFD_ALLOW_SEALING); - if (fd >= 0) { - fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL); - } -#else - char *path = getenv("XDG_RUNTIME_DIR"); - if (!path) { - errno = ENOENT; - return GHOST_kFailure; - } - - char *tmpname; - asprintf(&tmpname, "%s/%s", path, "blender-XXXXXX"); - const int fd = mkostemp(tmpname, O_CLOEXEC); - if (fd >= 0) { - unlink(tmpname); + if (cursor->custom_data) { + munmap(cursor->custom_data, cursor->custom_data_size); + cursor->custom_data = nullptr; + cursor->custom_data_size = 0; /* Not needed, but the value is no longer meaningful. */ } - free(tmpname); -#endif - - if (UNLIKELY(fd < 0)) { - return GHOST_kFailure; - } - - if (UNLIKELY(posix_fallocate(fd, 0, int32_t(cursor->file_buffer->size)) != 0)) { - return GHOST_kFailure; - } - - cursor->file_buffer->data = mmap( - nullptr, cursor->file_buffer->size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - if (UNLIKELY(cursor->file_buffer->data == MAP_FAILED)) { - cursor->file_buffer->data = nullptr; - close(fd); + const int32_t size_xy[2] = {sizex, sizey}; + wl_buffer *buffer = ghost_wl_buffer_create_for_image( + d->shm, size_xy, WL_SHM_FORMAT_ARGB8888, &cursor->custom_data, &cursor->custom_data_size); + if (buffer == nullptr) { return GHOST_kFailure; } - struct wl_shm_pool *pool = wl_shm_create_pool(d->shm, fd, int32_t(cursor->file_buffer->size)); - - wl_buffer *buffer = wl_shm_pool_create_buffer( - pool, 0, sizex, sizey, stride, WL_SHM_FORMAT_ARGB8888); - - wl_shm_pool_destroy(pool); - close(fd); - wl_buffer_add_listener(buffer, &cursor_buffer_listener, cursor); static constexpr uint32_t black = 0xFF000000; @@ -3406,7 +3511,7 @@ GHOST_TSuccess GHOST_SystemWayland::setCustomCursorShape(uint8_t *bitmap, uint32_t *pixel; for (int y = 0; y < sizey; ++y) { - pixel = &static_cast<uint32_t *>(cursor->file_buffer->data)[y * sizex]; + pixel = &static_cast<uint32_t *>(cursor->custom_data)[y * sizex]; for (int x = 0; x < sizex; ++x) { if ((x % 8) == 0) { datab = *bitmap++; @@ -3445,7 +3550,7 @@ GHOST_TSuccess GHOST_SystemWayland::setCustomCursorShape(uint8_t *bitmap, GHOST_TSuccess GHOST_SystemWayland::getCursorBitmap(GHOST_CursorBitmapRef *bitmap) { cursor_t *cursor = &d->inputs[0]->cursor; - if (cursor->file_buffer->data == nullptr) { + if (cursor->custom_data == nullptr) { return GHOST_kFailure; } if (!cursor->is_custom) { @@ -3458,7 +3563,7 @@ GHOST_TSuccess GHOST_SystemWayland::getCursorBitmap(GHOST_CursorBitmapRef *bitma bitmap->hot_spot[0] = cursor->wl_image.hotspot_x; bitmap->hot_spot[1] = cursor->wl_image.hotspot_y; - bitmap->data = (uint8_t *)static_cast<void *>(cursor->file_buffer->data); + bitmap->data = (uint8_t *)static_cast<void *>(cursor->custom_data); return GHOST_kSuccess; } @@ -3549,6 +3654,8 @@ static input_grab_state_t input_grab_state_from_mode(const GHOST_TGrabCursorMode static const char *ghost_wl_output_tag_id = "GHOST-output"; static const char *ghost_wl_surface_tag_id = "GHOST-window"; +static const char *ghost_wl_surface_cursor_pointer_tag_id = "GHOST-cursor-pointer"; +static const char *ghost_wl_surface_cursor_tablet_tag_id = "GHOST-cursor-tablet"; bool ghost_wl_output_own(const struct wl_output *output) { @@ -3560,6 +3667,16 @@ bool ghost_wl_surface_own(const struct wl_surface *surface) return wl_proxy_get_tag((struct wl_proxy *)surface) == &ghost_wl_surface_tag_id; } +bool ghost_wl_surface_own_cursor_pointer(const struct wl_surface *surface) +{ + return wl_proxy_get_tag((struct wl_proxy *)surface) == &ghost_wl_surface_cursor_pointer_tag_id; +} + +bool ghost_wl_surface_own_cursor_tablet(const struct wl_surface *surface) +{ + return wl_proxy_get_tag((struct wl_proxy *)surface) == &ghost_wl_surface_cursor_tablet_tag_id; +} + void ghost_wl_output_tag(struct wl_output *output) { wl_proxy_set_tag((struct wl_proxy *)output, &ghost_wl_output_tag_id); @@ -3570,6 +3687,16 @@ void ghost_wl_surface_tag(struct wl_surface *surface) wl_proxy_set_tag((struct wl_proxy *)surface, &ghost_wl_surface_tag_id); } +void ghost_wl_surface_tag_cursor_pointer(struct wl_surface *surface) +{ + wl_proxy_set_tag((struct wl_proxy *)surface, &ghost_wl_surface_cursor_pointer_tag_id); +} + +void ghost_wl_surface_tag_cursor_tablet(struct wl_surface *surface) +{ + wl_proxy_set_tag((struct wl_proxy *)surface, &ghost_wl_surface_cursor_tablet_tag_id); +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/intern/ghost/intern/GHOST_SystemWayland.h b/intern/ghost/intern/GHOST_SystemWayland.h index 0d51759aa2f..bdf5f2fc273 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.h +++ b/intern/ghost/intern/GHOST_SystemWayland.h @@ -41,6 +41,12 @@ bool ghost_wl_surface_own(const struct wl_surface *surface); void ghost_wl_surface_tag(struct wl_surface *surface); GHOST_WindowWayland *ghost_wl_surface_user_data(struct wl_surface *surface); +bool ghost_wl_surface_own_cursor_pointer(const struct wl_surface *surface); +void ghost_wl_surface_tag_cursor_pointer(struct wl_surface *surface); + +bool ghost_wl_surface_own_cursor_tablet(const struct wl_surface *surface); +void ghost_wl_surface_tag_cursor_tablet(struct wl_surface *surface); + #ifdef WITH_GHOST_WAYLAND_DYNLOAD /** * Return true when all required WAYLAND libraries are present, diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp index 1365c281ab4..5a930209376 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.cpp +++ b/intern/ghost/intern/GHOST_SystemWin32.cpp @@ -1256,9 +1256,8 @@ GHOST_EventKey *GHOST_SystemWin32::processKeyEvent(GHOST_WindowWin32 *window, RA keyDown ? GHOST_kEventKeyDown : GHOST_kEventKeyUp, window, key, - ascii, - utf8_char, - is_repeat); + is_repeat, + utf8_char); // GHOST_PRINTF("%c\n", ascii); // we already get this info via EventPrinter } diff --git a/intern/ghost/intern/GHOST_SystemX11.cpp b/intern/ghost/intern/GHOST_SystemX11.cpp index 5e549b54afd..00cc5f3af8f 100644 --- a/intern/ghost/intern/GHOST_SystemX11.cpp +++ b/intern/ghost/intern/GHOST_SystemX11.cpp @@ -745,9 +745,8 @@ bool GHOST_SystemX11::processEvents(bool waitForEvent) GHOST_kEventKeyDown, window, ghost_key_from_keysym(modifiers[i]), - '\0', - nullptr, - false)); + false, + nullptr)); } } } @@ -1017,7 +1016,9 @@ void GHOST_SystemX11::processEvent(XEvent *xe) case KeyRelease: { XKeyEvent *xke = &(xe->xkey); KeySym key_sym; + char *utf8_buf = nullptr; char ascii; + #if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING) /* utf8_array[] is initial buffer used for Xutf8LookupString(). * if the length of the utf8 string exceeds this array, allocate @@ -1026,12 +1027,10 @@ void GHOST_SystemX11::processEvent(XEvent *xe) * at the end of this buffer when the constructor of GHOST_EventKey * reads 6 bytes regardless of the effective data length. */ char utf8_array[16 * 6 + 5]; /* 16 utf8 characters */ - char *utf8_buf = utf8_array; - int len = 1; /* at least one null character will be stored */ + int len = 1; /* at least one null character will be stored */ #else - char *utf8_buf = nullptr; + char utf8_array[sizeof(GHOST_TEventKeyData::utf8_buf)] = {'\0'}; #endif - GHOST_TEventType type = (xke->type == KeyPress) ? GHOST_kEventKeyDown : GHOST_kEventKeyUp; GHOST_TKey gkey; @@ -1151,81 +1150,94 @@ void GHOST_SystemX11::processEvent(XEvent *xe) #endif #if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING) - /* Setting unicode on key-up events gives #XLookupNone status. */ - XIC xic = window->getX11_XIC(); - if (xic && xke->type == KeyPress) { - Status status; + /* Only used for key-press. */ + XIC xic = nullptr; +#endif - /* Use utf8 because its not locale repentant, from XORG docs. */ - if (!(len = Xutf8LookupString( - xic, xke, utf8_buf, sizeof(utf8_array) - 5, &key_sym, &status))) { - utf8_buf[0] = '\0'; - } + if (xke->type == KeyPress) { + utf8_buf = utf8_array; +#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING) + /* Setting unicode on key-up events gives #XLookupNone status. */ + xic = window->getX11_XIC(); + if (xic) { + Status status; + + /* Use utf8 because its not locale repentant, from XORG docs. */ + if (!(len = Xutf8LookupString( + xic, xke, utf8_buf, sizeof(utf8_array) - 5, &key_sym, &status))) { + utf8_buf[0] = '\0'; + } - if (status == XBufferOverflow) { - utf8_buf = (char *)malloc(len + 5); - len = Xutf8LookupString(xic, xke, utf8_buf, len, &key_sym, &status); - } + if (status == XBufferOverflow) { + utf8_buf = (char *)malloc(len + 5); + len = Xutf8LookupString(xic, xke, utf8_buf, len, &key_sym, &status); + } - if (ELEM(status, XLookupChars, XLookupBoth)) { - if ((unsigned char)utf8_buf[0] >= 32) { /* not an ascii control character */ - /* do nothing for now, this is valid utf8 */ + if (ELEM(status, XLookupChars, XLookupBoth)) { + if ((unsigned char)utf8_buf[0] >= 32) { /* not an ascii control character */ + /* do nothing for now, this is valid utf8 */ + } + else { + utf8_buf[0] = '\0'; + } + } + else if (status == XLookupKeySym) { + /* this key doesn't have a text representation, it is a command + * key of some sort */ } else { - utf8_buf[0] = '\0'; + printf("Bad keycode lookup. Keysym 0x%x Status: %s\n", + (unsigned int)key_sym, + (status == XLookupNone ? "XLookupNone" : + status == XLookupKeySym ? "XLookupKeySym" : + "Unknown status")); + + printf("'%.*s' %p %p\n", len, utf8_buf, xic, m_xim); } } - else if (status == XLookupKeySym) { - /* this key doesn't have a text representation, it is a command - * key of some sort */ - } else { - printf("Bad keycode lookup. Keysym 0x%x Status: %s\n", - (unsigned int)key_sym, - (status == XLookupNone ? "XLookupNone" : - status == XLookupKeySym ? "XLookupKeySym" : - "Unknown status")); - - printf("'%.*s' %p %p\n", len, utf8_buf, xic, m_xim); + utf8_buf[0] = '\0'; } - } - else { - utf8_buf[0] = '\0'; - } #endif + if (!utf8_buf[0] && ascii) { + utf8_buf[0] = ascii; + utf8_buf[1] = '\0'; + } + } - g_event = new GHOST_EventKey( - getMilliSeconds(), type, window, gkey, ascii, utf8_buf, is_repeat); + g_event = new GHOST_EventKey(getMilliSeconds(), type, window, gkey, is_repeat, utf8_buf); #if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING) /* when using IM for some languages such as Japanese, * one event inserts multiple utf8 characters */ - if (xic && xke->type == KeyPress) { + if (xke->type == KeyPress && xic) { unsigned char c; int i = 0; - while (1) { - /* search character boundary */ - if ((unsigned char)utf8_buf[i++] > 0x7f) { + while (true) { + /* Search character boundary. */ + if ((uchar)utf8_buf[i++] > 0x7f) { for (; i < len; ++i) { c = utf8_buf[i]; - if (c < 0x80 || c > 0xbf) + if (c < 0x80 || c > 0xbf) { break; + } } } - if (i >= len) + if (i >= len) { break; - - /* enqueue previous character */ + } + /* Enqueue previous character. */ pushEvent(g_event); g_event = new GHOST_EventKey( - getMilliSeconds(), type, window, gkey, '\0', &utf8_buf[i], is_repeat); + getMilliSeconds(), type, window, gkey, is_repeat, &utf8_buf[i]); } } - if (utf8_buf != utf8_array) + if (utf8_buf != utf8_array) { free(utf8_buf); + } #endif break; diff --git a/intern/ghost/intern/GHOST_WindowManager.cpp b/intern/ghost/intern/GHOST_WindowManager.cpp index 19684a44169..e8785cbdb24 100644 --- a/intern/ghost/intern/GHOST_WindowManager.cpp +++ b/intern/ghost/intern/GHOST_WindowManager.cpp @@ -162,17 +162,3 @@ GHOST_IWindow *GHOST_WindowManager::getWindowAssociatedWithOSWindow(void *osWind } return nullptr; } - -bool GHOST_WindowManager::getAnyModifiedState() -{ - bool isAnyModified = false; - std::vector<GHOST_IWindow *>::iterator iter; - - for (iter = m_windows.begin(); iter != m_windows.end(); ++iter) { - if ((*iter)->getModifiedState()) { - isAnyModified = true; - } - } - - return isAnyModified; -} diff --git a/intern/ghost/intern/GHOST_WindowManager.h b/intern/ghost/intern/GHOST_WindowManager.h index 9d20413c433..bf7a0f4ec61 100644 --- a/intern/ghost/intern/GHOST_WindowManager.h +++ b/intern/ghost/intern/GHOST_WindowManager.h @@ -109,12 +109,6 @@ class GHOST_WindowManager { */ GHOST_IWindow *getWindowAssociatedWithOSWindow(void *osWindow); - /** - * Return true if any windows has a modified status - * \return True if any window has unsaved changes - */ - bool getAnyModifiedState(); - protected: /** The list of windows managed */ std::vector<GHOST_IWindow *> m_windows; diff --git a/intern/ghost/test/multitest/EventToBuf.c b/intern/ghost/test/multitest/EventToBuf.c index baab32328c3..846a867a371 100644 --- a/intern/ghost/test/multitest/EventToBuf.c +++ b/intern/ghost/test/multitest/EventToBuf.c @@ -218,8 +218,10 @@ void event_to_buf(GHOST_EventHandle evt, char buf[128]) case GHOST_kEventKeyUp: { GHOST_TEventKeyData *kd = data; pos += sprintf(pos, " - key: %s (%d)", keytype_to_string(kd->key), kd->key); - if (kd->ascii) - pos += sprintf(pos, " ascii: '%c' (%d)", kd->ascii, kd->ascii); + /* TODO: ideally this would print the unicode character. */ + if (kd->utf8_buf[0]) { + pos += sprintf(pos, " ascii: '%c' (%d)", kd->utf8_buf[0], kd->utf8_buf[0]); + } break; } } diff --git a/intern/guardedalloc/intern/mallocn_lockfree_impl.c b/intern/guardedalloc/intern/mallocn_lockfree_impl.c index 73912ad07b1..300e2000a14 100644 --- a/intern/guardedalloc/intern/mallocn_lockfree_impl.c +++ b/intern/guardedalloc/intern/mallocn_lockfree_impl.c @@ -44,6 +44,7 @@ enum { #define PTR_FROM_MEMHEAD(memhead) (memhead + 1) #define MEMHEAD_ALIGNED_FROM_PTR(ptr) (((MemHeadAligned *)ptr) - 1) #define MEMHEAD_IS_ALIGNED(memhead) ((memhead)->len & (size_t)MEMHEAD_ALIGN_FLAG) +#define MEMHEAD_LEN(memhead) ((memhead)->len & ~((size_t)(MEMHEAD_ALIGN_FLAG))) /* Uncomment this to have proper peak counter. */ #define USE_ATOMIC_MAX @@ -78,8 +79,8 @@ print_error(const char *str, ...) size_t MEM_lockfree_allocN_len(const void *vmemh) { - if (vmemh) { - return MEMHEAD_FROM_PTR(vmemh)->len & ~((size_t)(MEMHEAD_ALIGN_FLAG)); + if (LIKELY(vmemh)) { + return MEMHEAD_LEN(MEMHEAD_FROM_PTR(vmemh)); } return 0; @@ -87,14 +88,11 @@ size_t MEM_lockfree_allocN_len(const void *vmemh) void MEM_lockfree_freeN(void *vmemh) { - if (leak_detector_has_run) { + if (UNLIKELY(leak_detector_has_run)) { print_error("%s\n", free_after_leak_detection_message); } - MemHead *memh = MEMHEAD_FROM_PTR(vmemh); - size_t len = MEM_lockfree_allocN_len(vmemh); - - if (vmemh == NULL) { + if (UNLIKELY(vmemh == NULL)) { print_error("Attempt to free NULL pointer\n"); #ifdef WITH_ASSERT_ABORT abort(); @@ -102,6 +100,9 @@ void MEM_lockfree_freeN(void *vmemh) return; } + MemHead *memh = MEMHEAD_FROM_PTR(vmemh); + size_t len = MEMHEAD_LEN(memh); + atomic_sub_and_fetch_u(&totblock, 1); atomic_sub_and_fetch_z(&mem_in_use, len); diff --git a/intern/opensubdiv/CMakeLists.txt b/intern/opensubdiv/CMakeLists.txt index bb3aa16a9fe..14cc6a70cd5 100644 --- a/intern/opensubdiv/CMakeLists.txt +++ b/intern/opensubdiv/CMakeLists.txt @@ -42,18 +42,6 @@ if(WITH_OPENSUBDIV) internal/base/util.cc internal/base/util.h - # Device. - internal/device/device_context_cuda.cc - internal/device/device_context_cuda.h - internal/device/device_context_glsl_compute.cc - internal/device/device_context_glsl_compute.h - internal/device/device_context_glsl_transform_feedback.cc - internal/device/device_context_glsl_transform_feedback.h - internal/device/device_context_opencl.cc - internal/device/device_context_opencl.h - internal/device/device_context_openmp.cc - internal/device/device_context_openmp.h - # Evaluator. internal/evaluator/eval_output.cc internal/evaluator/eval_output.h diff --git a/intern/opensubdiv/internal/base/opensubdiv_capi.cc b/intern/opensubdiv/internal/base/opensubdiv_capi.cc index 85f8120c76b..40d820836b9 100644 --- a/intern/opensubdiv/internal/base/opensubdiv_capi.cc +++ b/intern/opensubdiv/internal/base/opensubdiv_capi.cc @@ -21,55 +21,15 @@ #endif #include "internal/base/util.h" -#include "internal/device/device_context_cuda.h" -#include "internal/device/device_context_glsl_compute.h" -#include "internal/device/device_context_glsl_transform_feedback.h" -#include "internal/device/device_context_opencl.h" -#include "internal/device/device_context_openmp.h" - -using blender::opensubdiv::CUDADeviceContext; -using blender::opensubdiv::GLSLComputeDeviceContext; -using blender::opensubdiv::GLSLTransformFeedbackDeviceContext; -using blender::opensubdiv::OpenCLDeviceContext; -using blender::opensubdiv::OpenMPDeviceContext; void openSubdiv_init() { - // Ensure all OpenGL strings are cached. - openSubdiv_getAvailableEvaluators(); } void openSubdiv_cleanup() { } -int openSubdiv_getAvailableEvaluators() -{ - int flags = OPENSUBDIV_EVALUATOR_CPU; - - if (OpenMPDeviceContext::isSupported()) { - flags |= OPENSUBDIV_EVALUATOR_OPENMP; - } - - if (OpenCLDeviceContext::isSupported()) { - flags |= OPENSUBDIV_EVALUATOR_OPENCL; - } - - if (CUDADeviceContext::isSupported()) { - flags |= OPENSUBDIV_EVALUATOR_CUDA; - } - - if (GLSLTransformFeedbackDeviceContext::isSupported()) { - flags |= OPENSUBDIV_EVALUATOR_GLSL_TRANSFORM_FEEDBACK; - } - - if (GLSLComputeDeviceContext::isSupported()) { - flags |= OPENSUBDIV_EVALUATOR_GLSL_COMPUTE; - } - - return flags; -} - int openSubdiv_getVersionHex() { #if defined(OPENSUBDIV_VERSION_NUMBER) diff --git a/intern/opensubdiv/internal/device/device_context_cuda.cc b/intern/opensubdiv/internal/device/device_context_cuda.cc deleted file mode 100644 index cd4336265a5..00000000000 --- a/intern/opensubdiv/internal/device/device_context_cuda.cc +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2020 Blender Foundation. All rights reserved. -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software Foundation, -// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -// -// Author: Sergey Sharybin - -#include "internal/device/device_context_cuda.h" - -namespace blender { -namespace opensubdiv { - -bool CUDADeviceContext::isSupported() -{ - // TODO(sergey): Add CUDA device support, using CUDA-RT API. - return false; -} - -CUDADeviceContext::CUDADeviceContext() -{ -} - -CUDADeviceContext::~CUDADeviceContext() -{ -} - -} // namespace opensubdiv -} // namespace blender diff --git a/intern/opensubdiv/internal/device/device_context_cuda.h b/intern/opensubdiv/internal/device/device_context_cuda.h deleted file mode 100644 index d1bfb15fbcb..00000000000 --- a/intern/opensubdiv/internal/device/device_context_cuda.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2020 Blender Foundation. All rights reserved. -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software Foundation, -// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -// -// Author: Sergey Sharybin - -#ifndef OPENSUBDIV_DEVICE_CONTEXT_CUDA_H_ -#define OPENSUBDIV_DEVICE_CONTEXT_CUDA_H_ - -namespace blender { -namespace opensubdiv { - -class CUDADeviceContext { - public: - // Stateless check to see whether CUDA functionality is available on this - // platform. - static bool isSupported(); - - CUDADeviceContext(); - ~CUDADeviceContext(); -}; - -} // namespace opensubdiv -} // namespace blender - -#endif // _OPENSUBDIV_DEVICE_CONTEXT_CUDA_H_ diff --git a/intern/opensubdiv/internal/device/device_context_glsl_compute.cc b/intern/opensubdiv/internal/device/device_context_glsl_compute.cc deleted file mode 100644 index 7b416976099..00000000000 --- a/intern/opensubdiv/internal/device/device_context_glsl_compute.cc +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2020 Blender Foundation. All rights reserved. -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software Foundation, -// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -// -// Author: Sergey Sharybin - -#include "internal/device/device_context_glsl_compute.h" - -#include <GL/glew.h> - -namespace blender { -namespace opensubdiv { - -bool GLSLComputeDeviceContext::isSupported() -{ - return GLEW_VERSION_4_3 || GLEW_ARB_compute_shader; -} - -GLSLComputeDeviceContext::GLSLComputeDeviceContext() -{ -} - -GLSLComputeDeviceContext::~GLSLComputeDeviceContext() -{ -} - -} // namespace opensubdiv -} // namespace blender diff --git a/intern/opensubdiv/internal/device/device_context_glsl_compute.h b/intern/opensubdiv/internal/device/device_context_glsl_compute.h deleted file mode 100644 index f64c7d1954b..00000000000 --- a/intern/opensubdiv/internal/device/device_context_glsl_compute.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2020 Blender Foundation. All rights reserved. -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software Foundation, -// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -// -// Author: Sergey Sharybin - -#ifndef OPENSUBDIV_DEVICE_CONTEXT_GLSL_COMPUTE_H_ -#define OPENSUBDIV_DEVICE_CONTEXT_GLSL_COMPUTE_H_ - -namespace blender { -namespace opensubdiv { - -class GLSLComputeDeviceContext { - public: - // Stateless check to see whether GLSL compute functionality is - // available on this platform. - static bool isSupported(); - - GLSLComputeDeviceContext(); - ~GLSLComputeDeviceContext(); -}; - -} // namespace opensubdiv -} // namespace blender - -#endif // _OPENSUBDIV_DEVICE_CONTEXT_GLSL_COMPUTE_H_ diff --git a/intern/opensubdiv/internal/device/device_context_glsl_transform_feedback.cc b/intern/opensubdiv/internal/device/device_context_glsl_transform_feedback.cc deleted file mode 100644 index ef897608b6e..00000000000 --- a/intern/opensubdiv/internal/device/device_context_glsl_transform_feedback.cc +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2020 Blender Foundation. All rights reserved. -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software Foundation, -// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -// -// Author: Sergey Sharybin - -#include "internal/device/device_context_glsl_transform_feedback.h" - -#include <GL/glew.h> - -namespace blender { -namespace opensubdiv { - -bool GLSLTransformFeedbackDeviceContext::isSupported() -{ - return GLEW_VERSION_4_1; -} - -GLSLTransformFeedbackDeviceContext::GLSLTransformFeedbackDeviceContext() -{ -} - -GLSLTransformFeedbackDeviceContext::~GLSLTransformFeedbackDeviceContext() -{ -} - -} // namespace opensubdiv -} // namespace blender diff --git a/intern/opensubdiv/internal/device/device_context_glsl_transform_feedback.h b/intern/opensubdiv/internal/device/device_context_glsl_transform_feedback.h deleted file mode 100644 index 7bbbba1380f..00000000000 --- a/intern/opensubdiv/internal/device/device_context_glsl_transform_feedback.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2020 Blender Foundation. All rights reserved. -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software Foundation, -// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -// -// Author: Sergey Sharybin - -#ifndef OPENSUBDIV_DEVICE_CONTEXT_GLSL_TRANSFORM_FEEDBACK_H_ -#define OPENSUBDIV_DEVICE_CONTEXT_GLSL_TRANSFORM_FEEDBACK_H_ - -namespace blender { -namespace opensubdiv { - -class GLSLTransformFeedbackDeviceContext { - public: - // Stateless check to see whether GLSL transform feedback functionality is - // available on this platform. - static bool isSupported(); - - GLSLTransformFeedbackDeviceContext(); - ~GLSLTransformFeedbackDeviceContext(); -}; - -} // namespace opensubdiv -} // namespace blender - -#endif // _OPENSUBDIV_DEVICE_CONTEXT_GLSL_TRANSFORM_FEEDBACK_H_ diff --git a/intern/opensubdiv/internal/device/device_context_opencl.cc b/intern/opensubdiv/internal/device/device_context_opencl.cc deleted file mode 100644 index 1670ea3c9d0..00000000000 --- a/intern/opensubdiv/internal/device/device_context_opencl.cc +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2020 Blender Foundation. All rights reserved. -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software Foundation, -// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -// -// Author: Sergey Sharybin - -#include "internal/device/device_context_opencl.h" - -namespace blender { -namespace opensubdiv { - -bool OpenCLDeviceContext::isSupported() -{ - // TODO(sergey): Add support of OpenCL devices. - return false; -} - -OpenCLDeviceContext::OpenCLDeviceContext() -{ -} - -OpenCLDeviceContext::~OpenCLDeviceContext() -{ -} - -} // namespace opensubdiv -} // namespace blender diff --git a/intern/opensubdiv/internal/device/device_context_opencl.h b/intern/opensubdiv/internal/device/device_context_opencl.h deleted file mode 100644 index 57ec6ed3bb6..00000000000 --- a/intern/opensubdiv/internal/device/device_context_opencl.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2020 Blender Foundation. All rights reserved. -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software Foundation, -// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -// -// Author: Sergey Sharybin - -#ifndef OPENSUBDIV_DEVICE_CONTEXT_OPENCL_H_ -#define OPENSUBDIV_DEVICE_CONTEXT_OPENCL_H_ - -namespace blender { -namespace opensubdiv { - -class OpenCLDeviceContext { - public: - // Stateless check to see whether OpenCL functionality is available on this - // platform. - static bool isSupported(); - - OpenCLDeviceContext(); - ~OpenCLDeviceContext(); -}; - -} // namespace opensubdiv -} // namespace blender - -#endif // _OPENSUBDIV_DEVICE_CONTEXT_OPENCL_H_ diff --git a/intern/opensubdiv/internal/device/device_context_openmp.cc b/intern/opensubdiv/internal/device/device_context_openmp.cc deleted file mode 100644 index e01312fefaf..00000000000 --- a/intern/opensubdiv/internal/device/device_context_openmp.cc +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2020 Blender Foundation. All rights reserved. -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software Foundation, -// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -// -// Author: Sergey Sharybin - -#include "internal/device/device_context_openmp.h" - -namespace blender { -namespace opensubdiv { - -bool OpenMPDeviceContext::isSupported() -{ -#ifdef OPENSUBDIV_HAS_OPENMP - return true; -#else - return false; -#endif -} - -OpenMPDeviceContext::OpenMPDeviceContext() -{ -} - -OpenMPDeviceContext::~OpenMPDeviceContext() -{ -} - -} // namespace opensubdiv -} // namespace blender diff --git a/intern/opensubdiv/internal/device/device_context_openmp.h b/intern/opensubdiv/internal/device/device_context_openmp.h deleted file mode 100644 index 2bebbdf40bc..00000000000 --- a/intern/opensubdiv/internal/device/device_context_openmp.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2020 Blender Foundation. All rights reserved. -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software Foundation, -// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -// -// Author: Sergey Sharybin - -#ifndef OPENSUBDIV_DEVICE_CONTEXT_OPENMP_H_ -#define OPENSUBDIV_DEVICE_CONTEXT_OPENMP_H_ - -namespace blender { -namespace opensubdiv { - -class OpenMPDeviceContext { - public: - // Stateless check to see whether OpenMP functionality is available on this - // platform. - static bool isSupported(); - - OpenMPDeviceContext(); - ~OpenMPDeviceContext(); -}; - -} // namespace opensubdiv -} // namespace blender - -#endif // _OPENSUBDIV_DEVICE_CONTEXT_OPENMP_H_ diff --git a/intern/opensubdiv/internal/evaluator/evaluator_cache_impl.cc b/intern/opensubdiv/internal/evaluator/evaluator_cache_impl.cc index 2fffcefa460..efac0926fef 100644 --- a/intern/opensubdiv/internal/evaluator/evaluator_cache_impl.cc +++ b/intern/opensubdiv/internal/evaluator/evaluator_cache_impl.cc @@ -30,7 +30,7 @@ OpenSubdiv_EvaluatorCacheImpl::~OpenSubdiv_EvaluatorCacheImpl() OpenSubdiv_EvaluatorCacheImpl *openSubdiv_createEvaluatorCacheInternal( eOpenSubdivEvaluator evaluator_type) { - if (evaluator_type != eOpenSubdivEvaluator::OPENSUBDIV_EVALUATOR_GLSL_COMPUTE) { + if (evaluator_type != eOpenSubdivEvaluator::OPENSUBDIV_EVALUATOR_GPU) { return nullptr; } OpenSubdiv_EvaluatorCacheImpl *evaluator_cache; diff --git a/intern/opensubdiv/internal/evaluator/evaluator_impl.cc b/intern/opensubdiv/internal/evaluator/evaluator_impl.cc index 49a59c44be8..262b418169d 100644 --- a/intern/opensubdiv/internal/evaluator/evaluator_impl.cc +++ b/intern/opensubdiv/internal/evaluator/evaluator_impl.cc @@ -442,11 +442,6 @@ OpenSubdiv_EvaluatorImpl *openSubdiv_createEvaluatorInternal( eOpenSubdivEvaluator evaluator_type, OpenSubdiv_EvaluatorCacheImpl *evaluator_cache_descr) { - // Only CPU and GLCompute are implemented at the moment. - if (evaluator_type != OPENSUBDIV_EVALUATOR_CPU && - evaluator_type != OPENSUBDIV_EVALUATOR_GLSL_COMPUTE) { - return NULL; - } using blender::opensubdiv::vector; TopologyRefiner *refiner = topology_refiner->impl->topology_refiner; if (refiner == NULL) { @@ -551,8 +546,8 @@ OpenSubdiv_EvaluatorImpl *openSubdiv_createEvaluatorInternal( // Create OpenSubdiv's CPU side evaluator. blender::opensubdiv::EvalOutputAPI::EvalOutput *eval_output = nullptr; - const bool use_gl_evaluator = evaluator_type == OPENSUBDIV_EVALUATOR_GLSL_COMPUTE; - if (use_gl_evaluator) { + const bool use_gpu_evaluator = evaluator_type == OPENSUBDIV_EVALUATOR_GPU; + if (use_gpu_evaluator) { blender::opensubdiv::GpuEvalOutput::EvaluatorCache *evaluator_cache = nullptr; if (evaluator_cache_descr) { evaluator_cache = static_cast<blender::opensubdiv::GpuEvalOutput::EvaluatorCache *>( diff --git a/intern/opensubdiv/opensubdiv_capi.h b/intern/opensubdiv/opensubdiv_capi.h index a26ea36b863..1fa75a9a8c2 100644 --- a/intern/opensubdiv/opensubdiv_capi.h +++ b/intern/opensubdiv/opensubdiv_capi.h @@ -31,9 +31,6 @@ extern "C" { void openSubdiv_init(void); void openSubdiv_cleanup(void); -// Bitmask of eOpenSubdivEvaluator. -int openSubdiv_getAvailableEvaluators(void); - int openSubdiv_getVersionHex(void); #ifdef __cplusplus diff --git a/intern/opensubdiv/opensubdiv_capi_type.h b/intern/opensubdiv/opensubdiv_capi_type.h index e78842036be..585d9bc49df 100644 --- a/intern/opensubdiv/opensubdiv_capi_type.h +++ b/intern/opensubdiv/opensubdiv_capi_type.h @@ -23,15 +23,9 @@ extern "C" { #endif -// Keep this a bitmask so it's possible to pass available -// evaluators to Blender. typedef enum eOpenSubdivEvaluator { - OPENSUBDIV_EVALUATOR_CPU = (1 << 0), - OPENSUBDIV_EVALUATOR_OPENMP = (1 << 1), - OPENSUBDIV_EVALUATOR_OPENCL = (1 << 2), - OPENSUBDIV_EVALUATOR_CUDA = (1 << 3), - OPENSUBDIV_EVALUATOR_GLSL_TRANSFORM_FEEDBACK = (1 << 4), - OPENSUBDIV_EVALUATOR_GLSL_COMPUTE = (1 << 5), + OPENSUBDIV_EVALUATOR_CPU = 0, + OPENSUBDIV_EVALUATOR_GPU = 1, } eOpenSubdivEvaluator; typedef enum OpenSubdiv_SchemeType { diff --git a/intern/opensubdiv/stub/opensubdiv_stub.cc b/intern/opensubdiv/stub/opensubdiv_stub.cc index 24bdcbc79ff..5eaa2df9a27 100644 --- a/intern/opensubdiv/stub/opensubdiv_stub.cc +++ b/intern/opensubdiv/stub/opensubdiv_stub.cc @@ -28,11 +28,6 @@ void openSubdiv_cleanup() { } -int openSubdiv_getAvailableEvaluators() -{ - return 0; -} - int openSubdiv_getVersionHex() { return 0; diff --git a/release/datafiles/locale b/release/datafiles/locale -Subproject 055bc5223c1cd249e32ccbc8e8796ba9925c8c3 +Subproject a2eb507891449a0b67582be9561840075513661 diff --git a/release/datafiles/userdef/userdef_default.c b/release/datafiles/userdef/userdef_default.c index fba3c7882c6..b4a1ab6d5f8 100644 --- a/release/datafiles/userdef/userdef_default.c +++ b/release/datafiles/userdef/userdef_default.c @@ -174,7 +174,6 @@ const UserDef U_default = { .pie_menu_confirm = 0, .pie_menu_radius = 100, .pie_menu_threshold = 12, - .opensubdiv_compute_type = 0, .factor_display_type = USER_FACTOR_AS_FACTOR, .render_display_type = USER_RENDER_DISPLAY_WINDOW, .filebrowser_display_type = USER_TEMP_SPACE_DISPLAY_WINDOW, diff --git a/release/scripts/addons b/release/scripts/addons -Subproject 403b95ef6ff38918de966ed2a5843cfa3274a58 +Subproject 7a8502871c34db0343cc7de52d6b49b15a84238 diff --git a/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py b/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py index 319fd3396a0..3edb5b445fe 100644 --- a/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py +++ b/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py @@ -803,20 +803,21 @@ def dump_src_messages(msgs, reports, settings): line += data[pos:m.start()].count('\n') msgsrc = rel_path + ":" + str(line) _msgid = d.get("msg_raw") - # First, try the "multi-contexts" stuff! - _msgctxts = tuple(d.get("ctxt_raw{}".format(i)) for i in range(settings.PYGETTEXT_MAX_MULTI_CTXT)) - if _msgctxts[0]: - for _msgctxt in _msgctxts: - if not _msgctxt: - break + if _msgid not in {'""', "''"}: + # First, try the "multi-contexts" stuff! + _msgctxts = tuple(d.get("ctxt_raw{}".format(i)) for i in range(settings.PYGETTEXT_MAX_MULTI_CTXT)) + if _msgctxts[0]: + for _msgctxt in _msgctxts: + if not _msgctxt: + break + msgctxt, msgid = process_entry(_msgctxt, _msgid) + process_msg(msgs, msgctxt, msgid, msgsrc, reports, check_ctxt_src, settings) + reports["src_messages"].append((msgctxt, msgid, msgsrc)) + else: + _msgctxt = d.get("ctxt_raw") msgctxt, msgid = process_entry(_msgctxt, _msgid) process_msg(msgs, msgctxt, msgid, msgsrc, reports, check_ctxt_src, settings) reports["src_messages"].append((msgctxt, msgid, msgsrc)) - else: - _msgctxt = d.get("ctxt_raw") - msgctxt, msgid = process_entry(_msgctxt, _msgid) - process_msg(msgs, msgctxt, msgid, msgsrc, reports, check_ctxt_src, settings) - reports["src_messages"].append((msgctxt, msgid, msgsrc)) pos = m.end() line += data[m.start():pos].count('\n') diff --git a/release/scripts/modules/bl_i18n_utils/settings.py b/release/scripts/modules/bl_i18n_utils/settings.py index 408f8523b8d..3b69f8a6bf7 100644 --- a/release/scripts/modules/bl_i18n_utils/settings.py +++ b/release/scripts/modules/bl_i18n_utils/settings.py @@ -248,9 +248,14 @@ PYGETTEXT_KEYWORDS = (() + tuple(("{}\\((?:[^\"',]+,){{3}}\\s*" + _msg_re + r"\s*\)").format(it) for it in ("BMO_error_raise",)) + - tuple(("{}\\((?:[^\"',]+,)\\s*" + _msg_re + r"\s*(?:\)|,)").format(it) + tuple(("{}\\((?:[^\"',]+,){{2}}\\s*" + _msg_re + r"\s*(?:\)|,)").format(it) for it in ("BKE_modifier_set_error",)) + + # This one is a tad more risky, but in practice would not expect a name/uid string parameter + # (the second one in those functions) to ever have a comma in it, so think this is fine. + tuple(("{}\\((?:[^,]+,){{2}}\\s*" + _msg_re + r"\s*(?:\)|,)").format(it) + for it in ("modifier_subpanel_register", "gpencil_modifier_subpanel_register")) + + # bUnitDef unit names. # NOTE: regex is a bit more complex than it would need too. Since the actual # identifier (`B_UNIT_DEF_`) is at the end, if it's simpler/too general it @@ -297,6 +302,7 @@ WARN_MSGID_NOT_CAPITALIZED_ALLOWED = { "ascii", "author", # Addons' field. :/ "bItasc", + "blender.org", "color_index is invalid", "cos(A)", "cosh(A)", @@ -312,6 +318,7 @@ WARN_MSGID_NOT_CAPITALIZED_ALLOWED = { "glTF 2.0 (.glb/.gltf)", "glTF Binary (.glb)", "glTF Embedded (.gltf)", + "glTF Original PBR data", "glTF Separate (.gltf + .bin + textures)", "invoke() needs to be called before execute()", "iScale", @@ -330,6 +337,7 @@ WARN_MSGID_NOT_CAPITALIZED_ALLOWED = { "mp3", "normal", "ogg", + "oneAPI", "p0", "px", "re", @@ -340,6 +348,8 @@ WARN_MSGID_NOT_CAPITALIZED_ALLOWED = { "sinh(A)", "sqrt(x*x+y*y+z*z)", "sRGB", + "sRGB display space", + "sRGB display space with Filmic view transform", "tan(A)", "tanh(A)", "utf-8", @@ -356,7 +366,9 @@ WARN_MSGID_NOT_CAPITALIZED_ALLOWED = { "all and invert unselected", "and AMD driver version 22.10 or newer", "and AMD Radeon Pro 21.Q4 driver or newer", + "and Linux driver version xx.xx.28000 or newer", "and NVIDIA driver version 470 or newer", + "and Windows driver version 101.1660 or newer", "available with", "brown fox", "can't save image while rendering", @@ -431,6 +443,7 @@ WARN_MSGID_END_POINT_ALLOWED = { "The program will now close.", "Your graphics card or driver has limited support. It may work, but with issues.", "Your graphics card or driver is not supported.", + "Invalid surface UVs on %d curves.", } PARSER_CACHE_HASH = 'sha1' diff --git a/release/scripts/modules/bl_i18n_utils/utils_spell_check.py b/release/scripts/modules/bl_i18n_utils/utils_spell_check.py index 74785c81bfd..462c954d54a 100644 --- a/release/scripts/modules/bl_i18n_utils/utils_spell_check.py +++ b/release/scripts/modules/bl_i18n_utils/utils_spell_check.py @@ -433,6 +433,7 @@ class SpellChecker: "polyline", "polylines", "probabilistically", "pulldown", "pulldowns", + "quadratically", "quantized", "quartic", "quaternion", "quaternions", @@ -501,6 +502,7 @@ class SpellChecker: "luminance", "mantaflow", "matcap", + "microfacet", "midtones", "mipmap", "mipmaps", "mip", "ngon", "ngons", @@ -508,6 +510,7 @@ class SpellChecker: "nurb", "nurbs", "perlin", "phong", + "photorealistic", "pinlight", "posterize", "qi", @@ -675,7 +678,7 @@ class SpellChecker: "ascii", "atrac", "avx", - "bsdf", + "bsdf", "bsdfs", "bssrdf", "bw", "ccd", @@ -708,14 +711,17 @@ class SpellChecker: "hdc", "hdr", "hdri", "hdris", "hh", "mm", "ss", "ff", # hh:mm:ss:ff timecode + "hpg", # Intel Xe-HPG architecture "hsv", "hsva", "hsl", "id", "ies", "ior", "itu", "jonswap", + "lfe", "lhs", "lmb", "mmb", "rmb", + "lscm", "kb", "mocap", "msgid", "msgids", diff --git a/release/scripts/modules/blend_render_info.py b/release/scripts/modules/blend_render_info.py index 37c5f6dd3ba..6b45a6f7e72 100755 --- a/release/scripts/modules/blend_render_info.py +++ b/release/scripts/modules/blend_render_info.py @@ -93,6 +93,11 @@ def _read_blend_rend_chunk_from_file(blendfile, filepath): break sizeof_data_left = struct.unpack('>i' if is_big_endian else '<i', blendfile.read(4))[0] + if sizeof_data_left < 0: + # Very unlikely, but prevent other errors. + sys.stderr.write("Negative block size found (corrupt file): %s\n" % filepath) + break + # 4 from the `head_id`, another 4 for the size of the BHEAD. sizeof_bhead_left = sizeof_bhead - 8 diff --git a/release/scripts/modules/bpy_types.py b/release/scripts/modules/bpy_types.py index aaa2ae59a0d..df0631ec26d 100644 --- a/release/scripts/modules/bpy_types.py +++ b/release/scripts/modules/bpy_types.py @@ -56,6 +56,21 @@ class Context(StructRNA): if value is None: return value + # If the attribute is a list property, apply subscripting. + if isinstance(value, list) and path_rest.startswith("["): + index_str, div, index_tail = path_rest[1:].partition("]") + if not div: + raise ValueError("Path index is not terminated: %s%s" % (attr, path_rest)) + try: + index = int(index_str) + except ValueError: + raise ValueError("Path index is invalid: %s[%s]" % (attr, index_str)) + if 0 <= index < len(value): + path_rest = index_tail + value = value[index] + else: + raise IndexError("Path index out of range: %s[%s]" % (attr, index_str)) + # Resolve the rest of the path if necessary. if path_rest: path_resolve_fn = getattr(value, "path_resolve", None) diff --git a/release/scripts/presets/cycles/performance/Default.py b/release/scripts/presets/cycles/performance/Default.py new file mode 100644 index 00000000000..5c25f23eca0 --- /dev/null +++ b/release/scripts/presets/cycles/performance/Default.py @@ -0,0 +1,12 @@ +import bpy +render = bpy.context.scene.render +cycles = bpy.context.scene.cycles + +render.threads_mode = 'AUTO' +render.use_persistent_data = False +cycles.debug_use_spatial_splits = False +cycles.debug_use_compact_bvh = False +cycles.debug_use_hair_bvh = True +cycles.debug_bvh_time_steps = 0 +cycles.use_auto_tile = True +cycles.tile_size = 2048 diff --git a/release/scripts/presets/cycles/performance/Faster_Render.py b/release/scripts/presets/cycles/performance/Faster_Render.py new file mode 100644 index 00000000000..7f1e3c68f1f --- /dev/null +++ b/release/scripts/presets/cycles/performance/Faster_Render.py @@ -0,0 +1,12 @@ +import bpy +render = bpy.context.scene.render +cycles = bpy.context.scene.cycles + +render.threads_mode = 'AUTO' +render.use_persistent_data = True +cycles.debug_use_spatial_splits = True +cycles.debug_use_compact_bvh = False +cycles.debug_use_hair_bvh = True +cycles.debug_bvh_time_steps = 2 +cycles.use_auto_tile = True +cycles.tile_size = 2048 diff --git a/release/scripts/presets/cycles/performance/Lower_Memory.py b/release/scripts/presets/cycles/performance/Lower_Memory.py new file mode 100644 index 00000000000..d1a45f1888d --- /dev/null +++ b/release/scripts/presets/cycles/performance/Lower_Memory.py @@ -0,0 +1,12 @@ +import bpy +render = bpy.context.scene.render +cycles = bpy.context.scene.cycles + +render.threads_mode = 'AUTO' +render.use_persistent_data = False +cycles.debug_use_spatial_splits = False +cycles.debug_use_compact_bvh = True +cycles.debug_use_hair_bvh = True +cycles.debug_bvh_time_steps = 0 +cycles.use_auto_tile = True +cycles.tile_size = 512 diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index c0db6c5f523..d08efc6909b 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -1027,7 +1027,7 @@ def km_markers(params): ("marker.select_box", {"type": 'B', "value": 'PRESS'}, None), *_template_items_select_actions(params, "marker.select_all"), ("marker.delete", {"type": 'X', "value": 'PRESS'}, None), - ("marker.delete", {"type": 'DEL', "value": 'PRESS'}, None), + ("marker.delete", {"type": 'DEL', "value": 'PRESS'}, {"properties": [("confirm", False)]}), op_panel("TOPBAR_PT_name_marker", {"type": 'F2', "value": 'PRESS'}, [("keep_open", False)]), ("marker.move", {"type": 'G', "value": 'PRESS'}, None), ("marker.camera_bind", {"type": 'B', "value": 'PRESS', "ctrl": True}, None), @@ -2578,10 +2578,8 @@ def km_nla_editor(params): ("nla.soundclip_add", {"type": 'K', "value": 'PRESS', "shift": True}, None), ("nla.meta_add", {"type": 'G', "value": 'PRESS', "ctrl": True}, None), ("nla.meta_remove", {"type": 'G', "value": 'PRESS', "ctrl": True, "alt": True}, None), - ("nla.duplicate", {"type": 'D', "value": 'PRESS', "shift": True}, - {"properties": [("linked", False)]}), - ("nla.duplicate", {"type": 'D', "value": 'PRESS', "alt": True}, - {"properties": [("linked", True)]}), + ("nla.duplicate_move", {"type": 'D', "value": 'PRESS', "shift": True}, None), + ("nla.duplicate_linked_move", {"type": 'D', "value": 'PRESS', "alt": True}, None), ("nla.make_single_user", {"type": 'U', "value": 'PRESS'}, None), ("nla.delete", {"type": 'X', "value": 'PRESS'}, None), ("nla.delete", {"type": 'DEL', "value": 'PRESS'}, None), @@ -4397,7 +4395,7 @@ def km_face_mask(params): items.extend([ *_template_items_select_actions(params, "paint.face_select_all"), - *_template_items_hide_reveal_actions("paint.face_select_hide", "paint.face_select_reveal"), + *_template_items_hide_reveal_actions("paint.face_select_hide", "paint.face_vert_reveal"), ("paint.face_select_linked", {"type": 'L', "value": 'PRESS', "ctrl": True}, None), ("paint.face_select_linked_pick", {"type": 'L', "value": 'PRESS'}, {"properties": [("deselect", False)]}), @@ -4418,6 +4416,7 @@ def km_weight_paint_vertex_selection(params): items.extend([ *_template_items_select_actions(params, "paint.vert_select_all"), + *_template_items_hide_reveal_actions("paint.vert_select_hide", "paint.face_vert_reveal"), ("view3d.select_box", {"type": 'B', "value": 'PRESS'}, None), ("view3d.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True}, {"properties": [("mode", 'ADD')]}), @@ -4968,6 +4967,7 @@ def km_vertex_paint(params): op_menu("VIEW3D_MT_angle_control", {"type": 'R', "value": 'PRESS'}), ("wm.context_menu_enum", {"type": 'E', "value": 'PRESS'}, {"properties": [("data_path", 'tool_settings.vertex_paint.brush.stroke_method')]}), + ("paint.face_vert_reveal", {"type": 'H', "value": 'PRESS', "alt": True}, None), *_template_items_context_panel("VIEW3D_PT_paint_vertex_context_menu", params.context_menu_event), ]) @@ -5015,6 +5015,7 @@ def km_weight_paint(params): ("wm.context_toggle", {"type": 'S', "value": 'PRESS', "shift": True}, {"properties": [("data_path", 'tool_settings.weight_paint.brush.use_smooth_stroke')]}), op_menu_pie("VIEW3D_MT_wpaint_vgroup_lock_pie", {"type": 'K', "value": 'PRESS'}), + ("paint.face_vert_reveal", {"type": 'H', "value": 'PRESS', "alt": True}, None), *_template_items_context_panel("VIEW3D_PT_paint_weight_context_menu", params.context_menu_event), ]) diff --git a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py index d60bbfed67a..c9d66afb796 100644 --- a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py +++ b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py @@ -844,7 +844,7 @@ def km_markers(params): "shift": True}, {"properties": [("action", 'DESELECT')]}), ("marker.select_all", {"type": 'I', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'INVERT')]}), ("marker.delete", {"type": 'BACK_SPACE', "value": 'PRESS'}, None), - ("marker.delete", {"type": 'DEL', "value": 'PRESS'}, None), + ("marker.delete", {"type": 'DEL', "value": 'PRESS'}, {"properties": [("confirm", False)]}), op_panel("TOPBAR_PT_name_marker", {"type": 'RET', "value": 'PRESS'}, [("keep_open", False)]), ("marker.move", {"type": 'W', "value": 'PRESS'}, None), ]) @@ -2948,7 +2948,7 @@ def km_face_mask(params): {"properties": [("unselected", False)]}), ("paint.face_select_hide", {"type": 'H', "value": 'PRESS', "shift": True}, {"properties": [("unselected", True)]}), - ("paint.face_select_reveal", {"type": 'H', "value": 'PRESS', "alt": True}, None), + ("paint.face_vert_reveal", {"type": 'H', "value": 'PRESS', "alt": True}, None), ("paint.face_select_linked", {"type": 'L', "value": 'PRESS', "ctrl": True}, None), ("paint.face_select_linked_pick", {"type": 'L', "value": 'PRESS'}, {"properties": [("deselect", False)]}), @@ -2969,6 +2969,9 @@ def km_weight_paint_vertex_selection(params): items.extend([ ("paint.vert_select_all", {"type": 'A', "value": 'PRESS', "ctrl": True}, None), + ("paint.vert_select_hide", {"type": 'H', "value": 'PRESS', "shift": True}, + {"properties": [("unselected", True)]}), + ("paint.face_vert_reveal", {"type": 'H', "value": 'PRESS', "alt": True}, None), ]) return keymap @@ -3330,6 +3333,7 @@ def km_vertex_paint(params): ("wm.context_toggle", {"type": 'S', "value": 'PRESS', "shift": True}, {"properties": [("data_path", 'tool_settings.vertex_paint.brush.use_smooth_stroke')]}), op_menu("VIEW3D_MT_angle_control", {"type": 'R', "value": 'PRESS'}), + ("paint.face_vert_reveal", {"type": 'H', "value": 'PRESS', "alt": True}, None), *_template_items_context_panel("VIEW3D_PT_paint_vertex_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), # Tools ("paint.brush_select", {"type": 'D', "value": 'PRESS'}, @@ -3362,6 +3366,7 @@ def km_weight_paint(params): {"properties": [("data_path", 'weight_paint_object.data.use_paint_mask')]}), ("wm.context_toggle", {"type": 'S', "value": 'PRESS', "shift": True}, {"properties": [("data_path", 'tool_settings.weight_paint.brush.use_smooth_stroke')]}), + ("paint.face_vert_reveal", {"type": 'H', "value": 'PRESS', "alt": True}, None), *_template_items_context_panel("VIEW3D_PT_paint_weight_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), # Bone selection for combined weight paint + pose mode. ("view3d.select", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True}, None), diff --git a/release/scripts/startup/bl_operators/node.py b/release/scripts/startup/bl_operators/node.py index bad12ff4621..df4ca9ef170 100644 --- a/release/scripts/startup/bl_operators/node.py +++ b/release/scripts/startup/bl_operators/node.py @@ -14,6 +14,8 @@ from bpy.props import ( StringProperty, ) +from bpy.app.translations import pgettext_tip as tip_ + class NodeSetting(PropertyGroup): value: StringProperty( @@ -134,7 +136,7 @@ class NodeAddOperator: nodetype = properties["type"] bl_rna = bpy.types.Node.bl_rna_get_subclass(nodetype) if bl_rna is not None: - return bl_rna.description + return tip_(bl_rna.description) else: return "" diff --git a/release/scripts/startup/bl_ui/__init__.py b/release/scripts/startup/bl_ui/__init__.py index c61dde87d7c..c4e3df469b7 100644 --- a/release/scripts/startup/bl_ui/__init__.py +++ b/release/scripts/startup/bl_ui/__init__.py @@ -128,8 +128,8 @@ def register(): return items WindowManager.addon_search = StringProperty( - name="Search", - description="Search within the selected filter", + name="Filter", + description="Filter by add-on name, author & category", options={'TEXTEDIT_UPDATE'}, ) WindowManager.addon_filter = EnumProperty( diff --git a/release/scripts/startup/bl_ui/properties_data_camera.py b/release/scripts/startup/bl_ui/properties_data_camera.py index 8213a865990..a9a032ac4a3 100644 --- a/release/scripts/startup/bl_ui/properties_data_camera.py +++ b/release/scripts/startup/bl_ui/properties_data_camera.py @@ -218,6 +218,8 @@ class DATA_PT_camera_dof(CameraButtonsPanel, Panel): col = layout.column() col.prop(dof, "focus_object", text="Focus on Object") + if dof.focus_object and dof.focus_object.type == 'ARMATURE': + col.prop_search(dof, "focus_subtarget", dof.focus_object.data, "bones", text="Focus on Bone") sub = col.column() sub.active = (dof.focus_object is None) sub.prop(dof, "focus_distance", text="Focus Distance") diff --git a/release/scripts/startup/bl_ui/space_dopesheet.py b/release/scripts/startup/bl_ui/space_dopesheet.py index a1c086db745..4d1514c2777 100644 --- a/release/scripts/startup/bl_ui/space_dopesheet.py +++ b/release/scripts/startup/bl_ui/space_dopesheet.py @@ -567,10 +567,10 @@ class DOPESHEET_PT_action(DopesheetActionPanelBase, Panel): @classmethod def poll(cls, context): - return bool(context.selected_visible_actions) + return bool(context.active_action) def draw(self, context): - action = context.selected_visible_actions[0] + action = context.active_action self.draw_generic_panel(context, self.layout, action) diff --git a/release/scripts/startup/bl_ui/space_image.py b/release/scripts/startup/bl_ui/space_image.py index 5d25d02d98e..ab3c863ea2d 100644 --- a/release/scripts/startup/bl_ui/space_image.py +++ b/release/scripts/startup/bl_ui/space_image.py @@ -306,6 +306,7 @@ class IMAGE_MT_uvs_snap(Menu): layout.operator("uv.snap_cursor", text="Cursor to Pixels").target = 'PIXELS' layout.operator("uv.snap_cursor", text="Cursor to Selected").target = 'SELECTED' + layout.operator("uv.snap_cursor", text="Cursor to Origin").target = 'ORIGIN' class IMAGE_MT_uvs_mirror(Menu): @@ -572,6 +573,11 @@ class IMAGE_MT_uvs_snap_pie(Menu): text="Selected to Adjacent Unselected", icon='RESTRICT_SELECT_OFF', ).target = 'ADJACENT_UNSELECTED' + pie.operator( + "uv.snap_cursor", + text="Cursor to Origin", + icon='PIVOT_CURSOR', + ).target = 'ORIGIN' class IMAGE_MT_view_pie(Menu): diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index 060c00c0443..a99df1164a0 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -1650,7 +1650,7 @@ class SEQUENCER_PT_source(SequencerButtonsPanel, Panel): if sound.samplerate <= 0: split.label(text="Unknown") else: - split.label(text="%d Hz." % sound.samplerate, translate=False) + split.label(text="%d Hz" % sound.samplerate, translate=False) split = col.split(factor=0.5, align=False) split.alignment = 'RIGHT' diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index cccbb63d27c..52b2fb7f3da 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -589,12 +589,6 @@ class USERPREF_PT_system_cycles_devices(SystemPanel, CenterAlignMixIn, Panel): addon.preferences.draw_impl(col, context) del addon - # NOTE: Disabled for until GPU side of OpenSubdiv is brought back. - # system = prefs.system - # if hasattr(system, "opensubdiv_compute_type"): - # col.label(text="OpenSubdiv compute:") - # col.row().prop(system, "opensubdiv_compute_type", text="") - class USERPREF_PT_system_os_settings(SystemPanel, CenterAlignMixIn, Panel): bl_label = "Operating System Settings" @@ -1485,11 +1479,13 @@ class USERPREF_PT_saveload_file_browser(SaveLoadPanel, CenterAlignMixIn, Panel): prefs = context.preferences paths = prefs.filepaths + col = layout.column(heading="Show Locations") + col.prop(paths, "show_recent_locations", text="Recent") + col.prop(paths, "show_system_bookmarks", text="System") + col = layout.column(heading="Defaults") col.prop(paths, "use_filter_files") col.prop(paths, "show_hidden_files_datablocks") - col.prop(paths, "show_recent_locations") - col.prop(paths, "show_system_bookmarks") # ----------------------------------------------------------------------------- diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index 92dc4138530..1a30c666bcb 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -603,8 +603,8 @@ class VIEW3D_HT_header(Header): show_snap = True else: if (object_mode not in { - 'SCULPT', 'VERTEX_PAINT', 'WEIGHT_PAINT', 'TEXTURE_PAINT', - 'PAINT_GPENCIL', 'SCULPT_GPENCIL', 'WEIGHT_GPENCIL', 'VERTEX_GPENCIL' + 'SCULPT', 'SCULPT_CURVES', 'VERTEX_PAINT', 'WEIGHT_PAINT', 'TEXTURE_PAINT', + 'PAINT_GPENCIL', 'SCULPT_GPENCIL', 'WEIGHT_GPENCIL', 'VERTEX_GPENCIL', }) or has_pose_mode: show_snap = True else: diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index e59c98163d7..6f3054100f8 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -73,7 +73,7 @@ def curve_node_items(context): yield NodeItem("GeometryNodeCurveLength") yield NodeItem("GeometryNodeCurveToMesh") yield NodeItem("GeometryNodeCurveToPoints") - yield NodeItem("GeometryNodeDeformCurvesWithSurface") + yield NodeItem("GeometryNodeDeformCurvesOnSurface") yield NodeItem("GeometryNodeFillCurve") yield NodeItem("GeometryNodeFilletCurve") yield NodeItem("GeometryNodeResampleCurve") diff --git a/source/blender/blendthumb/src/blendthumb_extract.cc b/source/blender/blendthumb/src/blendthumb_extract.cc index de1f50dfdce..163197c8b67 100644 --- a/source/blender/blendthumb/src/blendthumb_extract.cc +++ b/source/blender/blendthumb/src/blendthumb_extract.cc @@ -121,6 +121,9 @@ static eThumbStatus blendthumb_extract_from_file_impl(FileReader *file, while (file_read(file, bhead_data, bhead_size)) { /* Parse type and size from `BHead`. */ const int32_t block_size = bytes_to_native_i32(&bhead_data[4], endian_switch); + if (UNLIKELY(block_size < 0)) { + return BT_INVALID_THUMB; + } /* We're looking for the thumbnail, so skip any other block. */ switch (*((int32_t *)bhead_data)) { @@ -134,7 +137,8 @@ static eThumbStatus blendthumb_extract_from_file_impl(FileReader *file, /* Verify that image dimensions and data size make sense. */ size_t data_size = block_size - 8; - const size_t expected_size = thumb->width * thumb->height * 4; + const uint64_t expected_size = static_cast<uint64_t>(thumb->width) * + static_cast<uint64_t>(thumb->height) * 4; if (thumb->width < 0 || thumb->height < 0 || data_size != expected_size) { return BT_INVALID_THUMB; } diff --git a/source/blender/blenkernel/BKE_attribute.hh b/source/blender/blenkernel/BKE_attribute.hh index b92d0d4326b..108993d91c0 100644 --- a/source/blender/blenkernel/BKE_attribute.hh +++ b/source/blender/blenkernel/BKE_attribute.hh @@ -351,8 +351,9 @@ class AttributeAccessor { /** * The data that actually owns the attributes, for example, a pointer to a #Mesh or #PointCloud * Most commonly this is a pointer to a #Mesh or #PointCloud. - * Under some circumstances this can be null. In that case most methods can't be used. Just e.g. - * the #domain_size method works and returns 0 for every domain. + * Under some circumstances this can be null. In that case most methods can't be used. Allowed + * methods are #domain_size, #for_all and #is_builtin. We could potentially make these methods + * accessible without #AttributeAccessor and then #owner_ could always be non-null. * * \note This class cannot modify the owner's attributes, but the pointer is still non-const, so * this class can be a base class for the mutable version. @@ -509,7 +510,10 @@ class AttributeAccessor { */ bool for_all(const AttributeForeachCallback fn) const { - return fn_->for_all(owner_, fn); + if (owner_ != nullptr) { + return fn_->for_all(owner_, fn); + } + return true; } /** @@ -662,6 +666,22 @@ class MutableAttributeAccessor : public AttributeAccessor { void remove_anonymous(); }; +struct AttributeTransferData { + /* Expect that if an attribute exists, it is stored as a contiguous array internally anyway. */ + GVArraySpan src; + AttributeMetaData meta_data; + bke::GSpanAttributeWriter dst; +}; +/** + * Retrieve attribute arrays and writers for attributes that should be transferred between + * data-blocks of the same type. + */ +Vector<AttributeTransferData> retrieve_attributes_for_transfer( + const bke::AttributeAccessor &src_attributes, + bke::MutableAttributeAccessor &dst_attributes, + eAttrDomainMask domain_mask, + const Set<std::string> &skip = {}); + bool allow_procedural_attribute_access(StringRef attribute_name); extern const char *no_procedural_access_message; diff --git a/source/blender/blenkernel/BKE_curves.hh b/source/blender/blenkernel/BKE_curves.hh index fb97e52f6da..68c90a45031 100644 --- a/source/blender/blenkernel/BKE_curves.hh +++ b/source/blender/blenkernel/BKE_curves.hh @@ -385,8 +385,6 @@ class CurvesGeometry : public ::CurvesGeometry { void calculate_bezier_auto_handles(); - void update_customdata_pointers(); - void remove_points(IndexMask points_to_delete); void remove_curves(IndexMask curves_to_delete); @@ -735,6 +733,12 @@ Curves *curves_new_nomain(CurvesGeometry curves); */ Curves *curves_new_nomain_single(int points_num, CurveType type); +/** + * Copy data from #src to #dst, except the geometry data in #CurvesGeometry. Typically used to + * copy high-level parameters when a geometry-altering operation creates a new curves data-block. + */ +void curves_copy_parameters(const Curves &src, Curves &dst); + std::array<int, CURVE_TYPES_NUM> calculate_type_counts(const VArray<int8_t> &types); /* -------------------------------------------------------------------- */ diff --git a/source/blender/blenkernel/BKE_curves_utils.hh b/source/blender/blenkernel/BKE_curves_utils.hh index f223e173ea9..0fbd33002e1 100644 --- a/source/blender/blenkernel/BKE_curves_utils.hh +++ b/source/blender/blenkernel/BKE_curves_utils.hh @@ -56,6 +56,13 @@ void fill_points(const CurvesGeometry &curves, } /** + * Copy only the information on the point domain, but not the offsets or any point attributes, + * meant for operations that change the number of points but not the number of curves. + * \warning The returned curves have invalid offsets! + */ +bke::CurvesGeometry copy_only_curve_domain(const bke::CurvesGeometry &src_curves); + +/** * Copy the size of every curve in #curve_ranges to the corresponding index in #counts. */ void fill_curve_counts(const bke::CurvesGeometry &curves, diff --git a/source/blender/blenkernel/BKE_fcurve.h b/source/blender/blenkernel/BKE_fcurve.h index 10d9ce3364d..c5788c07336 100644 --- a/source/blender/blenkernel/BKE_fcurve.h +++ b/source/blender/blenkernel/BKE_fcurve.h @@ -463,23 +463,39 @@ bool BKE_fcurve_bezt_subdivide_handles(struct BezTriple *bezt, struct BezTriple *next, float *r_pdelta); +/** + * Delete a keyframe from an F-curve at a specific index. + */ +void BKE_fcurve_delete_key(struct FCurve *fcu, int index); + +/** + * Delete selected keyframes from an F-curve. + */ +bool BKE_fcurve_delete_keys_selected(struct FCurve *fcu); + +/** + * Delete all keyframes from an F-curve. + */ +void BKE_fcurve_delete_keys_all(struct FCurve *fcu); + /* -------- Curve Sanity -------- */ /** * This function recalculates the handles of an F-Curve. Acts based on selection with `SELECT` - * flag. To use a different flag, use #calchandles_fcurve_ex(). + * flag. To use a different flag, use #BKE_fcurve_handles_recalc_ex(). * * If the BezTriples have been rearranged, sort them first before using this. */ -void calchandles_fcurve(struct FCurve *fcu); +void BKE_fcurve_handles_recalc(struct FCurve *fcu); /** - * Variant of #calchandles_fcurve() that allows calculating based on a different select flag. + * Variant of #BKE_fcurve_handles_recalc() that allows calculating based on a different select + * flag. * * \param handle_sel_flag: The flag (bezt.f1/2/3) value to use to determine selection. * Usually `SELECT`, but may want to use a different one at times * (if caller does not operate on selection). */ -void calchandles_fcurve_ex(struct FCurve *fcu, eBezTriple_Flag handle_sel_flag); +void BKE_fcurve_handles_recalc_ex(struct FCurve *fcu, eBezTriple_Flag handle_sel_flag); /** * Update handles, making sure the handle-types are valid (e.g. correctly deduced from an "Auto" * type), and recalculating their position vectors. diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h index 6ec1285af0c..4e622a5708f 100644 --- a/source/blender/blenkernel/BKE_image.h +++ b/source/blender/blenkernel/BKE_image.h @@ -97,7 +97,7 @@ int BKE_imbuf_write(struct ImBuf *ibuf, const char *name, const struct ImageForm */ int BKE_imbuf_write_as(struct ImBuf *ibuf, const char *name, - struct ImageFormatData *imf, + const struct ImageFormatData *imf, bool save_copy); /** diff --git a/source/blender/blenkernel/BKE_image_save.h b/source/blender/blenkernel/BKE_image_save.h index 673a7dffb82..e17136174eb 100644 --- a/source/blender/blenkernel/BKE_image_save.h +++ b/source/blender/blenkernel/BKE_image_save.h @@ -48,14 +48,14 @@ bool BKE_image_save_options_init(ImageSaveOptions *opts, struct ImageUser *iuser, const bool guess_path, const bool save_as_render); -void BKE_image_save_options_update(struct ImageSaveOptions *opts, struct Image *ima); +void BKE_image_save_options_update(struct ImageSaveOptions *opts, const struct Image *ima); void BKE_image_save_options_free(struct ImageSaveOptions *opts); bool BKE_image_save(struct ReportList *reports, struct Main *bmain, struct Image *ima, struct ImageUser *iuser, - struct ImageSaveOptions *opts); + const struct ImageSaveOptions *opts); /* Render saving. */ diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index 366083fee7f..6c61068b1c2 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -85,10 +85,18 @@ struct Mesh *BKE_mesh_from_bmesh_for_eval_nomain(struct BMesh *bm, * Add original index (#CD_ORIGINDEX) layers if they don't already exist. This is meant to be used * when creating an evaluated mesh from an original edit mode mesh, to allow mapping from the * evaluated vertices to the originals. + * + * The mesh is expected to of a `ME_WRAPPER_TYPE_MDATA` wrapper type. This is asserted. */ void BKE_mesh_ensure_default_orig_index_customdata(struct Mesh *mesh); /** + * Same as #BKE_mesh_ensure_default_orig_index_customdata but does not perform any checks: they + * must be done by the caller. + */ +void BKE_mesh_ensure_default_orig_index_customdata_no_check(struct Mesh *mesh); + +/** * Find the index of the loop in 'poly' which references vertex, * returns -1 if not found */ @@ -986,7 +994,7 @@ void BKE_mesh_strip_loose_edges(struct Mesh *me); /** * If the mesh is from a very old blender version, - * convert mface->edcode to edge drawflags + * convert #MFace.edcode to edge #ME_EDGEDRAW. */ void BKE_mesh_calc_edges_legacy(struct Mesh *me, bool use_old); void BKE_mesh_calc_edges_loose(struct Mesh *mesh); @@ -1002,8 +1010,8 @@ void BKE_mesh_calc_edges(struct Mesh *mesh, bool keep_existing_edges, bool selec void BKE_mesh_calc_edges_tessface(struct Mesh *mesh); /* In DerivedMesh.cc */ -void BKE_mesh_wrapper_deferred_finalize(struct Mesh *me_eval, - const struct CustomData_MeshMasks *cd_mask_finalize); +void BKE_mesh_wrapper_deferred_finalize_mdata(struct Mesh *me_eval, + const struct CustomData_MeshMasks *cd_mask_finalize); /* **** Depsgraph evaluation **** */ diff --git a/source/blender/blenkernel/BKE_mesh_mapping.h b/source/blender/blenkernel/BKE_mesh_mapping.h index 163acf062e0..c58bcbea242 100644 --- a/source/blender/blenkernel/BKE_mesh_mapping.h +++ b/source/blender/blenkernel/BKE_mesh_mapping.h @@ -69,8 +69,6 @@ typedef struct UvElementMap { int *islandIndices; } UvElementMap; -#define INVALID_ISLAND ((unsigned int)-1) - /* Connectivity data */ typedef struct MeshElemMap { int *indices; diff --git a/source/blender/blenkernel/BKE_modifier.h b/source/blender/blenkernel/BKE_modifier.h index 866b0353d07..46d609f9aa3 100644 --- a/source/blender/blenkernel/BKE_modifier.h +++ b/source/blender/blenkernel/BKE_modifier.h @@ -446,10 +446,22 @@ bool BKE_modifier_is_enabled(const struct Scene *scene, */ bool BKE_modifier_is_nonlocal_in_liboverride(const struct Object *ob, const struct ModifierData *md); + +/* Set modifier execution error. + * The message will be shown in the interface and will be logged as an error to the console. */ void BKE_modifier_set_error(const struct Object *ob, struct ModifierData *md, const char *format, ...) ATTR_PRINTF_FORMAT(3, 4); + +/* Set modifier execution warning, which does not prevent the modifier from being applied but which + * might need an attention. The message will only be shown in the interface, but will not appear in + * the logs. */ +void BKE_modifier_set_warning(const struct Object *ob, + struct ModifierData *md, + const char *format, + ...) ATTR_PRINTF_FORMAT(3, 4); + bool BKE_modifier_is_preview(struct ModifierData *md); void BKE_modifiers_foreach_ID_link(struct Object *ob, IDWalkFunc walk, void *userData); diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index b7962ade312..ea43fba1a85 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -314,6 +314,10 @@ typedef struct bNodeType { /* Execute a geometry node. */ NodeGeometryExecFunction geometry_node_execute; + /** + * If true, the geometry nodes evaluator can call the execute function multiple times to improve + * performance by specifying required data in one call and using it for calculations in another. + */ bool geometry_node_execute_supports_laziness; /* Declares which sockets the node has. */ diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index ffe80ff47b6..162459d2005 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -200,6 +200,11 @@ bool BKE_paint_select_vert_test(struct Object *ob); * (when we don't care if its face or vert) */ bool BKE_paint_select_elem_test(struct Object *ob); +/** + * Checks if face/vertex hiding is always applied in the current mode. + * Returns true in vertex/weight paint. + */ +bool BKE_paint_always_hide_test(struct Object *ob); /* Partial visibility. */ diff --git a/source/blender/blenkernel/BKE_pointcloud.h b/source/blender/blenkernel/BKE_pointcloud.h index 6dbba11a56d..ee90fea6506 100644 --- a/source/blender/blenkernel/BKE_pointcloud.h +++ b/source/blender/blenkernel/BKE_pointcloud.h @@ -29,7 +29,6 @@ struct PointCloud *BKE_pointcloud_new_nomain(int totpoint); struct BoundBox *BKE_pointcloud_boundbox_get(struct Object *ob); bool BKE_pointcloud_minmax(const struct PointCloud *pointcloud, float r_min[3], float r_max[3]); -void BKE_pointcloud_update_customdata_pointers(struct PointCloud *pointcloud); bool BKE_pointcloud_customdata_required(const struct PointCloud *pointcloud, const char *name); /* Dependency Graph */ diff --git a/source/blender/blenkernel/BKE_scene.h b/source/blender/blenkernel/BKE_scene.h index a6402a1e8a1..61fc883fe7f 100644 --- a/source/blender/blenkernel/BKE_scene.h +++ b/source/blender/blenkernel/BKE_scene.h @@ -251,6 +251,10 @@ bool BKE_scene_check_rigidbody_active(const struct Scene *scene); int BKE_scene_num_threads(const struct Scene *scene); int BKE_render_num_threads(const struct RenderData *r); +void BKE_render_resolution(const struct RenderData *r, + const bool use_crop, + int *r_width, + int *r_height); int BKE_render_preview_pixel_size(const struct RenderData *r); /**********************************/ diff --git a/source/blender/blenkernel/BKE_subdiv_eval.h b/source/blender/blenkernel/BKE_subdiv_eval.h index cb0f2ac7e32..e4a7f813a8b 100644 --- a/source/blender/blenkernel/BKE_subdiv_eval.h +++ b/source/blender/blenkernel/BKE_subdiv_eval.h @@ -20,7 +20,7 @@ struct Subdiv; typedef enum eSubdivEvaluatorType { SUBDIV_EVALUATOR_TYPE_CPU, - SUBDIV_EVALUATOR_TYPE_GLSL_COMPUTE, + SUBDIV_EVALUATOR_TYPE_GPU, } eSubdivEvaluatorType; /* Returns true if evaluator is ready for use. */ diff --git a/source/blender/blenkernel/BKE_writeffmpeg.h b/source/blender/blenkernel/BKE_writeffmpeg.h index 3f92d6fa117..736f7548bb4 100644 --- a/source/blender/blenkernel/BKE_writeffmpeg.h +++ b/source/blender/blenkernel/BKE_writeffmpeg.h @@ -68,7 +68,7 @@ void BKE_ffmpeg_filepath_get(char *string, const char *suffix); void BKE_ffmpeg_preset_set(struct RenderData *rd, int preset); -void BKE_ffmpeg_image_type_verify(struct RenderData *rd, struct ImageFormatData *imf); +void BKE_ffmpeg_image_type_verify(struct RenderData *rd, const struct ImageFormatData *imf); bool BKE_ffmpeg_alpha_channel_is_supported(const struct RenderData *rd); void *BKE_ffmpeg_context_create(void); diff --git a/source/blender/blenkernel/intern/DerivedMesh.cc b/source/blender/blenkernel/intern/DerivedMesh.cc index c2ea01bcadf..a29d8726f21 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.cc +++ b/source/blender/blenkernel/intern/DerivedMesh.cc @@ -84,6 +84,8 @@ static ThreadRWMutex loops_cache_lock = PTHREAD_RWLOCK_INITIALIZER; static void mesh_init_origspace(Mesh *mesh); static void editbmesh_calc_modifier_final_normals(Mesh *mesh_final, const CustomData_MeshMasks *final_datamask); +static void editbmesh_calc_modifier_final_normals_or_defer( + Mesh *mesh_final, const CustomData_MeshMasks *final_datamask); /* -------------------------------------------------------------------- */ @@ -663,8 +665,8 @@ static void mesh_calc_finalize(const Mesh *mesh_input, Mesh *mesh_eval) mesh_eval->edit_mesh = mesh_input->edit_mesh; } -void BKE_mesh_wrapper_deferred_finalize(Mesh *me_eval, - const CustomData_MeshMasks *cd_mask_finalize) +void BKE_mesh_wrapper_deferred_finalize_mdata(Mesh *me_eval, + const CustomData_MeshMasks *cd_mask_finalize) { if (me_eval->runtime.wrapper_type_finalize & (1 << ME_WRAPPER_TYPE_BMESH)) { editbmesh_calc_modifier_final_normals(me_eval, cd_mask_finalize); @@ -1286,12 +1288,6 @@ bool editbmesh_modifier_is_enabled(const Scene *scene, static void editbmesh_calc_modifier_final_normals(Mesh *mesh_final, const CustomData_MeshMasks *final_datamask) { - if (mesh_final->runtime.wrapper_type != ME_WRAPPER_TYPE_MDATA) { - /* Generated at draw time. */ - mesh_final->runtime.wrapper_type_finalize = (1 << mesh_final->runtime.wrapper_type); - return; - } - const bool calc_loop_normals = ((mesh_final->flag & ME_AUTOSMOOTH) != 0 || (final_datamask->lmask & CD_MASK_NORMAL) != 0); @@ -1319,6 +1315,18 @@ static void editbmesh_calc_modifier_final_normals(Mesh *mesh_final, } } +static void editbmesh_calc_modifier_final_normals_or_defer( + Mesh *mesh_final, const CustomData_MeshMasks *final_datamask) +{ + if (mesh_final->runtime.wrapper_type != ME_WRAPPER_TYPE_MDATA) { + /* Generated at draw time. */ + mesh_final->runtime.wrapper_type_finalize = (1 << mesh_final->runtime.wrapper_type); + return; + } + + editbmesh_calc_modifier_final_normals(mesh_final, final_datamask); +} + static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph, const Scene *scene, Object *ob, @@ -1598,9 +1606,9 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph, } /* Compute normals. */ - editbmesh_calc_modifier_final_normals(mesh_final, &final_datamask); + editbmesh_calc_modifier_final_normals_or_defer(mesh_final, &final_datamask); if (mesh_cage && (mesh_cage != mesh_final)) { - editbmesh_calc_modifier_final_normals(mesh_cage, &final_datamask); + editbmesh_calc_modifier_final_normals_or_defer(mesh_cage, &final_datamask); } /* Return final mesh. */ diff --git a/source/blender/blenkernel/intern/action_mirror.c b/source/blender/blenkernel/intern/action_mirror.c index 7ef561c8216..0663538f3bb 100644 --- a/source/blender/blenkernel/intern/action_mirror.c +++ b/source/blender/blenkernel/intern/action_mirror.c @@ -363,7 +363,7 @@ static void action_flip_pchan(Object *ob_arm, /* Recalculate handles. */ for (int i = 0; i < fcurve_array_len; i++) { - calchandles_fcurve_ex(fcurve_array[i], 0); + BKE_fcurve_handles_recalc_ex(fcurve_array[i], 0); } MEM_freeN((void *)keyed_frames); diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index ac1ee19927c..a834b77d65e 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -1011,6 +1011,37 @@ GSpanAttributeWriter MutableAttributeAccessor::lookup_or_add_for_write_only_span return {}; } +Vector<AttributeTransferData> retrieve_attributes_for_transfer( + const bke::AttributeAccessor &src_attributes, + bke::MutableAttributeAccessor &dst_attributes, + const eAttrDomainMask domain_mask, + const Set<std::string> &skip) +{ + Vector<AttributeTransferData> attributes; + src_attributes.for_all( + [&](const bke::AttributeIDRef &id, const bke::AttributeMetaData meta_data) { + if (!(ATTR_DOMAIN_AS_MASK(meta_data.domain) & domain_mask)) { + return true; + } + if (id.is_named() && skip.contains(id.name())) { + return true; + } + if (!id.should_be_kept()) { + return true; + } + + GVArray src = src_attributes.lookup(id, meta_data.domain); + BLI_assert(src); + bke::GSpanAttributeWriter dst = dst_attributes.lookup_or_add_for_write_only_span( + id, meta_data.domain, meta_data.data_type); + BLI_assert(dst); + attributes.append({std::move(src), meta_data, std::move(dst)}); + + return true; + }); + return attributes; +} + } // namespace blender::bke /** \} */ diff --git a/source/blender/blenkernel/intern/brush.cc b/source/blender/blenkernel/intern/brush.cc index ffa63f9792f..99733c8edb3 100644 --- a/source/blender/blenkernel/intern/brush.cc +++ b/source/blender/blenkernel/intern/brush.cc @@ -444,7 +444,8 @@ static void brush_defaults(Brush *brush) const Brush *brush_def = DNA_struct_default_get(Brush); -#define FROM_DEFAULT(member) memcpy(&brush->member, &brush_def->member, sizeof(brush->member)) +#define FROM_DEFAULT(member) \ + memcpy((void *)&brush->member, (void *)&brush_def->member, sizeof(brush->member)) #define FROM_DEFAULT_PTR(member) memcpy(brush->member, brush_def->member, sizeof(brush->member)) FROM_DEFAULT(blend); @@ -586,15 +587,15 @@ bool BKE_brush_delete(Main *bmain, Brush *brush) return true; } -/* grease pencil cumapping->preset */ -typedef enum eGPCurveMappingPreset { +/** Local grease pencil curve mapping preset. */ +using eGPCurveMappingPreset = enum eGPCurveMappingPreset { GPCURVE_PRESET_PENCIL = 0, GPCURVE_PRESET_INK = 1, GPCURVE_PRESET_INKNOISE = 2, GPCURVE_PRESET_MARKER = 3, GPCURVE_PRESET_CHISEL_SENSIVITY = 4, GPCURVE_PRESET_CHISEL_STRENGTH = 5, -} eGPCurveMappingPreset; +}; static void brush_gpencil_curvemap_reset(CurveMap *cuma, int tot, int preset) { diff --git a/source/blender/blenkernel/intern/bvhutils.cc b/source/blender/blenkernel/intern/bvhutils.cc index 35c2039634a..03dd5c89b70 100644 --- a/source/blender/blenkernel/intern/bvhutils.cc +++ b/source/blender/blenkernel/intern/bvhutils.cc @@ -19,6 +19,7 @@ #include "BLI_threads.h" #include "BLI_utildefines.h" +#include "BKE_attribute.hh" #include "BKE_bvhutils.h" #include "BKE_editmesh.h" #include "BKE_mesh.h" @@ -1430,13 +1431,17 @@ BVHTree *BKE_bvhtree_from_pointcloud_get(BVHTreeFromPointCloud *data, return nullptr; } - for (int i = 0; i < pointcloud->totpoint; i++) { - BLI_bvhtree_insert(tree, i, pointcloud->co[i], 1); + blender::bke::AttributeAccessor attributes = blender::bke::pointcloud_attributes(*pointcloud); + blender::VArraySpan<blender::float3> positions = attributes.lookup_or_default<blender::float3>( + "position", ATTR_DOMAIN_POINT, blender::float3(0)); + + for (const int i : positions.index_range()) { + BLI_bvhtree_insert(tree, i, positions[i], 1); } BLI_assert(BLI_bvhtree_get_len(tree) == pointcloud->totpoint); bvhtree_balance(tree, false); - data->coords = pointcloud->co; + data->coords = (const float(*)[3])positions.data(); data->tree = tree; data->nearest_callback = nullptr; diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c index fbe03ac365c..9aea3b2768f 100644 --- a/source/blender/blenkernel/intern/camera.c +++ b/source/blender/blenkernel/intern/camera.c @@ -25,6 +25,7 @@ #include "BLI_string.h" #include "BLI_utildefines.h" +#include "BKE_action.h" #include "BKE_anim_data.h" #include "BKE_camera.h" #include "BKE_idtype.h" @@ -221,7 +222,16 @@ float BKE_camera_object_dof_distance(const Object *ob) if (cam->dof.focus_object) { float view_dir[3], dof_dir[3]; normalize_v3_v3(view_dir, ob->obmat[2]); - sub_v3_v3v3(dof_dir, ob->obmat[3], cam->dof.focus_object->obmat[3]); + bPoseChannel *pchan = BKE_pose_channel_find_name(cam->dof.focus_object->pose, + cam->dof.focus_subtarget); + if (pchan) { + float posemat[4][4]; + mul_m4_m4m4(posemat, cam->dof.focus_object->obmat, pchan->pose_mat); + sub_v3_v3v3(dof_dir, ob->obmat[3], posemat[3]); + } + else { + sub_v3_v3v3(dof_dir, ob->obmat[3], cam->dof.focus_object->obmat[3]); + } return fabsf(dot_v3v3(view_dir, dof_dir)); } return cam->dof.focus_distance; @@ -559,6 +569,11 @@ void BKE_camera_view_frame(const Scene *scene, const Camera *camera, float r_vec #define CAMERA_VIEWFRAME_NUM_PLANES 4 +#define Y_MIN 0 +#define Y_MAX 1 +#define Z_MIN 2 +#define Z_MAX 3 + typedef struct CameraViewFrameData { float plane_tx[CAMERA_VIEWFRAME_NUM_PLANES][4]; /* 4 planes normalized */ float dist_vals[CAMERA_VIEWFRAME_NUM_PLANES]; /* distance (signed) */ @@ -622,15 +637,13 @@ static void camera_frame_fit_data_init(const Scene *scene, invert_m4(camera_rotmat_transposed_inversed); /* Extract frustum planes from projection matrix. */ - planes_from_projmat( - params->winmat, - /* left right top bottom near far */ - data->plane_tx[2], - data->plane_tx[0], - data->plane_tx[3], - data->plane_tx[1], - NULL, - NULL); + planes_from_projmat(params->winmat, + data->plane_tx[Y_MIN], + data->plane_tx[Y_MAX], + data->plane_tx[Z_MIN], + data->plane_tx[Z_MAX], + NULL, + NULL); /* Rotate planes and get normals from them */ for (uint i = 0; i < CAMERA_VIEWFRAME_NUM_PLANES; i++) { @@ -670,21 +683,21 @@ static bool camera_frame_fit_calc_from_data(CameraParams *params, const float *cam_axis_y = data->camera_rotmat[1]; const float *cam_axis_z = data->camera_rotmat[2]; const float *dists = data->dist_vals; - float scale_diff; + const float dist_span_y = dists[Y_MIN] + dists[Y_MAX]; + const float dist_span_z = dists[Z_MIN] + dists[Z_MAX]; + const float dist_mid_y = (dists[Y_MIN] - dists[Y_MAX]) * 0.5f; + const float dist_mid_z = (dists[Z_MIN] - dists[Z_MAX]) * 0.5f; + const float scale_diff = (dist_span_z < dist_span_y) ? + (dist_span_z * (BLI_rctf_size_x(¶ms->viewplane) / + BLI_rctf_size_y(¶ms->viewplane))) : + (dist_span_y * (BLI_rctf_size_y(¶ms->viewplane) / + BLI_rctf_size_x(¶ms->viewplane))); - if ((dists[0] + dists[2]) > (dists[1] + dists[3])) { - scale_diff = (dists[1] + dists[3]) * - (BLI_rctf_size_x(¶ms->viewplane) / BLI_rctf_size_y(¶ms->viewplane)); - } - else { - scale_diff = (dists[0] + dists[2]) * - (BLI_rctf_size_y(¶ms->viewplane) / BLI_rctf_size_x(¶ms->viewplane)); - } *r_scale = params->ortho_scale - scale_diff; zero_v3(r_co); - madd_v3_v3fl(r_co, cam_axis_x, (dists[2] - dists[0]) * 0.5f + params->shiftx * scale_diff); - madd_v3_v3fl(r_co, cam_axis_y, (dists[1] - dists[3]) * 0.5f + params->shifty * scale_diff); + madd_v3_v3fl(r_co, cam_axis_x, dist_mid_y + (params->shiftx * scale_diff)); + madd_v3_v3fl(r_co, cam_axis_y, dist_mid_z + (params->shifty * scale_diff)); madd_v3_v3fl(r_co, cam_axis_z, -(data->z_range[0] - 1.0f - params->clip_start)); } else { @@ -700,36 +713,37 @@ static bool camera_frame_fit_calc_from_data(CameraParams *params, plane_from_point_normal_v3(plane_tx[i], co, data->plane_tx[i]); } - if ((!isect_plane_plane_v3(plane_tx[0], plane_tx[2], plane_isect_1, plane_isect_1_no)) || - (!isect_plane_plane_v3(plane_tx[1], plane_tx[3], plane_isect_2, plane_isect_2_no))) { + if ((!isect_plane_plane_v3( + plane_tx[Y_MIN], plane_tx[Y_MAX], plane_isect_1, plane_isect_1_no)) || + (!isect_plane_plane_v3( + plane_tx[Z_MIN], plane_tx[Z_MAX], plane_isect_2, plane_isect_2_no))) { return false; } add_v3_v3v3(plane_isect_1_other, plane_isect_1, plane_isect_1_no); add_v3_v3v3(plane_isect_2_other, plane_isect_2, plane_isect_2_no); - if (isect_line_line_v3(plane_isect_1, - plane_isect_1_other, - plane_isect_2, - plane_isect_2_other, - plane_isect_pt_1, - plane_isect_pt_2) == 0) { + if (!isect_line_line_v3(plane_isect_1, + plane_isect_1_other, + plane_isect_2, + plane_isect_2_other, + plane_isect_pt_1, + plane_isect_pt_2)) { return false; } float cam_plane_no[3]; float plane_isect_delta[3]; - float plane_isect_delta_len; - float shift_fac = BKE_camera_sensor_size( - params->sensor_fit, params->sensor_x, params->sensor_y) / - params->lens; + const float shift_fac = BKE_camera_sensor_size( + params->sensor_fit, params->sensor_x, params->sensor_y) / + params->lens; /* we want (0, 0, -1) transformed by camera_rotmat, this is a quicker shortcut. */ negate_v3_v3(cam_plane_no, data->camera_rotmat[2]); sub_v3_v3v3(plane_isect_delta, plane_isect_pt_2, plane_isect_pt_1); - plane_isect_delta_len = len_v3(plane_isect_delta); + const float plane_isect_delta_len = len_v3(plane_isect_delta); if (dot_v3v3(plane_isect_delta, cam_plane_no) > 0.0f) { copy_v3_v3(r_co, plane_isect_pt_1); @@ -755,6 +769,11 @@ static bool camera_frame_fit_calc_from_data(CameraParams *params, return true; } +#undef Y_MIN +#undef Y_MAX +#undef Z_MIN +#undef Z_MAX + bool BKE_camera_view_frame_fit_to_scene(Depsgraph *depsgraph, const Scene *scene, Object *camera_ob, diff --git a/source/blender/blenkernel/intern/curves.cc b/source/blender/blenkernel/intern/curves.cc index 78791b55b4d..5684a2e5b07 100644 --- a/source/blender/blenkernel/intern/curves.cc +++ b/source/blender/blenkernel/intern/curves.cc @@ -53,8 +53,6 @@ using blender::Vector; static const char *ATTR_POSITION = "position"; -static void update_custom_data_pointers(Curves &curves); - static void curves_init_data(ID *id) { Curves *curves = (Curves *)id; @@ -97,8 +95,6 @@ static void curves_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, dst.runtime->type_counts = src.runtime->type_counts; - dst.update_customdata_pointers(); - curves_dst->batch_cache = nullptr; } @@ -170,7 +166,6 @@ static void curves_blend_read_data(BlendDataReader *reader, ID *id) /* Geometry */ CustomData_blend_read(reader, &curves->geometry.point_data, curves->geometry.point_num); CustomData_blend_read(reader, &curves->geometry.curve_data, curves->geometry.curve_num); - update_custom_data_pointers(*curves); BLO_read_int32_array(reader, curves->geometry.curve_num + 1, &curves->geometry.curve_offsets); @@ -233,11 +228,6 @@ IDTypeInfo IDType_ID_CV = { /*lib_override_apply_post */ nullptr, }; -static void update_custom_data_pointers(Curves &curves) -{ - blender::bke::CurvesGeometry::wrap(curves.geometry).update_customdata_pointers(); -} - void *BKE_curves_add(Main *bmain, const char *name) { Curves *curves = static_cast<Curves *>(BKE_id_new(bmain, ID_CV, name)); @@ -388,6 +378,23 @@ Curves *curves_new_nomain(CurvesGeometry curves) return curves_id; } +void curves_copy_parameters(const Curves &src, Curves &dst) +{ + dst.flag = src.flag; + dst.attributes_active_index = src.attributes_active_index; + MEM_SAFE_FREE(dst.mat); + dst.mat = static_cast<Material **>(MEM_malloc_arrayN(src.totcol, sizeof(Material *), __func__)); + dst.totcol = src.totcol; + MutableSpan(dst.mat, dst.totcol).copy_from(Span(src.mat, src.totcol)); + dst.symmetry = src.symmetry; + dst.selection_domain = src.selection_domain; + dst.surface = src.surface; + MEM_SAFE_FREE(dst.surface_uv_map); + if (src.surface_uv_map != nullptr) { + dst.surface_uv_map = BLI_strdup(src.surface_uv_map); + } +} + CurvesSurfaceTransforms::CurvesSurfaceTransforms(const Object &curves_ob, const Object *surface_ob) { this->curves_to_world = curves_ob.obmat; diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc index 8d955a46275..7fc660cfbfc 100644 --- a/source/blender/blenkernel/intern/curves_geometry.cc +++ b/source/blender/blenkernel/intern/curves_geometry.cc @@ -68,8 +68,6 @@ CurvesGeometry::CurvesGeometry(const int point_num, const int curve_num) #endif this->offsets_for_write().first() = 0; - this->update_customdata_pointers(); - this->runtime = MEM_new<CurvesGeometryRuntime>(__func__); /* Fill the type counts with the default so they're in a valid state. */ this->runtime->type_counts[CURVE_TYPE_CATMULL_ROM] = curve_num; @@ -95,8 +93,6 @@ static void copy_curves_geometry(CurvesGeometry &dst, const CurvesGeometry &src) /* Though type counts are a cache, they must be copied because they are calculated eagerly. */ dst.runtime->type_counts = src.runtime->type_counts; - - dst.update_customdata_pointers(); } CurvesGeometry::CurvesGeometry(const CurvesGeometry &other) @@ -130,9 +126,6 @@ static void move_curves_geometry(CurvesGeometry &dst, CurvesGeometry &src) MEM_SAFE_FREE(src.curve_offsets); std::swap(dst.runtime, src.runtime); - - src.update_customdata_pointers(); - dst.update_customdata_pointers(); } CurvesGeometry::CurvesGeometry(CurvesGeometry &&other) @@ -306,13 +299,11 @@ void CurvesGeometry::update_curve_types() Span<float3> CurvesGeometry::positions() const { - return {(const float3 *)this->position, this->point_num}; + return get_span_attribute<float3>(*this, ATTR_DOMAIN_POINT, ATTR_POSITION); } MutableSpan<float3> CurvesGeometry::positions_for_write() { - this->position = (float(*)[3])CustomData_duplicate_referenced_layer_named( - &this->point_data, CD_PROP_FLOAT3, ATTR_POSITION.c_str(), this->point_num); - return {(float3 *)this->position, this->point_num}; + return get_mutable_attribute<float3>(*this, ATTR_DOMAIN_POINT, ATTR_POSITION); } Span<int> CurvesGeometry::offsets() const @@ -961,7 +952,6 @@ void CurvesGeometry::resize(const int points_num, const int curves_num) this->curve_offsets = (int *)MEM_reallocN(this->curve_offsets, sizeof(int) * (curves_num + 1)); } this->tag_topology_changed(); - this->update_customdata_pointers(); } void CurvesGeometry::tag_positions_changed() @@ -1060,10 +1050,11 @@ void CurvesGeometry::transform(const float4x4 &matrix) static std::optional<bounds::MinMaxResult<float3>> curves_bounds(const CurvesGeometry &curves) { - Span<float3> positions = curves.positions(); - if (curves.radius) { - Span<float> radii{curves.radius, curves.points_num()}; - return bounds::min_max_with_radii(positions, radii); + const Span<float3> positions = curves.positions(); + const VArray<float> radii = curves.attributes().lookup_or_default<float>( + ATTR_RADIUS, ATTR_DOMAIN_POINT, 0.0f); + if (!(radii.is_single() && radii.get_internal_single() == 0.0f)) { + return bounds::min_max_with_radii(positions, radii.get_internal_span()); } return bounds::min_max(positions); } @@ -1079,16 +1070,6 @@ bool CurvesGeometry::bounds_min_max(float3 &min, float3 &max) const return true; } -void CurvesGeometry::update_customdata_pointers() -{ - this->position = (float(*)[3])CustomData_get_layer_named( - &this->point_data, CD_PROP_FLOAT3, ATTR_POSITION.c_str()); - this->radius = (float *)CustomData_get_layer_named( - &this->point_data, CD_PROP_FLOAT, ATTR_RADIUS.c_str()); - this->curve_type = (int8_t *)CustomData_get_layer_named( - &this->point_data, CD_PROP_INT8, ATTR_CURVE_TYPE.c_str()); -} - static void *ensure_customdata_layer(CustomData &custom_data, const StringRefNull name, const eCustomDataType data_type, @@ -1497,7 +1478,6 @@ void CurvesGeometry::remove_attributes_based_on_types() if (!this->has_curve_with_type({CURVE_TYPE_BEZIER, CURVE_TYPE_CATMULL_ROM, CURVE_TYPE_NURBS})) { CustomData_free_layer_named(&this->curve_data, ATTR_RESOLUTION.c_str(), curves_num); } - this->update_customdata_pointers(); } /** \} */ diff --git a/source/blender/blenkernel/intern/curves_utils.cc b/source/blender/blenkernel/intern/curves_utils.cc index f7db60ee588..d98832e796c 100644 --- a/source/blender/blenkernel/intern/curves_utils.cc +++ b/source/blender/blenkernel/intern/curves_utils.cc @@ -84,6 +84,18 @@ void fill_points(const CurvesGeometry &curves, }); } +bke::CurvesGeometry copy_only_curve_domain(const bke::CurvesGeometry &src_curves) +{ + bke::CurvesGeometry dst_curves(0, src_curves.curves_num()); + CustomData_copy(&src_curves.curve_data, + &dst_curves.curve_data, + CD_MASK_ALL, + CD_DUPLICATE, + src_curves.curves_num()); + dst_curves.runtime->type_counts = src_curves.runtime->type_counts; + return dst_curves; +} + IndexMask indices_for_type(const VArray<int8_t> &types, const std::array<int, CURVE_TYPES_NUM> &type_counts, const CurveType type, diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index 0203620df84..972ff377519 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -1146,7 +1146,7 @@ void fcurve_samples_to_keyframes(FCurve *fcu, const int start, const int end) MEM_SAFE_FREE(fcu->fpt); /* Not strictly needed since we use linear interpolation, but better be consistent here. */ - calchandles_fcurve(fcu); + BKE_fcurve_handles_recalc(fcu); } /* ***************************** F-Curve Sanity ********************************* */ @@ -1216,7 +1216,7 @@ static BezTriple *cycle_offset_triple( return out; } -void calchandles_fcurve_ex(FCurve *fcu, eBezTriple_Flag handle_sel_flag) +void BKE_fcurve_handles_recalc_ex(FCurve *fcu, eBezTriple_Flag handle_sel_flag) { BezTriple *bezt, *prev, *next; int a = fcu->totvert; @@ -1299,9 +1299,9 @@ void calchandles_fcurve_ex(FCurve *fcu, eBezTriple_Flag handle_sel_flag) } } -void calchandles_fcurve(FCurve *fcu) +void BKE_fcurve_handles_recalc(FCurve *fcu) { - calchandles_fcurve_ex(fcu, SELECT); + BKE_fcurve_handles_recalc_ex(fcu, SELECT); } void testhandles_fcurve(FCurve *fcu, eBezTriple_Flag sel_flag, const bool use_handle) @@ -1320,7 +1320,7 @@ void testhandles_fcurve(FCurve *fcu, eBezTriple_Flag sel_flag, const bool use_ha } /* Recalculate handles. */ - calchandles_fcurve_ex(fcu, sel_flag); + BKE_fcurve_handles_recalc_ex(fcu, sel_flag); } void sort_time_fcurve(FCurve *fcu) @@ -1590,6 +1590,12 @@ static void berekeny(float f1, float f2, float f3, float f4, float *o, int b) } } +static void fcurve_bezt_free(FCurve *fcu) +{ + MEM_SAFE_FREE(fcu->bezt); + fcu->totvert = 0; +} + bool BKE_fcurve_bezt_subdivide_handles(struct BezTriple *bezt, struct BezTriple *prev, struct BezTriple *next, @@ -1651,6 +1657,69 @@ bool BKE_fcurve_bezt_subdivide_handles(struct BezTriple *bezt, return true; } +void BKE_fcurve_delete_key(FCurve *fcu, int index) +{ + /* sanity check */ + if (fcu == NULL) { + return; + } + + /* verify the index: + * 1) cannot be greater than the number of available keyframes + * 2) negative indices are for specifying a value from the end of the array + */ + if (abs(index) >= fcu->totvert) { + return; + } + if (index < 0) { + index += fcu->totvert; + } + + /* Delete this keyframe */ + memmove( + &fcu->bezt[index], &fcu->bezt[index + 1], sizeof(BezTriple) * (fcu->totvert - index - 1)); + fcu->totvert--; + + /* Free the array of BezTriples if there are not keyframes */ + if (fcu->totvert == 0) { + fcurve_bezt_free(fcu); + } +} + +bool BKE_fcurve_delete_keys_selected(FCurve *fcu) +{ + bool changed = false; + + if (fcu->bezt == NULL) { /* ignore baked curves */ + return false; + } + + /* Delete selected BezTriples */ + for (int i = 0; i < fcu->totvert; i++) { + if (fcu->bezt[i].f2 & SELECT) { + if (i == fcu->active_keyframe_index) { + BKE_fcurve_active_keyframe_set(fcu, NULL); + } + memmove(&fcu->bezt[i], &fcu->bezt[i + 1], sizeof(BezTriple) * (fcu->totvert - i - 1)); + fcu->totvert--; + i--; + changed = true; + } + } + + /* Free the array of BezTriples if there are not keyframes */ + if (fcu->totvert == 0) { + fcurve_bezt_free(fcu); + } + + return changed; +} + +void BKE_fcurve_delete_keys_all(FCurve *fcu) +{ + fcurve_bezt_free(fcu); +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/blenkernel/intern/fmodifier.c b/source/blender/blenkernel/intern/fmodifier.c index 1bb7c49d616..e4c7572b9e4 100644 --- a/source/blender/blenkernel/intern/fmodifier.c +++ b/source/blender/blenkernel/intern/fmodifier.c @@ -1127,7 +1127,7 @@ FModifier *add_fmodifier(ListBase *modifiers, int type, FCurve *owner_fcu) /* update the fcurve if the Cycles modifier is added */ if ((owner_fcu) && (type == FMODIFIER_TYPE_CYCLES)) { - calchandles_fcurve(owner_fcu); + BKE_fcurve_handles_recalc(owner_fcu); } /* return modifier for further editing */ @@ -1215,7 +1215,7 @@ bool remove_fmodifier(ListBase *modifiers, FModifier *fcm) /* update the fcurve if the Cycles modifier is removed */ if (update_fcu) { - calchandles_fcurve(update_fcu); + BKE_fcurve_handles_recalc(update_fcu); } return true; diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc index 0d899ec7b06..22f105af0f1 100644 --- a/source/blender/blenkernel/intern/geometry_component_curve.cc +++ b/source/blender/blenkernel/intern/geometry_component_curve.cc @@ -1452,7 +1452,8 @@ std::optional<blender::bke::AttributeAccessor> CurveComponentLegacy::attributes( std::optional<blender::bke::MutableAttributeAccessor> CurveComponentLegacy::attributes_for_write() { - return blender::bke::MutableAttributeAccessor(curve_, + CurveEval *curve = this->get_for_write(); + return blender::bke::MutableAttributeAccessor(curve, blender::bke::get_curve_accessor_functions_ref()); } diff --git a/source/blender/blenkernel/intern/geometry_component_curves.cc b/source/blender/blenkernel/intern/geometry_component_curves.cc index 34c17bedc2c..2714c78e381 100644 --- a/source/blender/blenkernel/intern/geometry_component_curves.cc +++ b/source/blender/blenkernel/intern/geometry_component_curves.cc @@ -358,10 +358,7 @@ static ComponentAttributeProviders create_attribute_providers_for_curve() const CurvesGeometry &curves = *static_cast<const CurvesGeometry *>(owner); return curves.curves_num(); }, - [](void *owner) { - CurvesGeometry &curves = *static_cast<CurvesGeometry *>(owner); - curves.update_customdata_pointers(); - }}; + [](void * /*owner*/) {}}; static CustomDataAccessInfo point_access = { [](void *owner) -> CustomData * { CurvesGeometry &curves = *static_cast<CurvesGeometry *>(owner); @@ -375,10 +372,7 @@ static ComponentAttributeProviders create_attribute_providers_for_curve() const CurvesGeometry &curves = *static_cast<const CurvesGeometry *>(owner); return curves.points_num(); }, - [](void *owner) { - CurvesGeometry &curves = *static_cast<CurvesGeometry *>(owner); - curves.update_customdata_pointers(); - }}; + [](void * /*owner*/) {}}; static BuiltinCustomDataLayerProvider position("position", ATTR_DOMAIN_POINT, @@ -644,6 +638,7 @@ std::optional<blender::bke::AttributeAccessor> CurveComponent::attributes() cons std::optional<blender::bke::MutableAttributeAccessor> CurveComponent::attributes_for_write() { - return blender::bke::MutableAttributeAccessor(curves_ ? &curves_->geometry : nullptr, + Curves *curves = this->get_for_write(); + return blender::bke::MutableAttributeAccessor(curves ? &curves->geometry : nullptr, blender::bke::get_curves_accessor_functions_ref()); } diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc index cb36b9b19f7..436868ba375 100644 --- a/source/blender/blenkernel/intern/geometry_component_mesh.cc +++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc @@ -298,12 +298,14 @@ static void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh, const MPoly &poly = mesh.mpoly[poly_index]; /* For every edge, mix values from the two adjacent corners (the current and next corner). */ - for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { - const int loop_index_next = (loop_index + 1) % poly.totloop; - const MLoop &loop = mesh.mloop[loop_index]; + for (const int i : IndexRange(poly.totloop)) { + const int next_i = (i + 1) % poly.totloop; + const int loop_i = poly.loopstart + i; + const int next_loop_i = poly.loopstart + next_i; + const MLoop &loop = mesh.mloop[loop_i]; const int edge_index = loop.e; - mixer.mix_in(edge_index, old_values[loop_index]); - mixer.mix_in(edge_index, old_values[loop_index_next]); + mixer.mix_in(edge_index, old_values[loop_i]); + mixer.mix_in(edge_index, old_values[next_loop_i]); } } @@ -325,13 +327,16 @@ void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh, for (const int poly_index : IndexRange(mesh.totpoly)) { const MPoly &poly = mesh.mpoly[poly_index]; - for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { - const int loop_index_next = (loop_index == poly.totloop) ? poly.loopstart : (loop_index + 1); - const MLoop &loop = mesh.mloop[loop_index]; + for (const int i : IndexRange(poly.totloop)) { + const int next_i = (i + 1) % poly.totloop; + const int loop_i = poly.loopstart + i; + const int next_loop_i = poly.loopstart + next_i; + const MLoop &loop = mesh.mloop[loop_i]; const int edge_index = loop.e; + loose_edges[edge_index] = false; - if (!old_values[loop_index] || !old_values[loop_index_next]) { + if (!old_values[loop_i] || !old_values[next_loop_i]) { r_values[edge_index] = false; } } @@ -1324,7 +1329,8 @@ std::optional<blender::bke::AttributeAccessor> MeshComponent::attributes() const std::optional<blender::bke::MutableAttributeAccessor> MeshComponent::attributes_for_write() { - return blender::bke::MutableAttributeAccessor(mesh_, + Mesh *mesh = this->get_for_write(); + return blender::bke::MutableAttributeAccessor(mesh, blender::bke::get_mesh_accessor_functions_ref()); } diff --git a/source/blender/blenkernel/intern/geometry_component_pointcloud.cc b/source/blender/blenkernel/intern/geometry_component_pointcloud.cc index b439a9ba7f8..4953da8a5ee 100644 --- a/source/blender/blenkernel/intern/geometry_component_pointcloud.cc +++ b/source/blender/blenkernel/intern/geometry_component_pointcloud.cc @@ -111,10 +111,7 @@ namespace blender::bke { */ static ComponentAttributeProviders create_attribute_providers_for_point_cloud() { - static auto update_custom_data_pointers = [](void *owner) { - PointCloud *pointcloud = static_cast<PointCloud *>(owner); - BKE_pointcloud_update_customdata_pointers(pointcloud); - }; + static auto update_custom_data_pointers = [](void * /*owner*/) {}; static CustomDataAccessInfo point_access = { [](void *owner) -> CustomData * { PointCloud *pointcloud = static_cast<PointCloud *>(owner); @@ -226,8 +223,9 @@ std::optional<blender::bke::AttributeAccessor> PointCloudComponent::attributes() std::optional<blender::bke::MutableAttributeAccessor> PointCloudComponent::attributes_for_write() { + PointCloud *pointcloud = this->get_for_write(); return blender::bke::MutableAttributeAccessor( - pointcloud_, blender::bke::get_pointcloud_accessor_functions_ref()); + pointcloud, blender::bke::get_pointcloud_accessor_functions_ref()); } /** \} */ diff --git a/source/blender/blenkernel/intern/idtype.c b/source/blender/blenkernel/intern/idtype.c index e55143d6852..923582dff4c 100644 --- a/source/blender/blenkernel/intern/idtype.c +++ b/source/blender/blenkernel/intern/idtype.c @@ -209,7 +209,11 @@ uint64_t BKE_idtype_idcode_to_idfilter(const short idcode) case ID_##_id: \ return FILTER_ID_##_id - switch (idcode) { +#define CASE_IDFILTER_NONE(_id) \ + case ID_##_id: \ + return 0 + + switch ((ID_Type)idcode) { CASE_IDFILTER(AC); CASE_IDFILTER(AR); CASE_IDFILTER(BR); @@ -220,7 +224,11 @@ uint64_t BKE_idtype_idcode_to_idfilter(const short idcode) CASE_IDFILTER(GR); CASE_IDFILTER(CV); CASE_IDFILTER(IM); + CASE_IDFILTER_NONE(IP); + CASE_IDFILTER(KE); CASE_IDFILTER(LA); + CASE_IDFILTER(LI); + CASE_IDFILTER(LP); CASE_IDFILTER(LS); CASE_IDFILTER(LT); CASE_IDFILTER(MA); @@ -234,22 +242,25 @@ uint64_t BKE_idtype_idcode_to_idfilter(const short idcode) CASE_IDFILTER(PAL); CASE_IDFILTER(PC); CASE_IDFILTER(PT); - CASE_IDFILTER(LP); CASE_IDFILTER(SCE); + CASE_IDFILTER(SCR); CASE_IDFILTER(SIM); - CASE_IDFILTER(SPK); CASE_IDFILTER(SO); + CASE_IDFILTER(SPK); CASE_IDFILTER(TE); CASE_IDFILTER(TXT); CASE_IDFILTER(VF); CASE_IDFILTER(VO); + CASE_IDFILTER(WM); CASE_IDFILTER(WO); CASE_IDFILTER(WS); - default: - return 0; } + BLI_assert_unreachable(); + return 0; + #undef CASE_IDFILTER +#undef CASE_IDFILTER_NONE } short BKE_idtype_idcode_from_idfilter(const uint64_t idfilter) @@ -258,6 +269,8 @@ short BKE_idtype_idcode_from_idfilter(const uint64_t idfilter) case FILTER_ID_##_id: \ return ID_##_id +#define CASE_IDFILTER_NONE(_id) (void)0 + switch (idfilter) { CASE_IDFILTER(AC); CASE_IDFILTER(AR); @@ -269,7 +282,11 @@ short BKE_idtype_idcode_from_idfilter(const uint64_t idfilter) CASE_IDFILTER(GR); CASE_IDFILTER(CV); CASE_IDFILTER(IM); + CASE_IDFILTER_NONE(IP); + CASE_IDFILTER(KE); CASE_IDFILTER(LA); + CASE_IDFILTER(LI); + CASE_IDFILTER(LP); CASE_IDFILTER(LS); CASE_IDFILTER(LT); CASE_IDFILTER(MA); @@ -283,21 +300,25 @@ short BKE_idtype_idcode_from_idfilter(const uint64_t idfilter) CASE_IDFILTER(PAL); CASE_IDFILTER(PC); CASE_IDFILTER(PT); - CASE_IDFILTER(LP); CASE_IDFILTER(SCE); + CASE_IDFILTER(SCR); CASE_IDFILTER(SIM); - CASE_IDFILTER(SPK); CASE_IDFILTER(SO); + CASE_IDFILTER(SPK); CASE_IDFILTER(TE); CASE_IDFILTER(TXT); CASE_IDFILTER(VF); CASE_IDFILTER(VO); + CASE_IDFILTER(WM); CASE_IDFILTER(WO); - default: - return 0; + CASE_IDFILTER(WS); } + BLI_assert_unreachable(); + return 0; + #undef CASE_IDFILTER +#undef CASE_IDFILTER_NONE } int BKE_idtype_idcode_to_index(const short idcode) diff --git a/source/blender/blenkernel/intern/image.cc b/source/blender/blenkernel/intern/image.cc index 92e98935e83..f8b2d841028 100644 --- a/source/blender/blenkernel/intern/image.cc +++ b/source/blender/blenkernel/intern/image.cc @@ -2475,7 +2475,10 @@ int BKE_imbuf_write(ImBuf *ibuf, const char *name, const ImageFormatData *imf) return ok; } -int BKE_imbuf_write_as(ImBuf *ibuf, const char *name, ImageFormatData *imf, const bool save_copy) +int BKE_imbuf_write_as(ImBuf *ibuf, + const char *name, + const ImageFormatData *imf, + const bool save_copy) { ImBuf ibuf_back = *ibuf; int ok; @@ -5057,13 +5060,7 @@ void BKE_image_get_size(Image *image, ImageUser *iuser, int *r_width, int *r_hei } else if (image != nullptr && image->type == IMA_TYPE_R_RESULT && iuser != nullptr && iuser->scene != nullptr) { - Scene *scene = iuser->scene; - *r_width = (scene->r.xsch * scene->r.size) / 100; - *r_height = (scene->r.ysch * scene->r.size) / 100; - if ((scene->r.mode & R_BORDER) && (scene->r.mode & R_CROP)) { - *r_width *= BLI_rctf_size_x(&scene->r.border); - *r_height *= BLI_rctf_size_y(&scene->r.border); - } + BKE_render_resolution(&iuser->scene->r, true, r_width, r_height); } else { *r_width = IMG_SIZE_FALLBACK; diff --git a/source/blender/blenkernel/intern/image_gpu.cc b/source/blender/blenkernel/intern/image_gpu.cc index 6edb9e1b24c..6506b40b603 100644 --- a/source/blender/blenkernel/intern/image_gpu.cc +++ b/source/blender/blenkernel/intern/image_gpu.cc @@ -737,11 +737,11 @@ static void gpu_texture_update_from_ibuf( } else { /* Byte image is in original colorspace from the file, and may need conversion. */ - if (IMB_colormanagement_space_is_data(ibuf->rect_colorspace) || - IMB_colormanagement_space_is_scene_linear(ibuf->rect_colorspace)) { + if (IMB_colormanagement_space_is_data(ibuf->rect_colorspace)) { /* Non-color data, just store buffer as is. */ } - else if (IMB_colormanagement_space_is_srgb(ibuf->rect_colorspace)) { + else if (IMB_colormanagement_space_is_srgb(ibuf->rect_colorspace) || + IMB_colormanagement_space_is_scene_linear(ibuf->rect_colorspace)) { /* sRGB or scene linear, store as byte texture that the GPU can decode directly. */ rect = (uchar *)MEM_mallocN(sizeof(uchar[4]) * w * h, __func__); if (rect == nullptr) { diff --git a/source/blender/blenkernel/intern/image_save.cc b/source/blender/blenkernel/intern/image_save.cc index 3f6f81845e2..cd86d3f7087 100644 --- a/source/blender/blenkernel/intern/image_save.cc +++ b/source/blender/blenkernel/intern/image_save.cc @@ -204,7 +204,7 @@ bool BKE_image_save_options_init(ImageSaveOptions *opts, return (ibuf != nullptr); } -void BKE_image_save_options_update(ImageSaveOptions *opts, Image *image) +void BKE_image_save_options_update(ImageSaveOptions *opts, const Image *image) { /* Auto update color space when changing save as render and file type. */ if (opts->save_as_render) { @@ -272,7 +272,7 @@ static void image_save_post(ReportList *reports, Image *ima, ImBuf *ibuf, int ok, - ImageSaveOptions *opts, + const ImageSaveOptions *opts, int save_copy, const char *filepath, bool *r_colorspace_changed) @@ -359,7 +359,7 @@ static void imbuf_save_post(ImBuf *ibuf, ImBuf *colormanaged_ibuf) static bool image_save_single(ReportList *reports, Image *ima, ImageUser *iuser, - ImageSaveOptions *opts, + const ImageSaveOptions *opts, bool *r_colorspace_changed) { void *lock; @@ -375,7 +375,7 @@ static bool image_save_single(ReportList *reports, ImBuf *colormanaged_ibuf = nullptr; const bool save_copy = opts->save_copy; const bool save_as_render = opts->save_as_render; - ImageFormatData *imf = &opts->im_format; + const ImageFormatData *imf = &opts->im_format; if (ima->type == IMA_TYPE_R_RESULT) { /* enforce user setting for RGB or RGBA, but skip BW */ @@ -620,7 +620,7 @@ static bool image_save_single(ReportList *reports, } bool BKE_image_save( - ReportList *reports, Main *bmain, Image *ima, ImageUser *iuser, ImageSaveOptions *opts) + ReportList *reports, Main *bmain, Image *ima, ImageUser *iuser, const ImageSaveOptions *opts) { /* For saving a tiled image we need an iuser, so use a local one if there isn't already one. */ ImageUser save_iuser; diff --git a/source/blender/blenkernel/intern/key.c b/source/blender/blenkernel/intern/key.c index 594cffe6406..97eac0b9f91 100644 --- a/source/blender/blenkernel/intern/key.c +++ b/source/blender/blenkernel/intern/key.c @@ -191,7 +191,7 @@ static void shapekey_blend_read_expand(BlendExpander *expander, ID *id) IDTypeInfo IDType_ID_KE = { .id_code = ID_KE, - .id_filter = 0, + .id_filter = FILTER_ID_KE, .main_listbase_index = INDEX_ID_KE, .struct_size = sizeof(Key), .name = "Key", diff --git a/source/blender/blenkernel/intern/lib_id_remapper.cc b/source/blender/blenkernel/intern/lib_id_remapper.cc index 7e75e0f5d93..98ac9110a48 100644 --- a/source/blender/blenkernel/intern/lib_id_remapper.cc +++ b/source/blender/blenkernel/intern/lib_id_remapper.cc @@ -36,6 +36,7 @@ struct IDRemapper { BLI_assert(old_id != nullptr); BLI_assert(new_id == nullptr || (GS(old_id->name) == GS(new_id->name))); mappings.add(old_id, new_id); + BLI_assert(BKE_idtype_idcode_to_idfilter(GS(old_id->name)) != 0); source_types |= BKE_idtype_idcode_to_idfilter(GS(old_id->name)); } diff --git a/source/blender/blenkernel/intern/lib_id_test.cc b/source/blender/blenkernel/intern/lib_id_test.cc index d6101d71be5..1aba78eed8f 100644 --- a/source/blender/blenkernel/intern/lib_id_test.cc +++ b/source/blender/blenkernel/intern/lib_id_test.cc @@ -18,19 +18,18 @@ namespace blender::bke::tests { struct LibIDMainSortTestContext { - Main *bmain; -}; + Main *bmain = nullptr; -static void test_lib_id_main_sort_init(LibIDMainSortTestContext *ctx) -{ - BKE_idtype_init(); - ctx->bmain = BKE_main_new(); -} - -static void test_lib_id_main_sort_free(LibIDMainSortTestContext *ctx) -{ - BKE_main_free(ctx->bmain); -} + LibIDMainSortTestContext() + { + BKE_idtype_init(); + bmain = BKE_main_new(); + } + ~LibIDMainSortTestContext() + { + BKE_main_free(bmain); + } +}; static void test_lib_id_main_sort_check_order(std::initializer_list<ID *> list) { @@ -47,8 +46,7 @@ static void test_lib_id_main_sort_check_order(std::initializer_list<ID *> list) TEST(lib_id_main_sort, local_ids_1) { - LibIDMainSortTestContext ctx = {nullptr}; - test_lib_id_main_sort_init(&ctx); + LibIDMainSortTestContext ctx; EXPECT_TRUE(BLI_listbase_is_empty(&ctx.bmain->libraries)); ID *id_c = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_C")); @@ -57,14 +55,22 @@ TEST(lib_id_main_sort, local_ids_1) EXPECT_TRUE(ctx.bmain->objects.first == id_a); EXPECT_TRUE(ctx.bmain->objects.last == id_c); test_lib_id_main_sort_check_order({id_a, id_b, id_c}); +} - test_lib_id_main_sort_free(&ctx); +static void change_lib(Main * /*bmain*/, ID *id, Library *lib) +{ + id->lib = lib; +} + +static void change_name(Main *bmain, ID *id, const char *name) +{ + BLI_strncpy(id->name + 2, name, MAX_NAME); + BKE_id_new_name_validate(&bmain->objects, id, nullptr, true); } TEST(lib_id_main_sort, linked_ids_1) { - LibIDMainSortTestContext ctx = {nullptr}; - test_lib_id_main_sort_init(&ctx); + LibIDMainSortTestContext ctx; EXPECT_TRUE(BLI_listbase_is_empty(&ctx.bmain->libraries)); Library *lib_a = static_cast<Library *>(BKE_id_new(ctx.bmain, ID_LI, "LI_A")); @@ -92,14 +98,11 @@ TEST(lib_id_main_sort, linked_ids_1) EXPECT_TRUE(ctx.bmain->objects.first == id_c); EXPECT_TRUE(ctx.bmain->objects.last == id_b); test_lib_id_main_sort_check_order({id_c, id_a, id_b}); - - test_lib_id_main_sort_free(&ctx); } TEST(lib_id_main_unique_name, local_ids_1) { - LibIDMainSortTestContext ctx = {nullptr}; - test_lib_id_main_sort_init(&ctx); + LibIDMainSortTestContext ctx; EXPECT_TRUE(BLI_listbase_is_empty(&ctx.bmain->libraries)); ID *id_c = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_C")); @@ -107,21 +110,18 @@ TEST(lib_id_main_unique_name, local_ids_1) ID *id_b = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_B")); test_lib_id_main_sort_check_order({id_a, id_b, id_c}); - BLI_strncpy(id_c->name, id_a->name, sizeof(id_c->name)); - BKE_id_new_name_validate(&ctx.bmain->objects, id_c, nullptr, false); - EXPECT_TRUE(strcmp(id_c->name + 2, "OB_A.001") == 0); - EXPECT_TRUE(strcmp(id_a->name + 2, "OB_A") == 0); + change_name(ctx.bmain, id_c, "OB_A"); + + EXPECT_STREQ(id_c->name + 2, "OB_A.001"); + EXPECT_STREQ(id_a->name + 2, "OB_A"); EXPECT_TRUE(ctx.bmain->objects.first == id_a); EXPECT_TRUE(ctx.bmain->objects.last == id_b); test_lib_id_main_sort_check_order({id_a, id_c, id_b}); - - test_lib_id_main_sort_free(&ctx); } TEST(lib_id_main_unique_name, linked_ids_1) { - LibIDMainSortTestContext ctx = {nullptr}; - test_lib_id_main_sort_init(&ctx); + LibIDMainSortTestContext ctx; EXPECT_TRUE(BLI_listbase_is_empty(&ctx.bmain->libraries)); Library *lib_a = static_cast<Library *>(BKE_id_new(ctx.bmain, ID_LI, "LI_A")); @@ -130,29 +130,263 @@ TEST(lib_id_main_unique_name, linked_ids_1) ID *id_a = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_A")); ID *id_b = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_B")); - id_a->lib = lib_a; + change_lib(ctx.bmain, id_a, lib_a); id_sort_by_name(&ctx.bmain->objects, id_a, nullptr); - id_b->lib = lib_a; + change_lib(ctx.bmain, id_b, lib_a); id_sort_by_name(&ctx.bmain->objects, id_b, nullptr); - BLI_strncpy(id_b->name, id_a->name, sizeof(id_b->name)); - BKE_id_new_name_validate(&ctx.bmain->objects, id_b, nullptr, true); - EXPECT_TRUE(strcmp(id_b->name + 2, "OB_A.001") == 0); - EXPECT_TRUE(strcmp(id_a->name + 2, "OB_A") == 0); + + change_name(ctx.bmain, id_b, "OB_A"); + EXPECT_STREQ(id_b->name + 2, "OB_A.001"); + EXPECT_STREQ(id_a->name + 2, "OB_A"); EXPECT_TRUE(ctx.bmain->objects.first == id_c); EXPECT_TRUE(ctx.bmain->objects.last == id_b); test_lib_id_main_sort_check_order({id_c, id_a, id_b}); - id_b->lib = lib_b; + change_lib(ctx.bmain, id_b, lib_b); id_sort_by_name(&ctx.bmain->objects, id_b, nullptr); - BLI_strncpy(id_b->name, id_a->name, sizeof(id_b->name)); - BKE_id_new_name_validate(&ctx.bmain->objects, id_b, nullptr, true); - EXPECT_TRUE(strcmp(id_b->name + 2, "OB_A") == 0); - EXPECT_TRUE(strcmp(id_a->name + 2, "OB_A") == 0); + change_name(ctx.bmain, id_b, "OB_A"); + EXPECT_STREQ(id_b->name + 2, "OB_A"); + EXPECT_STREQ(id_a->name + 2, "OB_A"); EXPECT_TRUE(ctx.bmain->objects.first == id_c); EXPECT_TRUE(ctx.bmain->objects.last == id_b); test_lib_id_main_sort_check_order({id_c, id_a, id_b}); +} + +TEST(lib_id_main_unique_name, ids_sorted_by_default) +{ + LibIDMainSortTestContext ctx; + + ID *id_foo = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + ID *id_bar = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Bar")); + ID *id_baz = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Baz")); + ID *id_yes = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Yes")); + test_lib_id_main_sort_check_order({id_bar, id_baz, id_foo, id_yes}); +} + +static ID *add_id_in_library(Main *bmain, const char *name, Library *lib) +{ + ID *id = static_cast<ID *>(BKE_id_new(bmain, ID_OB, name)); + id->lib = lib; + id_sort_by_name(&bmain->objects, id, nullptr); + return id; +} + +TEST(lib_id_main_unique_name, ids_sorted_by_default_with_libraries) +{ + LibIDMainSortTestContext ctx; + + Library *lib_one = static_cast<Library *>(BKE_id_new(ctx.bmain, ID_LI, "LibOne")); + Library *lib_two = static_cast<Library *>(BKE_id_new(ctx.bmain, ID_LI, "LibTwo")); + + ID *id_foo = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + ID *id_bar = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Bar")); + + ID *id_l1c = add_id_in_library(ctx.bmain, "C", lib_one); + ID *id_l2b = add_id_in_library(ctx.bmain, "B", lib_two); + ID *id_l1a = add_id_in_library(ctx.bmain, "A", lib_one); + + ID *id_baz = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Baz")); + ID *id_yes = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Yes")); + + test_lib_id_main_sort_check_order({id_bar, id_baz, id_foo, id_yes, id_l1a, id_l1c, id_l2b}); +} + +TEST(lib_id_main_unique_name, name_too_long_handling) +{ + LibIDMainSortTestContext ctx; + const char *name_a = "Long_Name_That_Does_Not_Fit_Into_Max_Name_Limit_And_Should_Get_Truncated"; + const char *name_b = "Another_Long_Name_That_Does_Not_Fit_And_Has_A_Number_Suffix.123456"; + const char *name_c = "Name_That_Has_Too_Long_Number_Suffix.1234567890"; + + ID *id_a = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, name_a)); + ID *id_b = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, name_b)); + ID *id_c = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, name_c)); + + EXPECT_STREQ(id_a->name + 2, "Long_Name_That_Does_Not_Fit_Into_Max_Name_Limit_And_Should_Get_"); + EXPECT_STREQ(id_b->name + 2, "Another_Long_Name_That_Does_Not_Fit_And_Has_A_Number_Suffix.123"); + EXPECT_STREQ(id_c->name + 2, "Name_That_Has_Too_Long_Number_Suffix.1234567890"); /* Unchanged */ +} + +TEST(lib_id_main_unique_name, create_equivalent_numeric_suffixes) +{ + LibIDMainSortTestContext ctx; + + /* Create names where many of their numeric suffixes are + * the same number, yet the names are different and thus + * should be allowed as-is. */ + ID *id_a = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.123")); + ID *id_b = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.000")); + ID *id_c = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.003")); + ID *id_d = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.3")); + ID *id_e = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.0")); + ID *id_f = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.")); + ID *id_g = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.0123")); + ID *id_h = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + ID *id_i = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo..")); + ID *id_j = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo..001")); + ID *id_k = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo..000")); + + EXPECT_STREQ(id_a->name + 2, "Foo.123"); + EXPECT_STREQ(id_b->name + 2, "Foo.000"); + EXPECT_STREQ(id_c->name + 2, "Foo.003"); + EXPECT_STREQ(id_d->name + 2, "Foo.3"); + EXPECT_STREQ(id_e->name + 2, "Foo.0"); + EXPECT_STREQ(id_f->name + 2, "Foo."); + EXPECT_STREQ(id_g->name + 2, "Foo.0123"); + EXPECT_STREQ(id_h->name + 2, "Foo"); + EXPECT_STREQ(id_i->name + 2, "Foo.."); + EXPECT_STREQ(id_j->name + 2, "Foo..001"); + EXPECT_STREQ(id_k->name + 2, "Foo..000"); + + /* Now create their exact duplicates again, and check what happens. */ + id_a = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.123")); + id_b = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.000")); + id_c = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.003")); + id_d = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.3")); + id_e = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.0")); + id_f = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.")); + id_g = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.0123")); + id_h = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + id_i = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo..")); + id_j = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo..001")); + id_k = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo..000")); + + EXPECT_STREQ(id_a->name + 2, "Foo.001"); + EXPECT_STREQ(id_b->name + 2, "Foo.002"); + EXPECT_STREQ(id_c->name + 2, "Foo.004"); + EXPECT_STREQ(id_d->name + 2, "Foo.005"); + EXPECT_STREQ(id_e->name + 2, "Foo.006"); + EXPECT_STREQ(id_f->name + 2, "Foo..002"); + EXPECT_STREQ(id_g->name + 2, "Foo.007"); + EXPECT_STREQ(id_h->name + 2, "Foo.008"); + EXPECT_STREQ(id_i->name + 2, "Foo...001"); + EXPECT_STREQ(id_j->name + 2, "Foo..003"); + EXPECT_STREQ(id_k->name + 2, "Foo..004"); +} + +TEST(lib_id_main_unique_name, zero_suffix_is_never_assigned) +{ + LibIDMainSortTestContext ctx; + + /* Creating these should assign 002 to the first one, but the next + * ones should start numbers starting from 1: 001 and 003. */ + ID *id_002 = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.002")); + ID *id_001 = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.002")); + ID *id_003 = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.002")); + + EXPECT_STREQ(id_002->name + 2, "Foo.002"); + EXPECT_STREQ(id_001->name + 2, "Foo.001"); + EXPECT_STREQ(id_003->name + 2, "Foo.003"); +} + +TEST(lib_id_main_unique_name, remove_after_dup_get_original_name) +{ + LibIDMainSortTestContext ctx; + + ID *id_a = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + ID *id_b = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + + EXPECT_STREQ(id_a->name + 2, "Foo"); + EXPECT_STREQ(id_b->name + 2, "Foo.001"); + BKE_id_free(ctx.bmain, id_a); + + id_a = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + EXPECT_STREQ(id_a->name + 2, "Foo"); +} + +TEST(lib_id_main_unique_name, name_number_suffix_assignment) +{ + LibIDMainSortTestContext ctx; + + /* Create <1k objects first. */ + const int total_object_count = 1200; + ID *ids[total_object_count] = {}; + for (int i = 0; i < total_object_count / 2; ++i) { + ids[i] = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + } + + /* They should get assigned sequential numeric suffixes. */ + EXPECT_STREQ(ids[0]->name + 2, "Foo"); + EXPECT_STREQ(ids[1]->name + 2, "Foo.001"); + EXPECT_STREQ(ids[total_object_count / 2 - 1]->name + 2, "Foo.599"); + + /* Free some of the objects. */ + BKE_id_free(ctx.bmain, ids[10]); + BKE_id_free(ctx.bmain, ids[20]); + BKE_id_free(ctx.bmain, ids[30]); + + /* Create objects again; they should get suffixes that were just free'd up. */ + ID *id_010 = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + EXPECT_STREQ(id_010->name + 2, "Foo.010"); + ID *id_020 = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.123")); + EXPECT_STREQ(id_020->name + 2, "Foo.020"); + /* Suffixes >1k do not get the "use the most proper free one" treatment. */ + ID *id_2000 = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.2000")); + EXPECT_STREQ(id_2000->name + 2, "Foo.2000"); + /* But smaller than 1k suffixes do get proper empty spots. */ + ID *id_030 = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + EXPECT_STREQ(id_030->name + 2, "Foo.030"); + ID *id_600 = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + EXPECT_STREQ(id_600->name + 2, "Foo.600"); + + /* Max possible numeric suffix. */ + ID *id_max = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.999999999")); + EXPECT_STREQ(id_max->name + 2, "Foo.999999999"); + /* Try with max. possible suffix again: will assign free suffix under 1k. */ + ID *id_max1 = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.999999999")); + EXPECT_STREQ(id_max1->name + 2, "Foo.601"); + + /* Now create the rest of objects, to use all the suffixes up to 1k. + * Once all the ones up to 1k are used, the logic will fall back to + * "use largest number seen + 1", but the largest one is already the max + * possible. So it will shorten the name part and restart the counter, + * i.e. "Fo.001". */ + for (int i = total_object_count / 2; i < total_object_count; ++i) { + ids[i] = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + } + /* At this point creating "Foo" based objects will fall always + * result in shortened name to "Fo". */ + ID *id_fo178 = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + EXPECT_STREQ(id_fo178->name + 2, "Fo.178"); + ID *id_fo179 = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.2000")); + EXPECT_STREQ(id_fo179->name + 2, "Fo.179"); + ID *id_fo180 = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.999999999")); + EXPECT_STREQ(id_fo180->name + 2, "Fo.180"); +} + +TEST(lib_id_main_unique_name, renames_with_duplicates) +{ + LibIDMainSortTestContext ctx; + + ID *id_a = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + ID *id_b = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + ID *id_c = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Bar")); + + EXPECT_STREQ(id_a->name + 2, "Foo"); + EXPECT_STREQ(id_b->name + 2, "Foo.001"); + EXPECT_STREQ(id_c->name + 2, "Bar"); + + BKE_libblock_rename(ctx.bmain, id_a, "Foo.002"); + EXPECT_STREQ(id_a->name + 2, "Foo.002"); + BKE_libblock_rename(ctx.bmain, id_b, "Bar"); + EXPECT_STREQ(id_b->name + 2, "Bar.001"); + BKE_libblock_rename(ctx.bmain, id_c, "Foo"); + EXPECT_STREQ(id_c->name + 2, "Foo"); + BKE_libblock_rename(ctx.bmain, id_b, "Bar"); + EXPECT_STREQ(id_b->name + 2, "Bar"); +} + +TEST(lib_id_main_unique_name, names_are_unique_per_id_type) +{ + LibIDMainSortTestContext ctx; + + ID *id_a = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo")); + ID *id_b = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_CA, "Foo")); + ID *id_c = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo")); - test_lib_id_main_sort_free(&ctx); + EXPECT_STREQ(id_a->name + 2, "Foo"); + EXPECT_STREQ(id_b->name + 2, "Foo"); /* Different types (OB & CA) can have the same name. */ + EXPECT_STREQ(id_c->name + 2, "Foo.001"); } } // namespace blender::bke::tests diff --git a/source/blender/blenkernel/intern/lib_override.cc b/source/blender/blenkernel/intern/lib_override.cc index d816b5ede5f..0c5f59be768 100644 --- a/source/blender/blenkernel/intern/lib_override.cc +++ b/source/blender/blenkernel/intern/lib_override.cc @@ -428,14 +428,42 @@ static void lib_override_prefill_newid_from_existing_overrides(Main *bmain, ID * { ID *id_iter; FOREACH_MAIN_ID_BEGIN (bmain, id_iter) { - if (ID_IS_OVERRIDE_LIBRARY_REAL(id_iter) && - id_iter->override_library->hierarchy_root == id_hierarchy_root) { - id_iter->override_library->reference->newid = id_iter; + ID *id = id_iter; + if (GS(id_iter->name) == ID_KE) { + id = reinterpret_cast<Key *>(id_iter)->from; + BLI_assert(id != nullptr); + } + if (ID_IS_OVERRIDE_LIBRARY_REAL(id) && + id->override_library->hierarchy_root == id_hierarchy_root) { + id->override_library->reference->newid = id; + if (GS(id_iter->name) == ID_KE) { + Key *reference_key = BKE_key_from_id(id->override_library->reference); + if (reference_key != nullptr) { + reference_key->id.newid = id_iter; + } + } } } FOREACH_MAIN_ID_END; } +static void lib_override_remapper_overrides_add(IDRemapper *id_remapper, + ID *reference_id, + ID *local_id) +{ + BKE_id_remapper_add(id_remapper, reference_id, local_id); + + Key *reference_key, *local_key = nullptr; + if ((reference_key = BKE_key_from_id(reference_id)) != nullptr) { + if (reference_id->newid != nullptr) { + local_key = BKE_key_from_id(reference_id->newid); + BLI_assert(local_key != nullptr); + } + + BKE_id_remapper_add(id_remapper, &reference_key->id, &local_key->id); + } +} + /* TODO: Make this static local function instead? API is becoming complex, and it's not used * outside of this file anyway. */ bool BKE_lib_override_library_create_from_tag(Main *bmain, @@ -544,6 +572,7 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain, BLI_assert(id_hierarchy_root != nullptr); LinkNode *relinked_ids = nullptr; + IDRemapper *id_remapper = BKE_id_remapper_create(); /* Still checking the whole Main, that way we can tag other local IDs as needing to be * remapped to use newly created overriding IDs, if needed. */ ID *id; @@ -568,6 +597,14 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain, * consider we should also relink it, as part of recursive resync. */ if ((other_id->tag & LIB_TAG_DOIT) != 0 && other_id->lib != id_root_reference->lib) { BLI_linklist_prepend(&relinked_ids, other_id); + if (ID_IS_OVERRIDE_LIBRARY_REAL(other_id) && + other_id->override_library->hierarchy_root == id_hierarchy_root) { + reference_id = other_id->override_library->reference; + ID *local_id = reference_id->newid; + if (other_id == local_id) { + lib_override_remapper_overrides_add(id_remapper, reference_id, local_id); + } + } } if (other_id != id) { other_id->lib = id_root_reference->lib; @@ -575,7 +612,6 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain, } FOREACH_MAIN_ID_END; - IDRemapper *id_remapper = BKE_id_remapper_create(); for (todo_id_iter = static_cast<LinkData *>(todo_ids.first); todo_id_iter != nullptr; todo_id_iter = todo_id_iter->next) { reference_id = static_cast<ID *>(todo_id_iter->data); @@ -587,15 +623,7 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain, local_id->override_library->hierarchy_root = id_hierarchy_root; - BKE_id_remapper_add(id_remapper, reference_id, local_id); - - Key *reference_key, *local_key = nullptr; - if ((reference_key = BKE_key_from_id(reference_id)) != nullptr) { - local_key = BKE_key_from_id(reference_id->newid); - BLI_assert(local_key != nullptr); - - BKE_id_remapper_add(id_remapper, &reference_key->id, &local_key->id); - } + lib_override_remapper_overrides_add(id_remapper, reference_id, local_id); } BKE_libblock_relink_multiple(bmain, @@ -1121,14 +1149,26 @@ static bool lib_override_library_create_do(Main *bmain, BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false); lib_override_hierarchy_dependencies_recursive_tag(&data); + /* In case the operation is on an already partially overridden hierarchy, all existing overrides + * in that hierarchy need to be tagged for remapping from linked reference ID usages to newly + * created overrides ones. */ + if (id_hierarchy_root_reference->lib != id_root_reference->lib) { + BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference)); + BLI_assert(id_hierarchy_root_reference->override_library->reference->lib == + id_root_reference->lib); + + BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false); + data.hierarchy_root_id = id_hierarchy_root_reference; + data.id_root = id_hierarchy_root_reference; + data.is_override = true; + lib_override_overrides_group_tag(&data); + } + BKE_main_relations_free(bmain); lib_override_group_tag_data_clear(&data); bool success = false; if (id_hierarchy_root_reference->lib != id_root_reference->lib) { - BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference)); - BLI_assert(id_hierarchy_root_reference->override_library->reference->lib == - id_root_reference->lib); success = BKE_lib_override_library_create_from_tag(bmain, owner_library, id_root_reference, @@ -1771,6 +1811,11 @@ static bool lib_override_library_resync(Main *bmain, break; } } + if (reference_id == nullptr) { + /* Can happen e.g. when there is a local override of a shapekey, but the matching linked + * obdata (mesh etc.) does not have any shapekey anymore. */ + continue; + } BLI_assert(GS(reference_id->name) == GS(id->name)); if (!BLI_ghash_haskey(linkedref_to_old_override, reference_id)) { diff --git a/source/blender/blenkernel/intern/lib_query.c b/source/blender/blenkernel/intern/lib_query.c index 38252a46b93..a869bf4c4b0 100644 --- a/source/blender/blenkernel/intern/lib_query.c +++ b/source/blender/blenkernel/intern/lib_query.c @@ -391,8 +391,7 @@ uint64_t BKE_library_id_can_use_filter_id(const ID *id_owner) switch ((ID_Type)id_type_owner) { case ID_LI: - /* ID_LI doesn't exist as filter_id. */ - return 0; + return FILTER_ID_LI; case ID_SCE: return FILTER_ID_OB | FILTER_ID_WO | FILTER_ID_SCE | FILTER_ID_MC | FILTER_ID_MA | FILTER_ID_GR | FILTER_ID_TXT | FILTER_ID_LS | FILTER_ID_MSK | FILTER_ID_SO | @@ -472,6 +471,8 @@ uint64_t BKE_library_id_can_use_filter_id(const ID *id_owner) /* Deprecated... */ return 0; } + + BLI_assert_unreachable(); return 0; } diff --git a/source/blender/blenkernel/intern/lib_remap.c b/source/blender/blenkernel/intern/lib_remap.c index 246999a1179..28b0337d9a2 100644 --- a/source/blender/blenkernel/intern/lib_remap.c +++ b/source/blender/blenkernel/intern/lib_remap.c @@ -428,10 +428,13 @@ static void libblock_remap_data_update_tags(ID *old_id, ID *new_id, void *user_d } static void libblock_remap_reset_remapping_status_callback(ID *old_id, - ID *UNUSED(new_id), + ID *new_id, void *UNUSED(user_data)) { BKE_libblock_runtime_reset_remapping_status(old_id); + if (new_id != NULL) { + BKE_libblock_runtime_reset_remapping_status(new_id); + } } /** diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c index 03a17b2ecc5..4962b1c448e 100644 --- a/source/blender/blenkernel/intern/library.c +++ b/source/blender/blenkernel/intern/library.c @@ -63,7 +63,7 @@ static void library_foreach_path(ID *id, BPathForeachPathData *bpath_data) IDTypeInfo IDType_ID_LI = { .id_code = ID_LI, - .id_filter = 0, + .id_filter = FILTER_ID_LI, .main_listbase_index = INDEX_ID_LI, .struct_size = sizeof(Library), .name = "Library", diff --git a/source/blender/blenkernel/intern/mesh.cc b/source/blender/blenkernel/intern/mesh.cc index 2a14370bf93..cf05dc0404e 100644 --- a/source/blender/blenkernel/intern/mesh.cc +++ b/source/blender/blenkernel/intern/mesh.cc @@ -1190,6 +1190,11 @@ static void ensure_orig_index_layer(CustomData &data, const int size) void BKE_mesh_ensure_default_orig_index_customdata(Mesh *mesh) { BLI_assert(mesh->runtime.wrapper_type == ME_WRAPPER_TYPE_MDATA); + BKE_mesh_ensure_default_orig_index_customdata_no_check(mesh); +} + +void BKE_mesh_ensure_default_orig_index_customdata_no_check(Mesh *mesh) +{ ensure_orig_index_layer(mesh->vdata, mesh->totvert); ensure_orig_index_layer(mesh->edata, mesh->totedge); ensure_orig_index_layer(mesh->pdata, mesh->totpoly); diff --git a/source/blender/blenkernel/intern/mesh_convert.cc b/source/blender/blenkernel/intern/mesh_convert.cc index 7ebb3e25fd4..923d2703960 100644 --- a/source/blender/blenkernel/intern/mesh_convert.cc +++ b/source/blender/blenkernel/intern/mesh_convert.cc @@ -751,6 +751,8 @@ void BKE_mesh_to_curve(Main *bmain, Depsgraph *depsgraph, Scene *UNUSED(scene), void BKE_pointcloud_from_mesh(Mesh *me, PointCloud *pointcloud) { + using namespace blender; + BLI_assert(me != nullptr); pointcloud->totpoint = me->totvert; @@ -758,14 +760,17 @@ void BKE_pointcloud_from_mesh(Mesh *me, PointCloud *pointcloud) /* Copy over all attributes. */ CustomData_merge(&me->vdata, &pointcloud->pdata, CD_MASK_PROP_ALL, CD_DUPLICATE, me->totvert); - BKE_pointcloud_update_customdata_pointers(pointcloud); - CustomData_update_typemap(&pointcloud->pdata); - MVert *mvert; - mvert = me->mvert; - for (int i = 0; i < me->totvert; i++, mvert++) { - copy_v3_v3(pointcloud->co[i], mvert->co); - } + bke::AttributeAccessor mesh_attributes = bke::mesh_attributes(*me); + bke::MutableAttributeAccessor point_attributes = bke::pointcloud_attributes_for_write( + *pointcloud); + + const VArray<float3> mesh_positions = mesh_attributes.lookup_or_default<float3>( + "position", ATTR_DOMAIN_POINT, float3(0)); + bke::SpanAttributeWriter<float3> point_positions = + point_attributes.lookup_or_add_for_write_only_span<float3>("position", ATTR_DOMAIN_POINT); + mesh_positions.materialize(point_positions.span); + point_positions.finish(); } void BKE_mesh_to_pointcloud(Main *bmain, Depsgraph *depsgraph, Scene *UNUSED(scene), Object *ob) diff --git a/source/blender/blenkernel/intern/mesh_merge_customdata.cc b/source/blender/blenkernel/intern/mesh_merge_customdata.cc index adaf378ed27..7bc429954b0 100644 --- a/source/blender/blenkernel/intern/mesh_merge_customdata.cc +++ b/source/blender/blenkernel/intern/mesh_merge_customdata.cc @@ -33,11 +33,11 @@ static int compare_v2_classify(const float uv_a[2], const float uv_b[2]) if (uv_a[0] == uv_b[0] && uv_a[1] == uv_b[1]) { return CMP_EQUAL; } - /* Note that the ULP value is the primary value used to compare relative values - * as the absolute value doesn't account for float precision at difference scales. + /* NOTE(@campbellbarton): that the ULP value is the primary value used to compare relative + * values as the absolute value doesn't account for float precision at difference scales. * - For subdivision-surface ULP of 3 is sufficient, * although this value is extremely small. - * - For bevel the URL of 12 is sufficient to merge UV's that appear to be connected + * - For bevel the ULP of 12 is sufficient to merge UV's that appear to be connected * with bevel on Suzanne beveled 15% with 6 segments. * * These values could be tweaked but should be kept on the small side to prevent diff --git a/source/blender/blenkernel/intern/mesh_wrapper.cc b/source/blender/blenkernel/intern/mesh_wrapper.cc index 0362e4866e3..0b61b876abe 100644 --- a/source/blender/blenkernel/intern/mesh_wrapper.cc +++ b/source/blender/blenkernel/intern/mesh_wrapper.cc @@ -103,11 +103,7 @@ void BKE_mesh_wrapper_ensure_mdata(Mesh *me) /* Must isolate multithreaded tasks while holding a mutex lock. */ blender::threading::isolate_task([&]() { - const eMeshWrapperType geom_type_orig = static_cast<eMeshWrapperType>( - me->runtime.wrapper_type); - me->runtime.wrapper_type = ME_WRAPPER_TYPE_MDATA; - - switch (geom_type_orig) { + switch (static_cast<eMeshWrapperType>(me->runtime.wrapper_type)) { case ME_WRAPPER_TYPE_MDATA: case ME_WRAPPER_TYPE_SUBD: { break; /* Quiet warning. */ @@ -132,7 +128,7 @@ void BKE_mesh_wrapper_ensure_mdata(Mesh *me) * There is also a performance aspect, where this also assumes that original indices are * always needed when converting an edit mesh to a mesh. That might be wrong, but it's not * harmful. */ - BKE_mesh_ensure_default_orig_index_customdata(me); + BKE_mesh_ensure_default_orig_index_customdata_no_check(me); EditMeshData *edit_data = me->runtime.edit_data; if (edit_data->vertexCos) { @@ -144,8 +140,12 @@ void BKE_mesh_wrapper_ensure_mdata(Mesh *me) } if (me->runtime.wrapper_type_finalize) { - BKE_mesh_wrapper_deferred_finalize(me, &me->runtime.cd_mask_extra); + BKE_mesh_wrapper_deferred_finalize_mdata(me, &me->runtime.cd_mask_extra); } + + /* Keep type assignment last, so that read-only access only uses the mdata code paths after all + * the underlying data has been initialized. */ + me->runtime.wrapper_type = ME_WRAPPER_TYPE_MDATA; }); BLI_mutex_unlock(mesh_eval_mutex); diff --git a/source/blender/blenkernel/intern/modifier.c b/source/blender/blenkernel/intern/modifier.c index 6348d83362e..831ea084961 100644 --- a/source/blender/blenkernel/intern/modifier.c +++ b/source/blender/blenkernel/intern/modifier.c @@ -456,6 +456,40 @@ void BKE_modifier_set_error(const Object *ob, ModifierData *md, const char *_for CLOG_ERROR(&LOG, "Object: \"%s\", Modifier: \"%s\", %s", ob->id.name + 2, md->name, md->error); } +void BKE_modifier_set_warning(const struct Object *ob, + struct ModifierData *md, + const char *_format, + ...) +{ + char buffer[512]; + va_list ap; + const char *format = TIP_(_format); + + va_start(ap, _format); + vsnprintf(buffer, sizeof(buffer), format, ap); + va_end(ap); + buffer[sizeof(buffer) - 1] = '\0'; + + /* Store the warning in the same field as the error. + * It is not expected to have both error and warning and having a single place to store the + * message simplifies interface code. */ + + if (md->error) { + MEM_freeN(md->error); + } + + md->error = BLI_strdup(buffer); + +#ifndef NDEBUG + if ((md->mode & eModifierMode_Virtual) == 0) { + /* Ensure correct object is passed in. */ + BLI_assert(BKE_modifier_get_original(ob, md) != NULL); + } +#endif + + UNUSED_VARS_NDEBUG(ob); +} + int BKE_modifiers_get_cage_index(const Scene *scene, Object *ob, int *r_lastPossibleCageIndex, diff --git a/source/blender/blenkernel/intern/object.cc b/source/blender/blenkernel/intern/object.cc index f7436b6112c..62ebb45b0ed 100644 --- a/source/blender/blenkernel/intern/object.cc +++ b/source/blender/blenkernel/intern/object.cc @@ -1402,6 +1402,7 @@ bool BKE_object_supports_modifiers(const Object *ob) { return (ELEM(ob->type, OB_MESH, + OB_CURVES, OB_CURVES_LEGACY, OB_SURF, OB_FONT, diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c index 89cc25b31e6..9b0d15ac702 100644 --- a/source/blender/blenkernel/intern/paint.c +++ b/source/blender/blenkernel/intern/paint.c @@ -996,6 +996,12 @@ bool BKE_paint_select_elem_test(Object *ob) return (BKE_paint_select_vert_test(ob) || BKE_paint_select_face_test(ob)); } +bool BKE_paint_always_hide_test(Object *ob) +{ + return ((ob != NULL) && (ob->type == OB_MESH) && (ob->data != NULL) && + (ob->mode & OB_MODE_WEIGHT_PAINT || ob->mode & OB_MODE_VERTEX_PAINT)); +} + void BKE_paint_cavity_curve_preset(Paint *p, int preset) { CurveMapping *cumap = NULL; @@ -2255,12 +2261,7 @@ PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob) return NULL; } - bool respect_hide = true; - if (ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT)) { - if (!(BKE_paint_select_vert_test(ob) || BKE_paint_select_face_test(ob))) { - respect_hide = false; - } - } + const bool respect_hide = true; PBVH *pbvh = ob->sculpt->pbvh; if (pbvh != NULL) { diff --git a/source/blender/blenkernel/intern/pointcloud.cc b/source/blender/blenkernel/intern/pointcloud.cc index e38c20d8eb7..261b053e4f9 100644 --- a/source/blender/blenkernel/intern/pointcloud.cc +++ b/source/blender/blenkernel/intern/pointcloud.cc @@ -68,7 +68,6 @@ static void pointcloud_init_data(ID *id) nullptr, pointcloud->totpoint, POINTCLOUD_ATTR_POSITION); - BKE_pointcloud_update_customdata_pointers(pointcloud); } static void pointcloud_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, const int flag) @@ -83,7 +82,6 @@ static void pointcloud_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_s CD_MASK_ALL, alloc_type, pointcloud_dst->totpoint); - BKE_pointcloud_update_customdata_pointers(pointcloud_dst); pointcloud_dst->batch_cache = nullptr; } @@ -138,7 +136,6 @@ static void pointcloud_blend_read_data(BlendDataReader *reader, ID *id) /* Geometry */ CustomData_blend_read(reader, &pointcloud->pdata, pointcloud->totpoint); - BKE_pointcloud_update_customdata_pointers(pointcloud); /* Materials */ BLO_read_pointer_array(reader, (void **)&pointcloud->mat); @@ -194,17 +191,28 @@ static void pointcloud_random(PointCloud *pointcloud) { pointcloud->totpoint = 400; CustomData_realloc(&pointcloud->pdata, pointcloud->totpoint); - BKE_pointcloud_update_customdata_pointers(pointcloud); RNG *rng = BLI_rng_new(0); - for (int i = 0; i < pointcloud->totpoint; i++) { - pointcloud->co[i][0] = 2.0f * BLI_rng_get_float(rng) - 1.0f; - pointcloud->co[i][1] = 2.0f * BLI_rng_get_float(rng) - 1.0f; - pointcloud->co[i][2] = 2.0f * BLI_rng_get_float(rng) - 1.0f; - pointcloud->radius[i] = 0.05f * BLI_rng_get_float(rng); + blender::bke::MutableAttributeAccessor attributes = + blender::bke::pointcloud_attributes_for_write(*pointcloud); + blender::bke::SpanAttributeWriter positions = + attributes.lookup_or_add_for_write_only_span<float3>(POINTCLOUD_ATTR_POSITION, + ATTR_DOMAIN_POINT); + blender::bke::SpanAttributeWriter<float> radii = + attributes.lookup_or_add_for_write_only_span<float>(POINTCLOUD_ATTR_RADIUS, + ATTR_DOMAIN_POINT); + + for (const int i : positions.span.index_range()) { + positions.span[i] = + float3(BLI_rng_get_float(rng), BLI_rng_get_float(rng), BLI_rng_get_float(rng)) * 2.0f - + 1.0f; + radii.span[i] = 0.05f * BLI_rng_get_float(rng); } + positions.finish(); + radii.finish(); + BLI_rng_free(rng); } @@ -250,7 +258,6 @@ PointCloud *BKE_pointcloud_new_nomain(const int totpoint) pointcloud->totpoint = totpoint; CustomData_realloc(&pointcloud->pdata, pointcloud->totpoint); - BKE_pointcloud_update_customdata_pointers(pointcloud); return pointcloud; } @@ -258,10 +265,14 @@ PointCloud *BKE_pointcloud_new_nomain(const int totpoint) static std::optional<blender::bounds::MinMaxResult<float3>> point_cloud_bounds( const PointCloud &pointcloud) { - Span<float3> positions{reinterpret_cast<float3 *>(pointcloud.co), pointcloud.totpoint}; - if (pointcloud.radius) { - Span<float> radii{pointcloud.radius, pointcloud.totpoint}; - return blender::bounds::min_max_with_radii(positions, radii); + blender::bke::AttributeAccessor attributes = blender::bke::pointcloud_attributes(pointcloud); + blender::VArraySpan<float3> positions = attributes.lookup_or_default<float3>( + POINTCLOUD_ATTR_POSITION, ATTR_DOMAIN_POINT, float3(0)); + blender::VArray<float> radii = attributes.lookup_or_default<float>( + POINTCLOUD_ATTR_RADIUS, ATTR_DOMAIN_POINT, 0.0f); + + if (!(radii.is_single() && radii.get_internal_single() == 0.0f)) { + return blender::bounds::min_max_with_radii(positions, radii.get_internal_span()); } return blender::bounds::min_max(positions); } @@ -307,14 +318,6 @@ BoundBox *BKE_pointcloud_boundbox_get(Object *ob) return ob->runtime.bb; } -void BKE_pointcloud_update_customdata_pointers(PointCloud *pointcloud) -{ - pointcloud->co = static_cast<float(*)[3]>( - CustomData_get_layer_named(&pointcloud->pdata, CD_PROP_FLOAT3, POINTCLOUD_ATTR_POSITION)); - pointcloud->radius = static_cast<float *>( - CustomData_get_layer_named(&pointcloud->pdata, CD_PROP_FLOAT, POINTCLOUD_ATTR_RADIUS)); -} - bool BKE_pointcloud_customdata_required(const PointCloud *UNUSED(pointcloud), const char *name) { return STREQ(name, POINTCLOUD_ATTR_POSITION); @@ -334,7 +337,6 @@ PointCloud *BKE_pointcloud_new_for_eval(const PointCloud *pointcloud_src, int to pointcloud_dst->totpoint = totpoint; CustomData_copy( &pointcloud_src->pdata, &pointcloud_dst->pdata, CD_MASK_ALL, CD_CALLOC, totpoint); - BKE_pointcloud_update_customdata_pointers(pointcloud_dst); return pointcloud_dst; } diff --git a/source/blender/blenkernel/intern/scene.cc b/source/blender/blenkernel/intern/scene.cc index 39157bc9890..e2da27fc840 100644 --- a/source/blender/blenkernel/intern/scene.cc +++ b/source/blender/blenkernel/intern/scene.cc @@ -2952,6 +2952,20 @@ int BKE_scene_num_threads(const Scene *scene) return BKE_render_num_threads(&scene->r); } +void BKE_render_resolution(const struct RenderData *r, + const bool use_crop, + int *r_width, + int *r_height) +{ + *r_width = (r->xsch * r->size) / 100; + *r_height = (r->ysch * r->size) / 100; + + if (use_crop && (r->mode & R_BORDER) && (r->mode & R_CROP)) { + *r_width *= BLI_rctf_size_x(&r->border); + *r_height *= BLI_rctf_size_y(&r->border); + } +} + int BKE_render_preview_pixel_size(const RenderData *r) { if (r->preview_pixel_size == 0) { diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c index 12dc1b6d1fa..c16e5ce5655 100644 --- a/source/blender/blenkernel/intern/screen.c +++ b/source/blender/blenkernel/intern/screen.c @@ -277,7 +277,7 @@ static void screen_blend_read_lib(BlendLibReader *reader, ID *id) IDTypeInfo IDType_ID_SCR = { .id_code = ID_SCR, - .id_filter = 0, + .id_filter = FILTER_ID_SCR, .main_listbase_index = INDEX_ID_SCR, .struct_size = sizeof(bScreen), .name = "Screen", diff --git a/source/blender/blenkernel/intern/subdiv_eval.c b/source/blender/blenkernel/intern/subdiv_eval.c index fda833ffd27..841b47818f1 100644 --- a/source/blender/blenkernel/intern/subdiv_eval.c +++ b/source/blender/blenkernel/intern/subdiv_eval.c @@ -34,8 +34,8 @@ static eOpenSubdivEvaluator opensubdiv_evalutor_from_subdiv_evaluator_type( case SUBDIV_EVALUATOR_TYPE_CPU: { return OPENSUBDIV_EVALUATOR_CPU; } - case SUBDIV_EVALUATOR_TYPE_GLSL_COMPUTE: { - return OPENSUBDIV_EVALUATOR_GLSL_COMPUTE; + case SUBDIV_EVALUATOR_TYPE_GPU: { + return OPENSUBDIV_EVALUATOR_GPU; } } BLI_assert_msg(0, "Unknown evaluator type"); diff --git a/source/blender/blenkernel/intern/subdiv_modifier.c b/source/blender/blenkernel/intern/subdiv_modifier.c index 57af0192d59..2271fd90bda 100644 --- a/source/blender/blenkernel/intern/subdiv_modifier.c +++ b/source/blender/blenkernel/intern/subdiv_modifier.c @@ -98,11 +98,6 @@ static bool is_subdivision_evaluation_possible_on_gpu(void) return false; } - const int available_evaluators = openSubdiv_getAvailableEvaluators(); - if ((available_evaluators & OPENSUBDIV_EVALUATOR_GLSL_COMPUTE) == 0) { - return false; - } - return true; } diff --git a/source/blender/blenkernel/intern/writeffmpeg.c b/source/blender/blenkernel/intern/writeffmpeg.c index 5e11cd0703a..883591e3e87 100644 --- a/source/blender/blenkernel/intern/writeffmpeg.c +++ b/source/blender/blenkernel/intern/writeffmpeg.c @@ -1485,7 +1485,7 @@ void BKE_ffmpeg_preset_set(RenderData *rd, int preset) } } -void BKE_ffmpeg_image_type_verify(RenderData *rd, ImageFormatData *imf) +void BKE_ffmpeg_image_type_verify(RenderData *rd, const ImageFormatData *imf) { int audio = 0; diff --git a/source/blender/blenlib/BLI_bitmap.h b/source/blender/blenlib/BLI_bitmap.h index 19d8525311c..973cc5c3d1e 100644 --- a/source/blender/blenlib/BLI_bitmap.h +++ b/source/blender/blenlib/BLI_bitmap.h @@ -27,7 +27,7 @@ typedef unsigned int BLI_bitmap; /** * Number of blocks needed to hold '_num' bits. */ -#define _BITMAP_NUM_BLOCKS(_num) (((_num) >> _BITMAP_POWER) + 1) +#define _BITMAP_NUM_BLOCKS(_num) (((_num) + _BITMAP_MASK) >> _BITMAP_POWER) /** * Size (in bytes) used to hold '_num' bits. @@ -54,6 +54,11 @@ typedef unsigned int BLI_bitmap; ((BLI_bitmap *)BLI_memarena_calloc(_mem, BLI_BITMAP_SIZE(_num)))) /** + * Declares a bitmap as a variable. + */ +#define BLI_BITMAP_DECLARE(_name, _num) BLI_bitmap _name[_BITMAP_NUM_BLOCKS(_num)] = {} + +/** * Get the value of a single bit at '_index'. */ #define BLI_BITMAP_TEST(_bitmap, _index) \ @@ -137,6 +142,12 @@ void BLI_bitmap_and_all(BLI_bitmap *dst, const BLI_bitmap *src, size_t bits); */ void BLI_bitmap_or_all(BLI_bitmap *dst, const BLI_bitmap *src, size_t bits); +/** + * Find index of the lowest unset bit. + * Returns -1 if all the bits are set. + */ +int BLI_bitmap_find_first_unset(const BLI_bitmap *bitmap, size_t bits); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenlib/BLI_index_range.hh b/source/blender/blenlib/BLI_index_range.hh index bd0a7e5bb7a..6fcc560d856 100644 --- a/source/blender/blenlib/BLI_index_range.hh +++ b/source/blender/blenlib/BLI_index_range.hh @@ -198,6 +198,16 @@ class IndexRange { } /** + * Get the element one before the beginning. The returned value is undefined when the range is + * empty, and the range must start after zero already. + */ + constexpr int64_t one_before_start() const + { + BLI_assert(start_ > 0); + return start_ - 1; + } + + /** * Get the element one after the end. The returned value is undefined when the range is empty. */ constexpr int64_t one_after_last() const diff --git a/source/blender/blenlib/BLI_math_matrix.h b/source/blender/blenlib/BLI_math_matrix.h index 2cd2a299d53..c2dafbe3a1a 100644 --- a/source/blender/blenlib/BLI_math_matrix.h +++ b/source/blender/blenlib/BLI_math_matrix.h @@ -238,6 +238,7 @@ bool invert_m3_ex(float m[3][3], float epsilon); bool invert_m3_m3_ex(float m1[3][3], const float m2[3][3], float epsilon); bool invert_m3(float R[3][3]); +bool invert_m2_m2(float R[2][2], const float A[2][2]); bool invert_m3_m3(float R[3][3], const float A[3][3]); bool invert_m4(float R[4][4]); bool invert_m4_m4(float R[4][4], const float A[4][4]); diff --git a/source/blender/blenlib/BLI_math_rotation.hh b/source/blender/blenlib/BLI_math_rotation.hh index e8b746b34df..50a062162ad 100644 --- a/source/blender/blenlib/BLI_math_rotation.hh +++ b/source/blender/blenlib/BLI_math_rotation.hh @@ -15,4 +15,13 @@ namespace blender::math { */ float3 rotate_direction_around_axis(const float3 &direction, const float3 &axis, float angle); +/** + * Rotate any arbitrary \a vector around the \a center position, with a unit-length \a axis + * and the specified \a angle. + */ +float3 rotate_around_axis(const float3 &vector, + const float3 ¢er, + const float3 &axis, + float angle); + } // namespace blender::math diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index 95b4987596e..d39a586206f 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -424,6 +424,7 @@ if(WITH_GTESTS) tests/BLI_array_store_test.cc tests/BLI_array_test.cc tests/BLI_array_utils_test.cc + tests/BLI_bitmap_test.cc tests/BLI_bounds_test.cc tests/BLI_color_test.cc tests/BLI_cpp_type_test.cc diff --git a/source/blender/blenlib/intern/bitmap.c b/source/blender/blenlib/intern/bitmap.c index 7fcbc31c066..2cc2fbc3e2f 100644 --- a/source/blender/blenlib/intern/bitmap.c +++ b/source/blender/blenlib/intern/bitmap.c @@ -11,6 +11,7 @@ #include <string.h> #include "BLI_bitmap.h" +#include "BLI_math_bits.h" #include "BLI_utildefines.h" void BLI_bitmap_set_all(BLI_bitmap *bitmap, bool set, size_t bits) @@ -46,3 +47,22 @@ void BLI_bitmap_or_all(BLI_bitmap *dst, const BLI_bitmap *src, size_t bits) dst[i] |= src[i]; } } + +int BLI_bitmap_find_first_unset(const BLI_bitmap *bitmap, const size_t bits) +{ + const size_t blocks_num = _BITMAP_NUM_BLOCKS(bits); + int result = -1; + /* Skip over completely set blocks. */ + int index = 0; + while (index < blocks_num && bitmap[index] == ~0u) { + index++; + } + if (index < blocks_num) { + /* Found a partially used block: find the lowest unused bit. */ + const uint m = ~bitmap[index]; + BLI_assert(m != 0); + const uint bit_index = bitscan_forward_uint(m); + result = bit_index + (index << _BITMAP_POWER); + } + return result; +} diff --git a/source/blender/blenlib/intern/filereader_zstd.c b/source/blender/blenlib/intern/filereader_zstd.c index 5f114f24fb0..aeb000e9754 100644 --- a/source/blender/blenlib/intern/filereader_zstd.c +++ b/source/blender/blenlib/intern/filereader_zstd.c @@ -281,7 +281,10 @@ static void zstd_close(FileReader *reader) if (zstd->reader.seek) { MEM_freeN(zstd->seek.uncompressed_ofs); MEM_freeN(zstd->seek.compressed_ofs); - MEM_freeN(zstd->seek.cached_content); + /* When an error has occurred this may be NULL, see: T99744. */ + if (zstd->seek.cached_content) { + MEM_freeN(zstd->seek.cached_content); + } } else { MEM_freeN((void *)zstd->in_buf.src); diff --git a/source/blender/blenlib/intern/generic_virtual_array.cc b/source/blender/blenlib/intern/generic_virtual_array.cc index 28bc66e8524..1e548d006b2 100644 --- a/source/blender/blenlib/intern/generic_virtual_array.cc +++ b/source/blender/blenlib/intern/generic_virtual_array.cc @@ -323,6 +323,7 @@ GVArraySpan::GVArraySpan(GVArraySpan &&other) else { data_ = owned_data_; } + other.owned_data_ = nullptr; other.data_ = nullptr; other.size_ = 0; } @@ -393,6 +394,7 @@ GMutableVArraySpan::GMutableVArraySpan(GMutableVArraySpan &&other) else { data_ = owned_data_; } + other.owned_data_ = nullptr; other.data_ = nullptr; other.size_ = 0; } diff --git a/source/blender/blenlib/intern/index_mask.cc b/source/blender/blenlib/intern/index_mask.cc index f3590e4a41c..e9af183d60d 100644 --- a/source/blender/blenlib/intern/index_mask.cc +++ b/source/blender/blenlib/intern/index_mask.cc @@ -142,6 +142,7 @@ IndexMask find_indices_based_on_predicate__merge( int64_t result_mask_size = 0; for (Vector<Vector<int64_t>> &local_sub_masks : sub_masks) { for (Vector<int64_t> &sub_mask : local_sub_masks) { + BLI_assert(!sub_mask.is_empty()); all_vectors.append(&sub_mask); result_mask_size += sub_mask.size(); } @@ -232,7 +233,9 @@ IndexMask find_indices_from_virtual_array(const IndexMask indices_to_check, } } }); - sub_masks.local().append(std::move(masked_indices)); + if (!masked_indices.is_empty()) { + sub_masks.local().append(std::move(masked_indices)); + } }); return detail::find_indices_based_on_predicate__merge(indices_to_check, sub_masks, r_indices); diff --git a/source/blender/blenlib/intern/math_matrix.c b/source/blender/blenlib/intern/math_matrix.c index ce9abc36cad..fcd017b3082 100644 --- a/source/blender/blenlib/intern/math_matrix.c +++ b/source/blender/blenlib/intern/math_matrix.c @@ -1116,6 +1116,22 @@ double determinant_m3_array_db(const double m[3][3]) m[2][0] * (m[0][1] * m[1][2] - m[0][2] * m[1][1])); } +bool invert_m2_m2(float m1[2][2], const float m2[2][2]) +{ + adjoint_m2_m2(m1, m2); + float det = determinant_m2(m2[0][0], m2[1][0], m2[0][1], m2[1][1]); + + bool success = (det != 0.0f); + if (success) { + m1[0][0] /= det; + m1[1][0] /= det; + m1[0][1] /= det; + m1[1][1] /= det; + } + + return success; +} + bool invert_m3_ex(float m[3][3], const float epsilon) { float tmp[3][3]; diff --git a/source/blender/blenlib/intern/math_rotation.cc b/source/blender/blenlib/intern/math_rotation.cc index 74300d55954..091e8af85d9 100644 --- a/source/blender/blenlib/intern/math_rotation.cc +++ b/source/blender/blenlib/intern/math_rotation.cc @@ -23,4 +23,17 @@ float3 rotate_direction_around_axis(const float3 &direction, const float3 &axis, return axis_scaled + diff * std::cos(angle) + cross * std::sin(angle); } +float3 rotate_around_axis(const float3 &vector, + const float3 ¢er, + const float3 &axis, + const float angle) + +{ + float3 result = vector - center; + float mat[3][3]; + axis_angle_normalized_to_mat3(mat, axis, angle); + mul_m3_v3(mat, result); + return result + center; +} + } // namespace blender::math diff --git a/source/blender/blenlib/intern/rct.c b/source/blender/blenlib/intern/rct.c index 0bb606c288e..7248db5b718 100644 --- a/source/blender/blenlib/intern/rct.c +++ b/source/blender/blenlib/intern/rct.c @@ -265,7 +265,7 @@ bool BLI_rcti_isect_segment(const rcti *rect, const int s1[2], const int s2[2]) /* diagonal: [/] */ tvec1[0] = rect->xmin; tvec1[1] = rect->ymin; - tvec2[0] = rect->xmin; + tvec2[0] = rect->xmax; tvec2[1] = rect->ymax; if (isect_segments_i(s1, s2, tvec1, tvec2)) { return true; @@ -311,7 +311,7 @@ bool BLI_rctf_isect_segment(const rctf *rect, const float s1[2], const float s2[ /* diagonal: [/] */ tvec1[0] = rect->xmin; tvec1[1] = rect->ymin; - tvec2[0] = rect->xmin; + tvec2[0] = rect->xmax; tvec2[1] = rect->ymax; if (isect_segments_fl(s1, s2, tvec1, tvec2)) { return true; diff --git a/source/blender/blenlib/tests/BLI_bitmap_test.cc b/source/blender/blenlib/tests/BLI_bitmap_test.cc new file mode 100644 index 00000000000..fb9e03e3136 --- /dev/null +++ b/source/blender/blenlib/tests/BLI_bitmap_test.cc @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "BLI_bitmap.h" +#include "testing/testing.h" + +namespace blender::tests { + +TEST(bitmap, empty_is_all_unset) +{ + BLI_BITMAP_DECLARE(bitmap, 10); + for (int i = 0; i < 10; ++i) { + EXPECT_FALSE(BLI_BITMAP_TEST_BOOL(bitmap, i)); + } +} + +TEST(bitmap, find_first_unset_empty) +{ + BLI_BITMAP_DECLARE(bitmap, 10); + EXPECT_EQ(0, BLI_bitmap_find_first_unset(bitmap, 10)); +} + +TEST(bitmap, find_first_unset_full) +{ + BLI_BITMAP_DECLARE(bitmap, 10); + BLI_bitmap_flip_all(bitmap, 10); + EXPECT_EQ(-1, BLI_bitmap_find_first_unset(bitmap, 10)); +} + +TEST(bitmap, find_first_unset_middle) +{ + BLI_BITMAP_DECLARE(bitmap, 100); + BLI_bitmap_flip_all(bitmap, 100); + /* Turn some bits off */ + BLI_BITMAP_DISABLE(bitmap, 53); + BLI_BITMAP_DISABLE(bitmap, 81); + BLI_BITMAP_DISABLE(bitmap, 85); + BLI_BITMAP_DISABLE(bitmap, 86); + + /* Find lowest unset bit, and set it. */ + EXPECT_EQ(53, BLI_bitmap_find_first_unset(bitmap, 100)); + BLI_BITMAP_ENABLE(bitmap, 53); + /* Now should find the next lowest bit. */ + EXPECT_EQ(81, BLI_bitmap_find_first_unset(bitmap, 100)); +} + +} // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_index_range_test.cc b/source/blender/blenlib/tests/BLI_index_range_test.cc index 10f6784cd44..f5b994d409a 100644 --- a/source/blender/blenlib/tests/BLI_index_range_test.cc +++ b/source/blender/blenlib/tests/BLI_index_range_test.cc @@ -105,6 +105,12 @@ TEST(index_range, OneAfterEnd) EXPECT_EQ(range.one_after_last(), 8); } +TEST(index_range, OneBeforeStart) +{ + IndexRange range = IndexRange(5, 3); + EXPECT_EQ(range.one_before_start(), 4); +} + TEST(index_range, Start) { IndexRange range = IndexRange(6, 2); diff --git a/source/blender/blenloader/intern/versioning_legacy.c b/source/blender/blenloader/intern/versioning_legacy.c index a3f17878f68..75cc333e4b5 100644 --- a/source/blender/blenloader/intern/versioning_legacy.c +++ b/source/blender/blenloader/intern/versioning_legacy.c @@ -1468,7 +1468,7 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *bmain) for (me = bmain->meshes.first; me; me = me->id.next) { if (!me->medge) { - BKE_mesh_calc_edges_legacy(me, true); /* true = use mface->edcode */ + BKE_mesh_calc_edges_legacy(me, true); /* true = use #MFace.edcode. */ } else { BKE_mesh_strip_loose_faces(me); diff --git a/source/blender/bmesh/intern/bmesh_log.c b/source/blender/bmesh/intern/bmesh_log.c index a81ae934629..64e6c63e9f0 100644 --- a/source/blender/bmesh/intern/bmesh_log.c +++ b/source/blender/bmesh/intern/bmesh_log.c @@ -34,42 +34,41 @@ struct BMLogEntry { struct BMLogEntry *next, *prev; - /* The following GHashes map from an element ID to one of the log - * types above */ + /* The following #GHash members map from an element ID to one of the log types above. */ - /* Elements that were in the previous entry, but have been - * deleted */ + /** Elements that were in the previous entry, but have been deleted. */ GHash *deleted_verts; GHash *deleted_faces; - /* Elements that were not in the previous entry, but are in the - * result of this entry */ + /** Elements that were not in the previous entry, but are in the result of this entry. */ GHash *added_verts; GHash *added_faces; - /* Vertices whose coordinates, mask value, or hflag have changed */ + /** Vertices whose coordinates, mask value, or hflag have changed. */ GHash *modified_verts; GHash *modified_faces; BLI_mempool *pool_verts; BLI_mempool *pool_faces; - /* This is only needed for dropping BMLogEntries while still in + /** + * This is only needed for dropping BMLogEntries while still in * dynamic-topology mode, as that should release vert/face IDs - * back to the BMLog but no BMLog pointer is available at that - * time. + * back to the BMLog but no BMLog pointer is available at that time. * * This field is not guaranteed to be valid, any use of it should - * check for NULL. */ + * check for NULL. + */ BMLog *log; }; struct BMLog { - /* Tree of free IDs */ + /** Tree of free IDs */ struct RangeTreeUInt *unused_ids; - /* Mapping from unique IDs to vertices and faces + /** + * Mapping from unique IDs to vertices and faces * - * Each vertex and face in the log gets a unique uinteger + * Each vertex and face in the log gets a unique `uint` * assigned. That ID is taken from the set managed by the * unused_ids range tree. * @@ -79,10 +78,11 @@ struct BMLog { GHash *id_to_elem; GHash *elem_to_id; - /* All BMLogEntrys, ordered from earliest to most recent */ + /** All #BMLogEntrys, ordered from earliest to most recent. */ ListBase entries; - /* The current log entry from entries list + /** + * The current log entry from entries list * * If null, then the original mesh from before any of the log * entries is current (i.e. there is nothing left to undo.) diff --git a/source/blender/bmesh/intern/bmesh_log.h b/source/blender/bmesh/intern/bmesh_log.h index 189aa97509f..5daa5dd9a68 100644 --- a/source/blender/bmesh/intern/bmesh_log.h +++ b/source/blender/bmesh/intern/bmesh_log.h @@ -14,12 +14,13 @@ struct RangeTreeUInt; typedef struct BMLog BMLog; typedef struct BMLogEntry BMLogEntry; -/* Allocate and initialize a new BMLog */ -/* Allocate, initialize, and assign a new BMLog */ +/** + * Allocate, initialize, and assign a new BMLog. + */ BMLog *BM_log_create(BMesh *bm); -/* Allocate and initialize a new BMLog using existing BMLogEntries */ -/* Allocate and initialize a new BMLog using existing BMLogEntries +/** + * Allocate and initialize a new #BMLog using existing #BMLogEntries * * The 'entry' should be the last entry in the BMLog. Its prev pointer * will be followed back to find the first entry. @@ -29,20 +30,21 @@ BMLog *BM_log_create(BMesh *bm); */ BMLog *BM_log_from_existing_entries_create(BMesh *bm, BMLogEntry *entry); -/* Free all the data in a BMLog including the log itself */ -/* Free all the data in a BMLog including the log itself */ +/** + * Free all the data in a BMLog including the log itself. + */ void BM_log_free(BMLog *log); -/* Get the number of log entries */ -/* Get the number of log entries */ +/** + * Get the number of log entries. + */ int BM_log_length(const BMLog *log); -/* Apply a consistent ordering to BMesh vertices and faces */ -/* Apply a consistent ordering to BMesh vertices */ +/** Apply a consistent ordering to BMesh vertices and faces. */ void BM_log_mesh_elems_reorder(BMesh *bm, BMLog *log); -/* Start a new log entry and update the log entry list */ -/* Start a new log entry and update the log entry list +/** + * Start a new log entry and update the log entry list. * * If the log entry list is empty, or if the current log entry is the * last entry, the new entry is simply appended to the end. @@ -54,35 +56,36 @@ void BM_log_mesh_elems_reorder(BMesh *bm, BMLog *log); */ BMLogEntry *BM_log_entry_add(BMLog *log); -/* Mark all used ids as unused for this node */ +/** Mark all used ids as unused for this node */ void BM_log_cleanup_entry(BMLogEntry *entry); -/* Remove an entry from the log */ -/* Remove an entry from the log +/** + * Remove an entry from the log. * * Uses entry->log as the log. If the log is NULL, the entry will be - * free'd but not removed from any list, nor shall its IDs be - * released. + * free'd but not removed from any list, nor shall its IDs be released. * * This operation is only valid on the first and last entries in the * log. Deleting from the middle will assert. */ void BM_log_entry_drop(BMLogEntry *entry); -/* Undo one BMLogEntry */ -/* Undo one BMLogEntry +/** + * Undo one #BMLogEntry. * - * Has no effect if there's nothing left to undo */ + * Has no effect if there's nothing left to undo. + */ void BM_log_undo(BMesh *bm, BMLog *log); -/* Redo one BMLogEntry */ -/* Redo one BMLogEntry +/** + * Redo one #BMLogEntry. * - * Has no effect if there's nothing left to redo */ + * Has no effect if there's nothing left to redo. + */ void BM_log_redo(BMesh *bm, BMLog *log); -/* Log a vertex before it is modified */ -/* Log a vertex before it is modified +/** + * Log a vertex before it is modified. * * Before modifying vertex coordinates, masks, or hflags, call this * function to log its current values. This is better than logging @@ -107,8 +110,8 @@ void BM_log_redo(BMesh *bm, BMLog *log); */ void BM_log_vert_before_modified(BMLog *log, struct BMVert *v, int cd_vert_mask_offset); -/* Log a new vertex as added to the BMesh */ -/* Log a new vertex as added to the BMesh +/** + * Log a new vertex as added to the #BMesh. * * The new vertex gets a unique ID assigned. It is then added to a map * of added vertices, with the key being its ID and the value @@ -116,16 +119,16 @@ void BM_log_vert_before_modified(BMLog *log, struct BMVert *v, int cd_vert_mask_ */ void BM_log_vert_added(BMLog *log, struct BMVert *v, int cd_vert_mask_offset); -/* Log a face before it is modified */ -/* Log a face before it is modified +/** + * Log a face before it is modified. * * This is intended to handle only header flags and we always - * assume face has been added before + * assume face has been added before. */ void BM_log_face_modified(BMLog *log, struct BMFace *f); -/* Log a new face as added to the BMesh */ -/* Log a new face as added to the BMesh +/** + * Log a new face as added to the #BMesh. * * The new face gets a unique ID assigned. It is then added to a map * of added faces, with the key being its ID and the value containing @@ -133,8 +136,8 @@ void BM_log_face_modified(BMLog *log, struct BMFace *f); */ void BM_log_face_added(BMLog *log, struct BMFace *f); -/* Log a vertex as removed from the BMesh */ -/* Log a vertex as removed from the BMesh +/** + * Log a vertex as removed from the #BMesh. * * A couple things can happen here: * @@ -142,7 +145,7 @@ void BM_log_face_added(BMLog *log, struct BMFace *f); * deleted and forgotten about entirely. Its unique ID is returned to * the unused pool. * - * If the vertex was already part of the BMesh before the current log + * If the vertex was already part of the #BMesh before the current log * entry, it is added to a map of deleted vertices, with the key being * its ID and the value containing everything needed to reconstruct * that vertex. @@ -152,8 +155,8 @@ void BM_log_face_added(BMLog *log, struct BMFace *f); */ void BM_log_vert_removed(BMLog *log, struct BMVert *v, int cd_vert_mask_offset); -/* Log a face as removed from the BMesh */ -/* Log a face as removed from the BMesh +/** + * Log a face as removed from the #BMesh. * * A couple things can happen here: * @@ -161,43 +164,45 @@ void BM_log_vert_removed(BMLog *log, struct BMVert *v, int cd_vert_mask_offset); * deleted and forgotten about entirely. Its unique ID is returned to * the unused pool. * - * If the face was already part of the BMesh before the current log + * If the face was already part of the #BMesh before the current log * entry, it is added to a map of deleted faces, with the key being * its ID and the value containing everything needed to reconstruct * that face. */ void BM_log_face_removed(BMLog *log, struct BMFace *f); -/* Log all vertices/faces in the BMesh as added */ -/* Log all vertices/faces in the BMesh as added */ +/** + * Log all vertices/faces in the #BMesh as added. + */ void BM_log_all_added(BMesh *bm, BMLog *log); -/* Log all vertices/faces in the BMesh as removed */ -/* Log all vertices/faces in the BMesh as removed */ +/** Log all vertices/faces in the #BMesh as removed. */ void BM_log_before_all_removed(BMesh *bm, BMLog *log); -/* Get the logged coordinates of a vertex */ -/* Get the logged coordinates of a vertex +/** + * Get the logged coordinates of a vertex. * - * Does not modify the log or the vertex */ + * Does not modify the log or the vertex. + */ const float *BM_log_original_vert_co(BMLog *log, BMVert *v); -/* Get the logged normal of a vertex +/** + * Get the logged normal of a vertex * - * Does not modify the log or the vertex */ + * Does not modify the log or the vertex. + */ const float *BM_log_original_vert_no(BMLog *log, BMVert *v); -/* Get the logged mask of a vertex */ -/* Get the logged mask of a vertex +/** Get the logged mask of a vertex * - * Does not modify the log or the vertex */ + * Does not modify the log or the vertex. + */ float BM_log_original_mask(BMLog *log, BMVert *v); -/* Get the logged data of a vertex (avoid multiple lookups) */ +/** Get the logged data of a vertex (avoid multiple lookups). */ void BM_log_original_vert_data(BMLog *log, BMVert *v, const float **r_co, const float **r_no); -/* For internal use only (unit testing) */ -/* For internal use only (unit testing) */ +/** For internal use only (unit testing). */ BMLogEntry *BM_log_current_entry(BMLog *log); -/* For internal use only (unit testing) */ +/** For internal use only (unit testing) */ struct RangeTreeUInt *BM_log_unused_ids(BMLog *log); diff --git a/source/blender/compositor/operations/COM_CompositorOperation.cc b/source/blender/compositor/operations/COM_CompositorOperation.cc index e8b61ff229e..c0cc3fa1ba4 100644 --- a/source/blender/compositor/operations/COM_CompositorOperation.cc +++ b/source/blender/compositor/operations/COM_CompositorOperation.cc @@ -5,6 +5,7 @@ #include "BKE_global.h" #include "BKE_image.h" +#include "BKE_scene.h" #include "RE_pipeline.h" @@ -164,8 +165,8 @@ void CompositorOperation::execute_region(rcti *rect, unsigned int /*tile_number* * Full frame */ - int full_width = rd->xsch * rd->size / 100; - int full_height = rd->ysch * rd->size / 100; + int full_width, full_height; + BKE_render_resolution(rd, false, &full_width, &full_height); dx = rd->border.xmin * full_width - (full_width - this->get_width()) / 2.0f; dy = rd->border.ymin * full_height - (full_height - this->get_height()) / 2.0f; @@ -214,8 +215,8 @@ void CompositorOperation::update_memory_buffer_partial(MemoryBuffer *UNUSED(outp void CompositorOperation::determine_canvas(const rcti &UNUSED(preferred_area), rcti &r_area) { - int width = rd_->xsch * rd_->size / 100; - int height = rd_->ysch * rd_->size / 100; + int width, height; + BKE_render_resolution(rd_, false, &width, &height); /* Check actual render resolution with cropping it may differ with cropped border.rendering * Fix for T31777 Border Crop gives black (easy). */ diff --git a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc index 341541b4cdd..760ed94f882 100644 --- a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc +++ b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc @@ -21,7 +21,7 @@ OutputOpenExrSingleLayerMultiViewOperation::OutputOpenExrSingleLayerMultiViewOpe const RenderData *rd, const bNodeTree *tree, DataType datatype, - ImageFormatData *format, + const ImageFormatData *format, const char *path, const char *view_name, const bool save_as_render) @@ -243,7 +243,7 @@ OutputStereoOperation::OutputStereoOperation(const Scene *scene, const RenderData *rd, const bNodeTree *tree, DataType datatype, - ImageFormatData *format, + const ImageFormatData *format, const char *path, const char *name, const char *view_name, diff --git a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h index 69f4011d340..e36999e5cf1 100644 --- a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h +++ b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h @@ -22,7 +22,7 @@ class OutputOpenExrSingleLayerMultiViewOperation : public OutputSingleLayerOpera const RenderData *rd, const bNodeTree *tree, DataType datatype, - ImageFormatData *format, + const ImageFormatData *format, const char *path, const char *view_name, bool save_as_render); @@ -57,7 +57,7 @@ class OutputStereoOperation : public OutputSingleLayerOperation { const RenderData *rd, const bNodeTree *tree, DataType datatype, - struct ImageFormatData *format, + const struct ImageFormatData *format, const char *path, const char *name, const char *view_name, diff --git a/source/blender/compositor/operations/COM_OutputFileOperation.cc b/source/blender/compositor/operations/COM_OutputFileOperation.cc index 49de275c256..1d22f3e8cd2 100644 --- a/source/blender/compositor/operations/COM_OutputFileOperation.cc +++ b/source/blender/compositor/operations/COM_OutputFileOperation.cc @@ -204,7 +204,7 @@ OutputSingleLayerOperation::OutputSingleLayerOperation(const Scene *scene, const RenderData *rd, const bNodeTree *tree, DataType datatype, - ImageFormatData *format, + const ImageFormatData *format, const char *path, const char *view_name, const bool save_as_render) diff --git a/source/blender/compositor/operations/COM_OutputFileOperation.h b/source/blender/compositor/operations/COM_OutputFileOperation.h index 875defe00e9..df1d68838d9 100644 --- a/source/blender/compositor/operations/COM_OutputFileOperation.h +++ b/source/blender/compositor/operations/COM_OutputFileOperation.h @@ -35,7 +35,7 @@ class OutputSingleLayerOperation : public MultiThreadedOperation { const RenderData *rd, const bNodeTree *tree, DataType datatype, - ImageFormatData *format, + const ImageFormatData *format, const char *path, const char *view_name, bool save_as_render); diff --git a/source/blender/compositor/operations/COM_RenderLayersProg.cc b/source/blender/compositor/operations/COM_RenderLayersProg.cc index 522086658f8..40f2187b27b 100644 --- a/source/blender/compositor/operations/COM_RenderLayersProg.cc +++ b/source/blender/compositor/operations/COM_RenderLayersProg.cc @@ -109,8 +109,8 @@ void RenderLayersProg::execute_pixel_sampled(float output[4], /* see comment in execute_region describing coordinate mapping, * here it simply goes other way around */ - int full_width = rd->xsch * rd->size / 100; - int full_height = rd->ysch * rd->size / 100; + int full_width, full_height; + BKE_render_resolution(rd, false, &full_width, &full_height); dx = rd->border.xmin * full_width - (full_width - this->get_width()) / 2.0f; dy = rd->border.ymin * full_height - (full_height - this->get_height()) / 2.0f; diff --git a/source/blender/compositor/operations/COM_TextureOperation.cc b/source/blender/compositor/operations/COM_TextureOperation.cc index 8b27c3aded4..c1c8db2ae3f 100644 --- a/source/blender/compositor/operations/COM_TextureOperation.cc +++ b/source/blender/compositor/operations/COM_TextureOperation.cc @@ -6,6 +6,7 @@ #include "BKE_image.h" #include "BKE_node.h" +#include "BKE_scene.h" #include "NOD_texture.h" @@ -59,8 +60,8 @@ void TextureBaseOperation::determine_canvas(const rcti &preferred_area, rcti &r_ { r_area = preferred_area; if (BLI_rcti_is_empty(&preferred_area)) { - int width = rd_->xsch * rd_->size / 100; - int height = rd_->ysch * rd_->size / 100; + int width, height; + BKE_render_resolution(rd_, false, &width, &height); r_area.xmax = preferred_area.xmin + width; r_area.ymax = preferred_area.ymin + height; } diff --git a/source/blender/compositor/operations/COM_ViewerOperation.cc b/source/blender/compositor/operations/COM_ViewerOperation.cc index 58392b8a638..aeadf8f255d 100644 --- a/source/blender/compositor/operations/COM_ViewerOperation.cc +++ b/source/blender/compositor/operations/COM_ViewerOperation.cc @@ -104,8 +104,8 @@ void ViewerOperation::execute_region(rcti *rect, unsigned int /*tile_number*/) void ViewerOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { - const int scene_render_width = rd_->xsch * rd_->size / 100; - const int scene_render_height = rd_->ysch * rd_->size / 100; + int scene_render_width, scene_render_height; + BKE_render_resolution(rd_, false, &scene_render_width, &scene_render_height); rcti local_preferred = preferred_area; local_preferred.xmax = local_preferred.xmin + scene_render_width; diff --git a/source/blender/depsgraph/intern/builder/deg_builder.cc b/source/blender/depsgraph/intern/builder/deg_builder.cc index a3cd821e82f..1fec1fb3219 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder.cc @@ -63,7 +63,7 @@ DepsgraphBuilder::DepsgraphBuilder(Main *bmain, Depsgraph *graph, DepsgraphBuild { } -bool DepsgraphBuilder::need_pull_base_into_graph(Base *base) +bool DepsgraphBuilder::need_pull_base_into_graph(const Base *base) { /* Simple check: enabled bases are always part of dependency graph. */ const int base_flag = (graph_->mode == DAG_EVAL_VIEWPORT) ? BASE_ENABLED_VIEWPORT : @@ -74,7 +74,7 @@ bool DepsgraphBuilder::need_pull_base_into_graph(Base *base) /* More involved check: since we don't support dynamic changes in dependency graph topology and * all visible objects are to be part of dependency graph, we pull all objects which has animated * visibility. */ - Object *object = base->object; + const Object *object = base->object; AnimatedPropertyID property_id; if (graph_->mode == DAG_EVAL_VIEWPORT) { property_id = AnimatedPropertyID(&object->id, &RNA_Object, "hide_viewport"); @@ -89,7 +89,7 @@ bool DepsgraphBuilder::need_pull_base_into_graph(Base *base) return cache_->isPropertyAnimated(&object->id, property_id); } -bool DepsgraphBuilder::check_pchan_has_bbone(Object *object, const bPoseChannel *pchan) +bool DepsgraphBuilder::check_pchan_has_bbone(const Object *object, const bPoseChannel *pchan) { BLI_assert(object->type == OB_ARMATURE); if (pchan == nullptr || pchan->bone == nullptr) { @@ -109,12 +109,13 @@ bool DepsgraphBuilder::check_pchan_has_bbone(Object *object, const bPoseChannel cache_->isPropertyAnimated(&armature->id, property_id); } -bool DepsgraphBuilder::check_pchan_has_bbone_segments(Object *object, const bPoseChannel *pchan) +bool DepsgraphBuilder::check_pchan_has_bbone_segments(const Object *object, + const bPoseChannel *pchan) { return check_pchan_has_bbone(object, pchan); } -bool DepsgraphBuilder::check_pchan_has_bbone_segments(Object *object, const char *bone_name) +bool DepsgraphBuilder::check_pchan_has_bbone_segments(const Object *object, const char *bone_name) { const bPoseChannel *pchan = BKE_pose_channel_find_name(object->pose, bone_name); return check_pchan_has_bbone_segments(object, pchan); @@ -138,19 +139,6 @@ void deg_graph_build_flush_visibility(Depsgraph *graph) for (IDNode *id_node : graph->id_nodes) { for (ComponentNode *comp_node : id_node->components.values()) { comp_node->affects_directly_visible |= id_node->is_directly_visible; - - /* Enforce "visibility" of the synchronization component. - * - * This component is never connected to other ID nodes, and hence can not be handled in the - * same way as other components needed for evaluation. It is only needed for proper - * evaluation of the ID node it belongs to. - * - * The design is such that the synchronization is supposed to happen whenever any part of the - * ID changed/evaluated. Here we mark the component as "visible" so that genetic recalc flag - * flushing and scheduling will handle the component in a generic manner. */ - if (comp_node->type == NodeType::SYNCHRONIZATION) { - comp_node->affects_directly_visible = true; - } } } diff --git a/source/blender/depsgraph/intern/builder/deg_builder.h b/source/blender/depsgraph/intern/builder/deg_builder.h index 8fbe255ed9d..950ebfb35ba 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder.h +++ b/source/blender/depsgraph/intern/builder/deg_builder.h @@ -13,8 +13,7 @@ struct Main; struct Object; struct bPoseChannel; -namespace blender { -namespace deg { +namespace blender::deg { struct Depsgraph; class DepsgraphBuilderCache; @@ -23,11 +22,11 @@ class DepsgraphBuilder { public: virtual ~DepsgraphBuilder() = default; - virtual bool need_pull_base_into_graph(Base *base); + virtual bool need_pull_base_into_graph(const Base *base); - virtual bool check_pchan_has_bbone(Object *object, const bPoseChannel *pchan); - virtual bool check_pchan_has_bbone_segments(Object *object, const bPoseChannel *pchan); - virtual bool check_pchan_has_bbone_segments(Object *object, const char *bone_name); + virtual bool check_pchan_has_bbone(const Object *object, const bPoseChannel *pchan); + virtual bool check_pchan_has_bbone_segments(const Object *object, const bPoseChannel *pchan); + virtual bool check_pchan_has_bbone_segments(const Object *object, const char *bone_name); protected: /* NOTE: The builder does NOT take ownership over any of those resources. */ @@ -43,5 +42,4 @@ bool deg_check_id_in_depsgraph(const Depsgraph *graph, ID *id_orig); bool deg_check_base_in_depsgraph(const Depsgraph *graph, Base *base); void deg_graph_build_finalize(Main *bmain, Depsgraph *graph); -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/builder/deg_builder_cache.cc b/source/blender/depsgraph/intern/builder/deg_builder_cache.cc index 7f88f54fdca..6474f853390 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_cache.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_cache.cc @@ -35,13 +35,13 @@ AnimatedPropertyID::AnimatedPropertyID(const PointerRNA &pointer_rna, { } -AnimatedPropertyID::AnimatedPropertyID(ID *id, StructRNA *type, const char *property_name) +AnimatedPropertyID::AnimatedPropertyID(const ID *id, StructRNA *type, const char *property_name) : data(id) { property_rna = RNA_struct_type_find_property(type, property_name); } -AnimatedPropertyID::AnimatedPropertyID(ID * /*id*/, +AnimatedPropertyID::AnimatedPropertyID(const ID * /*id*/, StructRNA *type, void *data, const char *property_name) @@ -100,13 +100,13 @@ AnimatedPropertyStorage::AnimatedPropertyStorage() : is_fully_initialized(false) { } -void AnimatedPropertyStorage::initializeFromID(DepsgraphBuilderCache *builder_cache, ID *id) +void AnimatedPropertyStorage::initializeFromID(DepsgraphBuilderCache *builder_cache, const ID *id) { AnimatedPropertyCallbackData data; - RNA_id_pointer_create(id, &data.pointer_rna); + RNA_id_pointer_create(const_cast<ID *>(id), &data.pointer_rna); data.animated_property_storage = this; data.builder_cache = builder_cache; - BKE_fcurves_id_cb(id, animated_property_cb, &data); + BKE_fcurves_id_cb(const_cast<ID *>(id), animated_property_cb, &data); } void AnimatedPropertyStorage::tagPropertyAsAnimated(const AnimatedPropertyID &property_id) @@ -147,13 +147,14 @@ DepsgraphBuilderCache::~DepsgraphBuilderCache() } } -AnimatedPropertyStorage *DepsgraphBuilderCache::ensureAnimatedPropertyStorage(ID *id) +AnimatedPropertyStorage *DepsgraphBuilderCache::ensureAnimatedPropertyStorage(const ID *id) { return animated_property_storage_map_.lookup_or_add_cb( id, []() { return new AnimatedPropertyStorage(); }); } -AnimatedPropertyStorage *DepsgraphBuilderCache::ensureInitializedAnimatedPropertyStorage(ID *id) +AnimatedPropertyStorage *DepsgraphBuilderCache::ensureInitializedAnimatedPropertyStorage( + const ID *id) { AnimatedPropertyStorage *animated_property_storage = ensureAnimatedPropertyStorage(id); if (!animated_property_storage->is_fully_initialized) { diff --git a/source/blender/depsgraph/intern/builder/deg_builder_cache.h b/source/blender/depsgraph/intern/builder/deg_builder_cache.h index 2d2bdeaf825..d85269c0f8b 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_cache.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_cache.h @@ -17,8 +17,7 @@ struct ID; struct PointerRNA; struct PropertyRNA; -namespace blender { -namespace deg { +namespace blender::deg { class DepsgraphBuilderCache; @@ -28,14 +27,14 @@ class AnimatedPropertyID { AnimatedPropertyID(); AnimatedPropertyID(const PointerRNA *pointer_rna, const PropertyRNA *property_rna); AnimatedPropertyID(const PointerRNA &pointer_rna, const PropertyRNA *property_rna); - AnimatedPropertyID(ID *id, StructRNA *type, const char *property_name); - AnimatedPropertyID(ID *id, StructRNA *type, void *data, const char *property_name); + AnimatedPropertyID(const ID *id, StructRNA *type, const char *property_name); + AnimatedPropertyID(const ID *id, StructRNA *type, void *data, const char *property_name); uint64_t hash() const; friend bool operator==(const AnimatedPropertyID &a, const AnimatedPropertyID &b); /* Corresponds to PointerRNA.data. */ - void *data; + const void *data; const PropertyRNA *property_rna; MEM_CXX_CLASS_ALLOC_FUNCS("AnimatedPropertyID"); @@ -45,7 +44,7 @@ class AnimatedPropertyStorage { public: AnimatedPropertyStorage(); - void initializeFromID(DepsgraphBuilderCache *builder_cache, ID *id); + void initializeFromID(DepsgraphBuilderCache *builder_cache, const ID *id); void tagPropertyAsAnimated(const AnimatedPropertyID &property_id); void tagPropertyAsAnimated(const PointerRNA *pointer_rna, const PropertyRNA *property_rna); @@ -59,7 +58,7 @@ class AnimatedPropertyStorage { bool is_fully_initialized; /* indexed by PointerRNA.data. */ - Set<void *> animated_objects_set; + Set<const void *> animated_objects_set; Set<AnimatedPropertyID> animated_properties_set; MEM_CXX_CLASS_ALLOC_FUNCS("AnimatedPropertyStorage"); @@ -71,8 +70,8 @@ class DepsgraphBuilderCache { ~DepsgraphBuilderCache(); /* Makes sure storage for animated properties exists and initialized for the given ID. */ - AnimatedPropertyStorage *ensureAnimatedPropertyStorage(ID *id); - AnimatedPropertyStorage *ensureInitializedAnimatedPropertyStorage(ID *id); + AnimatedPropertyStorage *ensureAnimatedPropertyStorage(const ID *id); + AnimatedPropertyStorage *ensureInitializedAnimatedPropertyStorage(const ID *id); /* Shortcuts to go through ensureInitializedAnimatedPropertyStorage and its * isPropertyAnimated. @@ -82,7 +81,7 @@ class DepsgraphBuilderCache { * * TODO(sergey): Technically, this makes this class something else than just a cache, but what is * the better name? */ - template<typename... Args> bool isPropertyAnimated(ID *id, Args... args) + template<typename... Args> bool isPropertyAnimated(const ID *id, Args... args) { AnimatedPropertyStorage *animated_property_storage = ensureInitializedAnimatedPropertyStorage( id); @@ -96,10 +95,9 @@ class DepsgraphBuilderCache { return animated_property_storage->isAnyPropertyAnimated(ptr); } - Map<ID *, AnimatedPropertyStorage *> animated_property_storage_map_; + Map<const ID *, AnimatedPropertyStorage *> animated_property_storage_map_; MEM_CXX_CLASS_ALLOC_FUNCS("DepsgraphBuilderCache"); }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/builder/deg_builder_cycle.h b/source/blender/depsgraph/intern/builder/deg_builder_cycle.h index 8e94e0ae21f..83d25f8c23f 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_cycle.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_cycle.h @@ -7,13 +7,11 @@ #pragma once -namespace blender { -namespace deg { +namespace blender::deg { struct Depsgraph; /* Detect and solve dependency cycles. */ void deg_graph_detect_cycles(Depsgraph *graph); -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/builder/deg_builder_map.h b/source/blender/depsgraph/intern/builder/deg_builder_map.h index 50ebadeb382..2b2b4d089e9 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_map.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_map.h @@ -11,8 +11,7 @@ struct ID; -namespace blender { -namespace deg { +namespace blender::deg { class BuilderMap { public: @@ -60,5 +59,4 @@ class BuilderMap { Map<ID *, int> id_tags_; }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index 657bc3eb25c..473dda2290c 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -174,15 +174,13 @@ IDNode *DepsgraphNodeBuilder::add_id_node(ID *id) ComponentNode *comp_cow = id_node->add_component(NodeType::COPY_ON_WRITE); OperationNode *op_cow = comp_cow->add_operation( [id_node](::Depsgraph *depsgraph) { deg_evaluate_copy_on_write(depsgraph, id_node); }, - OperationCode::COPY_ON_WRITE, - "", - -1); + OperationCode::COPY_ON_WRITE); graph_->operations.append(op_cow); } ComponentNode *visibility_component = id_node->add_component(NodeType::VISIBILITY); OperationNode *visibility_operation = visibility_component->add_operation( - nullptr, OperationCode::OPERATION, "", -1); + nullptr, OperationCode::VISIBILITY); /* Pin the node so that it and its relations are preserved by the unused nodes/relations * deletion. This is mainly to make it easier to debug visibility. */ visibility_operation->flag |= OperationFlag::DEPSOP_FLAG_PINNED; diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h index be9983edf85..18e28311132 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h @@ -50,8 +50,7 @@ struct bNodeTree; struct bPoseChannel; struct bSound; -namespace blender { -namespace deg { +namespace blender::deg { struct ComponentNode; struct Depsgraph; @@ -306,5 +305,4 @@ class DepsgraphNodeBuilder : public DepsgraphBuilder { BuilderMap built_map_; }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.h b/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.h index 2f98c8b419c..afb3a3c22bd 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.h @@ -9,8 +9,7 @@ #include "intern/depsgraph_type.h" -namespace blender { -namespace deg { +namespace blender::deg { struct RootPChanMap { /** Debug contents of map. */ @@ -30,5 +29,4 @@ struct RootPChanMap { Map<StringRefNull, Set<StringRefNull>> map_; }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index 31d5308e825..4fe8a626af5 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -692,7 +692,7 @@ void DepsgraphRelationBuilder::build_object(Object *object) const BuilderStack::ScopedEntry stack_entry = stack_.trace(object->id); - /* Object Transforms */ + /* Object Transforms. */ OperationCode base_op = (object->parent) ? OperationCode::TRANSFORM_PARENT : OperationCode::TRANSFORM_LOCAL; OperationKey base_op_key(&object->id, NodeType::TRANSFORM, base_op); @@ -705,9 +705,12 @@ void DepsgraphRelationBuilder::build_object(Object *object) OperationKey final_transform_key( &object->id, NodeType::TRANSFORM, OperationCode::TRANSFORM_FINAL); OperationKey ob_eval_key(&object->id, NodeType::TRANSFORM, OperationCode::TRANSFORM_EVAL); + add_relation(init_transform_key, local_transform_key, "Transform Init"); + /* Various flags, flushing from bases/collections. */ build_object_layer_component_relations(object); + /* Parenting. */ if (object->parent != nullptr) { /* Make sure parent object's relations are built. */ @@ -717,30 +720,35 @@ void DepsgraphRelationBuilder::build_object(Object *object) /* Local -> parent. */ add_relation(local_transform_key, parent_transform_key, "ObLocal -> ObParent"); } + /* Modifiers. */ if (object->modifiers.first != nullptr) { BuilderWalkUserData data; data.builder = this; BKE_modifiers_foreach_ID_link(object, modifier_walk, &data); } + /* Grease Pencil Modifiers. */ if (object->greasepencil_modifiers.first != nullptr) { BuilderWalkUserData data; data.builder = this; BKE_gpencil_modifiers_foreach_ID_link(object, modifier_walk, &data); } + /* Shader FX. */ if (object->shader_fx.first != nullptr) { BuilderWalkUserData data; data.builder = this; BKE_shaderfx_foreach_ID_link(object, modifier_walk, &data); } + /* Constraints. */ if (object->constraints.first != nullptr) { BuilderWalkUserData data; data.builder = this; BKE_constraints_id_loop(&object->constraints, constraint_walk, &data); } + /* Object constraints. */ OperationKey object_transform_simulation_init_key( &object->id, NodeType::TRANSFORM, OperationCode::TRANSFORM_SIMULATION_INIT); @@ -767,30 +775,39 @@ void DepsgraphRelationBuilder::build_object(Object *object) final_transform_key, "Simulation -> Final Transform"); } + build_idproperties(object->id.properties); + /* Animation data */ build_animdata(&object->id); + /* Object data. */ build_object_data(object); + /* Particle systems. */ if (object->particlesystem.first != nullptr) { build_particle_systems(object); } + /* Force field Texture. */ if ((object->pd != nullptr) && (object->pd->forcefield == PFIELD_TEXTURE) && (object->pd->tex != nullptr)) { build_texture(object->pd->tex); } + /* Object dupligroup. */ if (object->instance_collection != nullptr) { build_collection(nullptr, object, object->instance_collection); } + /* Point caches. */ build_object_pointcache(object); + /* Synchronization back to original object. */ OperationKey synchronize_key( &object->id, NodeType::SYNCHRONIZATION, OperationCode::SYNCHRONIZE_TO_ORIGINAL); add_relation(final_transform_key, synchronize_key, "Synchronize to Original"); + /* Parameters. */ build_parameters(&object->id); } @@ -2459,6 +2476,13 @@ void DepsgraphRelationBuilder::build_camera(Camera *camera) ComponentKey camera_parameters_key(&camera->id, NodeType::PARAMETERS); ComponentKey dof_ob_key(&camera->dof.focus_object->id, NodeType::TRANSFORM); add_relation(dof_ob_key, camera_parameters_key, "Camera DOF"); + if (camera->dof.focus_subtarget[0]) { + OperationKey target_key(&camera->dof.focus_object->id, + NodeType::BONE, + camera->dof.focus_subtarget, + OperationCode::BONE_DONE); + add_relation(target_key, camera_parameters_key, "Camera DOF subtarget"); + } } } diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.h b/source/blender/depsgraph/intern/builder/deg_builder_relations.h index 64bdd2334d8..0cb0b60dfb0 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.h @@ -70,8 +70,7 @@ struct bSound; struct PropertyRNA; -namespace blender { -namespace deg { +namespace blender::deg { struct ComponentNode; struct DepsNodeHandle; @@ -381,7 +380,6 @@ struct DepsNodeHandle { const char *default_name; }; -} // namespace deg -} // namespace blender +} // namespace blender::deg #include "intern/builder/deg_builder_relations_impl.h" diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations_drivers.h b/source/blender/depsgraph/intern/builder/deg_builder_relations_drivers.h index e517dd6a927..9860b17fd56 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations_drivers.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_drivers.h @@ -15,8 +15,7 @@ struct FCurve; -namespace blender { -namespace deg { +namespace blender::deg { /* Helper class for determining which relations are needed between driver evaluation nodes. */ class DriverDescriptor { @@ -59,5 +58,4 @@ class DriverDescriptor { bool resolve_rna(); }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations_impl.h b/source/blender/depsgraph/intern/builder/deg_builder_relations_impl.h index aba4a011e72..76066ce97a7 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations_impl.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_impl.h @@ -15,8 +15,7 @@ #include "DNA_object_types.h" #include "DNA_rigidbody_types.h" -namespace blender { -namespace deg { +namespace blender::deg { template<typename KeyType> OperationNode *DepsgraphRelationBuilder::find_operation_node(const KeyType &key) @@ -209,5 +208,4 @@ bool DepsgraphRelationBuilder::is_same_nodetree_node_dependency(const KeyFrom &k return true; } -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/builder/deg_builder_remove_noop.h b/source/blender/depsgraph/intern/builder/deg_builder_remove_noop.h index 922d2d7dc05..ad10eb7cd10 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_remove_noop.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_remove_noop.h @@ -7,13 +7,11 @@ #pragma once -namespace blender { -namespace deg { +namespace blender::deg { struct Depsgraph; /* Remove all no-op nodes that have zero outgoing relations. */ void deg_graph_remove_unused_noops(Depsgraph *graph); -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/builder/deg_builder_rna.h b/source/blender/depsgraph/intern/builder/deg_builder_rna.h index 4f482d4352d..9baa956bd80 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_rna.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_rna.h @@ -14,8 +14,7 @@ struct ID; struct PointerRNA; struct PropertyRNA; -namespace blender { -namespace deg { +namespace blender::deg { struct Depsgraph; struct Node; @@ -94,5 +93,4 @@ class RNANodeQuery { bool rna_prop_affects_parameters_node(const PointerRNA *ptr, const PropertyRNA *prop); -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/builder/deg_builder_transitive.h b/source/blender/depsgraph/intern/builder/deg_builder_transitive.h index 8b208610203..63016431eec 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_transitive.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_transitive.h @@ -7,13 +7,11 @@ #pragma once -namespace blender { -namespace deg { +namespace blender::deg { struct Depsgraph; /* Performs a transitive reduction to remove redundant relations. */ void deg_graph_transitive_reduction(Depsgraph *graph); -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/builder/pipeline.cc b/source/blender/depsgraph/intern/builder/pipeline.cc index 540b8e173f1..815a06d03d1 100644 --- a/source/blender/depsgraph/intern/builder/pipeline.cc +++ b/source/blender/depsgraph/intern/builder/pipeline.cc @@ -90,7 +90,7 @@ void AbstractBuilderPipeline::build_step_finalize() } #endif /* Relations are up to date. */ - deg_graph_->need_update = false; + deg_graph_->need_update_relations = false; } unique_ptr<DepsgraphNodeBuilder> AbstractBuilderPipeline::construct_node_builder() diff --git a/source/blender/depsgraph/intern/builder/pipeline.h b/source/blender/depsgraph/intern/builder/pipeline.h index b106e73b97c..7568aa78106 100644 --- a/source/blender/depsgraph/intern/builder/pipeline.h +++ b/source/blender/depsgraph/intern/builder/pipeline.h @@ -16,8 +16,7 @@ struct Main; struct Scene; struct ViewLayer; -namespace blender { -namespace deg { +namespace blender::deg { struct Depsgraph; class DepsgraphNodeBuilder; @@ -57,5 +56,4 @@ class AbstractBuilderPipeline { virtual void build_relations(DepsgraphRelationBuilder &relation_builder) = 0; }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/builder/pipeline_all_objects.cc b/source/blender/depsgraph/intern/builder/pipeline_all_objects.cc index 74d151c65d7..6bc3b59a9d6 100644 --- a/source/blender/depsgraph/intern/builder/pipeline_all_objects.cc +++ b/source/blender/depsgraph/intern/builder/pipeline_all_objects.cc @@ -20,7 +20,7 @@ class AllObjectsNodeBuilder : public DepsgraphNodeBuilder { { } - bool need_pull_base_into_graph(Base * /*base*/) override + bool need_pull_base_into_graph(const Base * /*base*/) override { return true; } @@ -33,7 +33,7 @@ class AllObjectsRelationBuilder : public DepsgraphRelationBuilder { { } - bool need_pull_base_into_graph(Base * /*base*/) override + bool need_pull_base_into_graph(const Base * /*base*/) override { return true; } diff --git a/source/blender/depsgraph/intern/builder/pipeline_all_objects.h b/source/blender/depsgraph/intern/builder/pipeline_all_objects.h index 75d9605dec7..fde5e7e2163 100644 --- a/source/blender/depsgraph/intern/builder/pipeline_all_objects.h +++ b/source/blender/depsgraph/intern/builder/pipeline_all_objects.h @@ -9,8 +9,7 @@ #include "pipeline_view_layer.h" -namespace blender { -namespace deg { +namespace blender::deg { /* Builds a dependency graph that contains all objects in the view layer. * This is contrary to the regular ViewLayerBuilderPipeline, which is limited to visible objects @@ -24,5 +23,4 @@ class AllObjectsBuilderPipeline : public ViewLayerBuilderPipeline { virtual unique_ptr<DepsgraphRelationBuilder> construct_relation_builder() override; }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/builder/pipeline_compositor.h b/source/blender/depsgraph/intern/builder/pipeline_compositor.h index 3325741c94a..304f2d4ec9a 100644 --- a/source/blender/depsgraph/intern/builder/pipeline_compositor.h +++ b/source/blender/depsgraph/intern/builder/pipeline_compositor.h @@ -11,8 +11,7 @@ struct bNodeTree; -namespace blender { -namespace deg { +namespace blender::deg { class CompositorBuilderPipeline : public AbstractBuilderPipeline { public: @@ -26,5 +25,4 @@ class CompositorBuilderPipeline : public AbstractBuilderPipeline { bNodeTree *nodetree_; }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/builder/pipeline_from_ids.cc b/source/blender/depsgraph/intern/builder/pipeline_from_ids.cc index ee10b28a306..e256c8271f2 100644 --- a/source/blender/depsgraph/intern/builder/pipeline_from_ids.cc +++ b/source/blender/depsgraph/intern/builder/pipeline_from_ids.cc @@ -39,7 +39,7 @@ class DepsgraphFromIDsNodeBuilder : public DepsgraphNodeBuilder { { } - bool need_pull_base_into_graph(Base *base) override + bool need_pull_base_into_graph(const Base *base) override { if (!filter_.contains(&base->object->id)) { return false; @@ -61,7 +61,7 @@ class DepsgraphFromIDsRelationBuilder : public DepsgraphRelationBuilder { { } - bool need_pull_base_into_graph(Base *base) override + bool need_pull_base_into_graph(const Base *base) override { if (!filter_.contains(&base->object->id)) { return false; diff --git a/source/blender/depsgraph/intern/builder/pipeline_from_ids.h b/source/blender/depsgraph/intern/builder/pipeline_from_ids.h index a2c75c048cb..c277d44aaad 100644 --- a/source/blender/depsgraph/intern/builder/pipeline_from_ids.h +++ b/source/blender/depsgraph/intern/builder/pipeline_from_ids.h @@ -9,8 +9,7 @@ #include "pipeline.h" -namespace blender { -namespace deg { +namespace blender::deg { /* Optimized builders for dependency graph built from a given set of IDs. * @@ -37,5 +36,4 @@ class FromIDsBuilderPipeline : public AbstractBuilderPipeline { Span<ID *> ids_; }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/builder/pipeline_render.h b/source/blender/depsgraph/intern/builder/pipeline_render.h index cb704f84c59..7eb65168ea6 100644 --- a/source/blender/depsgraph/intern/builder/pipeline_render.h +++ b/source/blender/depsgraph/intern/builder/pipeline_render.h @@ -9,8 +9,7 @@ #include "pipeline.h" -namespace blender { -namespace deg { +namespace blender::deg { class RenderBuilderPipeline : public AbstractBuilderPipeline { public: @@ -21,5 +20,4 @@ class RenderBuilderPipeline : public AbstractBuilderPipeline { virtual void build_relations(DepsgraphRelationBuilder &relation_builder) override; }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/builder/pipeline_view_layer.h b/source/blender/depsgraph/intern/builder/pipeline_view_layer.h index 18b9ce5d5ff..ffd6fa07776 100644 --- a/source/blender/depsgraph/intern/builder/pipeline_view_layer.h +++ b/source/blender/depsgraph/intern/builder/pipeline_view_layer.h @@ -9,8 +9,7 @@ #include "pipeline.h" -namespace blender { -namespace deg { +namespace blender::deg { class ViewLayerBuilderPipeline : public AbstractBuilderPipeline { public: @@ -21,5 +20,4 @@ class ViewLayerBuilderPipeline : public AbstractBuilderPipeline { virtual void build_relations(DepsgraphRelationBuilder &relation_builder) override; }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/debug/deg_debug.h b/source/blender/depsgraph/intern/debug/deg_debug.h index ee6c5b046b7..8f18bd70d36 100644 --- a/source/blender/depsgraph/intern/debug/deg_debug.h +++ b/source/blender/depsgraph/intern/debug/deg_debug.h @@ -14,8 +14,7 @@ #include "DEG_depsgraph_debug.h" -namespace blender { -namespace deg { +namespace blender::deg { class DepsgraphDebug { public: @@ -74,5 +73,4 @@ bool terminal_do_color(void); string color_for_pointer(const void *pointer); string color_end(void); -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/debug/deg_time_average.h b/source/blender/depsgraph/intern/debug/deg_time_average.h index cc47f03b221..d4eb05a9ddb 100644 --- a/source/blender/depsgraph/intern/debug/deg_time_average.h +++ b/source/blender/depsgraph/intern/debug/deg_time_average.h @@ -7,8 +7,7 @@ #pragma once -namespace blender { -namespace deg { +namespace blender::deg { /* Utility class which takes care of calculating average of time series, such as FPS counters. */ template<int MaxSamples> class AveragedTimeSampler { @@ -52,5 +51,4 @@ template<int MaxSamples> class AveragedTimeSampler { int next_sample_index_; }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/depsgraph.cc b/source/blender/depsgraph/intern/depsgraph.cc index 4514084059a..d460a68747d 100644 --- a/source/blender/depsgraph/intern/depsgraph.cc +++ b/source/blender/depsgraph/intern/depsgraph.cc @@ -45,9 +45,9 @@ namespace blender::deg { Depsgraph::Depsgraph(Main *bmain, Scene *scene, ViewLayer *view_layer, eEvaluationMode mode) : time_source(nullptr), - need_update(true), - need_visibility_update(true), - need_visibility_time_update(false), + need_update_relations(true), + need_tag_id_on_graph_visibility_update(true), + need_tag_id_on_graph_visibility_time_update(false), bmain(bmain), scene(scene), view_layer(view_layer), diff --git a/source/blender/depsgraph/intern/depsgraph.h b/source/blender/depsgraph/intern/depsgraph.h index ca4ce058904..33d97e4b8b2 100644 --- a/source/blender/depsgraph/intern/depsgraph.h +++ b/source/blender/depsgraph/intern/depsgraph.h @@ -31,8 +31,7 @@ struct ID; struct Scene; struct ViewLayer; -namespace blender { -namespace deg { +namespace blender::deg { struct IDNode; struct Node; @@ -90,12 +89,12 @@ struct Depsgraph { TimeSourceNode *time_source; /* Indicates whether relations needs to be updated. */ - bool need_update; + bool need_update_relations; /* Indicated whether IDs in this graph are to be tagged as if they first appear visible, with * an optional tag for their animation (time) update. */ - bool need_visibility_update; - bool need_visibility_time_update; + bool need_tag_id_on_graph_visibility_update; + bool need_tag_id_on_graph_visibility_time_update; /* Indicates which ID types were updated. */ char id_type_updated[INDEX_ID_MAX]; @@ -162,5 +161,4 @@ struct Depsgraph { MEM_CXX_CLASS_ALLOC_FUNCS("Depsgraph"); }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/depsgraph_build.cc b/source/blender/depsgraph/intern/depsgraph_build.cc index c64b7bc1eb7..a207c13d646 100644 --- a/source/blender/depsgraph/intern/depsgraph_build.cc +++ b/source/blender/depsgraph/intern/depsgraph_build.cc @@ -270,7 +270,7 @@ void DEG_graph_tag_relations_update(Depsgraph *graph) { DEG_DEBUG_PRINTF(graph, TAG, "%s: Tagging relations for update.\n", __func__); deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(graph); - deg_graph->need_update = true; + deg_graph->need_update_relations = true; /* NOTE: When relations are updated, it's quite possible that * we've got new bases in the scene. This means, we need to * re-create flat array of bases in view layer. @@ -286,7 +286,7 @@ void DEG_graph_tag_relations_update(Depsgraph *graph) void DEG_graph_relations_update(Depsgraph *graph) { deg::Depsgraph *deg_graph = (deg::Depsgraph *)graph; - if (!deg_graph->need_update) { + if (!deg_graph->need_update_relations) { /* Graph is up to date, nothing to do. */ return; } diff --git a/source/blender/depsgraph/intern/depsgraph_physics.h b/source/blender/depsgraph/intern/depsgraph_physics.h index a8d37f76b09..2105eb62ce1 100644 --- a/source/blender/depsgraph/intern/depsgraph_physics.h +++ b/source/blender/depsgraph/intern/depsgraph_physics.h @@ -10,8 +10,7 @@ struct Collection; struct ListBase; -namespace blender { -namespace deg { +namespace blender::deg { struct Depsgraph; @@ -21,5 +20,4 @@ ListBase *build_collision_relations(Depsgraph *graph, unsigned int modifier_type); void clear_physics_relations(Depsgraph *graph); -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/depsgraph_query.cc b/source/blender/depsgraph/intern/depsgraph_query.cc index 6ffc711a475..9a047c70d01 100644 --- a/source/blender/depsgraph/intern/depsgraph_query.cc +++ b/source/blender/depsgraph/intern/depsgraph_query.cc @@ -328,7 +328,7 @@ bool DEG_is_fully_evaluated(const struct Depsgraph *depsgraph) { const deg::Depsgraph *deg_graph = (const deg::Depsgraph *)depsgraph; /* Check whether relations are up to date. */ - if (deg_graph->need_update) { + if (deg_graph->need_update_relations) { return false; } /* Check whether IDs are up to date. */ diff --git a/source/blender/depsgraph/intern/depsgraph_registry.h b/source/blender/depsgraph/intern/depsgraph_registry.h index 2746f17590a..4ab63dcc77d 100644 --- a/source/blender/depsgraph/intern/depsgraph_registry.h +++ b/source/blender/depsgraph/intern/depsgraph_registry.h @@ -11,8 +11,7 @@ struct Main; -namespace blender { -namespace deg { +namespace blender::deg { struct Depsgraph; @@ -20,5 +19,4 @@ void register_graph(Depsgraph *depsgraph); void unregister_graph(Depsgraph *depsgraph); Span<Depsgraph *> get_all_registered_graphs(Main *bmain); -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/depsgraph_relation.h b/source/blender/depsgraph/intern/depsgraph_relation.h index 4d51ad85878..1bacb9abfa6 100644 --- a/source/blender/depsgraph/intern/depsgraph_relation.h +++ b/source/blender/depsgraph/intern/depsgraph_relation.h @@ -9,8 +9,7 @@ #include "MEM_guardedalloc.h" -namespace blender { -namespace deg { +namespace blender::deg { struct Node; @@ -49,5 +48,4 @@ struct Relation { MEM_CXX_CLASS_ALLOC_FUNCS("Relation"); }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc index b50081458ad..9cd5980d8fe 100644 --- a/source/blender/depsgraph/intern/depsgraph_tag.cc +++ b/source/blender/depsgraph/intern/depsgraph_tag.cc @@ -494,19 +494,19 @@ void deg_graph_node_tag_zero(Main *bmain, void graph_tag_on_visible_update(Depsgraph *graph, const bool do_time) { - graph->need_visibility_update = true; - graph->need_visibility_time_update |= do_time; + graph->need_tag_id_on_graph_visibility_update = true; + graph->need_tag_id_on_graph_visibility_time_update |= do_time; } } /* namespace */ void graph_tag_ids_for_visible_update(Depsgraph *graph) { - if (!graph->need_visibility_update) { + if (!graph->need_tag_id_on_graph_visibility_update) { return; } - const bool do_time = graph->need_visibility_time_update; + const bool do_time = graph->need_tag_id_on_graph_visibility_time_update; Main *bmain = graph->bmain; /* NOTE: It is possible to have this function called with `do_time=false` first and later (prior @@ -561,8 +561,8 @@ void graph_tag_ids_for_visible_update(Depsgraph *graph) id_node->previously_visible_components_mask = id_node->visible_components_mask; } - graph->need_visibility_update = false; - graph->need_visibility_time_update = false; + graph->need_tag_id_on_graph_visibility_update = false; + graph->need_tag_id_on_graph_visibility_time_update = false; } NodeType geometry_tag_to_component(const ID *id) diff --git a/source/blender/depsgraph/intern/depsgraph_tag.h b/source/blender/depsgraph/intern/depsgraph_tag.h index a0518420bfb..b722aab5719 100644 --- a/source/blender/depsgraph/intern/depsgraph_tag.h +++ b/source/blender/depsgraph/intern/depsgraph_tag.h @@ -10,8 +10,7 @@ struct ID; struct Main; -namespace blender { -namespace deg { +namespace blender::deg { struct Depsgraph; @@ -29,5 +28,4 @@ void graph_id_tag_update( * Will do nothing if the graph is not tagged for visibility update. */ void graph_tag_ids_for_visible_update(Depsgraph *graph); -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/depsgraph_type.h b/source/blender/depsgraph/intern/depsgraph_type.h index 9cf14a831bc..9e21d124f83 100644 --- a/source/blender/depsgraph/intern/depsgraph_type.h +++ b/source/blender/depsgraph/intern/depsgraph_type.h @@ -34,8 +34,7 @@ struct Depsgraph; struct CustomData_MeshMasks; -namespace blender { -namespace deg { +namespace blender::deg { /* Commonly used types. */ using std::deque; @@ -153,5 +152,4 @@ struct DEGCustomDataMeshMasks { } }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/depsgraph_update.h b/source/blender/depsgraph/intern/depsgraph_update.h index 18f8e6acab6..c5a72157bd2 100644 --- a/source/blender/depsgraph/intern/depsgraph_update.h +++ b/source/blender/depsgraph/intern/depsgraph_update.h @@ -10,12 +10,10 @@ struct DEGEditorUpdateContext; struct ID; -namespace blender { -namespace deg { +namespace blender::deg { void deg_editors_id_update(const DEGEditorUpdateContext *update_ctx, struct ID *id); void deg_editors_scene_update(const DEGEditorUpdateContext *update_ctx, bool updated); -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/eval/deg_eval.cc b/source/blender/depsgraph/intern/eval/deg_eval.cc index 2d9d40aede6..9ac1f5275ac 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval.cc @@ -302,8 +302,28 @@ void schedule_node_to_queue(OperationNode *node, BLI_gsqueue_push(evaluation_queue, &node); } -void evaluate_graph_single_threaded(DepsgraphEvalState *state) +/* Evaluate given stage of the dependency graph evaluation using multiple threads. + * + * NOTE: Will assign the `state->stage` to the given stage. */ +void evaluate_graph_threaded_stage(DepsgraphEvalState *state, + TaskPool *task_pool, + const EvaluationStage stage) { + state->stage = stage; + + schedule_graph(state, schedule_node_to_pool, task_pool); + BLI_task_pool_work_and_wait(task_pool); +} + +/* Evaluate remaining operations of the dependency graph in a single threaded manner. */ +void evaluate_graph_single_threaded_if_needed(DepsgraphEvalState *state) +{ + if (!state->need_single_thread_pass) { + return; + } + + state->stage = EvaluationStage::SINGLE_THREADED_WORKAROUND; + GSQueue *evaluation_queue = BLI_gsqueue_new(sizeof(OperationNode *)); schedule_graph(state, schedule_node_to_queue, evaluation_queue); @@ -334,9 +354,7 @@ void depsgraph_ensure_view_layer(Depsgraph *graph) deg_update_copy_on_write_datablock(graph, scene_id_node); } -} // namespace - -static TaskPool *deg_evaluate_task_pool_create(DepsgraphEvalState *state) +TaskPool *deg_evaluate_task_pool_create(DepsgraphEvalState *state) { if (G.debug & G_DEBUG_DEPSGRAPH_NO_THREADS) { return BLI_task_pool_create_no_threads(state); @@ -345,6 +363,8 @@ static TaskPool *deg_evaluate_task_pool_create(DepsgraphEvalState *state) return BLI_task_pool_create_suspended(state, TASK_PRIORITY_HIGH); } +} // namespace + void deg_evaluate_on_refresh(Depsgraph *graph) { /* Nothing to update, early out. */ @@ -361,33 +381,37 @@ void deg_evaluate_on_refresh(Depsgraph *graph) graph->is_evaluating = true; depsgraph_ensure_view_layer(graph); + /* Set up evaluation state. */ DepsgraphEvalState state; state.graph = graph; state.do_stats = graph->debug.do_time_debug(); state.need_single_thread_pass = false; + /* Prepare all nodes for evaluation. */ initialize_execution(&state, graph); - /* Do actual evaluation now. */ - /* First, process all Copy-On-Write nodes. */ - state.stage = EvaluationStage::COPY_ON_WRITE; + /* Evaluation happens in several incremental steps: + * + * - Start with the copy-on-write operations which never form dependency cycles. This will ensure + * that if a dependency graph has a cycle evaluation functions will always "see" valid expanded + * datablock. It might not be evaluated yet, but at least the datablock will be valid. + * + * - Multi-threaded evaluation of all possible nodes. + * Certain operations (and their subtrees) could be ignored. For example, meta-balls are not + * safe from threading point of view, so the threaded evaluation will stop at the metaball + * operation node. + * + * - Single-threaded pass of all remaining operations. */ + TaskPool *task_pool = deg_evaluate_task_pool_create(&state); - schedule_graph(&state, schedule_node_to_pool, task_pool); - BLI_task_pool_work_and_wait(task_pool); - BLI_task_pool_free(task_pool); - /* After that, process all other nodes. */ - state.stage = EvaluationStage::THREADED_EVALUATION; - task_pool = deg_evaluate_task_pool_create(&state); - schedule_graph(&state, schedule_node_to_pool, task_pool); - BLI_task_pool_work_and_wait(task_pool); + evaluate_graph_threaded_stage(&state, task_pool, EvaluationStage::COPY_ON_WRITE); + evaluate_graph_threaded_stage(&state, task_pool, EvaluationStage::THREADED_EVALUATION); + BLI_task_pool_free(task_pool); - if (state.need_single_thread_pass) { - state.stage = EvaluationStage::SINGLE_THREADED_WORKAROUND; - evaluate_graph_single_threaded(&state); - } + evaluate_graph_single_threaded_if_needed(&state); /* Finalize statistics gathering. This is because we only gather single * operation timing here, without aggregating anything to avoid any extra @@ -395,6 +419,7 @@ void deg_evaluate_on_refresh(Depsgraph *graph) if (state.do_stats) { deg_eval_stats_aggregate(graph); } + /* Clear any uncleared tags - just in case. */ deg_graph_clear_tags(graph); graph->is_evaluating = false; diff --git a/source/blender/depsgraph/intern/eval/deg_eval.h b/source/blender/depsgraph/intern/eval/deg_eval.h index ba86e1a349d..6937231d81a 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval.h +++ b/source/blender/depsgraph/intern/eval/deg_eval.h @@ -9,8 +9,7 @@ #pragma once -namespace blender { -namespace deg { +namespace blender::deg { struct Depsgraph; @@ -23,5 +22,4 @@ struct Depsgraph; */ void deg_evaluate_on_refresh(Depsgraph *graph); -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h index 29322d58218..cbf450aa3f1 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h +++ b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h @@ -27,8 +27,7 @@ struct ID; struct Depsgraph; -namespace blender { -namespace deg { +namespace blender::deg { struct Depsgraph; class DepsgraphNodeBuilder; @@ -77,5 +76,4 @@ bool deg_copy_on_write_is_expanded(const struct ID *id_cow); bool deg_copy_on_write_is_needed(const ID *id_orig); bool deg_copy_on_write_is_needed(const ID_Type id_type); -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/eval/deg_eval_flush.h b/source/blender/depsgraph/intern/eval/deg_eval_flush.h index 6eb232e76e5..614ca66faed 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_flush.h +++ b/source/blender/depsgraph/intern/eval/deg_eval_flush.h @@ -9,8 +9,7 @@ #pragma once -namespace blender { -namespace deg { +namespace blender::deg { struct Depsgraph; @@ -24,5 +23,4 @@ void deg_graph_flush_updates(struct Depsgraph *graph); */ void deg_graph_clear_tags(struct Depsgraph *graph); -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.h index deb21715a28..3d9b308c5ad 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.h +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.h @@ -17,8 +17,7 @@ #include "intern/eval/deg_eval_runtime_backup_sound.h" #include "intern/eval/deg_eval_runtime_backup_volume.h" -namespace blender { -namespace deg { +namespace blender::deg { struct Depsgraph; @@ -59,5 +58,4 @@ class RuntimeBackup { GPencilBackup gpencil_backup; }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_animation.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_animation.h index 92847b330e8..807cc91242e 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_animation.h +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_animation.h @@ -11,8 +11,7 @@ #include "intern/depsgraph_type.h" -namespace blender { -namespace deg { +namespace blender::deg { struct Depsgraph; @@ -46,5 +45,4 @@ class AnimationBackup { Vector<AnimationValueBackup> values_backup; }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_gpencil.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_gpencil.h index 68eff01fd60..95c0ca3a2fe 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_gpencil.h +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_gpencil.h @@ -9,8 +9,7 @@ struct bGPdata; -namespace blender { -namespace deg { +namespace blender::deg { struct Depsgraph; @@ -25,5 +24,4 @@ class GPencilBackup { const Depsgraph *depsgraph; }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_modifier.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_modifier.h index faec8f7c065..ee51204b24c 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_modifier.h +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_modifier.h @@ -11,8 +11,7 @@ struct ModifierData; -namespace blender { -namespace deg { +namespace blender::deg { class ModifierDataBackup { public: @@ -22,5 +21,4 @@ class ModifierDataBackup { void *runtime; }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_movieclip.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_movieclip.h index 0e826e8f72a..aa13914d8c8 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_movieclip.h +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_movieclip.h @@ -11,8 +11,7 @@ struct MovieClip; struct MovieClipCache; struct anim; -namespace blender { -namespace deg { +namespace blender::deg { struct Depsgraph; @@ -30,5 +29,4 @@ class MovieClipBackup { struct MovieClipCache *cache; }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.h index 3b138feec0b..c9cc167d927 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.h +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.h @@ -17,8 +17,7 @@ struct Object; -namespace blender { -namespace deg { +namespace blender::deg { struct Depsgraph; @@ -46,5 +45,4 @@ class ObjectRuntimeBackup { Map<SessionUUID, bPoseChannel_Runtime> pose_channel_runtime_data; }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_pose.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_pose.h index 8fd5de44001..2f6a3dd4371 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_pose.h +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_pose.h @@ -11,8 +11,6 @@ #include "DNA_action_types.h" -namespace blender { -namespace deg { +namespace blender::deg { -} -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_scene.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_scene.h index 17683966a22..155cb42a96d 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_scene.h +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_scene.h @@ -11,8 +11,7 @@ struct Scene; -namespace blender { -namespace deg { +namespace blender::deg { struct Depsgraph; @@ -40,5 +39,4 @@ class SceneBackup { SequencerBackup sequencer_backup; }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequence.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequence.h index 95fbd2a3e4e..f97b6b200e9 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequence.h +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequence.h @@ -11,8 +11,7 @@ struct Sequence; -namespace blender { -namespace deg { +namespace blender::deg { struct Depsgraph; @@ -32,5 +31,4 @@ class SequenceBackup { ListBase anims; }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequencer.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequencer.h index 19e1a63ab4c..38fb8e81cc3 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequencer.h +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequencer.h @@ -16,8 +16,7 @@ struct Scene; -namespace blender { -namespace deg { +namespace blender::deg { struct Depsgraph; @@ -34,5 +33,4 @@ class SequencerBackup { Map<SessionUUID, SequenceBackup> sequences_backup; }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sound.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sound.h index c4267be1421..9e1d15251e8 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sound.h +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sound.h @@ -9,8 +9,7 @@ struct bSound; -namespace blender { -namespace deg { +namespace blender::deg { struct Depsgraph; @@ -29,5 +28,4 @@ class SoundBackup { void *playback_handle; }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_volume.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_volume.h index bc263cc58f8..60cba64f120 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_volume.h +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_volume.h @@ -10,8 +10,7 @@ struct Volume; struct VolumeGridVector; -namespace blender { -namespace deg { +namespace blender::deg { struct Depsgraph; @@ -27,5 +26,4 @@ class VolumeBackup { char filepath[1024]; /* FILE_MAX */ }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/eval/deg_eval_stats.h b/source/blender/depsgraph/intern/eval/deg_eval_stats.h index a1e3cdaca76..c24c5f07135 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_stats.h +++ b/source/blender/depsgraph/intern/eval/deg_eval_stats.h @@ -7,13 +7,11 @@ #pragma once -namespace blender { -namespace deg { +namespace blender::deg { struct Depsgraph; /* Aggregate operation timings to overall component and ID nodes timing. */ void deg_eval_stats_aggregate(Depsgraph *graph); -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/node/deg_node.h b/source/blender/depsgraph/intern/node/deg_node.h index 509867ad47a..5b33528df33 100644 --- a/source/blender/depsgraph/intern/node/deg_node.h +++ b/source/blender/depsgraph/intern/node/deg_node.h @@ -18,8 +18,7 @@ struct ID; struct Scene; -namespace blender { -namespace deg { +namespace blender::deg { struct Depsgraph; struct OperationNode; @@ -217,5 +216,4 @@ struct Node { void deg_register_base_depsnodes(); -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/node/deg_node_component.h b/source/blender/depsgraph/intern/node/deg_node_component.h index 6958866af3b..375a26ab49b 100644 --- a/source/blender/depsgraph/intern/node/deg_node_component.h +++ b/source/blender/depsgraph/intern/node/deg_node_component.h @@ -22,8 +22,7 @@ struct ID; struct bPoseChannel; -namespace blender { -namespace deg { +namespace blender::deg { struct BoneComponentNode; struct Depsgraph; @@ -56,32 +55,45 @@ struct ComponentNode : public Node { virtual string identifier() const override; - /* Find an existing operation, if requested operation does not exist - * nullptr will be returned. */ + /* Find an existing operation, if requested operation does not exist nullptr will be returned. + * See #add_operation for the meaning and examples of #name and #name_tag. + */ OperationNode *find_operation(OperationIDKey key) const; - OperationNode *find_operation(OperationCode opcode, const char *name, int name_tag) const; + OperationNode *find_operation(OperationCode opcode, + const char *name = "", + int name_tag = -1) const; - /* Find an existing operation, will throw an assert() if it does not exist. */ + /* Find an existing operation, will throw an assert() if it does not exist. + * See #add_operation for the meaning and examples of #name and #name_tag. */ OperationNode *get_operation(OperationIDKey key) const; - OperationNode *get_operation(OperationCode opcode, const char *name, int name_tag) const; + OperationNode *get_operation(OperationCode opcode, + const char *name = "", + int name_tag = -1) const; /* Check operation exists and return it. */ bool has_operation(OperationIDKey key) const; - bool has_operation(OperationCode opcode, const char *name, int name_tag) const; + bool has_operation(OperationCode opcode, const char *name = "", int name_tag = -1) const; /** * Create a new node for representing an operation and add this to graph + * * \warning If an existing node is found, it will be modified. This helps * when node may have been partially created earlier (e.g. parent ref before * parent item is added) * * \param opcode: The operation to perform. - * \param name: Identifier for operation - used to find/locate it again. + * \param name: An optional identifier for operation. It will be used to tell operation nodes + * with the same code apart. For example, parameter operation code will have name + * set to the corresponding custom property name + * \param name_tag: An optional integer tag for the name. Is an additional way to tell operations + * apart. For example, RNA path to an array property will have the same opcode + * of PARAMETERS, name corresponding to the property name, and name tag + * corresponding to the array index within the property. */ OperationNode *add_operation(const DepsEvalOperationCb &op, OperationCode opcode, - const char *name, - int name_tag); + const char *name = "", + int name_tag = -1); /* Entry/exit operations management. * @@ -182,7 +194,6 @@ DEG_COMPONENT_NODE_DECLARE_GENERIC(ShadingParameters); DEG_COMPONENT_NODE_DECLARE_GENERIC(Transform); DEG_COMPONENT_NODE_DECLARE_NO_COW_TAG_ON_UPDATE(ObjectFromLayer); DEG_COMPONENT_NODE_DECLARE_GENERIC(Dupli); -DEG_COMPONENT_NODE_DECLARE_GENERIC(Synchronization); DEG_COMPONENT_NODE_DECLARE_GENERIC(Audio); DEG_COMPONENT_NODE_DECLARE_GENERIC(Armature); DEG_COMPONENT_NODE_DECLARE_GENERIC(GenericDatablock); @@ -190,6 +201,25 @@ DEG_COMPONENT_NODE_DECLARE_NO_COW(Visibility); DEG_COMPONENT_NODE_DECLARE_GENERIC(Simulation); DEG_COMPONENT_NODE_DECLARE_GENERIC(NTreeOutput); +/* Synchronization Component. */ +struct SynchronizationComponentNode : public ComponentNode { + SynchronizationComponentNode() + { + /* Enforce "visibility" of the synchronization component. + * + * This component is never connected to other ID nodes, and hence can not be handled in the + * same way as other components needed for evaluation. It is only needed for proper + * evaluation of the ID node it belongs to. + * + * The design is such that the synchronization is supposed to happen whenever any part of the + * ID changed/evaluated. Here we mark the component as "visible" so that genetic recalc flag + * flushing and scheduling will handle the component in a generic manner. */ + affects_directly_visible = true; + } + + DEG_COMPONENT_NODE_DECLARE; +}; + /* Bone Component */ struct BoneComponentNode : public ComponentNode { /** Initialize 'bone component' node - from pointer data given. */ @@ -218,5 +248,4 @@ struct ParametersComponentNode : public ComponentNode { void deg_register_component_depsnodes(); -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/node/deg_node_factory.h b/source/blender/depsgraph/intern/node/deg_node_factory.h index 6caf99ac9ba..ec5029e8352 100644 --- a/source/blender/depsgraph/intern/node/deg_node_factory.h +++ b/source/blender/depsgraph/intern/node/deg_node_factory.h @@ -14,9 +14,7 @@ struct ID; -namespace blender { -namespace deg { - +namespace blender::deg { struct DepsNodeFactory { virtual NodeType type() const = 0; virtual const char *type_name() const = 0; @@ -41,7 +39,6 @@ void register_node_typeinfo(DepsNodeFactory *factory); /* Get typeinfo for specified type */ DepsNodeFactory *type_get_factory(NodeType type); -} // namespace deg -} // namespace blender +} // namespace blender::deg #include "intern/node/deg_node_factory_impl.h" diff --git a/source/blender/depsgraph/intern/node/deg_node_factory_impl.h b/source/blender/depsgraph/intern/node/deg_node_factory_impl.h index 76a91860cc1..d9d0a1c1e3e 100644 --- a/source/blender/depsgraph/intern/node/deg_node_factory_impl.h +++ b/source/blender/depsgraph/intern/node/deg_node_factory_impl.h @@ -11,8 +11,7 @@ struct ID; -namespace blender { -namespace deg { +namespace blender::deg { template<class ModeObjectType> NodeType DepsNodeFactoryImpl<ModeObjectType>::type() const { @@ -48,5 +47,4 @@ Node *DepsNodeFactoryImpl<ModeObjectType>::create_node(const ID *id, return node; } -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/node/deg_node_id.h b/source/blender/depsgraph/intern/node/deg_node_id.h index a8b6294b482..406ca828049 100644 --- a/source/blender/depsgraph/intern/node/deg_node_id.h +++ b/source/blender/depsgraph/intern/node/deg_node_id.h @@ -12,8 +12,7 @@ #include "DNA_ID.h" #include "intern/node/deg_node.h" -namespace blender { -namespace deg { +namespace blender::deg { struct ComponentNode; @@ -117,5 +116,4 @@ struct IDNode : public Node { DEG_DEPSNODE_DECLARE; }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/node/deg_node_operation.cc b/source/blender/depsgraph/intern/node/deg_node_operation.cc index c29aeefd9b2..3029379d141 100644 --- a/source/blender/depsgraph/intern/node/deg_node_operation.cc +++ b/source/blender/depsgraph/intern/node/deg_node_operation.cc @@ -32,6 +32,8 @@ const char *operationCodeAsString(OperationCode opcode) return "PARAMETERS_EVAL"; case OperationCode::PARAMETERS_EXIT: return "PARAMETERS_EXIT"; + case OperationCode::VISIBILITY: + return "VISIBILITY"; /* Animation, Drivers, etc. */ case OperationCode::ANIMATION_ENTRY: return "ANIMATION_ENTRY"; diff --git a/source/blender/depsgraph/intern/node/deg_node_operation.h b/source/blender/depsgraph/intern/node/deg_node_operation.h index d4916be1113..fd772fbce9d 100644 --- a/source/blender/depsgraph/intern/node/deg_node_operation.h +++ b/source/blender/depsgraph/intern/node/deg_node_operation.h @@ -13,8 +13,7 @@ struct Depsgraph; -namespace blender { -namespace deg { +namespace blender::deg { struct ComponentNode; @@ -34,6 +33,7 @@ enum class OperationCode { PARAMETERS_ENTRY, PARAMETERS_EVAL, PARAMETERS_EXIT, + VISIBILITY, /* Animation, Drivers, etc. --------------------------------------------- */ /* NLA + Action */ @@ -202,13 +202,16 @@ const char *operationCodeAsString(OperationCode opcode); enum OperationFlag { /* Node needs to be updated. */ DEPSOP_FLAG_NEEDS_UPDATE = (1 << 0), + /* Node was directly modified, causing need for update. */ DEPSOP_FLAG_DIRECTLY_MODIFIED = (1 << 1), + /* Node was updated due to user input. */ DEPSOP_FLAG_USER_MODIFIED = (1 << 2), - /* Node may not be removed, even when it has no evaluation callback and no - * outgoing relations. This is for NO-OP nodes that are purely used to indicate a - * relation between components/IDs, and not for connecting to an operation. */ + + /* Node may not be removed, even when it has no evaluation callback and no outgoing relations. + * This is for NO-OP nodes that are purely used to indicate a relation between components/IDs, + * and not for connecting to an operation. */ DEPSOP_FLAG_PINNED = (1 << 3), /* Set of flags which gets flushed along the relations. */ @@ -268,5 +271,4 @@ struct OperationNode : public Node { void deg_register_operation_depsnodes(); -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/depsgraph/intern/node/deg_node_time.h b/source/blender/depsgraph/intern/node/deg_node_time.h index fcfe9a0fa80..3946b63384d 100644 --- a/source/blender/depsgraph/intern/node/deg_node_time.h +++ b/source/blender/depsgraph/intern/node/deg_node_time.h @@ -9,8 +9,7 @@ #include "intern/node/deg_node.h" -namespace blender { -namespace deg { +namespace blender::deg { /* Time Source Node. */ struct TimeSourceNode : public Node { @@ -25,5 +24,4 @@ struct TimeSourceNode : public Node { DEG_DEPSNODE_DECLARE; }; -} // namespace deg -} // namespace blender +} // namespace blender::deg diff --git a/source/blender/draw/engines/eevee/eevee_sampling.c b/source/blender/draw/engines/eevee/eevee_sampling.c index a1a3e98f34f..34d3cd74b36 100644 --- a/source/blender/draw/engines/eevee/eevee_sampling.c +++ b/source/blender/draw/engines/eevee/eevee_sampling.c @@ -74,7 +74,8 @@ void EEVEE_sample_ellipse(int sample_ofs, BLI_halton_2d(ht_primes, ht_offset, sample_ofs, ht_point); - /* Decorelate AA and shadow samples. (see T68594) */ + /* Decorrelate AA and shadow samples. (see T68594) */ + ht_point[0] = fmod(ht_point[0] * 1151.0, 1.0); ht_point[1] = fmod(ht_point[1] * 1069.0, 1.0); @@ -97,7 +98,7 @@ void EEVEE_random_rotation_m4(int sample_ofs, float scale, float r_mat[4][4]) BLI_halton_3d(ht_primes, ht_offset, sample_ofs, ht_point); - /* Decorelate AA and shadow samples. (see T68594) */ + /* Decorrelate AA and shadow samples. (see T68594) */ ht_point[0] = fmod(ht_point[0] * 1151.0, 1.0); ht_point[1] = fmod(ht_point[1] * 1069.0, 1.0); ht_point[2] = fmod(ht_point[2] * 1151.0, 1.0); diff --git a/source/blender/draw/engines/eevee/eevee_volumes.c b/source/blender/draw/engines/eevee/eevee_volumes.c index 8223df22ee9..533e71b9b32 100644 --- a/source/blender/draw/engines/eevee/eevee_volumes.c +++ b/source/blender/draw/engines/eevee/eevee_volumes.c @@ -316,12 +316,13 @@ void EEVEE_volumes_cache_object_add(EEVEE_ViewLayerData *sldata, DRWShadingGroup *grp = DRW_shgroup_create(sh, vedata->psl->volumetric_objects_ps); grp = DRW_shgroup_volume_create_sub(scene, ob, grp, mat); - DRW_shgroup_add_material_resources(grp, mat); if (grp == NULL) { return; } + DRW_shgroup_add_material_resources(grp, mat); + /* TODO(fclem): remove those "unnecessary" UBOs */ DRW_shgroup_uniform_block(grp, "planar_block", sldata->planar_ubo); DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo); diff --git a/source/blender/draw/engines/eevee/shaders/surface_lib.glsl b/source/blender/draw/engines/eevee/shaders/surface_lib.glsl index 8e1bafe8d92..80c6b935187 100644 --- a/source/blender/draw/engines/eevee/shaders/surface_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/surface_lib.glsl @@ -97,7 +97,7 @@ GlobalData init_globals(void) GlobalData surf; # if defined(WORLD_BACKGROUND) || defined(PROBE_CAPTURE) - surf.P = -cameraVec(worldPosition); + surf.P = transform_direction(ViewMatrixInverse, viewCameraVec(viewPosition)); surf.N = surf.Ng = -surf.P; surf.ray_length = 0.0; # else diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_vert.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_vert.glsl index 5f5419bac47..b0ee059cb9d 100644 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_vert.glsl +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_vert.glsl @@ -32,7 +32,7 @@ void main() vec3 vert_N; gpMaterial gp_mat = materials[ma1.x + gpMaterialOffset]; - gpMaterialFlag gp_flag = floatBitsToInt(gp_mat._flag); + gpMaterialFlag gp_flag = floatBitsToUint(gp_mat._flag); gl_Position = gpencil_vertex(ma, ma1, diff --git a/source/blender/draw/intern/draw_cache_impl_curves.cc b/source/blender/draw/intern/draw_cache_impl_curves.cc index d5f188402d6..1d3d6222f8f 100644 --- a/source/blender/draw/intern/draw_cache_impl_curves.cc +++ b/source/blender/draw/intern/draw_cache_impl_curves.cc @@ -75,13 +75,14 @@ static void curves_batch_cache_init(Curves &curves) if (!cache) { cache = MEM_cnew<CurvesBatchCache>(__func__); - BLI_mutex_init(&cache->render_mutex); curves.batch_cache = cache; } else { memset(cache, 0, sizeof(*cache)); } + BLI_mutex_init(&cache->render_mutex); + cache->is_dirty = false; } diff --git a/source/blender/draw/intern/draw_cache_impl_pointcloud.c b/source/blender/draw/intern/draw_cache_impl_pointcloud.c index d715899e291..55d0eee00e5 100644 --- a/source/blender/draw/intern/draw_cache_impl_pointcloud.c +++ b/source/blender/draw/intern/draw_cache_impl_pointcloud.c @@ -18,6 +18,7 @@ #include "DNA_object_types.h" #include "DNA_pointcloud_types.h" +#include "BKE_customdata.h" #include "BKE_pointcloud.h" #include "GPU_batch.h" @@ -139,7 +140,11 @@ static void pointcloud_batch_cache_ensure_pos(Object *ob, PointCloudBatchCache * } PointCloud *pointcloud = ob->data; - const bool has_radius = pointcloud->radius != NULL; + const float(*positions)[3] = (float(*)[3])CustomData_get_layer_named( + &pointcloud->pdata, CD_PROP_FLOAT3, "position"); + const float *radii = (float *)CustomData_get_layer_named( + &pointcloud->pdata, CD_PROP_FLOAT, "radius"); + const bool has_radius = radii != NULL; static GPUVertFormat format = {0}; static GPUVertFormat format_no_radius = {0}; @@ -162,14 +167,14 @@ static void pointcloud_batch_cache_ensure_pos(Object *ob, PointCloudBatchCache * if (has_radius) { float(*vbo_data)[4] = (float(*)[4])GPU_vertbuf_get_data(cache->pos); for (int i = 0; i < pointcloud->totpoint; i++) { - copy_v3_v3(vbo_data[i], pointcloud->co[i]); + copy_v3_v3(vbo_data[i], positions[i]); /* TODO(fclem): remove multiplication here. * Here only for keeping the size correct for now. */ - vbo_data[i][3] = pointcloud->radius[i] * 100.0f; + vbo_data[i][3] = radii[i] * 100.0f; } } else { - GPU_vertbuf_attr_fill(cache->pos, pos, pointcloud->co); + GPU_vertbuf_attr_fill(cache->pos, pos, positions); } } diff --git a/source/blender/draw/intern/draw_cache_impl_subdivision.cc b/source/blender/draw/intern/draw_cache_impl_subdivision.cc index 29ab814cfa4..cde2b59ea23 100644 --- a/source/blender/draw/intern/draw_cache_impl_subdivision.cc +++ b/source/blender/draw/intern/draw_cache_impl_subdivision.cc @@ -2045,7 +2045,7 @@ static bool draw_subdiv_create_requested_buffers(Object *ob, draw_subdiv_invalidate_evaluator_for_orco(subdiv, mesh_eval); if (!BKE_subdiv_eval_begin_from_mesh( - subdiv, mesh_eval, nullptr, SUBDIV_EVALUATOR_TYPE_GLSL_COMPUTE, evaluator_cache)) { + subdiv, mesh_eval, nullptr, SUBDIV_EVALUATOR_TYPE_GPU, evaluator_cache)) { /* This could happen in two situations: * - OpenSubdiv is disabled. * - Something totally bad happened, and OpenSubdiv rejected our @@ -2220,7 +2220,7 @@ void DRW_create_subdivision(Object *ob, const bool use_hide) { if (g_evaluator_cache == nullptr) { - g_evaluator_cache = openSubdiv_createEvaluatorCache(OPENSUBDIV_EVALUATOR_GLSL_COMPUTE); + g_evaluator_cache = openSubdiv_createEvaluatorCache(OPENSUBDIV_EVALUATOR_GPU); } #undef TIME_SUBDIV diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index bc9d0a3d02a..5d21ab75650 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -235,7 +235,7 @@ bool DRW_object_use_hide_faces(const struct Object *ob) return (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; case OB_MODE_VERTEX_PAINT: case OB_MODE_WEIGHT_PAINT: - return (me->editflag & (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0; + return true; } } diff --git a/source/blender/editors/animation/anim_deps.c b/source/blender/editors/animation/anim_deps.c index d80eac2422e..ff53ad42e84 100644 --- a/source/blender/editors/animation/anim_deps.c +++ b/source/blender/editors/animation/anim_deps.c @@ -356,7 +356,7 @@ void ANIM_animdata_update(bAnimContext *ac, ListBase *anim_data) if (ale->update & ANIM_UPDATE_HANDLES) { ale->update &= ~ANIM_UPDATE_HANDLES; if (fcu) { - calchandles_fcurve(fcu); + BKE_fcurve_handles_recalc(fcu); } } diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c index e20932fa53e..d9eeed94868 100644 --- a/source/blender/editors/animation/anim_filter.c +++ b/source/blender/editors/animation/anim_filter.c @@ -1807,11 +1807,13 @@ static size_t animdata_filter_gpencil_data(ListBase *anim_data, ListBase tmp_data = {NULL, NULL}; size_t tmp_items = 0; - /* add gpencil animation channels */ - BEGIN_ANIMFILTER_SUBCHANNELS (EXPANDED_GPD(gpd)) { - tmp_items += animdata_filter_gpencil_layers_data(&tmp_data, ads, gpd, filter_mode); + if (!(filter_mode & ANIMFILTER_FCURVESONLY)) { + /* add gpencil animation channels */ + BEGIN_ANIMFILTER_SUBCHANNELS (EXPANDED_GPD(gpd)) { + tmp_items += animdata_filter_gpencil_layers_data(&tmp_data, ads, gpd, filter_mode); + } + END_ANIMFILTER_SUBCHANNELS; } - END_ANIMFILTER_SUBCHANNELS; /* did we find anything? */ if (tmp_items) { diff --git a/source/blender/editors/animation/anim_markers.c b/source/blender/editors/animation/anim_markers.c index 03a2caf4b7d..3608140a29d 100644 --- a/source/blender/editors/animation/anim_markers.c +++ b/source/blender/editors/animation/anim_markers.c @@ -1588,12 +1588,13 @@ static void MARKER_OT_delete(wmOperatorType *ot) ot->idname = "MARKER_OT_delete"; /* api callbacks */ - ot->invoke = WM_operator_confirm; + ot->invoke = WM_operator_confirm_or_exec; ot->exec = ed_marker_delete_exec; ot->poll = ed_markers_poll_selected_no_locked_markers; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + WM_operator_properties_confirm_or_exec(ot); } /** \} */ diff --git a/source/blender/editors/animation/drivers.c b/source/blender/editors/animation/drivers.c index c6f68228807..effedd4307d 100644 --- a/source/blender/editors/animation/drivers.c +++ b/source/blender/editors/animation/drivers.c @@ -124,7 +124,7 @@ struct FCurve *alloc_driver_fcurve(const char rna_path[], insert_vert_fcurve( fcu, 1.0f, 1.0f, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_FAST | INSERTKEY_NO_USERPREF); fcu->extend = FCURVE_EXTRAPOLATE_LINEAR; - calchandles_fcurve(fcu); + BKE_fcurve_handles_recalc(fcu); } } diff --git a/source/blender/editors/animation/fmodifier_ui.c b/source/blender/editors/animation/fmodifier_ui.c index 6f31472907b..d2f0ee622c4 100644 --- a/source/blender/editors/animation/fmodifier_ui.c +++ b/source/blender/editors/animation/fmodifier_ui.c @@ -1020,7 +1020,7 @@ bool ANIM_fmodifiers_paste_from_buf(ListBase *modifiers, bool replace, FCurve *c /* adding or removing the Cycles modifier requires an update to handles */ if (curve && BKE_fcurve_is_cyclic(curve) != was_cyclic) { - calchandles_fcurve(curve); + BKE_fcurve_handles_recalc(curve); } /* did we succeed? */ diff --git a/source/blender/editors/animation/keyframes_edit.c b/source/blender/editors/animation/keyframes_edit.c index 88207f7d514..706db498a82 100644 --- a/source/blender/editors/animation/keyframes_edit.c +++ b/source/blender/editors/animation/keyframes_edit.c @@ -444,7 +444,7 @@ void ANIM_animdata_keyframe_callback(bAnimContext *ac, ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); for (ale = anim_data.first; ale; ale = ale->next) { - ANIM_fcurve_keyframes_loop(NULL, ale->key_data, NULL, callback_fn, calchandles_fcurve); + ANIM_fcurve_keyframes_loop(NULL, ale->key_data, NULL, callback_fn, BKE_fcurve_handles_recalc); ale->update |= ANIM_UPDATE_DEFAULT; } diff --git a/source/blender/editors/animation/keyframes_general.c b/source/blender/editors/animation/keyframes_general.c index dd88752af14..7723c221a40 100644 --- a/source/blender/editors/animation/keyframes_general.c +++ b/source/blender/editors/animation/keyframes_general.c @@ -47,77 +47,6 @@ /* **************************************************** */ -void delete_fcurve_key(FCurve *fcu, int index, bool do_recalc) -{ - /* sanity check */ - if (fcu == NULL) { - return; - } - - /* verify the index: - * 1) cannot be greater than the number of available keyframes - * 2) negative indices are for specifying a value from the end of the array - */ - if (abs(index) >= fcu->totvert) { - return; - } - if (index < 0) { - index += fcu->totvert; - } - - /* Delete this keyframe */ - memmove( - &fcu->bezt[index], &fcu->bezt[index + 1], sizeof(BezTriple) * (fcu->totvert - index - 1)); - fcu->totvert--; - - if (fcu->totvert == 0) { - MEM_SAFE_FREE(fcu->bezt); - } - - /* recalc handles - only if it won't cause problems */ - if (do_recalc) { - calchandles_fcurve(fcu); - } -} - -bool delete_fcurve_keys(FCurve *fcu) -{ - bool changed = false; - - if (fcu->bezt == NULL) { /* ignore baked curves */ - return false; - } - - /* Delete selected BezTriples */ - for (int i = 0; i < fcu->totvert; i++) { - if (fcu->bezt[i].f2 & SELECT) { - if (i == fcu->active_keyframe_index) { - BKE_fcurve_active_keyframe_set(fcu, NULL); - } - memmove(&fcu->bezt[i], &fcu->bezt[i + 1], sizeof(BezTriple) * (fcu->totvert - i - 1)); - fcu->totvert--; - i--; - changed = true; - } - } - - /* Free the array of BezTriples if there are not keyframes */ - if (fcu->totvert == 0) { - clear_fcurve_keys(fcu); - } - - return changed; -} - -void clear_fcurve_keys(FCurve *fcu) -{ - MEM_SAFE_FREE(fcu->bezt); - - fcu->totvert = 0; -} - -/* ---------------- */ - bool duplicate_fcurve_keys(FCurve *fcu) { bool changed = false; @@ -282,7 +211,7 @@ void clean_fcurve(struct bAnimContext *ac, bAnimListElem *ale, float thresh, boo } if (fcu->bezt->vec[1][1] == default_value) { - clear_fcurve_keys(fcu); + BKE_fcurve_delete_keys_all(fcu); /* check if curve is really unused and if it is, return signal for deletion */ if (BKE_fcurve_is_empty(fcu)) { @@ -679,7 +608,7 @@ void smooth_fcurve(FCurve *fcu) } /* recalculate handles */ - calchandles_fcurve(fcu); + BKE_fcurve_handles_recalc(fcu); } /* ---------------- */ @@ -762,7 +691,7 @@ void sample_fcurve(FCurve *fcu) } /* recalculate channel's handles? */ - calchandles_fcurve(fcu); + BKE_fcurve_handles_recalc(fcu); } /* **************************************************** */ @@ -1121,7 +1050,7 @@ static void paste_animedit_keys_fcurve( case KEYFRAME_PASTE_MERGE_OVER: /* remove all keys */ - clear_fcurve_keys(fcu); + BKE_fcurve_delete_keys_all(fcu); break; case KEYFRAME_PASTE_MERGE_OVER_RANGE: @@ -1148,7 +1077,7 @@ static void paste_animedit_keys_fcurve( } /* remove frames in the range */ - delete_fcurve_keys(fcu); + BKE_fcurve_delete_keys_selected(fcu); } break; } @@ -1182,7 +1111,7 @@ static void paste_animedit_keys_fcurve( } /* recalculate F-Curve's handles? */ - calchandles_fcurve(fcu); + BKE_fcurve_handles_recalc(fcu); } const EnumPropertyItem rna_enum_keyframe_paste_offset_items[] = { diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c index 2fa8907de71..9084b9bb214 100644 --- a/source/blender/editors/animation/keyframing.c +++ b/source/blender/editors/animation/keyframing.c @@ -639,7 +639,7 @@ int insert_vert_fcurve( * - we may calculate twice (due to auto-handle needing to be calculated twice) */ if ((flag & INSERTKEY_FAST) == 0) { - calchandles_fcurve(fcu); + BKE_fcurve_handles_recalc(fcu); } /* return the index at which the keyframe was added */ @@ -1282,10 +1282,12 @@ static bool insert_keyframe_value(ReportList *reports, /* delete keyframe immediately before/after newly added */ switch (insert_mode) { case KEYNEEDED_DELPREV: - delete_fcurve_key(fcu, fcu->totvert - 2, 1); + BKE_fcurve_delete_key(fcu, fcu->totvert - 2); + BKE_fcurve_handles_recalc(fcu); break; case KEYNEEDED_DELNEXT: - delete_fcurve_key(fcu, 1, 1); + BKE_fcurve_delete_key(fcu, 1); + BKE_fcurve_handles_recalc(fcu); break; } @@ -1683,7 +1685,8 @@ static bool delete_keyframe_fcurve(AnimData *adt, FCurve *fcu, float cfra) i = BKE_fcurve_bezt_binarysearch_index(fcu->bezt, cfra, fcu->totvert, &found); if (found) { /* delete the key at the index (will sanity check + do recalc afterwards) */ - delete_fcurve_key(fcu, i, 1); + BKE_fcurve_delete_key(fcu, i); + BKE_fcurve_handles_recalc(fcu); /* Only delete curve too if it won't be doing anything anymore */ if (BKE_fcurve_is_empty(fcu)) { @@ -2709,7 +2712,8 @@ static int delete_key_button_exec(bContext *C, wmOperator *op) i = BKE_fcurve_bezt_binarysearch_index(fcu->bezt, cfra, fcu->totvert, &found); if (found) { /* delete the key at the index (will sanity check + do recalc afterwards) */ - delete_fcurve_key(fcu, i, 1); + BKE_fcurve_delete_key(fcu, i); + BKE_fcurve_handles_recalc(fcu); changed = true; } } diff --git a/source/blender/editors/armature/armature_naming.c b/source/blender/editors/armature/armature_naming.c index 1f02e24666d..4f329dbe449 100644 --- a/source/blender/editors/armature/armature_naming.c +++ b/source/blender/editors/armature/armature_naming.c @@ -13,6 +13,7 @@ #include "MEM_guardedalloc.h" #include "DNA_armature_types.h" +#include "DNA_camera_types.h" #include "DNA_constraint_types.h" #include "DNA_gpencil_modifier_types.h" #include "DNA_gpencil_types.h" @@ -281,6 +282,17 @@ void ED_armature_bone_rename(Main *bmain, } } + /* fix camera focus */ + if (ob->type == OB_CAMERA) { + Camera *cam = (Camera *)ob->data; + if ((cam->dof.focus_object != NULL) && (cam->dof.focus_object->data == arm)) { + if (STREQ(cam->dof.focus_subtarget, oldname)) { + BLI_strncpy(cam->dof.focus_subtarget, newname, MAXBONENAME); + DEG_id_tag_update(&cam->id, ID_RECALC_COPY_ON_WRITE); + } + } + } + /* fix grease pencil modifiers and vertex groups */ if (ob->type == OB_GPENCIL) { diff --git a/source/blender/editors/armature/pose_lib.c b/source/blender/editors/armature/pose_lib.c index b8e7c2624fd..ff187a52154 100644 --- a/source/blender/editors/armature/pose_lib.c +++ b/source/blender/editors/armature/pose_lib.c @@ -24,6 +24,7 @@ #include "BKE_action.h" #include "BKE_animsys.h" #include "BKE_armature.h" +#include "BKE_fcurve.h" #include "BKE_idprop.h" #include "BKE_lib_id.h" #include "BKE_main.h" @@ -615,7 +616,8 @@ static int poselib_remove_exec(bContext *C, wmOperator *op) for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) { /* check if remove */ if (IS_EQF(bezt->vec[1][0], (float)marker->frame)) { - delete_fcurve_key(fcu, i, 1); + BKE_fcurve_delete_key(fcu, i); + BKE_fcurve_handles_recalc(fcu); break; } } @@ -1578,7 +1580,7 @@ static int poselib_preview_handle_event(bContext *UNUSED(C), wmOperator *op, con case EVT_PADMINUS: if (pld->searchstr[0]) { /* searching... */ - poselib_preview_handle_search(pld, event->type, event->ascii); + poselib_preview_handle_search(pld, event->type, WM_event_utf8_to_ascii(event)); } else { /* view manipulation (see above) */ @@ -1589,7 +1591,7 @@ static int poselib_preview_handle_event(bContext *UNUSED(C), wmOperator *op, con /* otherwise, assume that searching might be able to handle it */ default: - poselib_preview_handle_search(pld, event->type, event->ascii); + poselib_preview_handle_search(pld, event->type, WM_event_utf8_to_ascii(event)); break; } diff --git a/source/blender/editors/curve/editcurve_add.c b/source/blender/editors/curve/editcurve_add.c index ba5a7409ba7..ee6376ca95f 100644 --- a/source/blender/editors/curve/editcurve_add.c +++ b/source/blender/editors/curve/editcurve_add.c @@ -87,6 +87,8 @@ static const char *get_surf_defname(int type) return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "SurfCircle"); case CU_PRIM_PATCH: return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "SurfPatch"); + case CU_PRIM_TUBE: + return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "SurfCylinder"); case CU_PRIM_SPHERE: return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "SurfSphere"); case CU_PRIM_DONUT: diff --git a/source/blender/editors/curve/editfont.c b/source/blender/editors/curve/editfont.c index 611dbb2e80c..ceed12dcff1 100644 --- a/source/blender/editors/curve/editfont.c +++ b/source/blender/editors/curve/editfont.c @@ -59,7 +59,7 @@ static int kill_selection(Object *obedit, int ins); /** \name Internal Utilities * \{ */ -static char32_t findaccent(char32_t char1, uint code) +static char32_t findaccent(char32_t char1, const char code) { char32_t new = 0; @@ -1638,12 +1638,11 @@ static int insert_text_invoke(bContext *C, wmOperator *op, const wmEvent *event) Object *obedit = CTX_data_edit_object(C); Curve *cu = obedit->data; EditFont *ef = cu->editfont; - static int accentcode = 0; - uintptr_t ascii = event->ascii; + static bool accentcode = false; const bool alt = event->modifier & KM_ALT; const bool shift = event->modifier & KM_SHIFT; const bool ctrl = event->modifier & KM_CTRL; - int event_type = event->type, event_val = event->val; + char32_t insert_char_override = 0; char32_t inserted_text[2] = {0}; if (RNA_struct_property_is_set(op->ptr, "text")) { @@ -1652,48 +1651,47 @@ static int insert_text_invoke(bContext *C, wmOperator *op, const wmEvent *event) if (RNA_struct_property_is_set(op->ptr, "accent")) { if (ef->len != 0 && ef->pos > 0) { - accentcode = 1; + accentcode = true; } return OPERATOR_FINISHED; } - /* tab should exit editmode, but we allow it to be typed using modifier keys */ - if (event_type == EVT_TABKEY) { - if ((alt || ctrl || shift) == 0) { - return OPERATOR_PASS_THROUGH; - } - - ascii = 9; - } - - if (event_type == EVT_BACKSPACEKEY) { + if (event->type == EVT_BACKSPACEKEY) { if (alt && ef->len != 0 && ef->pos > 0) { - accentcode = 1; + accentcode = true; } return OPERATOR_PASS_THROUGH; } - if (event_val && (ascii || event->utf8_buf[0])) { - /* handle case like TAB (== 9) */ - if ((ascii > 31 && ascii < 254 && ascii != 127) || (ELEM(ascii, 13, 10)) || (ascii == 8) || - (event->utf8_buf[0])) { + /* Tab typically exit edit-mode, but we allow it to be typed using modifier keys. */ + if (event->type == EVT_TABKEY) { + if ((alt || ctrl || shift) == 0) { + return OPERATOR_PASS_THROUGH; + } + insert_char_override = '\t'; + } + if (insert_char_override || event->utf8_buf[0]) { + if (insert_char_override) { + /* Handle case like TAB ('\t'). */ + inserted_text[0] = insert_char_override; + insert_into_textbuf(obedit, insert_char_override); + text_update_edited(C, obedit, FO_EDIT); + } + else { + BLI_assert(event->utf8_buf[0]); if (accentcode) { if (ef->pos > 0) { - inserted_text[0] = findaccent(ef->textbuf[ef->pos - 1], ascii); + inserted_text[0] = findaccent(ef->textbuf[ef->pos - 1], + BLI_str_utf8_as_unicode(event->utf8_buf)); ef->textbuf[ef->pos - 1] = inserted_text[0]; } - accentcode = 0; + accentcode = false; } else if (event->utf8_buf[0]) { inserted_text[0] = BLI_str_utf8_as_unicode(event->utf8_buf); - ascii = inserted_text[0]; - insert_into_textbuf(obedit, ascii); - accentcode = 0; - } - else if (ascii) { - insert_into_textbuf(obedit, ascii); - accentcode = 0; + insert_into_textbuf(obedit, inserted_text[0]); + accentcode = false; } else { BLI_assert(0); @@ -1702,11 +1700,6 @@ static int insert_text_invoke(bContext *C, wmOperator *op, const wmEvent *event) kill_selection(obedit, 1); text_update_edited(C, obedit, FO_EDIT); } - else { - inserted_text[0] = ascii; - insert_into_textbuf(obedit, ascii); - text_update_edited(C, obedit, FO_EDIT); - } } else { return OPERATOR_PASS_THROUGH; @@ -1720,11 +1713,6 @@ static int insert_text_invoke(bContext *C, wmOperator *op, const wmEvent *event) RNA_string_set(op->ptr, "text", inserted_utf8); } - /* reset property? */ - if (event_val == 0) { - accentcode = 0; - } - return OPERATOR_FINISHED; } diff --git a/source/blender/editors/curves/intern/curves_ops.cc b/source/blender/editors/curves/intern/curves_ops.cc index a4492a1d516..f4d1c8046f1 100644 --- a/source/blender/editors/curves/intern/curves_ops.cc +++ b/source/blender/editors/curves/intern/curves_ops.cc @@ -528,9 +528,6 @@ static void snap_curves_to_surface_exec_object(Object &curves_ob, Mesh &surface_mesh = *static_cast<Mesh *>(surface_ob.data); - MeshComponent surface_mesh_component; - surface_mesh_component.replace(&surface_mesh, GeometryOwnershipType::ReadOnly); - VArraySpan<float2> surface_uv_map; if (curves_id.surface_uv_map != nullptr) { const bke::AttributeAccessor surface_attributes = bke::mesh_attributes(surface_mesh); diff --git a/source/blender/editors/gpencil/gpencil_convert.c b/source/blender/editors/gpencil/gpencil_convert.c index 25b5466e260..e02a82f4555 100644 --- a/source/blender/editors/gpencil/gpencil_convert.c +++ b/source/blender/editors/gpencil/gpencil_convert.c @@ -588,7 +588,7 @@ static void gpencil_stroke_path_animation(bContext *C, } /* As we used INSERTKEY_FAST mode, we need to recompute all curve's handles now */ - calchandles_fcurve(fcu); + BKE_fcurve_handles_recalc(fcu); WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index 06f3c169fa9..71cf9b1fafd 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -3709,35 +3709,44 @@ static int gpencil_stroke_flip_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } + const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd); + bool changed = false; - /* read all selected strokes */ + /* Read all selected strokes. */ CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { - bGPDframe *gpf = gpl->actframe; - if (gpf == NULL) { - continue; - } + bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe; - LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { - if (gps->flag & GP_STROKE_SELECT) { - /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps) == false) { - continue; - } - /* check if the color is editable */ - if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) { + for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { + if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { + if (gpf == NULL) { continue; } + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { + if (gps->flag & GP_STROKE_SELECT) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) { + continue; + } + /* check if the color is editable */ + if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) { + continue; + } - if (is_curve_edit) { - BKE_report(op->reports, RPT_ERROR, "Not implemented!"); - } - else { - /* Flip stroke. */ - BKE_gpencil_stroke_flip(gps); + if (is_curve_edit) { + BKE_report(op->reports, RPT_ERROR, "Not implemented!"); + } + else { + /* Flip stroke. */ + BKE_gpencil_stroke_flip(gps); + changed = true; + } + } } - - changed = true; + } + /* If not multi-edit, exit loop. */ + if (!is_multiedit) { + break; } } } diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h index ac3b4133007..cc3c68abc55 100644 --- a/source/blender/editors/include/ED_anim_api.h +++ b/source/blender/editors/include/ED_anim_api.h @@ -1046,6 +1046,8 @@ void ED_keymap_anim(struct wmKeyConfig *keyconf); void ED_operatormacros_graph(void); /* space_action */ void ED_operatormacros_action(void); +/* space_nla*/ +void ED_operatormacros_nla(void); /** \} */ diff --git a/source/blender/editors/include/ED_keyframes_edit.h b/source/blender/editors/include/ED_keyframes_edit.h index 9596c3a7fed..1d63e01c84b 100644 --- a/source/blender/editors/include/ED_keyframes_edit.h +++ b/source/blender/editors/include/ED_keyframes_edit.h @@ -388,9 +388,6 @@ bool keyframe_region_circle_test(const KeyframeEdit_CircleData *data_circle, con /* ************************************************ */ /* Destructive Editing API (keyframes_general.c) */ -void delete_fcurve_key(struct FCurve *fcu, int index, bool do_recalc); -bool delete_fcurve_keys(struct FCurve *fcu); -void clear_fcurve_keys(struct FCurve *fcu); bool duplicate_fcurve_keys(struct FCurve *fcu); float get_default_rna_value(struct FCurve *fcu, struct PropertyRNA *prop, struct PointerRNA *ptr); diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h index 30a98129ee6..b73b62d5a9d 100644 --- a/source/blender/editors/include/ED_mesh.h +++ b/source/blender/editors/include/ED_mesh.h @@ -137,7 +137,6 @@ void EDBM_update_extern(struct Mesh *me, bool do_tessellation, bool is_destructi */ struct UvElementMap *BM_uv_element_map_create(struct BMesh *bm, const struct Scene *scene, - bool face_selected, bool uv_selected, bool use_winding, bool do_islands); @@ -425,6 +424,9 @@ void paintvert_select_ungrouped(struct Object *ob, bool extend, bool flush_flags void paintvert_flush_flags(struct Object *ob); void paintvert_tag_select_update(struct bContext *C, struct Object *ob); +void paintvert_hide(struct bContext *C, struct Object *ob, bool unselected); +void paintvert_reveal(struct bContext *C, struct Object *ob, bool select); + /* mirrtopo */ typedef struct MirrTopoStore_t { intptr_t *index_lookup; diff --git a/source/blender/editors/include/UI_abstract_view.hh b/source/blender/editors/include/UI_abstract_view.hh index 82f81f1702b..dfddace8899 100644 --- a/source/blender/editors/include/UI_abstract_view.hh +++ b/source/blender/editors/include/UI_abstract_view.hh @@ -3,11 +3,16 @@ /** \file * \ingroup editorui * - * Base for all views (UIs to display data sets), supporting common features. + * Base class for all views (UIs to display data sets) and view items, supporting common features. * https://wiki.blender.org/wiki/Source/Interface/Views * * One of the most important responsibilities of the base class is managing reconstruction, - * enabling state that is persistent over reconstructions/redraws. + * enabling state that is persistent over reconstructions/redraws. Other features: + * - Renaming + * - Custom context menus + * - Notifier listening + * - Drag controllers (dragging view items) + * - Drop controllers (dropping onto/into view items) */ #pragma once @@ -15,13 +20,28 @@ #include <array> #include <memory> +#include "DNA_defs.h" + #include "BLI_span.hh" +#include "BLI_string_ref.hh" +struct bContext; +struct uiBlock; +struct uiBut; +struct uiLayout; +struct uiViewItemHandle; +struct wmDrag; struct wmNotifier; namespace blender::ui { +class AbstractViewItem; +class AbstractViewItemDropController; +class AbstractViewItemDragController; + class AbstractView { + friend class AbstractViewItem; + bool is_reconstructed_ = false; /** * Only one item can be renamed at a time. So rather than giving each item an own rename buffer @@ -38,6 +58,12 @@ class AbstractView { /** Listen to a notifier, returning true if a redraw is needed. */ virtual bool listen(const wmNotifier &) const; + /** + * Makes \a item valid for display in this view. Behavior is undefined for items not registered + * with this. + */ + void register_item(AbstractViewItem &item); + /** Only one item can be renamed at a time. */ bool is_renaming() const; /** \return If renaming was started successfully. */ @@ -58,7 +84,6 @@ class AbstractView { * After this, reconstruction is complete (see #is_reconstructed()). */ void update_from_old(uiBlock &new_block); - /** * Check if the view is fully (re-)constructed. That means, both the build function and * #update_from_old() have finished. @@ -66,4 +91,190 @@ class AbstractView { bool is_reconstructed() const; }; +class AbstractViewItem { + friend class AbstractView; + friend class ViewItemAPIWrapper; + + protected: + /** + * The view this item is a part of, and was registered for using #AbstractView::register_item(). + * If this wasn't done, the behavior of items is undefined. + */ + AbstractView *view_ = nullptr; + bool is_active_ = false; + bool is_renaming_ = false; + + public: + virtual ~AbstractViewItem() = default; + + virtual void build_context_menu(bContext &C, uiLayout &column) const; + + /** + * Queries if the view item supports renaming in principle. Renaming may still fail, e.g. if + * another item is already being renamed. + */ + virtual bool supports_renaming() const; + /** + * Try renaming the item, or the data it represents. Can assume + * #AbstractViewItem::supports_renaming() returned true. Sub-classes that override this should + * usually call this, unless they have a custom #AbstractViewItem.matches() implementation. + * + * \return True if the renaming was successful. + */ + virtual bool rename(StringRefNull new_name); + /** + * Get the string that should be used for renaming, typically the item's label. This string will + * not be modified, but if the renaming is canceled, the value will be reset to this. + */ + virtual StringRef get_rename_string() const; + + /** + * If an item wants to support being dragged, it has to return a drag controller here. + * That is an object implementing #AbstractViewItemDragController. + */ + virtual std::unique_ptr<AbstractViewItemDragController> create_drag_controller() const; + /** + * If an item wants to support dropping data into it, it has to return a drop controller here. + * That is an object implementing #AbstractViewItemDropController. + * + * \note This drop controller may be requested for each event. The view doesn't keep a drop + * controller around currently. So it can not contain persistent state. + */ + virtual std::unique_ptr<AbstractViewItemDropController> create_drop_controller() const; + + /** Get the view this item is registered for using #AbstractView::register_item(). */ + AbstractView &get_view() const; + + /** + * Requires the view to have completed reconstruction, see #is_reconstructed(). Otherwise we + * can't be sure about the item state. + */ + bool is_active() const; + + bool is_renaming() const; + void begin_renaming(); + void end_renaming(); + void rename_apply(); + + template<typename ToType = AbstractViewItem> + static ToType *from_item_handle(uiViewItemHandle *handle); + + protected: + AbstractViewItem() = default; + + /** + * Compare this item's identity to \a other to check if they represent the same data. + * Implementations can assume that the types match already (caller must check). + * + * Used to recognize an item from a previous redraw, to be able to keep its state (e.g. active, + * renaming, etc.). + */ + virtual bool matches(const AbstractViewItem &other) const = 0; + + /** + * Copy persistent state (e.g. active, selection, etc.) from a matching item of + * the last redraw to this item. If sub-classes introduce more advanced state they should + * override this and make it update their state accordingly. + * + * \note Always call the base class implementation when overriding this! + */ + virtual void update_from_old(const AbstractViewItem &old); + + /** + * Add a text button for renaming the item to \a block. This must be used for the built-in + * renaming to work. This button is meant to appear temporarily. It is removed when renaming is + * done. + */ + void add_rename_button(uiBlock &block); +}; + +template<typename ToType> ToType *AbstractViewItem::from_item_handle(uiViewItemHandle *handle) +{ + static_assert(std::is_base_of<AbstractViewItem, ToType>::value, + "Type must derive from and implement the AbstractViewItem interface"); + + return dynamic_cast<ToType *>(reinterpret_cast<AbstractViewItem *>(handle)); +} + +/* ---------------------------------------------------------------------- */ +/** \name Drag 'n Drop + * \{ */ + +/** + * Class to enable dragging a view item. An item can return a drop controller for itself by + * implementing #AbstractViewItem::create_drag_controller(). + */ +class AbstractViewItemDragController { + protected: + AbstractView &view_; + + public: + AbstractViewItemDragController(AbstractView &view); + virtual ~AbstractViewItemDragController() = default; + + virtual int get_drag_type() const = 0; + virtual void *create_drag_data() const = 0; + virtual void on_drag_start(); + + /** Request the view the item is registered for as type #ViewType. Throws a `std::bad_cast` + * exception if the view is not of the requested type. */ + template<class ViewType> inline ViewType &get_view() const; +}; + +/** + * Class to define the behavior when dropping something onto/into a view item, plus the behavior + * when dragging over this item. An item can return a drop controller for itself via a custom + * implementation of #AbstractViewItem::create_drop_controller(). + */ +class AbstractViewItemDropController { + protected: + AbstractView &view_; + + public: + AbstractViewItemDropController(AbstractView &view); + virtual ~AbstractViewItemDropController() = default; + + /** + * Check if the data dragged with \a drag can be dropped on the item this controller is for. + * \param r_disabled_hint: Return a static string to display to the user, explaining why dropping + * isn't possible on this item. Shouldn't be done too aggressively, e.g. + * don't set this if the drag-type can't be dropped here; only if it can + * but there's another reason it can't be dropped. + * Can assume this is a non-null pointer. + */ + virtual bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const = 0; + /** + * Custom text to display when dragging over a view item. Should explain what happens when + * dropping the data onto this item. Will only be used if #AbstractViewItem::can_drop() + * returns true, so the implementing override doesn't have to check that again. + * The returned value must be a translated string. + */ + virtual std::string drop_tooltip(const wmDrag &drag) const = 0; + /** + * Execute the logic to apply a drop of the data dragged with \a drag onto/into the item this + * controller is for. + */ + virtual bool on_drop(struct bContext *C, const wmDrag &drag) = 0; + + /** Request the view the item is registered for as type #ViewType. Throws a `std::bad_cast` + * exception if the view is not of the requested type. */ + template<class ViewType> inline ViewType &get_view() const; +}; + +template<class ViewType> ViewType &AbstractViewItemDragController::get_view() const +{ + static_assert(std::is_base_of<AbstractView, ViewType>::value, + "Type must derive from and implement the ui::AbstractView interface"); + return dynamic_cast<ViewType &>(view_); +} + +template<class ViewType> ViewType &AbstractViewItemDropController::get_view() const +{ + static_assert(std::is_base_of<AbstractView, ViewType>::value, + "Type must derive from and implement the ui::AbstractView interface"); + return dynamic_cast<ViewType &>(view_); +} + +/** \} */ + } // namespace blender::ui diff --git a/source/blender/editors/include/UI_grid_view.hh b/source/blender/editors/include/UI_grid_view.hh index cabc49e411c..805198f38ef 100644 --- a/source/blender/editors/include/UI_grid_view.hh +++ b/source/blender/editors/include/UI_grid_view.hh @@ -19,7 +19,7 @@ struct bContext; struct PreviewImage; struct uiBlock; -struct uiButGridTile; +struct uiButViewItem; struct uiLayout; struct View2D; struct wmNotifier; @@ -32,43 +32,31 @@ class AbstractGridView; /** \name Grid-View Item Type * \{ */ -class AbstractGridViewItem { +class AbstractGridViewItem : public AbstractViewItem { friend class AbstractGridView; friend class GridViewLayoutBuilder; const AbstractGridView *view_; - bool is_active_ = false; - protected: /** Reference to a string that uniquely identifies this item in the view. */ StringRef identifier_{}; - /** Every visible item gets a button of type #UI_BTYPE_GRID_TILE during the layout building. */ - uiButGridTile *grid_tile_but_ = nullptr; + /** Every visible item gets a button of type #UI_BTYPE_VIEW_ITEM during the layout building. */ + uiButViewItem *view_item_but_ = nullptr; public: virtual ~AbstractGridViewItem() = default; virtual void build_grid_tile(uiLayout &layout) const = 0; - /** - * Compare this item's identifier to \a other to check if they represent the same data. - * Used to recognize an item from a previous redraw, to be able to keep its state (e.g. active, - * renaming, etc.). - */ - bool matches(const AbstractGridViewItem &other) const; - const AbstractGridView &get_view() const; - /** - * Requires the tree to have completed reconstruction, see #is_reconstructed(). Otherwise we - * can't be sure about the item state. - */ - bool is_active() const; - protected: AbstractGridViewItem(StringRef identifier); + /** See AbstractViewItem::matches(). */ + virtual bool matches(const AbstractViewItem &other) const override; + /** Called when the item's state changes from inactive to active. */ virtual void on_activate(); /** @@ -78,13 +66,6 @@ class AbstractGridViewItem { virtual std::optional<bool> should_be_active() const; /** - * Copy persistent state (e.g. active, selection, etc.) from a matching item of - * the last redraw to this item. If sub-classes introduce more advanced state they should - * override this and make it update their state accordingly. - */ - virtual void update_from_old(const AbstractGridViewItem &old); - - /** * Activates this item, deactivates other items, and calls the * #AbstractGridViewItem::on_activate() function. * Requires the tree to have completed reconstruction, see #is_reconstructed(). Otherwise the diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index f39c62d8e2b..a8d25b75036 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -74,10 +74,8 @@ typedef struct uiLayout uiLayout; typedef struct uiPopupBlockHandle uiPopupBlockHandle; /* C handle for C++ #ui::AbstractView type. */ typedef struct uiViewHandle uiViewHandle; -/* C handle for C++ #ui::AbstractTreeViewItem type. */ -typedef struct uiTreeViewItemHandle uiTreeViewItemHandle; -/* C handle for C++ #ui::AbstractGridViewItem type. */ -typedef struct uiGridViewItemHandle uiGridViewItemHandle; +/* C handle for C++ #ui::AbstractViewItem type. */ +typedef struct uiViewItemHandle uiViewItemHandle; /* Defines */ @@ -391,10 +389,8 @@ typedef enum { /** Resize handle (resize uilist). */ UI_BTYPE_GRIP = 57 << 9, UI_BTYPE_DECORATOR = 58 << 9, - /* An item in a tree view. Parent items may be collapsible. */ - UI_BTYPE_TREEROW = 59 << 9, - /* An item in a grid view. */ - UI_BTYPE_GRID_TILE = 60 << 9, + /* An item a view (see #ui::AbstractViewItem). */ + UI_BTYPE_VIEW_ITEM = 59 << 9, } eButType; #define BUTTYPE (63 << 9) @@ -1685,8 +1681,6 @@ int UI_search_items_find_index(uiSearchItems *items, const char *name); */ void UI_but_hint_drawstr_set(uiBut *but, const char *string); -void UI_but_treerow_indentation_set(uiBut *but, int indentation); - void UI_but_node_link_set(uiBut *but, struct bNodeSocket *socket, const float draw_color[4]); void UI_but_number_step_size_set(uiBut *but, float step_size); @@ -3201,45 +3195,44 @@ void UI_interface_tag_script_reload(void); void UI_block_views_listen(const uiBlock *block, const struct wmRegionListenerParams *listener_params); -bool UI_grid_view_item_is_active(const uiGridViewItemHandle *item_handle); -bool UI_tree_view_item_is_active(const uiTreeViewItemHandle *item); -bool UI_grid_view_item_matches(const uiGridViewItemHandle *a, const uiGridViewItemHandle *b); -bool UI_tree_view_item_matches(const uiTreeViewItemHandle *a, const uiTreeViewItemHandle *b); +bool UI_view_item_is_active(const uiViewItemHandle *item_handle); +bool UI_view_item_matches(const uiViewItemHandle *a_handle, const uiViewItemHandle *b_handle); /** - * Attempt to start dragging the tree-item \a item_. This will not work if the tree item doesn't - * support dragging, i.e. it won't create a drag-controller upon request. - * \return True if dragging started successfully, otherwise false. + * Can \a item_handle be renamed right now? Note that this isn't just a mere wrapper around + * #AbstractViewItem::supports_renaming(). This also checks if there is another item being renamed, + * and returns false if so. */ -bool UI_tree_view_item_drag_start(struct bContext *C, uiTreeViewItemHandle *item_); -bool UI_tree_view_item_can_drop(const uiTreeViewItemHandle *item_, - const struct wmDrag *drag, - const char **r_disabled_hint); -char *UI_tree_view_item_drop_tooltip(const uiTreeViewItemHandle *item, const struct wmDrag *drag); +bool UI_view_item_can_rename(const uiViewItemHandle *item_handle); +void UI_view_item_begin_rename(uiViewItemHandle *item_handle); + +void UI_view_item_context_menu_build(struct bContext *C, + const uiViewItemHandle *item_handle, + uiLayout *column); + /** - * Let a tree-view item handle a drop event. - * \return True if the drop was handled by the tree-view item. + * Attempt to start dragging \a item_. This will not work if the view item doesn't + * support dragging, i.e. if it won't create a drag-controller upon request. + * \return True if dragging started successfully, otherwise false. */ -bool UI_tree_view_item_drop_handle(struct bContext *C, - const uiTreeViewItemHandle *item_, - const struct ListBase *drags); +bool UI_view_item_drag_start(struct bContext *C, const uiViewItemHandle *item_); +bool UI_view_item_can_drop(const uiViewItemHandle *item_, + const struct wmDrag *drag, + const char **r_disabled_hint); +char *UI_view_item_drop_tooltip(const uiViewItemHandle *item, const struct wmDrag *drag); /** - * Can \a item_handle be renamed right now? Not that this isn't just a mere wrapper around - * #AbstractTreeViewItem::can_rename(). This also checks if there is another item being renamed, - * and returns false if so. + * Let a view item handle a drop event. + * \return True if the drop was handled by the view item. */ -bool UI_tree_view_item_can_rename(const uiTreeViewItemHandle *item_handle); -void UI_tree_view_item_begin_rename(uiTreeViewItemHandle *item_handle); - -void UI_tree_view_item_context_menu_build(struct bContext *C, - const uiTreeViewItemHandle *item, - uiLayout *column); +bool UI_view_item_drop_handle(struct bContext *C, + const uiViewItemHandle *item_, + const struct ListBase *drags); /** - * \param xy: Coordinate to find a tree-row item at, in window space. + * \param xy: Coordinate to find a view item at, in window space. */ -uiTreeViewItemHandle *UI_block_tree_view_find_item_at(const struct ARegion *region, - const int xy[2]) ATTR_NONNULL(1, 2); -uiTreeViewItemHandle *UI_block_tree_view_find_active_item(const struct ARegion *region); +uiViewItemHandle *UI_region_views_find_item_at(const struct ARegion *region, const int xy[2]) + ATTR_NONNULL(); +uiViewItemHandle *UI_region_views_find_active_item(const struct ARegion *region); #ifdef __cplusplus } diff --git a/source/blender/editors/include/UI_tree_view.hh b/source/blender/editors/include/UI_tree_view.hh index 9527df590b7..872a6085060 100644 --- a/source/blender/editors/include/UI_tree_view.hh +++ b/source/blender/editors/include/UI_tree_view.hh @@ -9,7 +9,6 @@ #pragma once -#include <array> #include <functional> #include <memory> #include <string> @@ -25,18 +24,13 @@ struct bContext; struct uiBlock; struct uiBut; -struct uiButTreeRow; +struct uiButViewItem; struct uiLayout; -struct wmDrag; -struct wmEvent; -struct wmNotifier; namespace blender::ui { class AbstractTreeView; class AbstractTreeViewItem; -class AbstractTreeViewItemDropController; -class AbstractTreeViewItemDragController; /* ---------------------------------------------------------------------- */ /** \name Tree-View Item Container @@ -153,7 +147,7 @@ class AbstractTreeView : public AbstractView, public TreeViewItemContainer { * It also stores state information that needs to be persistent over redraws, like the collapsed * state. */ -class AbstractTreeViewItem : public TreeViewItemContainer { +class AbstractTreeViewItem : public AbstractViewItem, public TreeViewItemContainer { friend class AbstractTreeView; friend class TreeViewLayoutBuilder; /* Higher-level API. */ @@ -161,20 +155,17 @@ class AbstractTreeViewItem : public TreeViewItemContainer { private: bool is_open_ = false; - bool is_active_ = false; - bool is_renaming_ = false; protected: /** This label is used as the default way to identifying an item within its parent. */ std::string label_{}; - /** Every visible item gets a button of type #UI_BTYPE_TREEROW during the layout building. */ - uiButTreeRow *tree_row_but_ = nullptr; + /** Every visible item gets a button of type #UI_BTYPE_VIEW_ITEM during the layout building. */ + uiButViewItem *view_item_but_ = nullptr; public: virtual ~AbstractTreeViewItem() = default; virtual void build_row(uiLayout &row) = 0; - virtual void build_context_menu(bContext &C, uiLayout &column) const; AbstractTreeView &get_tree_view() const; @@ -186,11 +177,6 @@ class AbstractTreeViewItem : public TreeViewItemContainer { * can't be sure about the item state. */ bool is_collapsed() const; - /** - * Requires the tree to have completed reconstruction, see #is_reconstructed(). Otherwise we - * can't be sure about the item state. - */ - bool is_active() const; protected: /** @@ -203,31 +189,21 @@ class AbstractTreeViewItem : public TreeViewItemContainer { */ virtual std::optional<bool> should_be_active() const; - /** - * Queries if the tree-view item supports renaming in principle. Renaming may still fail, e.g. if - * another item is already being renamed. - */ - virtual bool supports_renaming() const; - /** - * Try renaming the item, or the data it represents. Can assume - * #AbstractTreeViewItem::supports_renaming() returned true. Sub-classes that override this - * should usually call this, unless they have a custom #AbstractTreeViewItem.matches(). - * - * \return True if the renaming was successful. - */ - virtual bool rename(StringRefNull new_name); + /** See AbstractViewItem::get_rename_string(). */ + virtual StringRef get_rename_string() const override; + /** See AbstractViewItem::rename(). */ + virtual bool rename(StringRefNull new_name) override; /** * Return whether the item can be collapsed. Used to disable collapsing for items with children. */ virtual bool supports_collapsing() const; - /** - * Copy persistent state (e.g. is-collapsed flag, selection, etc.) from a matching item of - * the last redraw to this item. If sub-classes introduce more advanced state they should - * override this and make it update their state accordingly. - */ - virtual void update_from_old(const AbstractTreeViewItem &old); + /** See #AbstractViewItem::matches(). */ + virtual bool matches(const AbstractViewItem &other) const override; + + /** See #AbstractViewItem::update_from_old(). */ + virtual void update_from_old(const AbstractViewItem &old) override; /** * Compare this item to \a other to check if they represent the same data. @@ -235,22 +211,11 @@ class AbstractTreeViewItem : public TreeViewItemContainer { * open/closed, active, etc.). Items are only matched if their parents also match. * By default this just matches the item's label (if the parents match!). If that isn't * good enough for a sub-class, that can override it. - */ - virtual bool matches(const AbstractTreeViewItem &other) const; - - /** - * If an item wants to support being dragged, it has to return a drag controller here. - * That is an object implementing #AbstractTreeViewItemDragController. - */ - virtual std::unique_ptr<AbstractTreeViewItemDragController> create_drag_controller() const; - /** - * If an item wants to support dropping data into it, it has to return a drop controller here. - * That is an object implementing #AbstractTreeViewItemDropController. * - * \note This drop controller may be requested for each event. The tree-view doesn't keep a drop - * controller around currently. So it can not contain persistent state. + * TODO #matches_single() is a rather temporary name, used to indicate that this only compares + * the item itself, not the parents. Item matching is expected to change quite a bit anyway. */ - virtual std::unique_ptr<AbstractTreeViewItemDropController> create_drop_controller() const; + virtual bool matches_single(const AbstractTreeViewItem &other) const; /** * Activates this item, deactivates other items, calls the #AbstractTreeViewItem::on_activate() @@ -268,29 +233,24 @@ class AbstractTreeViewItem : public TreeViewItemContainer { */ bool is_hovered() const; bool is_collapsible() const; - bool is_renaming() const; void ensure_parents_uncollapsed(); - uiButTreeRow *tree_row_button(); + uiButViewItem *view_item_button(); private: - static void rename_button_fn(bContext *, void *, char *); - static AbstractTreeViewItem *find_tree_item_from_rename_button(const uiBut &but); static void tree_row_click_fn(struct bContext *, void *, void *); static void collapse_chevron_click_fn(bContext *, void *but_arg1, void *); static bool is_collapse_chevron_but(const uiBut *but); /** See #AbstractTreeView::change_state_delayed() */ void change_state_delayed(); - void end_renaming(); void add_treerow_button(uiBlock &block); void add_indent(uiLayout &row) const; void add_collapse_chevron(uiBlock &block) const; void add_rename_button(uiLayout &row); - bool matches_including_parents(const AbstractTreeViewItem &other) const; bool has_active_child() const; int count_parents() const; }; @@ -298,69 +258,6 @@ class AbstractTreeViewItem : public TreeViewItemContainer { /** \} */ /* ---------------------------------------------------------------------- */ -/** \name Drag 'n Drop - * \{ */ - -/** - * Class to enable dragging a tree-item. An item can return a drop controller for itself via a - * custom implementation of #AbstractTreeViewItem::create_drag_controller(). - */ -class AbstractTreeViewItemDragController { - protected: - AbstractTreeView &tree_view_; - - public: - AbstractTreeViewItemDragController(AbstractTreeView &tree_view); - virtual ~AbstractTreeViewItemDragController() = default; - - virtual int get_drag_type() const = 0; - virtual void *create_drag_data() const = 0; - virtual void on_drag_start(); - - template<class TreeViewType> inline TreeViewType &tree_view() const; -}; - -/** - * Class to customize the drop behavior of a tree-item, plus the behavior when dragging over this - * item. An item can return a drop controller for itself via a custom implementation of - * #AbstractTreeViewItem::create_drop_controller(). - */ -class AbstractTreeViewItemDropController { - protected: - AbstractTreeView &tree_view_; - - public: - AbstractTreeViewItemDropController(AbstractTreeView &tree_view); - virtual ~AbstractTreeViewItemDropController() = default; - - /** - * Check if the data dragged with \a drag can be dropped on the item this controller is for. - * \param r_disabled_hint: Return a static string to display to the user, explaining why dropping - * isn't possible on this item. Shouldn't be done too aggressively, e.g. - * don't set this if the drag-type can't be dropped here; only if it can - * but there's another reason it can't be dropped. - * Can assume this is a non-null pointer. - */ - virtual bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const = 0; - /** - * Custom text to display when dragging over a tree item. Should explain what happens when - * dropping the data onto this item. Will only be used if #AbstractTreeViewItem::can_drop() - * returns true, so the implementing override doesn't have to check that again. - * The returned value must be a translated string. - */ - virtual std::string drop_tooltip(const wmDrag &drag) const = 0; - /** - * Execute the logic to apply a drop of the data dragged with \a drag onto/into the item this - * controller is for. - */ - virtual bool on_drop(struct bContext *C, const wmDrag &drag) = 0; - - template<class TreeViewType> inline TreeViewType &tree_view() const; -}; - -/** \} */ - -/* ---------------------------------------------------------------------- */ /** \name Predefined Tree-View Item Types * * Common, Basic Tree-View Item Types. @@ -431,18 +328,4 @@ inline ItemT &TreeViewItemContainer::add_tree_item(Args &&...args) add_tree_item(std::make_unique<ItemT>(std::forward<Args>(args)...))); } -template<class TreeViewType> TreeViewType &AbstractTreeViewItemDragController::tree_view() const -{ - static_assert(std::is_base_of<AbstractTreeView, TreeViewType>::value, - "Type must derive from and implement the AbstractTreeView interface"); - return static_cast<TreeViewType &>(tree_view_); -} - -template<class TreeViewType> TreeViewType &AbstractTreeViewItemDropController::tree_view() const -{ - static_assert(std::is_base_of<AbstractTreeView, TreeViewType>::value, - "Type must derive from and implement the AbstractTreeView interface"); - return static_cast<TreeViewType &>(tree_view_); -} - } // namespace blender::ui diff --git a/source/blender/editors/include/UI_view2d.h b/source/blender/editors/include/UI_view2d.h index 5c4eb254462..e508c96b4f1 100644 --- a/source/blender/editors/include/UI_view2d.h +++ b/source/blender/editors/include/UI_view2d.h @@ -309,6 +309,12 @@ float UI_view2d_view_to_region_y(const struct View2D *v2d, float y); bool UI_view2d_view_to_region_clip( const struct View2D *v2d, float x, float y, int *r_region_x, int *r_region_y) ATTR_NONNULL(); +bool UI_view2d_view_to_region_segment_clip(const View2D *v2d, + const float xy_a[2], + const float xy_b[2], + int r_region_a[2], + int r_region_b[2]) ATTR_NONNULL(); + /** * Convert from 2d-view space to screen/region space * diff --git a/source/blender/editors/interface/CMakeLists.txt b/source/blender/editors/interface/CMakeLists.txt index 1bdec57ac59..07c3bfdafbf 100644 --- a/source/blender/editors/interface/CMakeLists.txt +++ b/source/blender/editors/interface/CMakeLists.txt @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-or-later set(INC + . ../include ../../blenfont ../../blenkernel @@ -25,8 +26,13 @@ set(INC ) set(SRC - abstract_view.cc - grid_view.cc + eyedroppers/interface_eyedropper.c + eyedroppers/eyedropper_color.c + eyedroppers/eyedropper_colorband.c + eyedroppers/eyedropper_datablock.c + eyedroppers/eyedropper_depth.c + eyedroppers/eyedropper_driver.c + eyedroppers/eyedropper_gpencil_color.c interface.cc interface_align.c interface_anim.c @@ -36,13 +42,6 @@ set(SRC interface_drag.cc interface_draw.c interface_dropboxes.cc - interface_eyedropper.c - interface_eyedropper_color.c - interface_eyedropper_colorband.c - interface_eyedropper_datablock.c - interface_eyedropper_depth.c - interface_eyedropper_driver.c - interface_eyedropper_gpencil_color.c interface_handlers.c interface_icons.c interface_icons_event.c @@ -68,17 +67,20 @@ set(SRC interface_templates.c interface_undo.c interface_utils.cc - interface_view.cc interface_widgets.c resources.c - tree_view.cc view2d.cc view2d_draw.cc view2d_edge_pan.cc view2d_gizmo_navigate.cc view2d_ops.cc + views/abstract_view.cc + views/abstract_view_item.cc + views/grid_view.cc + views/interface_view.cc + views/tree_view.cc - interface_eyedropper_intern.h + eyedroppers/eyedropper_intern.h interface_intern.h interface_regions_intern.h ) diff --git a/source/blender/editors/interface/interface_eyedropper_color.c b/source/blender/editors/interface/eyedroppers/eyedropper_color.c index c015a60de89..a2161008ec4 100644 --- a/source/blender/editors/interface/interface_eyedropper_color.c +++ b/source/blender/editors/interface/eyedroppers/eyedropper_color.c @@ -50,7 +50,7 @@ #include "RE_pipeline.h" -#include "interface_eyedropper_intern.h" +#include "eyedropper_intern.h" typedef struct Eyedropper { struct ColorManagedDisplay *display; diff --git a/source/blender/editors/interface/interface_eyedropper_colorband.c b/source/blender/editors/interface/eyedroppers/eyedropper_colorband.c index a69c36fefbd..3f63a8020ed 100644 --- a/source/blender/editors/interface/interface_eyedropper_colorband.c +++ b/source/blender/editors/interface/eyedroppers/eyedropper_colorband.c @@ -35,7 +35,7 @@ #include "interface_intern.h" -#include "interface_eyedropper_intern.h" +#include "eyedropper_intern.h" typedef struct Colorband_RNAUpdateCb { PointerRNA ptr; diff --git a/source/blender/editors/interface/interface_eyedropper_datablock.c b/source/blender/editors/interface/eyedroppers/eyedropper_datablock.c index 01b958576b6..1710d0faa4d 100644 --- a/source/blender/editors/interface/interface_eyedropper_datablock.c +++ b/source/blender/editors/interface/eyedroppers/eyedropper_datablock.c @@ -38,7 +38,7 @@ #include "ED_space_api.h" #include "ED_view3d.h" -#include "interface_eyedropper_intern.h" +#include "eyedropper_intern.h" #include "interface_intern.h" /** diff --git a/source/blender/editors/interface/interface_eyedropper_depth.c b/source/blender/editors/interface/eyedroppers/eyedropper_depth.c index 3c6f127582a..3fb5a74944b 100644 --- a/source/blender/editors/interface/interface_eyedropper_depth.c +++ b/source/blender/editors/interface/eyedroppers/eyedropper_depth.c @@ -38,7 +38,7 @@ #include "ED_space_api.h" #include "ED_view3d.h" -#include "interface_eyedropper_intern.h" +#include "eyedropper_intern.h" #include "interface_intern.h" /** diff --git a/source/blender/editors/interface/interface_eyedropper_driver.c b/source/blender/editors/interface/eyedroppers/eyedropper_driver.c index 0cacb55c60c..14c00c21a5c 100644 --- a/source/blender/editors/interface/interface_eyedropper_driver.c +++ b/source/blender/editors/interface/eyedroppers/eyedropper_driver.c @@ -32,7 +32,7 @@ #include "ED_keyframing.h" -#include "interface_eyedropper_intern.h" +#include "eyedropper_intern.h" #include "interface_intern.h" typedef struct DriverDropper { diff --git a/source/blender/editors/interface/interface_eyedropper_gpencil_color.c b/source/blender/editors/interface/eyedroppers/eyedropper_gpencil_color.c index f3c70e6a96a..c3879fe8bbd 100644 --- a/source/blender/editors/interface/interface_eyedropper_gpencil_color.c +++ b/source/blender/editors/interface/eyedroppers/eyedropper_gpencil_color.c @@ -46,7 +46,7 @@ #include "DEG_depsgraph.h" #include "DEG_depsgraph_build.h" -#include "interface_eyedropper_intern.h" +#include "eyedropper_intern.h" #include "interface_intern.h" typedef struct EyedropperGPencil { diff --git a/source/blender/editors/interface/interface_eyedropper_intern.h b/source/blender/editors/interface/eyedroppers/eyedropper_intern.h index 76316a85807..946f2145d1d 100644 --- a/source/blender/editors/interface/interface_eyedropper_intern.h +++ b/source/blender/editors/interface/eyedroppers/eyedropper_intern.h @@ -3,7 +3,7 @@ /** \file * \ingroup edinterface * - * Share between interface_eyedropper_*.c files. + * Share between `interface/eyedropper/` files. */ #pragma once @@ -35,7 +35,7 @@ void datadropper_win_area_find(const struct bContext *C, * * Special check for image or nodes where we MAY have HDR pixels which don't display. * - * \note Exposed by 'interface_eyedropper_intern.h' for use with color band picking. + * \note Exposed by 'eyedropper_intern.h' for use with color band picking. */ void eyedropper_color_sample_fl(bContext *C, const int m_xy[2], float r_col[3]); diff --git a/source/blender/editors/interface/interface_eyedropper.c b/source/blender/editors/interface/eyedroppers/interface_eyedropper.c index eaec1e249b7..c6fb8f0ab68 100644 --- a/source/blender/editors/interface/interface_eyedropper.c +++ b/source/blender/editors/interface/eyedroppers/interface_eyedropper.c @@ -21,7 +21,7 @@ #include "interface_intern.h" -#include "interface_eyedropper_intern.h" /* own include */ +#include "eyedropper_intern.h" /* own include */ /* -------------------------------------------------------------------- */ /* Keymap diff --git a/source/blender/editors/interface/interface.cc b/source/blender/editors/interface/interface.cc index 3f623566807..2f9e69137ed 100644 --- a/source/blender/editors/interface/interface.cc +++ b/source/blender/editors/interface/interface.cc @@ -769,20 +769,11 @@ static bool ui_but_equals_old(const uiBut *but, const uiBut *oldbut) return false; } - if ((but->type == UI_BTYPE_TREEROW) && (oldbut->type == UI_BTYPE_TREEROW)) { - uiButTreeRow *but_treerow = (uiButTreeRow *)but; - uiButTreeRow *oldbut_treerow = (uiButTreeRow *)oldbut; - if (!but_treerow->tree_item || !oldbut_treerow->tree_item || - !UI_tree_view_item_matches(but_treerow->tree_item, oldbut_treerow->tree_item)) { - return false; - } - } - - if ((but->type == UI_BTYPE_GRID_TILE) && (oldbut->type == UI_BTYPE_GRID_TILE)) { - uiButGridTile *but_gridtile = (uiButGridTile *)but; - uiButGridTile *oldbut_gridtile = (uiButGridTile *)oldbut; - if (!but_gridtile->view_item || !oldbut_gridtile->view_item || - !UI_grid_view_item_matches(but_gridtile->view_item, oldbut_gridtile->view_item)) { + if ((but->type == UI_BTYPE_VIEW_ITEM) && (oldbut->type == UI_BTYPE_VIEW_ITEM)) { + uiButViewItem *but_item = (uiButViewItem *)but; + uiButViewItem *oldbut_item = (uiButViewItem *)oldbut; + if (!but_item->view_item || !oldbut_item->view_item || + !UI_view_item_matches(but_item->view_item, oldbut_item->view_item)) { return false; } } @@ -907,16 +898,10 @@ static void ui_but_update_old_active_from_new(uiBut *oldbut, uiBut *but) progress_oldbut->progress = progress_but->progress; break; } - case UI_BTYPE_TREEROW: { - uiButTreeRow *treerow_oldbut = (uiButTreeRow *)oldbut; - uiButTreeRow *treerow_newbut = (uiButTreeRow *)but; - SWAP(uiTreeViewItemHandle *, treerow_newbut->tree_item, treerow_oldbut->tree_item); - break; - } - case UI_BTYPE_GRID_TILE: { - uiButGridTile *gridtile_oldbut = (uiButGridTile *)oldbut; - uiButGridTile *gridtile_newbut = (uiButGridTile *)but; - SWAP(uiGridViewItemHandle *, gridtile_newbut->view_item, gridtile_oldbut->view_item); + case UI_BTYPE_VIEW_ITEM: { + uiButViewItem *view_item_oldbut = (uiButViewItem *)oldbut; + uiButViewItem *view_item_newbut = (uiButViewItem *)but; + SWAP(uiViewItemHandle *, view_item_newbut->view_item, view_item_oldbut->view_item); break; } default: @@ -1013,7 +998,7 @@ static bool ui_but_update_from_old_block(const bContext *C, /* Stupid special case: The active button may be inside (as in, overlapped on top) a view-item * button which we also want to keep highlighted then. */ - if (ui_but_is_view_item(but)) { + if (but->type == UI_BTYPE_VIEW_ITEM) { flag_copy |= UI_ACTIVE; } @@ -2245,21 +2230,12 @@ int ui_but_is_pushed_ex(uiBut *but, double *value) } } break; - case UI_BTYPE_TREEROW: { - uiButTreeRow *tree_row_but = (uiButTreeRow *)but; - - is_push = -1; - if (tree_row_but->tree_item) { - is_push = UI_tree_view_item_is_active(tree_row_but->tree_item); - } - break; - } - case UI_BTYPE_GRID_TILE: { - uiButGridTile *grid_tile_but = (uiButGridTile *)but; + case UI_BTYPE_VIEW_ITEM: { + const uiButViewItem *view_item_but = (const uiButViewItem *)but; is_push = -1; - if (grid_tile_but->view_item) { - is_push = UI_grid_view_item_is_active(grid_tile_but->view_item); + if (view_item_but->view_item) { + is_push = UI_view_item_is_active(view_item_but->view_item); } break; } @@ -4011,17 +3987,13 @@ static void ui_but_alloc_info(const eButType type, alloc_size = sizeof(uiButCurveProfile); alloc_str = "uiButCurveProfile"; break; - case UI_BTYPE_TREEROW: - alloc_size = sizeof(uiButTreeRow); - alloc_str = "uiButTreeRow"; - break; case UI_BTYPE_HOTKEY_EVENT: alloc_size = sizeof(uiButHotkeyEvent); alloc_str = "uiButHotkeyEvent"; break; - case UI_BTYPE_GRID_TILE: - alloc_size = sizeof(uiButGridTile); - alloc_str = "uiButGridTile"; + case UI_BTYPE_VIEW_ITEM: + alloc_size = sizeof(uiButViewItem); + alloc_str = "uiButViewItem"; break; default: alloc_size = sizeof(uiBut); @@ -4214,7 +4186,6 @@ static uiBut *ui_def_but(uiBlock *block, UI_BTYPE_BLOCK, UI_BTYPE_BUT_MENU, UI_BTYPE_SEARCH_MENU, - UI_BTYPE_TREEROW, UI_BTYPE_POPOVER)) { but->drawflag |= (UI_BUT_TEXT_LEFT | UI_BUT_ICON_LEFT); } @@ -6469,15 +6440,6 @@ uiBut *uiDefSearchButO_ptr(uiBlock *block, return but; } -void UI_but_treerow_indentation_set(uiBut *but, int indentation) -{ - uiButTreeRow *but_row = (uiButTreeRow *)but; - BLI_assert(but->type == UI_BTYPE_TREEROW); - - but_row->indentation = indentation; - BLI_assert(indentation >= 0); -} - void UI_but_hint_drawstr_set(uiBut *but, const char *string) { ui_but_add_shortcut(but, string, false); @@ -6623,7 +6585,7 @@ void UI_but_string_info_get(bContext *C, uiBut *but, ...) MenuType *mt = UI_but_menutype_get(but); if (mt) { if (type == BUT_GET_RNA_LABEL) { - tmp = BLI_strdup(mt->label); + tmp = BLI_strdup(CTX_TIP_(mt->translation_context, mt->label)); } else { /* Not all menus are from Python. */ @@ -6653,7 +6615,7 @@ void UI_but_string_info_get(bContext *C, uiBut *but, ...) PanelType *pt = UI_but_paneltype_get(but); if (pt) { if (type == BUT_GET_RNA_LABEL) { - tmp = BLI_strdup(pt->label); + tmp = BLI_strdup(CTX_TIP_(pt->translation_context, pt->label)); } else { /* Not all panels are from Python. */ diff --git a/source/blender/editors/interface/interface_context_menu.c b/source/blender/editors/interface/interface_context_menu.c index e58298cdaee..518fe65ee09 100644 --- a/source/blender/editors/interface/interface_context_menu.c +++ b/source/blender/editors/interface/interface_context_menu.c @@ -927,11 +927,11 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but, const wmEvent *ev { const ARegion *region = CTX_wm_menu(C) ? CTX_wm_menu(C) : CTX_wm_region(C); - uiButTreeRow *treerow_but = (uiButTreeRow *)ui_tree_row_find_mouse_over(region, event->xy); - if (treerow_but) { - BLI_assert(treerow_but->but.type == UI_BTYPE_TREEROW); - UI_tree_view_item_context_menu_build( - C, treerow_but->tree_item, uiLayoutColumn(layout, false)); + uiButViewItem *view_item_but = (uiButViewItem *)ui_view_item_find_mouse_over(region, + event->xy); + if (view_item_but) { + BLI_assert(view_item_but->but.type == UI_BTYPE_VIEW_ITEM); + UI_view_item_context_menu_build(C, view_item_but->view_item, uiLayoutColumn(layout, false)); uiItemS(layout); } } diff --git a/source/blender/editors/interface/interface_drag.cc b/source/blender/editors/interface/interface_drag.cc index 4c68870b2c7..1db3db32411 100644 --- a/source/blender/editors/interface/interface_drag.cc +++ b/source/blender/editors/interface/interface_drag.cc @@ -122,7 +122,7 @@ bool ui_but_drag_is_draggable(const uiBut *but) void ui_but_drag_start(bContext *C, uiBut *but) { - wmDrag *drag = WM_event_start_drag(C, + wmDrag *drag = WM_drag_data_create(C, but->icon, but->dragtype, but->dragpoin, @@ -136,6 +136,8 @@ void ui_but_drag_start(bContext *C, uiBut *but) WM_event_drag_image(drag, but->imb, but->imb_scale); } + WM_event_start_prepared_drag(C, drag); + /* Special feature for assets: We add another drag item that supports multiple assets. It * gets the assets from context. */ if (ELEM(but->dragtype, WM_DRAG_ASSET, WM_DRAG_ID)) { diff --git a/source/blender/editors/interface/interface_dropboxes.cc b/source/blender/editors/interface/interface_dropboxes.cc index 9d3c1372b15..b72d8d2c238 100644 --- a/source/blender/editors/interface/interface_dropboxes.cc +++ b/source/blender/editors/interface/interface_dropboxes.cc @@ -22,15 +22,14 @@ #include "UI_interface.h" /* -------------------------------------------------------------------- */ -/** \name Tree View Drag/Drop Callbacks +/** \name View Drag/Drop Callbacks * \{ */ -static bool ui_tree_view_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) +static bool ui_view_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { const ARegion *region = CTX_wm_region(C); - const uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at(region, - event->xy); - if (!hovered_tree_item) { + const uiViewItemHandle *hovered_item = UI_region_views_find_item_at(region, event->xy); + if (!hovered_item) { return false; } @@ -39,21 +38,21 @@ static bool ui_tree_view_drop_poll(bContext *C, wmDrag *drag, const wmEvent *eve } drag->drop_state.free_disabled_info = false; - return UI_tree_view_item_can_drop(hovered_tree_item, drag, &drag->drop_state.disabled_info); + return UI_view_item_can_drop(hovered_item, drag, &drag->drop_state.disabled_info); } -static char *ui_tree_view_drop_tooltip(bContext *C, - wmDrag *drag, - const int xy[2], - wmDropBox *UNUSED(drop)) +static char *ui_view_drop_tooltip(bContext *C, + wmDrag *drag, + const int xy[2], + wmDropBox *UNUSED(drop)) { const ARegion *region = CTX_wm_region(C); - const uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at(region, xy); - if (!hovered_tree_item) { + const uiViewItemHandle *hovered_item = UI_region_views_find_item_at(region, xy); + if (!hovered_item) { return nullptr; } - return UI_tree_view_item_drop_tooltip(hovered_tree_item, drag); + return UI_view_item_drop_tooltip(hovered_item, drag); } /** \} */ @@ -140,12 +139,7 @@ void ED_dropboxes_ui() { ListBase *lb = WM_dropboxmap_find("User Interface", SPACE_EMPTY, 0); - WM_dropbox_add(lb, - "UI_OT_tree_view_drop", - ui_tree_view_drop_poll, - nullptr, - nullptr, - ui_tree_view_drop_tooltip); + WM_dropbox_add(lb, "UI_OT_view_drop", ui_view_drop_poll, nullptr, nullptr, ui_view_drop_tooltip); WM_dropbox_add(lb, "UI_OT_drop_name", ui_drop_name_poll, diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 7c00c4f1875..0a50522a141 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -1151,7 +1151,10 @@ static void ui_apply_but_ROW(bContext *C, uiBlock *block, uiBut *but, uiHandleBu data->applied = true; } -static void ui_apply_but_TREEROW(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data) +static void ui_apply_but_VIEW_ITEM(bContext *C, + uiBlock *block, + uiBut *but, + uiHandleButtonData *data) { if (data->apply_through_extra_icon) { /* Don't apply this, it would cause unintended tree-row toggling when clicking on extra icons. @@ -2128,10 +2131,10 @@ static bool ui_but_drag_init(bContext *C, return false; } } - else if (but->type == UI_BTYPE_TREEROW) { - uiButTreeRow *tree_row_but = (uiButTreeRow *)but; - if (tree_row_but->tree_item) { - UI_tree_view_item_drag_start(C, tree_row_but->tree_item); + else if (but->type == UI_BTYPE_VIEW_ITEM) { + const uiButViewItem *view_item_but = (uiButViewItem *)but; + if (view_item_but->view_item) { + UI_view_item_drag_start(C, view_item_but->view_item); } } else { @@ -2289,11 +2292,8 @@ static void ui_apply_but( case UI_BTYPE_ROW: ui_apply_but_ROW(C, block, but, data); break; - case UI_BTYPE_GRID_TILE: - ui_apply_but_ROW(C, block, but, data); - break; - case UI_BTYPE_TREEROW: - ui_apply_but_TREEROW(C, block, but, data); + case UI_BTYPE_VIEW_ITEM: + ui_apply_but_VIEW_ITEM(C, block, but, data); break; case UI_BTYPE_LISTROW: ui_apply_but_LISTROW(C, block, but, data); @@ -3172,19 +3172,11 @@ static bool ui_textedit_insert_buf(uiBut *but, return changed; } -static bool ui_textedit_insert_ascii(uiBut *but, uiHandleButtonData *data, char ascii) +static bool ui_textedit_insert_ascii(uiBut *but, uiHandleButtonData *data, const char ascii) { + BLI_assert(isascii(ascii)); const char buf[2] = {ascii, '\0'}; - - if (UI_but_is_utf8(but) && (BLI_str_utf8_size(buf) == -1)) { - printf( - "%s: entering invalid ascii char into an ascii key (%d)\n", __func__, (int)(uchar)ascii); - - return false; - } - - /* in some cases we want to allow invalid utf8 chars */ - return ui_textedit_insert_buf(but, data, buf, 1); + return ui_textedit_insert_buf(but, data, buf, sizeof(buf) - 1); } static void ui_textedit_move(uiBut *but, @@ -3897,30 +3889,27 @@ static void ui_do_but_textedit( } } - if ((event->ascii || event->utf8_buf[0]) && (retval == WM_UI_HANDLER_CONTINUE) + if ((event->utf8_buf[0]) && (retval == WM_UI_HANDLER_CONTINUE) #ifdef WITH_INPUT_IME && !is_ime_composing && !WM_event_is_ime_switch(event) #endif ) { - char ascii = event->ascii; + char utf8_buf_override[2] = {'\0', '\0'}; const char *utf8_buf = event->utf8_buf; /* Exception that's useful for number buttons, some keyboard * numpads have a comma instead of a period. */ if (ELEM(but->type, UI_BTYPE_NUM, UI_BTYPE_NUM_SLIDER)) { /* Could use `data->min`. */ - if (event->type == EVT_PADPERIOD && ascii == ',') { - ascii = '.'; - utf8_buf = NULL; /* force ascii fallback */ + if ((event->type == EVT_PADPERIOD) && (utf8_buf[0] == ',')) { + utf8_buf_override[0] = '.'; + utf8_buf = utf8_buf_override; } } - if (utf8_buf && utf8_buf[0]) { + if (utf8_buf[0]) { const int utf8_buf_len = BLI_str_utf8_size(utf8_buf); BLI_assert(utf8_buf_len != -1); - changed = ui_textedit_insert_buf(but, data, event->utf8_buf, utf8_buf_len); - } - else { - changed = ui_textedit_insert_ascii(but, data, ascii); + changed = ui_textedit_insert_buf(but, data, utf8_buf, utf8_buf_len); } retval = WM_UI_HANDLER_BREAK; @@ -3952,6 +3941,9 @@ static void ui_do_but_textedit( else if (event->type == WM_IME_COMPOSITE_END) { changed = true; } +#else + /* Prevent the function from being unused. */ + (void)ui_textedit_insert_ascii; #endif if (changed) { @@ -4772,53 +4764,13 @@ static int ui_do_but_TOG(bContext *C, uiBut *but, uiHandleButtonData *data, cons return WM_UI_HANDLER_CONTINUE; } -static int ui_do_but_TREEROW(bContext *C, - uiBut *but, - uiHandleButtonData *data, - const wmEvent *event) -{ - uiButTreeRow *tree_row_but = (uiButTreeRow *)but; - BLI_assert(tree_row_but->but.type == UI_BTYPE_TREEROW); - - if (data->state == BUTTON_STATE_HIGHLIGHT) { - if (event->type == LEFTMOUSE) { - switch (event->val) { - case KM_PRESS: - /* Extra icons have priority, don't mess with them. */ - if (ui_but_extra_operator_icon_mouse_over_get(but, data->region, event)) { - return WM_UI_HANDLER_BREAK; - } - button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG); - data->dragstartx = event->xy[0]; - data->dragstarty = event->xy[1]; - return WM_UI_HANDLER_CONTINUE; - - case KM_CLICK: - button_activate_state(C, but, BUTTON_STATE_EXIT); - return WM_UI_HANDLER_BREAK; - - case KM_DBL_CLICK: - data->cancel = true; - UI_tree_view_item_begin_rename(tree_row_but->tree_item); - ED_region_tag_redraw(CTX_wm_region(C)); - return WM_UI_HANDLER_BREAK; - } - } - } - else if (data->state == BUTTON_STATE_WAIT_DRAG) { - /* Let "default" button handling take care of the drag logic. */ - return ui_do_but_EXIT(C, but, data, event); - } - - return WM_UI_HANDLER_CONTINUE; -} - -static int ui_do_but_GRIDTILE(bContext *C, - uiBut *but, - uiHandleButtonData *data, - const wmEvent *event) +static int ui_do_but_VIEW_ITEM(bContext *C, + uiBut *but, + uiHandleButtonData *data, + const wmEvent *event) { - BLI_assert(but->type == UI_BTYPE_GRID_TILE); + uiButViewItem *view_item_but = (uiButViewItem *)but; + BLI_assert(view_item_but->but.type == UI_BTYPE_VIEW_ITEM); if (data->state == BUTTON_STATE_HIGHLIGHT) { if (event->type == LEFTMOUSE) { @@ -4839,8 +4791,7 @@ static int ui_do_but_GRIDTILE(bContext *C, case KM_DBL_CLICK: data->cancel = true; - // uiButGridTile *grid_tile_but = (uiButGridTile *)but; - // UI_tree_view_item_begin_rename(grid_tile_but->tree_item); + UI_view_item_begin_rename(view_item_but->view_item); ED_region_tag_redraw(CTX_wm_region(C)); return WM_UI_HANDLER_BREAK; } @@ -7988,7 +7939,7 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * * to spawn the context menu should also activate the item. This makes it clear which item * will be operated on. * Apply the button immediately, so context menu polls get the right active item. */ - if (ELEM(but->type, UI_BTYPE_TREEROW)) { + if (ELEM(but->type, UI_BTYPE_VIEW_ITEM)) { ui_apply_but(C, but->block, but, but->active, true); } @@ -8053,11 +8004,8 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * case UI_BTYPE_ROW: retval = ui_do_but_TOG(C, but, data, event); break; - case UI_BTYPE_GRID_TILE: - retval = ui_do_but_GRIDTILE(C, but, data, event); - break; - case UI_BTYPE_TREEROW: - retval = ui_do_but_TREEROW(C, but, data, event); + case UI_BTYPE_VIEW_ITEM: + retval = ui_do_but_VIEW_ITEM(C, but, data, event); break; case UI_BTYPE_SCROLL: retval = ui_do_but_SCROLL(C, block, but, data, event); @@ -9487,7 +9435,7 @@ static int ui_list_activate_hovered_row(bContext *C, } /* Simulate click on listrow button itself (which may be overlapped by another button). Also - * calls the custom activate operator (ui_list->custom_activate_opname). */ + * calls the custom activate operator (#uiListDyn::custom_activate_optype). */ UI_but_execute(C, region, listrow); ((uiList *)ui_list)->dyn_data->custom_activate_optype = custom_activate_optype; @@ -9558,13 +9506,13 @@ static void ui_list_activate_row_from_index( uiBut *new_active_row = ui_list_row_find_from_index(region, index, listbox); if (new_active_row) { /* Preferred way to update the active item, also calls the custom activate operator - * (#uiList.custom_activate_opname). */ + * (#uiListDyn::custom_activate_optype). */ UI_but_execute(C, region, new_active_row); } else { /* A bit ugly, set the active index in RNA directly. That's because a button that's * scrolled away in the list box isn't created at all. - * The custom activate operator (#uiList.custom_activate_opname) is not called in this case + * The custom activate operator (#uiListDyn::custom_activate_optype) is not called in this case * (which may need the row button context). */ RNA_property_int_set(&listbox->rnapoin, listbox->rnaprop, index); RNA_property_update(C, &listbox->rnapoin, listbox->rnaprop); @@ -9733,7 +9681,7 @@ static int ui_handle_view_items_hover(const wmEvent *event, const ARegion *regio } LISTBASE_FOREACH (uiBut *, but, &block->buttons) { - if (ui_but_is_view_item(but)) { + if (but->type == UI_BTYPE_VIEW_ITEM) { but->flag &= ~UI_ACTIVE; has_view_item = true; } @@ -9760,7 +9708,7 @@ static int ui_handle_view_item_event(bContext *C, ARegion *region, uiBut *view_but) { - BLI_assert(ui_but_is_view_item(view_but)); + BLI_assert(view_but->type == UI_BTYPE_VIEW_ITEM); if (event->type == LEFTMOUSE) { /* Will free active button if there already is one. */ ui_handle_button_activate(C, region, view_but, BUTTON_ACTIVATE_OVER); diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index 5e0382f73a9..03b9d03a6e3 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -343,20 +343,12 @@ typedef struct uiButProgressbar { float progress; } uiButProgressbar; -/** Derived struct for #UI_BTYPE_TREEROW. */ -typedef struct uiButTreeRow { +typedef struct uiButViewItem { uiBut but; - uiTreeViewItemHandle *tree_item; - int indentation; -} uiButTreeRow; - -/** Derived struct for #UI_BTYPE_GRID_TILE. */ -typedef struct uiButGridTile { - uiBut but; - - uiGridViewItemHandle *view_item; -} uiButGridTile; + /* C-Handle to the view item this button was created for. */ + uiViewItemHandle *view_item; +} uiButViewItem; /** Derived struct for #UI_BTYPE_HSVCUBE. */ typedef struct uiButHSVCube { @@ -1372,7 +1364,6 @@ void ui_but_anim_decorate_update_from_flag(uiButDecorator *but); bool ui_but_is_editable(const uiBut *but) ATTR_WARN_UNUSED_RESULT; bool ui_but_is_editable_as_text(const uiBut *but) ATTR_WARN_UNUSED_RESULT; bool ui_but_is_toggle(const uiBut *but) ATTR_WARN_UNUSED_RESULT; -bool ui_but_is_view_item(const uiBut *but) ATTR_WARN_UNUSED_RESULT; /** * Can we mouse over the button or is it hidden/disabled/layout. * \note ctrl is kind of a hack currently, @@ -1406,9 +1397,7 @@ uiBut *ui_list_row_find_from_index(const struct ARegion *region, uiBut *listbox) ATTR_WARN_UNUSED_RESULT; uiBut *ui_view_item_find_mouse_over(const struct ARegion *region, const int xy[2]) ATTR_NONNULL(1, 2); -uiBut *ui_tree_row_find_mouse_over(const struct ARegion *region, const int xy[2]) - ATTR_NONNULL(1, 2); -uiBut *ui_tree_row_find_active(const struct ARegion *region); +uiBut *ui_view_item_find_active(const struct ARegion *region); typedef bool (*uiButFindPollFn)(const uiBut *but, const void *customdata); /** @@ -1546,8 +1535,8 @@ void ui_block_free_views(struct uiBlock *block); uiViewHandle *ui_block_view_find_matching_in_old_block(const uiBlock *new_block, const uiViewHandle *new_view); -uiButTreeRow *ui_block_view_find_treerow_in_old_block(const uiBlock *new_block, - const uiTreeViewItemHandle *new_item_handle); +uiButViewItem *ui_block_view_find_matching_view_item_but_in_old_block( + const uiBlock *new_block, const uiViewItemHandle *new_item_handle); /* interface_templates.c */ diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index aafb56119ae..ddd805f6010 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -2054,40 +2054,39 @@ static void UI_OT_list_start_filter(wmOperatorType *ot) /** \name UI Tree-View Drop Operator * \{ */ -static bool ui_tree_view_drop_poll(bContext *C) +static bool ui_view_drop_poll(bContext *C) { const wmWindow *win = CTX_wm_window(C); const ARegion *region = CTX_wm_region(C); - const uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at( - region, win->eventstate->xy); + const uiViewItemHandle *hovered_item = UI_region_views_find_item_at(region, win->eventstate->xy); - return hovered_tree_item != NULL; + return hovered_item != NULL; } -static int ui_tree_view_drop_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) +static int ui_view_drop_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) { if (event->custom != EVT_DATA_DRAGDROP) { return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; } const ARegion *region = CTX_wm_region(C); - uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at(region, event->xy); + uiViewItemHandle *hovered_item = UI_region_views_find_item_at(region, event->xy); - if (!UI_tree_view_item_drop_handle(C, hovered_tree_item, event->customdata)) { + if (!UI_view_item_drop_handle(C, hovered_item, event->customdata)) { return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; } return OPERATOR_FINISHED; } -static void UI_OT_tree_view_drop(wmOperatorType *ot) +static void UI_OT_view_drop(wmOperatorType *ot) { - ot->name = "Tree View drop"; - ot->idname = "UI_OT_tree_view_drop"; - ot->description = "Drag and drop items onto a tree item"; + ot->name = "View drop"; + ot->idname = "UI_OT_view_drop"; + ot->description = "Drag and drop items onto a data-set item"; - ot->invoke = ui_tree_view_drop_invoke; - ot->poll = ui_tree_view_drop_poll; + ot->invoke = ui_view_drop_invoke; + ot->poll = ui_view_drop_poll; ot->flag = OPTYPE_INTERNAL; } @@ -2095,43 +2094,42 @@ static void UI_OT_tree_view_drop(wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ -/** \name UI Tree-View Item Rename Operator +/** \name UI View Item Rename Operator * - * General purpose renaming operator for tree-views. Thanks to this, to add a rename button to - * context menus for example, tree-view API users don't have to implement their own renaming - * operators with the same logic as they already have for their #ui::AbstractTreeViewItem::rename() - * override. + * General purpose renaming operator for views. Thanks to this, to add a rename button to context + * menus for example, view API users don't have to implement their own renaming operators with the + * same logic as they already have for their #ui::AbstractViewItem::rename() override. * * \{ */ -static bool ui_tree_view_item_rename_poll(bContext *C) +static bool ui_view_item_rename_poll(bContext *C) { const ARegion *region = CTX_wm_region(C); - const uiTreeViewItemHandle *active_item = UI_block_tree_view_find_active_item(region); - return active_item != NULL && UI_tree_view_item_can_rename(active_item); + const uiViewItemHandle *active_item = UI_region_views_find_active_item(region); + return active_item != NULL && UI_view_item_can_rename(active_item); } -static int ui_tree_view_item_rename_exec(bContext *C, wmOperator *UNUSED(op)) +static int ui_view_item_rename_exec(bContext *C, wmOperator *UNUSED(op)) { ARegion *region = CTX_wm_region(C); - uiTreeViewItemHandle *active_item = UI_block_tree_view_find_active_item(region); + uiViewItemHandle *active_item = UI_region_views_find_active_item(region); - UI_tree_view_item_begin_rename(active_item); + UI_view_item_begin_rename(active_item); ED_region_tag_redraw(region); return OPERATOR_FINISHED; } -static void UI_OT_tree_view_item_rename(wmOperatorType *ot) +static void UI_OT_view_item_rename(wmOperatorType *ot) { - ot->name = "Rename Tree-View Item"; - ot->idname = "UI_OT_tree_view_item_rename"; - ot->description = "Rename the active item in the tree"; + ot->name = "Rename View Item"; + ot->idname = "UI_OT_view_item_rename"; + ot->description = "Rename the active item in the data-set view"; - ot->exec = ui_tree_view_item_rename_exec; - ot->poll = ui_tree_view_item_rename_poll; + ot->exec = ui_view_item_rename_exec; + ot->poll = ui_view_item_rename_poll; /* Could get a custom tooltip via the `get_description()` callback and another overridable - * function of the tree-view. */ + * function of the view. */ ot->flag = OPTYPE_INTERNAL; } @@ -2235,8 +2233,8 @@ void ED_operatortypes_ui(void) WM_operatortype_append(UI_OT_list_start_filter); - WM_operatortype_append(UI_OT_tree_view_drop); - WM_operatortype_append(UI_OT_tree_view_item_rename); + WM_operatortype_append(UI_OT_view_drop); + WM_operatortype_append(UI_OT_view_item_rename); /* external */ WM_operatortype_append(UI_OT_eyedropper_color); diff --git a/source/blender/editors/interface/interface_query.cc b/source/blender/editors/interface/interface_query.cc index 71cf60985df..f084f3e06cb 100644 --- a/source/blender/editors/interface/interface_query.cc +++ b/source/blender/editors/interface/interface_query.cc @@ -54,13 +54,7 @@ bool ui_but_is_toggle(const uiBut *but) UI_BTYPE_TOGGLE_N, UI_BTYPE_CHECKBOX, UI_BTYPE_CHECKBOX_N, - UI_BTYPE_ROW, - UI_BTYPE_TREEROW); -} - -bool ui_but_is_view_item(const uiBut *but) -{ - return ELEM(but->type, UI_BTYPE_TREEROW, UI_BTYPE_GRID_TILE); + UI_BTYPE_ROW); } bool ui_but_is_interactive_ex(const uiBut *but, const bool labeledit, const bool for_tooltip) @@ -462,14 +456,9 @@ uiBut *ui_list_row_find_from_index(const ARegion *region, const int index, uiBut return ui_but_find(region, ui_but_is_listrow_at_index, &data); } -static bool ui_but_is_treerow(const uiBut *but, const void *UNUSED(customdata)) -{ - return but->type == UI_BTYPE_TREEROW; -} - static bool ui_but_is_view_item_fn(const uiBut *but, const void *UNUSED(customdata)) { - return ui_but_is_view_item(but); + return but->type == UI_BTYPE_VIEW_ITEM; } uiBut *ui_view_item_find_mouse_over(const ARegion *region, const int xy[2]) @@ -477,24 +466,19 @@ uiBut *ui_view_item_find_mouse_over(const ARegion *region, const int xy[2]) return ui_but_find_mouse_over_ex(region, xy, false, false, ui_but_is_view_item_fn, nullptr); } -uiBut *ui_tree_row_find_mouse_over(const ARegion *region, const int xy[2]) -{ - return ui_but_find_mouse_over_ex(region, xy, false, false, ui_but_is_treerow, nullptr); -} - -static bool ui_but_is_active_treerow(const uiBut *but, const void *customdata) +static bool ui_but_is_active_view_item(const uiBut *but, const void *UNUSED(customdata)) { - if (!ui_but_is_treerow(but, customdata)) { + if (but->type != UI_BTYPE_VIEW_ITEM) { return false; } - const uiButTreeRow *treerow_but = (const uiButTreeRow *)but; - return UI_tree_view_item_is_active(treerow_but->tree_item); + const uiButViewItem *view_item_but = (const uiButViewItem *)but; + return UI_view_item_is_active(view_item_but->view_item); } -uiBut *ui_tree_row_find_active(const ARegion *region) +uiBut *ui_view_item_find_active(const ARegion *region) { - return ui_but_find(region, ui_but_is_active_treerow, nullptr); + return ui_but_find(region, ui_but_is_active_view_item, nullptr); } /** \} */ diff --git a/source/blender/editors/interface/interface_region_menu_pie.cc b/source/blender/editors/interface/interface_region_menu_pie.cc index b11564f09c5..09902dd6c35 100644 --- a/source/blender/editors/interface/interface_region_menu_pie.cc +++ b/source/blender/editors/interface/interface_region_menu_pie.cc @@ -371,7 +371,7 @@ void ui_pie_menu_level_create(uiBlock *block, EnumPropertyItem *remaining = static_cast<EnumPropertyItem *>( MEM_mallocN(array_size + sizeof(EnumPropertyItem), "pie_level_item_array")); memcpy(remaining, items + totitem_parent, array_size); - /* A nullptr terminating sentinel element is required. */ + /* A null terminating sentinel element is required. */ memset(&remaining[totitem_remain], 0, sizeof(EnumPropertyItem)); /* yuk, static... issue is we can't reliably free this without doing dangerous changes */ diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index e2df2d77817..855e72788d2 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -104,8 +104,7 @@ typedef enum { UI_WTYPE_LISTITEM, UI_WTYPE_PROGRESSBAR, UI_WTYPE_NODESOCKET, - UI_WTYPE_TREEROW, - UI_WTYPE_GRID_TILE, + UI_WTYPE_VIEW_ITEM, } uiWidgetTypeEnum; /** @@ -3672,12 +3671,11 @@ static void widget_progressbar(uiBut *but, widgetbase_draw(&wtb_bar, wcol); } -static void widget_treerow_exec(uiWidgetColors *wcol, - rcti *rect, - const uiWidgetStateInfo *state, - int UNUSED(roundboxalign), - int indentation, - const float zoom) +static void widget_view_item(uiWidgetColors *wcol, + rcti *rect, + const uiWidgetStateInfo *state, + int UNUSED(roundboxalign), + const float zoom) { uiWidgetBase wtb; widget_init(&wtb); @@ -3690,31 +3688,6 @@ static void widget_treerow_exec(uiWidgetColors *wcol, if ((state->but_flag & UI_ACTIVE) || (state->but_flag & UI_SELECT)) { widgetbase_draw(&wtb, wcol); } - - BLI_rcti_resize(rect, BLI_rcti_size_x(rect) - UI_UNIT_X * indentation, BLI_rcti_size_y(rect)); - BLI_rcti_translate(rect, 0.5f * UI_UNIT_X * indentation, 0); -} - -static void widget_treerow(uiBut *but, - uiWidgetColors *wcol, - rcti *rect, - const uiWidgetStateInfo *state, - int roundboxalign, - const float zoom) -{ - uiButTreeRow *tree_row = (uiButTreeRow *)but; - BLI_assert(but->type == UI_BTYPE_TREEROW); - widget_treerow_exec(wcol, rect, state, roundboxalign, tree_row->indentation, zoom); -} - -static void widget_gridtile(uiWidgetColors *wcol, - rcti *rect, - const uiWidgetStateInfo *state, - int roundboxalign, - const float zoom) -{ - /* TODO Reuse tree-row drawing. */ - widget_treerow_exec(wcol, rect, state, roundboxalign, 0, zoom); } static void widget_nodesocket(uiBut *but, @@ -4608,14 +4581,9 @@ static uiWidgetType *widget_type(uiWidgetTypeEnum type) wt.custom = widget_progressbar; break; - case UI_WTYPE_TREEROW: - wt.wcol_theme = &btheme->tui.wcol_view_item; - wt.custom = widget_treerow; - break; - - case UI_WTYPE_GRID_TILE: + case UI_WTYPE_VIEW_ITEM: wt.wcol_theme = &btheme->tui.wcol_view_item; - wt.draw = widget_gridtile; + wt.draw = widget_view_item; break; case UI_WTYPE_NODESOCKET: @@ -4949,13 +4917,8 @@ void ui_draw_but(const bContext *C, struct ARegion *region, uiStyle *style, uiBu fstyle = &style->widgetlabel; break; - case UI_BTYPE_TREEROW: - wt = widget_type(UI_WTYPE_TREEROW); - fstyle = &style->widgetlabel; - break; - - case UI_BTYPE_GRID_TILE: - wt = widget_type(UI_WTYPE_GRID_TILE); + case UI_BTYPE_VIEW_ITEM: + wt = widget_type(UI_WTYPE_VIEW_ITEM); fstyle = &style->widgetlabel; break; diff --git a/source/blender/editors/interface/view2d.cc b/source/blender/editors/interface/view2d.cc index ee4bfd351ae..1bf7e25b154 100644 --- a/source/blender/editors/interface/view2d.cc +++ b/source/blender/editors/interface/view2d.cc @@ -1695,6 +1695,41 @@ void UI_view2d_view_to_region_fl( *r_region_y = v2d->mask.ymin + (y * BLI_rcti_size_y(&v2d->mask)); } +bool UI_view2d_view_to_region_segment_clip(const View2D *v2d, + const float xy_a[2], + const float xy_b[2], + int r_region_a[2], + int r_region_b[2]) +{ + rctf rect_unit; + rect_unit.xmin = rect_unit.ymin = 0.0f; + rect_unit.xmax = rect_unit.ymax = 1.0f; + + /* Express given coordinates as proportional values. */ + const float s_a[2] = { + (xy_a[0] - v2d->cur.xmin) / BLI_rctf_size_x(&v2d->cur), + (xy_a[1] - v2d->cur.ymin) / BLI_rctf_size_y(&v2d->cur), + }; + const float s_b[2] = { + (xy_b[0] - v2d->cur.xmin) / BLI_rctf_size_x(&v2d->cur), + (xy_b[1] - v2d->cur.ymin) / BLI_rctf_size_y(&v2d->cur), + }; + + /* Set initial value in case coordinates lie outside bounds. */ + r_region_a[0] = r_region_b[0] = r_region_a[1] = r_region_b[1] = V2D_IS_CLIPPED; + + if (BLI_rctf_isect_segment(&rect_unit, s_a, s_b)) { + r_region_a[0] = (int)(v2d->mask.xmin + (s_a[0] * BLI_rcti_size_x(&v2d->mask))); + r_region_a[1] = (int)(v2d->mask.ymin + (s_a[1] * BLI_rcti_size_y(&v2d->mask))); + r_region_b[0] = (int)(v2d->mask.xmin + (s_b[0] * BLI_rcti_size_x(&v2d->mask))); + r_region_b[1] = (int)(v2d->mask.ymin + (s_b[1] * BLI_rcti_size_y(&v2d->mask))); + + return true; + } + + return false; +} + void UI_view2d_view_to_region_rcti(const View2D *v2d, const rctf *rect_src, rcti *rect_dst) { const float cur_size[2] = {BLI_rctf_size_x(&v2d->cur), BLI_rctf_size_y(&v2d->cur)}; diff --git a/source/blender/editors/interface/abstract_view.cc b/source/blender/editors/interface/views/abstract_view.cc index dea9600fde4..077c76a08f1 100644 --- a/source/blender/editors/interface/abstract_view.cc +++ b/source/blender/editors/interface/views/abstract_view.cc @@ -10,6 +10,13 @@ namespace blender::ui { +void AbstractView::register_item(AbstractViewItem &item) +{ + /* Actually modifies the item, not the view. But for the public API it "feels" a bit nicer to + * have the view base class register the items, rather than setting the view on the item. */ + item.view_ = this; +} + /* ---------------------------------------------------------------------- */ /** \name View Reconstruction * \{ */ diff --git a/source/blender/editors/interface/views/abstract_view_item.cc b/source/blender/editors/interface/views/abstract_view_item.cc new file mode 100644 index 00000000000..f73183d07e9 --- /dev/null +++ b/source/blender/editors/interface/views/abstract_view_item.cc @@ -0,0 +1,373 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup edinterface + */ + +#include "BKE_context.h" + +#include "BLI_listbase.h" +#include "BLI_string.h" + +#include "WM_api.h" + +#include "UI_interface.h" +#include "interface_intern.h" + +#include "UI_abstract_view.hh" + +namespace blender::ui { + +/* ---------------------------------------------------------------------- */ +/** \name View Reconstruction + * \{ */ + +void AbstractViewItem::update_from_old(const AbstractViewItem &old) +{ + is_active_ = old.is_active_; + is_renaming_ = old.is_renaming_; +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Renaming + * \{ */ + +bool AbstractViewItem::supports_renaming() const +{ + /* No renaming by default. */ + return false; +} +bool AbstractViewItem::rename(StringRefNull /*new_name*/) +{ + /* No renaming by default. */ + return false; +} + +StringRef AbstractViewItem::get_rename_string() const +{ + /* No rename string by default. */ + return {}; +} + +bool AbstractViewItem::is_renaming() const +{ + return is_renaming_; +} + +void AbstractViewItem::begin_renaming() +{ + AbstractView &view = get_view(); + if (view.is_renaming() || !supports_renaming()) { + return; + } + + if (view.begin_renaming()) { + is_renaming_ = true; + } + + StringRef initial_str = get_rename_string(); + std::copy(std::begin(initial_str), std::end(initial_str), std::begin(view.get_rename_buffer())); +} + +void AbstractViewItem::rename_apply() +{ + const AbstractView &view = get_view(); + rename(view.get_rename_buffer().data()); + end_renaming(); +} + +void AbstractViewItem::end_renaming() +{ + if (!is_renaming()) { + return; + } + + is_renaming_ = false; + + AbstractView &view = get_view(); + view.end_renaming(); +} + +static AbstractViewItem *find_item_from_rename_button(const uiBut &rename_but) +{ + /* A minimal sanity check, can't do much more here. */ + BLI_assert(rename_but.type == UI_BTYPE_TEXT && rename_but.poin); + + LISTBASE_FOREACH (uiBut *, but, &rename_but.block->buttons) { + if (but->type != UI_BTYPE_VIEW_ITEM) { + continue; + } + + uiButViewItem *view_item_but = (uiButViewItem *)but; + AbstractViewItem *item = reinterpret_cast<AbstractViewItem *>(view_item_but->view_item); + const AbstractView &view = item->get_view(); + + if (item->is_renaming() && (view.get_rename_buffer().data() == rename_but.poin)) { + return item; + } + } + + return nullptr; +} + +static void rename_button_fn(bContext *UNUSED(C), void *arg, char *UNUSED(origstr)) +{ + const uiBut *rename_but = static_cast<uiBut *>(arg); + AbstractViewItem *item = find_item_from_rename_button(*rename_but); + BLI_assert(item); + item->rename_apply(); +} + +void AbstractViewItem::add_rename_button(uiBlock &block) +{ + AbstractView &view = get_view(); + uiBut *rename_but = uiDefBut(&block, + UI_BTYPE_TEXT, + 1, + "", + 0, + 0, + UI_UNIT_X * 10, + UI_UNIT_Y, + view.get_rename_buffer().data(), + 1.0f, + view.get_rename_buffer().size(), + 0, + 0, + ""); + + /* Gotta be careful with what's passed to the `arg1` here. Any view data will be freed once the + * callback is executed. */ + UI_but_func_rename_set(rename_but, rename_button_fn, rename_but); + UI_but_flag_disable(rename_but, UI_BUT_UNDO); + + const bContext *evil_C = reinterpret_cast<bContext *>(block.evil_C); + ARegion *region = CTX_wm_region(evil_C); + /* Returns false if the button was removed. */ + if (UI_but_active_only(evil_C, region, &block, rename_but) == false) { + end_renaming(); + } +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Context Menu + * \{ */ + +void AbstractViewItem::build_context_menu(bContext & /*C*/, uiLayout & /*column*/) const +{ + /* No context menu by default. */ +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Drag 'n Drop + * \{ */ + +std::unique_ptr<AbstractViewItemDragController> AbstractViewItem::create_drag_controller() const +{ + /* There's no drag controller (and hence no drag support) by default. */ + return nullptr; +} + +std::unique_ptr<AbstractViewItemDropController> AbstractViewItem::create_drop_controller() const +{ + /* There's no drop controller (and hence no drop support) by default. */ + return nullptr; +} + +AbstractViewItemDragController::AbstractViewItemDragController(AbstractView &view) : view_(view) +{ +} + +void AbstractViewItemDragController::on_drag_start() +{ + /* Do nothing by default. */ +} + +AbstractViewItemDropController::AbstractViewItemDropController(AbstractView &view) : view_(view) +{ +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name General Getters & Setters + * \{ */ + +AbstractView &AbstractViewItem::get_view() const +{ + if (UNLIKELY(!view_)) { + throw std::runtime_error( + "Invalid state, item must be registered through AbstractView::register_item()"); + } + return *view_; +} + +bool AbstractViewItem::is_active() const +{ + BLI_assert_msg(get_view().is_reconstructed(), + "State can't be queried until reconstruction is completed"); + return is_active_; +} + +/** \} */ + +} // namespace blender::ui + +/* ---------------------------------------------------------------------- */ +/** \name C-API + * \{ */ + +namespace blender::ui { + +/** + * Helper class to provide a higher level public (C-)API. Has access to private/protected view item + * members and ensures some invariants that way. + */ +class ViewItemAPIWrapper { + public: + static bool matches(const AbstractViewItem &a, const AbstractViewItem &b) + { + if (typeid(a) != typeid(b)) { + return false; + } + /* TODO should match the view as well. */ + return a.matches(b); + } + + static bool can_rename(const AbstractViewItem &item) + { + const AbstractView &view = item.get_view(); + return !view.is_renaming() && item.supports_renaming(); + } + + static bool drag_start(bContext &C, const AbstractViewItem &item) + { + const std::unique_ptr<AbstractViewItemDragController> drag_controller = + item.create_drag_controller(); + if (!drag_controller) { + return false; + } + + WM_event_start_drag(&C, + ICON_NONE, + drag_controller->get_drag_type(), + drag_controller->create_drag_data(), + 0, + WM_DRAG_FREE_DATA); + drag_controller->on_drag_start(); + + return true; + } + + static bool can_drop(const AbstractViewItem &item, + const wmDrag &drag, + const char **r_disabled_hint) + { + const std::unique_ptr<AbstractViewItemDropController> drop_controller = + item.create_drop_controller(); + if (!drop_controller) { + return false; + } + + return drop_controller->can_drop(drag, r_disabled_hint); + } + + static std::string drop_tooltip(const AbstractViewItem &item, const wmDrag &drag) + { + const std::unique_ptr<AbstractViewItemDropController> drop_controller = + item.create_drop_controller(); + if (!drop_controller) { + return {}; + } + + return drop_controller->drop_tooltip(drag); + } + + static bool drop_handle(bContext &C, const AbstractViewItem &item, const ListBase &drags) + { + std::unique_ptr<AbstractViewItemDropController> drop_controller = + item.create_drop_controller(); + + const char *disabled_hint_dummy = nullptr; + LISTBASE_FOREACH (const wmDrag *, drag, &drags) { + if (drop_controller->can_drop(*drag, &disabled_hint_dummy)) { + return drop_controller->on_drop(&C, *drag); + } + } + + return false; + } +}; + +} // namespace blender::ui + +using namespace blender::ui; + +bool UI_view_item_is_active(const uiViewItemHandle *item_handle) +{ + const AbstractViewItem &item = reinterpret_cast<const AbstractViewItem &>(*item_handle); + return item.is_active(); +} + +bool UI_view_item_matches(const uiViewItemHandle *a_handle, const uiViewItemHandle *b_handle) +{ + const AbstractViewItem &a = reinterpret_cast<const AbstractViewItem &>(*a_handle); + const AbstractViewItem &b = reinterpret_cast<const AbstractViewItem &>(*b_handle); + return ViewItemAPIWrapper::matches(a, b); +} + +bool UI_view_item_can_rename(const uiViewItemHandle *item_handle) +{ + const AbstractViewItem &item = reinterpret_cast<const AbstractViewItem &>(*item_handle); + return ViewItemAPIWrapper::can_rename(item); +} + +void UI_view_item_begin_rename(uiViewItemHandle *item_handle) +{ + AbstractViewItem &item = reinterpret_cast<AbstractViewItem &>(*item_handle); + item.begin_renaming(); +} + +void UI_view_item_context_menu_build(bContext *C, + const uiViewItemHandle *item_handle, + uiLayout *column) +{ + const AbstractViewItem &item = reinterpret_cast<const AbstractViewItem &>(*item_handle); + item.build_context_menu(*C, *column); +} + +bool UI_view_item_drag_start(bContext *C, const uiViewItemHandle *item_) +{ + const AbstractViewItem &item = reinterpret_cast<const AbstractViewItem &>(*item_); + return ViewItemAPIWrapper::drag_start(*C, item); +} + +bool UI_view_item_can_drop(const uiViewItemHandle *item_, + const wmDrag *drag, + const char **r_disabled_hint) +{ + const AbstractViewItem &item = reinterpret_cast<const AbstractViewItem &>(*item_); + return ViewItemAPIWrapper::can_drop(item, *drag, r_disabled_hint); +} + +char *UI_view_item_drop_tooltip(const uiViewItemHandle *item_, const wmDrag *drag) +{ + const AbstractViewItem &item = reinterpret_cast<const AbstractViewItem &>(*item_); + + const std::string tooltip = ViewItemAPIWrapper::drop_tooltip(item, *drag); + return tooltip.empty() ? nullptr : BLI_strdup(tooltip.c_str()); +} + +bool UI_view_item_drop_handle(bContext *C, const uiViewItemHandle *item_, const ListBase *drags) +{ + const AbstractViewItem &item = reinterpret_cast<const AbstractViewItem &>(*item_); + return ViewItemAPIWrapper::drop_handle(*C, item, *drags); +} + +/** \} */ diff --git a/source/blender/editors/interface/grid_view.cc b/source/blender/editors/interface/views/grid_view.cc index 19a2326fba1..37fbb33f83b 100644 --- a/source/blender/editors/interface/grid_view.cc +++ b/source/blender/editors/interface/views/grid_view.cc @@ -95,18 +95,19 @@ AbstractGridViewItem::AbstractGridViewItem(StringRef identifier) : identifier_(i { } -bool AbstractGridViewItem::matches(const AbstractGridViewItem &other) const +bool AbstractGridViewItem::matches(const AbstractViewItem &other) const { - return identifier_ == other.identifier_; + const AbstractGridViewItem &other_grid_item = dynamic_cast<const AbstractGridViewItem &>(other); + return identifier_ == other_grid_item.identifier_; } void AbstractGridViewItem::grid_tile_click_fn(struct bContext * /*C*/, void *but_arg1, void * /*arg2*/) { - uiButGridTile *grid_tile_but = (uiButGridTile *)but_arg1; + uiButViewItem *view_item_but = (uiButViewItem *)but_arg1; AbstractGridViewItem &grid_item = reinterpret_cast<AbstractGridViewItem &>( - *grid_tile_but->view_item); + *view_item_but->view_item); grid_item.activate(); } @@ -114,8 +115,8 @@ void AbstractGridViewItem::grid_tile_click_fn(struct bContext * /*C*/, void AbstractGridViewItem::add_grid_tile_button(uiBlock &block) { const GridViewStyle &style = get_view().get_style(); - grid_tile_but_ = (uiButGridTile *)uiDefBut(&block, - UI_BTYPE_GRID_TILE, + view_item_but_ = (uiButViewItem *)uiDefBut(&block, + UI_BTYPE_VIEW_ITEM, 0, "", 0, @@ -129,15 +130,8 @@ void AbstractGridViewItem::add_grid_tile_button(uiBlock &block) 0, ""); - grid_tile_but_->view_item = reinterpret_cast<uiGridViewItemHandle *>(this); - UI_but_func_set(&grid_tile_but_->but, grid_tile_click_fn, grid_tile_but_, nullptr); -} - -bool AbstractGridViewItem::is_active() const -{ - BLI_assert_msg(get_view().is_reconstructed(), - "State can't be queried until reconstruction is completed"); - return is_active_; + view_item_but_->view_item = reinterpret_cast<uiViewItemHandle *>(this); + UI_but_func_set(&view_item_but_->but, grid_tile_click_fn, view_item_but_, nullptr); } void AbstractGridViewItem::on_activate() @@ -158,11 +152,6 @@ void AbstractGridViewItem::change_state_delayed() } } -void AbstractGridViewItem::update_from_old(const AbstractGridViewItem &old) -{ - is_active_ = old.is_active_; -} - void AbstractGridViewItem::activate() { BLI_assert_msg(get_view().is_reconstructed(), @@ -473,24 +462,3 @@ std::optional<bool> PreviewGridItem::should_be_active() const } } // namespace blender::ui - -using namespace blender::ui; - -/* ---------------------------------------------------------------------- */ -/* C-API */ - -using namespace blender::ui; - -bool UI_grid_view_item_is_active(const uiGridViewItemHandle *item_handle) -{ - const AbstractGridViewItem &item = reinterpret_cast<const AbstractGridViewItem &>(*item_handle); - return item.is_active(); -} - -bool UI_grid_view_item_matches(const uiGridViewItemHandle *a_handle, - const uiGridViewItemHandle *b_handle) -{ - const AbstractGridViewItem &a = reinterpret_cast<const AbstractGridViewItem &>(*a_handle); - const AbstractGridViewItem &b = reinterpret_cast<const AbstractGridViewItem &>(*b_handle); - return a.matches(b); -} diff --git a/source/blender/editors/interface/interface_view.cc b/source/blender/editors/interface/views/interface_view.cc index 70728565263..c568a8cab74 100644 --- a/source/blender/editors/interface/interface_view.cc +++ b/source/blender/editors/interface/views/interface_view.cc @@ -3,10 +3,16 @@ /** \file * \ingroup edinterface * - * This part of the UI-View API is mostly needed to support persistent state of items within the - * view. Views are stored in #uiBlock's, and kept alive with it until after the next redraw. So we - * can compare the old view items with the new view items and keep state persistent for matching - * ones. + * Code to manage views as part of the regular screen hierarchy. E.g. managing ownership of views + * inside blocks (#uiBlock.views), looking up items in the region, passing WM notifiers to views, + * etc. + * + * Blocks and their contained views are reconstructed on every redraw. This file also contains + * functions related to this recreation of views inside blocks. For example to query state + * information before the view is done reconstructing (#AbstractView.is_reconstructed() returns + * false), it may be enough to query the previous version of the block/view/view-item. Since such + * queries rely on the details of the UI reconstruction process, they should remain internal to + * `interface/` code. */ #include <memory> @@ -86,24 +92,24 @@ void UI_block_views_listen(const uiBlock *block, const wmRegionListenerParams *l } } -uiTreeViewItemHandle *UI_block_tree_view_find_item_at(const ARegion *region, const int xy[2]) +uiViewItemHandle *UI_region_views_find_item_at(const ARegion *region, const int xy[2]) { - uiButTreeRow *tree_row_but = (uiButTreeRow *)ui_tree_row_find_mouse_over(region, xy); - if (!tree_row_but) { + uiButViewItem *item_but = (uiButViewItem *)ui_view_item_find_mouse_over(region, xy); + if (!item_but) { return nullptr; } - return tree_row_but->tree_item; + return item_but->view_item; } -uiTreeViewItemHandle *UI_block_tree_view_find_active_item(const ARegion *region) +uiViewItemHandle *UI_region_views_find_active_item(const ARegion *region) { - uiButTreeRow *tree_row_but = (uiButTreeRow *)ui_tree_row_find_active(region); - if (!tree_row_but) { + uiButViewItem *item_but = (uiButViewItem *)ui_view_item_find_active(region); + if (!item_but) { return nullptr; } - return tree_row_but->tree_item; + return item_but->view_item; } static StringRef ui_block_view_find_idname(const uiBlock &block, const AbstractView &view) @@ -151,39 +157,38 @@ uiViewHandle *ui_block_view_find_matching_in_old_block(const uiBlock *new_block, return reinterpret_cast<uiViewHandle *>(old_view); } -uiButTreeRow *ui_block_view_find_treerow_in_old_block(const uiBlock *new_block, - const uiTreeViewItemHandle *new_item_handle) +uiButViewItem *ui_block_view_find_matching_view_item_but_in_old_block( + const uiBlock *new_block, const uiViewItemHandle *new_item_handle) { uiBlock *old_block = new_block->oldblock; if (!old_block) { return nullptr; } - const AbstractTreeViewItem &new_item = *reinterpret_cast<const AbstractTreeViewItem *>( - new_item_handle); + const AbstractViewItem &new_item = *reinterpret_cast<const AbstractViewItem *>(new_item_handle); const AbstractView *old_view = ui_block_view_find_matching_in_old_block_impl( - *new_block, new_item.get_tree_view()); + *new_block, new_item.get_view()); if (!old_view) { return nullptr; } LISTBASE_FOREACH (uiBut *, old_but, &old_block->buttons) { - if (old_but->type != UI_BTYPE_TREEROW) { + if (old_but->type != UI_BTYPE_VIEW_ITEM) { continue; } - uiButTreeRow *old_treerow_but = (uiButTreeRow *)old_but; - if (!old_treerow_but->tree_item) { + uiButViewItem *old_item_but = (uiButViewItem *)old_but; + if (!old_item_but->view_item) { continue; } - AbstractTreeViewItem &old_item = *reinterpret_cast<AbstractTreeViewItem *>( - old_treerow_but->tree_item); - /* Check if the row is from the expected tree-view. */ - if (&old_item.get_tree_view() != old_view) { + AbstractViewItem &old_item = *reinterpret_cast<AbstractViewItem *>(old_item_but->view_item); + /* Check if the item is from the expected view. */ + if (&old_item.get_view() != old_view) { continue; } - if (UI_tree_view_item_matches(new_item_handle, old_treerow_but->tree_item)) { - return old_treerow_but; + if (UI_view_item_matches(reinterpret_cast<const uiViewItemHandle *>(&new_item), + reinterpret_cast<const uiViewItemHandle *>(&old_item))) { + return old_item_but; } } diff --git a/source/blender/editors/interface/tree_view.cc b/source/blender/editors/interface/views/tree_view.cc index d29cf367e59..21078b711c7 100644 --- a/source/blender/editors/interface/tree_view.cc +++ b/source/blender/editors/interface/views/tree_view.cc @@ -37,9 +37,11 @@ AbstractTreeViewItem &TreeViewItemContainer::add_tree_item( if (root_ == nullptr) { root_ = this; } - + AbstractTreeView &tree_view = static_cast<AbstractTreeView &>(*root_); AbstractTreeViewItem &added_item = *children_.last(); added_item.root_ = root_; + tree_view.register_item(added_item); + if (root_ != this) { /* Any item that isn't the root can be assumed to the a #AbstractTreeViewItem. Not entirely * nice to static_cast this, but well... */ @@ -95,7 +97,7 @@ AbstractTreeViewItem *AbstractTreeView::find_matching_child( const AbstractTreeViewItem &lookup_item, const TreeViewOrItem &items) { for (const auto &iter_item : items.children_) { - if (lookup_item.matches(*iter_item)) { + if (lookup_item.matches_single(*iter_item)) { /* We have a matching item! */ return iter_item.get(); } @@ -118,9 +120,8 @@ void AbstractTreeViewItem::tree_row_click_fn(struct bContext * /*C*/, void *but_arg1, void * /*arg2*/) { - uiButTreeRow *tree_row_but = (uiButTreeRow *)but_arg1; - AbstractTreeViewItem &tree_item = reinterpret_cast<AbstractTreeViewItem &>( - *tree_row_but->tree_item); + uiButViewItem *item_but = (uiButViewItem *)but_arg1; + AbstractTreeViewItem &tree_item = reinterpret_cast<AbstractTreeViewItem &>(*item_but->view_item); tree_item.activate(); /* Not only activate the item, also show its children. Maybe this should be optional, or @@ -131,11 +132,11 @@ void AbstractTreeViewItem::tree_row_click_fn(struct bContext * /*C*/, void AbstractTreeViewItem::add_treerow_button(uiBlock &block) { /* For some reason a width > (UI_UNIT_X * 2) make the layout system use all available width. */ - tree_row_but_ = (uiButTreeRow *)uiDefBut( - &block, UI_BTYPE_TREEROW, 0, "", 0, 0, UI_UNIT_X * 10, UI_UNIT_Y, nullptr, 0, 0, 0, 0, ""); + view_item_but_ = (uiButViewItem *)uiDefBut( + &block, UI_BTYPE_VIEW_ITEM, 0, "", 0, 0, UI_UNIT_X * 10, UI_UNIT_Y, nullptr, 0, 0, 0, 0, ""); - tree_row_but_->tree_item = reinterpret_cast<uiTreeViewItemHandle *>(this); - UI_but_func_set(&tree_row_but_->but, tree_row_click_fn, tree_row_but_, nullptr); + view_item_but_->view_item = reinterpret_cast<uiViewItemHandle *>(this); + UI_but_func_set(&view_item_but_->but, tree_row_click_fn, view_item_but_, nullptr); } void AbstractTreeViewItem::add_indent(uiLayout &row) const @@ -167,10 +168,9 @@ void AbstractTreeViewItem::collapse_chevron_click_fn(struct bContext *C, const wmWindow *win = CTX_wm_window(C); const ARegion *region = CTX_wm_region(C); - uiTreeViewItemHandle *hovered_item_handle = UI_block_tree_view_find_item_at(region, - win->eventstate->xy); - AbstractTreeViewItem *hovered_item = reinterpret_cast<AbstractTreeViewItem *>( - hovered_item_handle); + uiViewItemHandle *hovered_item_handle = UI_region_views_find_item_at(region, win->eventstate->xy); + + AbstractTreeViewItem *hovered_item = from_item_handle<AbstractTreeViewItem>(hovered_item_handle); BLI_assert(hovered_item != nullptr); hovered_item->toggle_collapsed(); @@ -204,40 +204,6 @@ void AbstractTreeViewItem::add_collapse_chevron(uiBlock &block) const BLI_assert(is_collapse_chevron_but(but)); } -AbstractTreeViewItem *AbstractTreeViewItem::find_tree_item_from_rename_button( - const uiBut &rename_but) -{ - /* A minimal sanity check, can't do much more here. */ - BLI_assert(rename_but.type == UI_BTYPE_TEXT && rename_but.poin); - - LISTBASE_FOREACH (uiBut *, but, &rename_but.block->buttons) { - if (but->type != UI_BTYPE_TREEROW) { - continue; - } - - uiButTreeRow *tree_row_but = (uiButTreeRow *)but; - AbstractTreeViewItem *item = reinterpret_cast<AbstractTreeViewItem *>(tree_row_but->tree_item); - const AbstractTreeView &tree_view = item->get_tree_view(); - - if (item->is_renaming() && (tree_view.get_rename_buffer().data() == rename_but.poin)) { - return item; - } - } - - return nullptr; -} - -void AbstractTreeViewItem::rename_button_fn(bContext *UNUSED(C), void *arg, char *UNUSED(origstr)) -{ - const uiBut *rename_but = static_cast<uiBut *>(arg); - AbstractTreeViewItem *item = find_tree_item_from_rename_button(*rename_but); - BLI_assert(item); - - const AbstractTreeView &tree_view = item->get_tree_view(); - item->rename(tree_view.get_rename_buffer().data()); - item->end_renaming(); -} - void AbstractTreeViewItem::add_rename_button(uiLayout &row) { uiBlock *block = uiLayoutGetBlock(&row); @@ -247,33 +213,7 @@ void AbstractTreeViewItem::add_rename_button(uiLayout &row) /* Enable emboss for the text button. */ UI_block_emboss_set(block, UI_EMBOSS); - AbstractTreeView &tree_view = get_tree_view(); - uiBut *rename_but = uiDefBut(block, - UI_BTYPE_TEXT, - 1, - "", - 0, - 0, - UI_UNIT_X * 10, - UI_UNIT_Y, - tree_view.get_rename_buffer().data(), - 1.0f, - tree_view.get_rename_buffer().size(), - 0, - 0, - ""); - - /* Gotta be careful with what's passed to the `arg1` here. Any tree data will be freed once the - * callback is executed. */ - UI_but_func_rename_set(rename_but, AbstractTreeViewItem::rename_button_fn, rename_but); - UI_but_flag_disable(rename_but, UI_BUT_UNDO); - - const bContext *evil_C = static_cast<bContext *>(block->evil_C); - ARegion *region = CTX_wm_region(evil_C); - /* Returns false if the button was removed. */ - if (UI_but_active_only(evil_C, region, block, rename_but) == false) { - end_renaming(); - } + AbstractViewItem::add_rename_button(*block); UI_block_emboss_set(block, previous_emboss); UI_block_layout_set_current(block, &row); @@ -306,80 +246,35 @@ bool AbstractTreeViewItem::supports_collapsing() const return true; } -std::unique_ptr<AbstractTreeViewItemDragController> AbstractTreeViewItem::create_drag_controller() - const -{ - /* There's no drag controller (and hence no drag support) by default. */ - return nullptr; -} - -std::unique_ptr<AbstractTreeViewItemDropController> AbstractTreeViewItem::create_drop_controller() - const -{ - /* There's no drop controller (and hence no drop support) by default. */ - return nullptr; -} - -bool AbstractTreeViewItem::supports_renaming() const +StringRef AbstractTreeViewItem::get_rename_string() const { - /* No renaming by default. */ - return false; + return label_; } bool AbstractTreeViewItem::rename(StringRefNull new_name) { - /* It is important to update the label after renaming, so #AbstractTreeViewItem::matches() + /* It is important to update the label after renaming, so #AbstractTreeViewItem::matches_single() * recognizes the item. (It only compares labels by default.) */ label_ = new_name; return true; } -void AbstractTreeViewItem::build_context_menu(bContext & /*C*/, uiLayout & /*column*/) const +void AbstractTreeViewItem::update_from_old(const AbstractViewItem &old) { - /* No context menu by default. */ -} + AbstractViewItem::update_from_old(old); -void AbstractTreeViewItem::update_from_old(const AbstractTreeViewItem &old) -{ - is_open_ = old.is_open_; - is_active_ = old.is_active_; - is_renaming_ = old.is_renaming_; + const AbstractTreeViewItem &old_tree_item = dynamic_cast<const AbstractTreeViewItem &>(old); + is_open_ = old_tree_item.is_open_; } -bool AbstractTreeViewItem::matches(const AbstractTreeViewItem &other) const +bool AbstractTreeViewItem::matches_single(const AbstractTreeViewItem &other) const { return label_ == other.label_; } -void AbstractTreeViewItem::begin_renaming() -{ - AbstractTreeView &tree_view = get_tree_view(); - if (tree_view.is_renaming() || !supports_renaming()) { - return; - } - - if (tree_view.begin_renaming()) { - is_renaming_ = true; - } - - std::copy(std::begin(label_), std::end(label_), std::begin(tree_view.get_rename_buffer())); -} - -void AbstractTreeViewItem::end_renaming() -{ - if (!is_renaming()) { - return; - } - - is_renaming_ = false; - - AbstractTreeView &tree_view = get_tree_view(); - tree_view.end_renaming(); -} - AbstractTreeView &AbstractTreeViewItem::get_tree_view() const { - return static_cast<AbstractTreeView &>(*root_); + return dynamic_cast<AbstractTreeView &>(get_view()); } int AbstractTreeViewItem::count_parents() const @@ -415,26 +310,19 @@ void AbstractTreeViewItem::deactivate() is_active_ = false; } -bool AbstractTreeViewItem::is_active() const -{ - BLI_assert_msg(get_tree_view().is_reconstructed(), - "State can't be queried until reconstruction is completed"); - return is_active_; -} - bool AbstractTreeViewItem::is_hovered() const { BLI_assert_msg(get_tree_view().is_reconstructed(), "State can't be queried until reconstruction is completed"); - BLI_assert_msg(tree_row_but_ != nullptr, + BLI_assert_msg(view_item_but_ != nullptr, "Hovered state can't be queried before the tree row is being built"); - const uiTreeViewItemHandle *this_handle = reinterpret_cast<const uiTreeViewItemHandle *>(this); + const uiViewItemHandle *this_item_handle = reinterpret_cast<const uiViewItemHandle *>(this); /* The new layout hasn't finished construction yet, so the final state of the button is unknown. * Get the matching button from the previous redraw instead. */ - uiButTreeRow *old_treerow_but = ui_block_view_find_treerow_in_old_block(tree_row_but_->but.block, - this_handle); - return old_treerow_but && (old_treerow_but->but.flag & UI_ACTIVE); + uiButViewItem *old_item_but = ui_block_view_find_matching_view_item_but_in_old_block( + view_item_but_->but.block, this_item_handle); + return old_item_but && (old_item_but->but.flag & UI_ACTIVE); } bool AbstractTreeViewItem::is_collapsed() const @@ -462,11 +350,6 @@ bool AbstractTreeViewItem::is_collapsible() const return this->supports_collapsing(); } -bool AbstractTreeViewItem::is_renaming() const -{ - return is_renaming_; -} - void AbstractTreeViewItem::ensure_parents_uncollapsed() { for (AbstractTreeViewItem *parent = parent_; parent; parent = parent->parent_) { @@ -474,19 +357,21 @@ void AbstractTreeViewItem::ensure_parents_uncollapsed() } } -bool AbstractTreeViewItem::matches_including_parents(const AbstractTreeViewItem &other) const +bool AbstractTreeViewItem::matches(const AbstractViewItem &other) const { - if (!matches(other)) { + const AbstractTreeViewItem &other_tree_item = dynamic_cast<const AbstractTreeViewItem &>(other); + + if (!matches_single(other_tree_item)) { return false; } - if (count_parents() != other.count_parents()) { + if (count_parents() != other_tree_item.count_parents()) { return false; } - for (AbstractTreeViewItem *parent = parent_, *other_parent = other.parent_; + for (AbstractTreeViewItem *parent = parent_, *other_parent = other_tree_item.parent_; parent && other_parent; parent = parent->parent_, other_parent = other_parent->parent_) { - if (!parent->matches(*other_parent)) { + if (!parent->matches_single(*other_parent)) { return false; } } @@ -494,9 +379,9 @@ bool AbstractTreeViewItem::matches_including_parents(const AbstractTreeViewItem return true; } -uiButTreeRow *AbstractTreeViewItem::tree_row_button() +uiButViewItem *AbstractTreeViewItem::view_item_button() { - return tree_row_but_; + return view_item_but_; } void AbstractTreeViewItem::change_state_delayed() @@ -509,25 +394,6 @@ void AbstractTreeViewItem::change_state_delayed() /* ---------------------------------------------------------------------- */ -AbstractTreeViewItemDragController::AbstractTreeViewItemDragController(AbstractTreeView &tree_view) - : tree_view_(tree_view) -{ -} - -void AbstractTreeViewItemDragController::on_drag_start() -{ - /* Do nothing by default. */ -} - -/* ---------------------------------------------------------------------- */ - -AbstractTreeViewItemDropController::AbstractTreeViewItemDropController(AbstractTreeView &tree_view) - : tree_view_(tree_view) -{ -} - -/* ---------------------------------------------------------------------- */ - class TreeViewLayoutBuilder { uiBlock &block_; @@ -573,7 +439,7 @@ void TreeViewLayoutBuilder::polish_layout(const uiBlock &block) UI_but_drawflag_enable(static_cast<uiBut *>(but->next), UI_BUT_NO_TEXT_PADDING); } - if (but->type == UI_BTYPE_TREEROW) { + if (but->type == UI_BTYPE_VIEW_ITEM) { break; } } @@ -685,154 +551,4 @@ std::optional<bool> BasicTreeViewItem::should_be_active() const return std::nullopt; } -/* ---------------------------------------------------------------------- */ - -/** - * Helper for a public (C-)API, presenting higher level functionality. Has access to internal - * data/functionality (friend of #AbstractTreeViewItem), which is sometimes needed when - * functionality of the API needs to be constructed from multiple internal conditions and/or - * functions that on their own shouldn't be part of the API. - */ -class TreeViewItemAPIWrapper { - public: - static bool matches(const AbstractTreeViewItem &a, const AbstractTreeViewItem &b) - { - /* TODO should match the tree-view as well. */ - return a.matches_including_parents(b); - } - - static bool drag_start(bContext &C, const AbstractTreeViewItem &item) - { - const std::unique_ptr<AbstractTreeViewItemDragController> drag_controller = - item.create_drag_controller(); - if (!drag_controller) { - return false; - } - - WM_event_start_drag(&C, - ICON_NONE, - drag_controller->get_drag_type(), - drag_controller->create_drag_data(), - 0, - WM_DRAG_FREE_DATA); - drag_controller->on_drag_start(); - - return true; - } - - static bool can_drop(const AbstractTreeViewItem &item, - const wmDrag &drag, - const char **r_disabled_hint) - { - const std::unique_ptr<AbstractTreeViewItemDropController> drop_controller = - item.create_drop_controller(); - if (!drop_controller) { - return false; - } - - return drop_controller->can_drop(drag, r_disabled_hint); - } - - static std::string drop_tooltip(const AbstractTreeViewItem &item, const wmDrag &drag) - { - const std::unique_ptr<AbstractTreeViewItemDropController> drop_controller = - item.create_drop_controller(); - if (!drop_controller) { - return {}; - } - - return drop_controller->drop_tooltip(drag); - } - - static bool drop_handle(bContext &C, const AbstractTreeViewItem &item, const ListBase &drags) - { - std::unique_ptr<AbstractTreeViewItemDropController> drop_controller = - item.create_drop_controller(); - - const char *disabled_hint_dummy = nullptr; - LISTBASE_FOREACH (const wmDrag *, drag, &drags) { - if (drop_controller->can_drop(*drag, &disabled_hint_dummy)) { - return drop_controller->on_drop(&C, *drag); - } - } - - return false; - } - - static bool can_rename(const AbstractTreeViewItem &item) - { - const AbstractTreeView &tree_view = item.get_tree_view(); - return !tree_view.is_renaming() && item.supports_renaming(); - } -}; - } // namespace blender::ui - -/* ---------------------------------------------------------------------- */ -/* C-API */ - -using namespace blender::ui; - -bool UI_tree_view_item_is_active(const uiTreeViewItemHandle *item_handle) -{ - const AbstractTreeViewItem &item = reinterpret_cast<const AbstractTreeViewItem &>(*item_handle); - return item.is_active(); -} - -bool UI_tree_view_item_matches(const uiTreeViewItemHandle *a_handle, - const uiTreeViewItemHandle *b_handle) -{ - const AbstractTreeViewItem &a = reinterpret_cast<const AbstractTreeViewItem &>(*a_handle); - const AbstractTreeViewItem &b = reinterpret_cast<const AbstractTreeViewItem &>(*b_handle); - return TreeViewItemAPIWrapper::matches(a, b); -} - -bool UI_tree_view_item_drag_start(bContext *C, uiTreeViewItemHandle *item_) -{ - const AbstractTreeViewItem &item = reinterpret_cast<const AbstractTreeViewItem &>(*item_); - return TreeViewItemAPIWrapper::drag_start(*C, item); -} - -bool UI_tree_view_item_can_drop(const uiTreeViewItemHandle *item_, - const wmDrag *drag, - const char **r_disabled_hint) -{ - const AbstractTreeViewItem &item = reinterpret_cast<const AbstractTreeViewItem &>(*item_); - return TreeViewItemAPIWrapper::can_drop(item, *drag, r_disabled_hint); -} - -char *UI_tree_view_item_drop_tooltip(const uiTreeViewItemHandle *item_, const wmDrag *drag) -{ - const AbstractTreeViewItem &item = reinterpret_cast<const AbstractTreeViewItem &>(*item_); - - const std::string tooltip = TreeViewItemAPIWrapper::drop_tooltip(item, *drag); - return tooltip.empty() ? nullptr : BLI_strdup(tooltip.c_str()); -} - -bool UI_tree_view_item_drop_handle(bContext *C, - const uiTreeViewItemHandle *item_, - const ListBase *drags) -{ - const AbstractTreeViewItem &item = reinterpret_cast<const AbstractTreeViewItem &>(*item_); - return TreeViewItemAPIWrapper::drop_handle(*C, item, *drags); -} - -bool UI_tree_view_item_can_rename(const uiTreeViewItemHandle *item_handle) -{ - const AbstractTreeViewItem &item = reinterpret_cast<const AbstractTreeViewItem &>(*item_handle); - return TreeViewItemAPIWrapper::can_rename(item); -} - -void UI_tree_view_item_begin_rename(uiTreeViewItemHandle *item_handle) -{ - AbstractTreeViewItem &item = reinterpret_cast<AbstractTreeViewItem &>(*item_handle); - item.begin_renaming(); -} - -void UI_tree_view_item_context_menu_build(bContext *C, - const uiTreeViewItemHandle *item_handle, - uiLayout *column) -{ - const AbstractTreeViewItem &item = reinterpret_cast<const AbstractTreeViewItem &>(*item_handle); - item.build_context_menu(*C, *column); -} diff --git a/source/blender/editors/mask/mask_query.c b/source/blender/editors/mask/mask_query.c index 02e1524e23e..bb865e925d7 100644 --- a/source/blender/editors/mask/mask_query.c +++ b/source/blender/editors/mask/mask_query.c @@ -682,8 +682,7 @@ void ED_mask_get_size(ScrArea *area, int *width, int *height) } case SPACE_SEQ: { // Scene *scene = CTX_data_scene(C); - // *width = (scene->r.size * scene->r.xsch) / 100; - // *height = (scene->r.size * scene->r.ysch) / 100; + // BKE_render_resolution(&scene->r, false, width, height); break; } case SPACE_IMAGE: { diff --git a/source/blender/editors/mesh/editface.cc b/source/blender/editors/mesh/editface.cc index 69fe69fe117..b69cd8b8606 100644 --- a/source/blender/editors/mesh/editface.cc +++ b/source/blender/editors/mesh/editface.cc @@ -551,3 +551,54 @@ void paintvert_select_ungrouped(Object *ob, bool extend, bool flush_flags) paintvert_flush_flags(ob); } } + +void paintvert_hide(bContext *C, Object *ob, const bool unselected) +{ + Mesh *const me = BKE_mesh_from_object(ob); + + if (me == NULL || me->totvert == 0) { + return; + } + + for (int i = 0; i < me->totvert; i++) { + MVert *const mvert = &me->mvert[i]; + + if ((mvert->flag & ME_HIDE) == 0) { + if (((mvert->flag & SELECT) == 0) == unselected) { + mvert->flag |= ME_HIDE; + } + } + + if (mvert->flag & ME_HIDE) { + mvert->flag &= ~SELECT; + } + } + + BKE_mesh_flush_hidden_from_verts(me); + + paintvert_flush_flags(ob); + paintvert_tag_select_update(C, ob); +} + +void paintvert_reveal(bContext *C, Object *ob, const bool select) +{ + Mesh *const me = BKE_mesh_from_object(ob); + + if (me == NULL || me->totvert == 0) { + return; + } + + for (int i = 0; i < me->totvert; i++) { + MVert *const mvert = &me->mvert[i]; + + if (mvert->flag & ME_HIDE) { + SET_FLAG_FROM_TEST(mvert->flag, select, SELECT); + mvert->flag &= ~ME_HIDE; + } + } + + BKE_mesh_flush_hidden_from_verts(me); + + paintvert_flush_flags(ob); + paintvert_tag_select_update(C, ob); +} diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c index 83968955583..ac5530c8ea9 100644 --- a/source/blender/editors/mesh/editmesh_utils.c +++ b/source/blender/editors/mesh/editmesh_utils.c @@ -593,13 +593,143 @@ UvMapVert *BM_uv_vert_map_at_index(UvVertMap *vmap, uint v) return vmap->vert[v]; } +#define INVALID_ISLAND ((unsigned int)-1) + +static void bm_uv_assign_island(UvElementMap *element_map, + UvElement *element, + int nisland, + uint *map, + UvElement *islandbuf, + int islandbufsize) +{ + element->island = nisland; + map[element - element_map->buf] = islandbufsize; + + /* Copy *element to islandbuf[islandbufsize]. */ + islandbuf[islandbufsize].l = element->l; + islandbuf[islandbufsize].separate = element->separate; + islandbuf[islandbufsize].loop_of_poly_index = element->loop_of_poly_index; + islandbuf[islandbufsize].island = element->island; + islandbuf[islandbufsize].flag = element->flag; +} + +static int bm_uv_edge_select_build_islands(UvElementMap *element_map, + const Scene *scene, + UvElement *islandbuf, + uint *map, + bool uv_selected, + int cd_loop_uv_offset) +{ + int totuv = element_map->totalUVs; + + /* For each UvElement, locate the "separate" UvElement that precedes it in the linked list. */ + UvElement **head_table = MEM_mallocN(sizeof(*head_table) * totuv, "uv_island_head_table"); + for (int i = 0; i < totuv; i++) { + UvElement *head = element_map->buf + i; + if (head->separate) { + UvElement *element = head; + while (element) { + head_table[element - element_map->buf] = head; + element = element->next; + if (element && element->separate) { + break; + } + } + } + } + + /* Depth first search the graph, building islands as we go. */ + int nislands = 0; + int islandbufsize = 0; + int stack_upper_bound = totuv; + UvElement **stack_uv = MEM_mallocN(sizeof(*stack_uv) * stack_upper_bound, + "uv_island_element_stack"); + int stacksize_uv = 0; + for (int i = 0; i < totuv; i++) { + UvElement *element = element_map->buf + i; + if (element->island != INVALID_ISLAND) { + /* Unique UV (element and all it's children) are already part of an island. */ + continue; + } + + /* Create a new island, i.e. nislands++. */ + + BLI_assert(element->separate); /* Ensure we're the head of this unique UV. */ + + /* Seed the graph search. */ + stack_uv[stacksize_uv++] = element; + while (element) { + bm_uv_assign_island(element_map, element, nislands, map, islandbuf, islandbufsize++); + element = element->next; + if (element && element->separate) { + break; + } + } + + /* Traverse the graph. */ + while (stacksize_uv) { + BLI_assert(stacksize_uv < stack_upper_bound); + element = stack_uv[--stacksize_uv]; + while (element) { + + /* Scan forwards around the BMFace that contains element->l. */ + if (!uv_selected || uvedit_edge_select_test(scene, element->l, cd_loop_uv_offset)) { + UvElement *next = BM_uv_element_get(element_map, element->l->next->f, element->l->next); + if (next->island == INVALID_ISLAND) { + UvElement *tail = head_table[next - element_map->buf]; + stack_uv[stacksize_uv++] = tail; + while (tail) { + bm_uv_assign_island(element_map, tail, nislands, map, islandbuf, islandbufsize++); + tail = tail->next; + if (tail && tail->separate) { + break; + } + } + } + } + + /* Scan backwards around the BMFace that contains element->l. */ + if (!uv_selected || uvedit_edge_select_test(scene, element->l->prev, cd_loop_uv_offset)) { + UvElement *prev = BM_uv_element_get(element_map, element->l->prev->f, element->l->prev); + if (prev->island == INVALID_ISLAND) { + UvElement *tail = head_table[prev - element_map->buf]; + stack_uv[stacksize_uv++] = tail; + while (tail) { + bm_uv_assign_island(element_map, tail, nislands, map, islandbuf, islandbufsize++); + tail = tail->next; + if (tail && tail->separate) { + break; + } + } + } + } + + /* The same for all the UvElements in this unique UV. */ + element = element->next; + if (element && element->separate) { + break; + } + } + } + nislands++; + } + BLI_assert(islandbufsize == totuv); + + MEM_SAFE_FREE(stack_uv); + MEM_SAFE_FREE(head_table); + + return nislands; +} + UvElementMap *BM_uv_element_map_create(BMesh *bm, const Scene *scene, - const bool face_selected, const bool uv_selected, const bool use_winding, const bool do_islands) { + /* In uv sync selection, all UVs are visible. */ + const bool face_selected = !(scene->toolsettings->uv_flag & UV_SYNC_SELECTION); + BMVert *ev; BMFace *efa; BMLoop *l; @@ -623,15 +753,21 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm, /* generate UvElement array */ BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { - if (!face_selected || BM_elem_flag_test(efa, BM_ELEM_SELECT)) { - if (!uv_selected) { - totuv += efa->len; - } - else { - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) { - totuv++; - } + if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { + continue; + } + + if (face_selected && !BM_elem_flag_test(efa, BM_ELEM_SELECT)) { + continue; + } + + if (!uv_selected) { + totuv += efa->len; + } + else { + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) { + totuv++; } } } @@ -649,46 +785,48 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm, "UvElement"); if (use_winding) { - winding = MEM_mallocN(sizeof(*winding) * totfaces, "winding"); + winding = MEM_callocN(sizeof(*winding) * totfaces, "winding"); } BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, j) { - if (use_winding) { - winding[j] = false; + if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { + continue; } - if (!face_selected || BM_elem_flag_test(efa, BM_ELEM_SELECT)) { - float(*tf_uv)[2] = NULL; - - if (use_winding) { - tf_uv = (float(*)[2])BLI_buffer_reinit_data(&tf_uv_buf, vec2f, efa->len); - } + if (face_selected && !BM_elem_flag_test(efa, BM_ELEM_SELECT)) { + continue; + } - BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) { - if (uv_selected && !uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) { - continue; - } + float(*tf_uv)[2] = NULL; - buf->l = l; - buf->separate = 0; - buf->island = INVALID_ISLAND; - buf->loop_of_poly_index = i; + if (use_winding) { + tf_uv = (float(*)[2])BLI_buffer_reinit_data(&tf_uv_buf, vec2f, efa->len); + } - buf->next = element_map->vert[BM_elem_index_get(l->v)]; - element_map->vert[BM_elem_index_get(l->v)] = buf; + BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) { + if (uv_selected && !uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) { + continue; + } - if (use_winding) { - luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - copy_v2_v2(tf_uv[i], luv->uv); - } + buf->l = l; + buf->separate = 0; + buf->island = INVALID_ISLAND; + buf->loop_of_poly_index = i; - buf++; - } + buf->next = element_map->vert[BM_elem_index_get(l->v)]; + element_map->vert[BM_elem_index_get(l->v)] = buf; if (use_winding) { - winding[j] = cross_poly_v2(tf_uv, efa->len) > 0; + luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + copy_v2_v2(tf_uv[i], luv->uv); } + + buf++; + } + + if (winding) { + winding[j] = cross_poly_v2(tf_uv, efa->len) > 0; } } @@ -771,6 +909,15 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm, island_number = MEM_mallocN(sizeof(*island_number) * totfaces, "uv_island_number_face"); copy_vn_i(island_number, totfaces, INVALID_ISLAND); + const bool use_uv_edge_connectivity = scene->toolsettings->uv_flag & UV_SYNC_SELECTION ? + scene->toolsettings->selectmode & SCE_SELECT_EDGE : + scene->toolsettings->uv_selectmode & UV_SELECT_EDGE; + if (use_uv_edge_connectivity) { + nislands = bm_uv_edge_select_build_islands( + element_map, scene, islandbuf, map, uv_selected, cd_loop_uv_offset); + islandbufsize = totuv; + } + /* at this point, every UvElement in vert points to a UvElement sharing the same vertex. * Now we should sort uv's in islands. */ for (i = 0; i < totuv; i++) { @@ -798,13 +945,8 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm, if (element->l->f == efa) { /* found the uv corresponding to our face and vertex. * Now fill it to the buffer */ - element->island = nislands; - map[element - element_map->buf] = islandbufsize; - islandbuf[islandbufsize].l = element->l; - islandbuf[islandbufsize].separate = element->separate; - islandbuf[islandbufsize].loop_of_poly_index = element->loop_of_poly_index; - islandbuf[islandbufsize].island = nislands; - islandbufsize++; + bm_uv_assign_island( + element_map, element, nislands, map, islandbuf, islandbufsize++); for (element = initelement; element; element = element->next) { if (element->separate && element != initelement) { diff --git a/source/blender/editors/object/CMakeLists.txt b/source/blender/editors/object/CMakeLists.txt index 97376a495c1..a2f993c92b9 100644 --- a/source/blender/editors/object/CMakeLists.txt +++ b/source/blender/editors/object/CMakeLists.txt @@ -76,7 +76,6 @@ endif() if(WITH_EXPERIMENTAL_FEATURES) add_definitions(-DWITH_SIMULATION_DATABLOCK) add_definitions(-DWITH_POINT_CLOUD) - add_definitions(-DWITH_NEW_CURVES_TYPE) endif() blender_add_lib(bf_editor_object "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/editors/object/object_add.cc b/source/blender/editors/object/object_add.cc index 66e76addd6f..a8e11afba65 100644 --- a/source/blender/editors/object/object_add.cc +++ b/source/blender/editors/object/object_add.cc @@ -1335,21 +1335,21 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op) const char *ob_name = nullptr; switch (type) { case GP_EMPTY: { - ob_name = "GPencil"; + ob_name = CTX_DATA_(BLT_I18NCONTEXT_ID_GPENCIL, "GPencil"); break; } case GP_MONKEY: { - ob_name = "Suzanne"; + ob_name = CTX_DATA_(BLT_I18NCONTEXT_ID_GPENCIL, "Suzanne"); break; } case GP_STROKE: { - ob_name = "Stroke"; + ob_name = CTX_DATA_(BLT_I18NCONTEXT_ID_GPENCIL, "Stroke"); break; } case GP_LRT_OBJECT: case GP_LRT_SCENE: case GP_LRT_COLLECTION: { - ob_name = "Line Art"; + ob_name = CTX_DATA_(BLT_I18NCONTEXT_ID_GPENCIL, "LineArt"); break; } default: { @@ -2071,16 +2071,16 @@ static int object_curves_empty_hair_add_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); ushort local_view_bits; - blender::float3 loc, rot; if (!ED_object_add_generic_get_opts( - C, op, 'Z', loc, rot, nullptr, nullptr, &local_view_bits, nullptr)) { + C, op, 'Z', nullptr, nullptr, nullptr, nullptr, &local_view_bits, nullptr)) { return OPERATOR_CANCELLED; } Object *surface_ob = CTX_data_active_object(C); BLI_assert(surface_ob != nullptr); - Object *curves_ob = ED_object_add_type(C, OB_CURVES, nullptr, loc, rot, false, local_view_bits); + Object *curves_ob = ED_object_add_type(C, OB_CURVES, nullptr, nullptr, nullptr, false, local_view_bits); + BKE_object_apply_mat4(curves_ob, surface_ob->obmat, false, false); /* Set surface object. */ Curves *curves_id = static_cast<Curves *>(curves_ob->data); diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c index 58daf753679..a664d93bb2e 100644 --- a/source/blender/editors/object/object_bake_api.c +++ b/source/blender/editors/object/object_bake_api.c @@ -324,7 +324,7 @@ static bool write_external_bake_pixels(const char *filepath, const int height, const int margin, const int margin_type, - ImageFormatData *im_format, + ImageFormatData const *im_format, const bool is_noncolor, Mesh const *mesh_eval, char const *uv_layer, diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index e65b5a61299..4896ddb5258 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -1469,8 +1469,6 @@ void OBJECT_OT_paths_clear(wmOperatorType *ot) static int shade_smooth_exec(bContext *C, wmOperator *op) { const bool use_smooth = STREQ(op->idname, "OBJECT_OT_shade_smooth"); - const bool use_auto_smooth = RNA_boolean_get(op->ptr, "use_auto_smooth"); - const float auto_smooth_angle = RNA_float_get(op->ptr, "auto_smooth_angle"); bool changed_multi = false; bool has_linked_data = false; @@ -1518,7 +1516,11 @@ static int shade_smooth_exec(bContext *C, wmOperator *op) bool changed = false; if (ob->type == OB_MESH) { BKE_mesh_smooth_flag_set(ob->data, use_smooth); - BKE_mesh_auto_smooth_flag_set(ob->data, use_auto_smooth, auto_smooth_angle); + if (use_smooth) { + const bool use_auto_smooth = RNA_boolean_get(op->ptr, "use_auto_smooth"); + const float auto_smooth_angle = RNA_float_get(op->ptr, "auto_smooth_angle"); + BKE_mesh_auto_smooth_flag_set(ob->data, use_auto_smooth, auto_smooth_angle); + } BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL); changed = true; } diff --git a/source/blender/editors/render/render_opengl.cc b/source/blender/editors/render/render_opengl.cc index 7bd9812a178..77ad23f1e3f 100644 --- a/source/blender/editors/render/render_opengl.cc +++ b/source/blender/editors/render/render_opengl.cc @@ -758,8 +758,7 @@ static bool screen_opengl_render_init(bContext *C, wmOperator *op) WM_jobs_kill_all_except(wm, CTX_wm_screen(C)); /* create offscreen buffer */ - sizex = (scene->r.size * scene->r.xsch) / 100; - sizey = (scene->r.size * scene->r.ysch) / 100; + BKE_render_resolution(&scene->r, false, &sizex, &sizey); /* corrects render size with actual size, not every card supports non-power-of-two dimensions */ DRW_opengl_context_enable(); /* Off-screen creation needs to be done in DRW context. */ diff --git a/source/blender/editors/render/render_view.cc b/source/blender/editors/render/render_view.cc index a7ff2aad05a..9a16c910205 100644 --- a/source/blender/editors/render/render_view.cc +++ b/source/blender/editors/render/render_view.cc @@ -19,6 +19,7 @@ #include "BKE_image.h" #include "BKE_main.h" #include "BKE_report.h" +#include "BKE_scene.h" #include "BKE_screen.h" #include "BLT_translation.h" @@ -130,8 +131,11 @@ ScrArea *render_view_open(bContext *C, int mx, int my, ReportList *reports) } if (U.render_display_type == USER_RENDER_DISPLAY_WINDOW) { - int sizex = 30 * UI_DPI_FAC + (scene->r.xsch * scene->r.size) / 100; - int sizey = 60 * UI_DPI_FAC + (scene->r.ysch * scene->r.size) / 100; + int sizex, sizey; + BKE_render_resolution(&scene->r, false, &sizex, &sizey); + + sizex += 30 * UI_DPI_FAC; + sizey += 60 * UI_DPI_FAC; /* arbitrary... miniature image window views don't make much sense */ if (sizex < 320) { diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c index 239113c28a4..0d6b6ee1d78 100644 --- a/source/blender/editors/screen/screen_context.c +++ b/source/blender/editors/screen/screen_context.c @@ -100,6 +100,7 @@ const char *screen_context_dir[] = { "active_gpencil_frame", "active_annotation_layer", "active_operator", + "active_action", "selected_visible_actions", "selected_editable_actions", "visible_fcurves", @@ -969,6 +970,7 @@ static eContextResult screen_ctx_active_operator(const bContext *C, bContextData } static eContextResult screen_ctx_sel_actions_impl(const bContext *C, bContextDataResult *result, + bool active_only, bool editable) { bAnimContext ac; @@ -978,11 +980,17 @@ static eContextResult screen_ctx_sel_actions_impl(const bContext *C, SpaceAction *saction = (SpaceAction *)ac.sl; if (ELEM(saction->mode, SACTCONT_ACTION, SACTCONT_SHAPEKEY)) { - if (saction->action && !(editable && ID_IS_LINKED(saction->action))) { - CTX_data_id_list_add(result, &saction->action->id); + if (active_only) { + CTX_data_id_pointer_set(result, (ID *)saction->action); + } + else { + if (saction->action && !(editable && ID_IS_LINKED(saction->action))) { + CTX_data_id_list_add(result, &saction->action->id); + } + + CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); } - CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); return CTX_RESULT_OK; } } @@ -995,7 +1003,8 @@ static eContextResult screen_ctx_sel_actions_impl(const bContext *C, switch (ac.spacetype) { case SPACE_GRAPH: - filter |= ANIMFILTER_FCURVESONLY | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_SEL; + filter |= ANIMFILTER_FCURVESONLY | ANIMFILTER_CURVE_VISIBLE | + (active_only ? ANIMFILTER_ACTIVE : ANIMFILTER_SEL); break; case SPACE_ACTION: @@ -1006,7 +1015,7 @@ static eContextResult screen_ctx_sel_actions_impl(const bContext *C, ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); - GSet *seen_set = BLI_gset_ptr_new("seen actions"); + GSet *seen_set = active_only ? NULL : BLI_gset_ptr_new("seen actions"); LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) { /* In dopesheet check selection status of individual items, skipping @@ -1019,36 +1028,48 @@ static eContextResult screen_ctx_sel_actions_impl(const bContext *C, bAction *action = ANIM_channel_action_get(ale); if (action) { - if (editable && ID_IS_LINKED(action)) { - continue; + if (active_only) { + CTX_data_id_pointer_set(result, (ID *)action); + break; } + else { + if (editable && ID_IS_LINKED(action)) { + continue; + } - /* Add the action to the output list if not already added. */ - if (BLI_gset_add(seen_set, action)) { - CTX_data_id_list_add(result, &action->id); + /* Add the action to the output list if not already added. */ + if (BLI_gset_add(seen_set, action)) { + CTX_data_id_list_add(result, &action->id); + } } } } - BLI_gset_free(seen_set, NULL); - ANIM_animdata_freelist(&anim_data); - CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); + if (!active_only) { + BLI_gset_free(seen_set, NULL); + + CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); + } + return CTX_RESULT_OK; } return CTX_RESULT_NO_DATA; } - +static eContextResult screen_ctx_active_action(const bContext *C, bContextDataResult *result) +{ + return screen_ctx_sel_actions_impl(C, result, true, false); +} static eContextResult screen_ctx_selected_visible_actions(const bContext *C, bContextDataResult *result) { - return screen_ctx_sel_actions_impl(C, result, false); + return screen_ctx_sel_actions_impl(C, result, false, false); } static eContextResult screen_ctx_selected_editable_actions(const bContext *C, bContextDataResult *result) { - return screen_ctx_sel_actions_impl(C, result, true); + return screen_ctx_sel_actions_impl(C, result, false, true); } static eContextResult screen_ctx_sel_edit_fcurves_(const bContext *C, bContextDataResult *result, @@ -1262,6 +1283,7 @@ static void ensure_ed_screen_context_functions(void) register_context_function("editable_gpencil_layers", screen_ctx_editable_gpencil_layers); register_context_function("editable_gpencil_strokes", screen_ctx_editable_gpencil_strokes); register_context_function("active_operator", screen_ctx_active_operator); + register_context_function("active_action", screen_ctx_active_action); register_context_function("selected_visible_actions", screen_ctx_selected_visible_actions); register_context_function("selected_editable_actions", screen_ctx_selected_editable_actions); register_context_function("editable_fcurves", screen_ctx_editable_fcurves); diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index c616ca2b5eb..3618b933443 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -4718,7 +4718,7 @@ static int screen_animation_step_invoke(bContext *C, wmOperator *UNUSED(op), con #endif } - /* since we follow drawflags, we can't send notifier but tag regions ourselves */ + /* Since we follow draw-flags, we can't send notifier but tag regions ourselves. */ if (depsgraph != NULL) { ED_update_for_newframe(bmain, depsgraph); } diff --git a/source/blender/editors/screen/screen_user_menu.c b/source/blender/editors/screen/screen_user_menu.c index 1156452310c..9d66debda6f 100644 --- a/source/blender/editors/screen/screen_user_menu.c +++ b/source/blender/editors/screen/screen_user_menu.c @@ -214,7 +214,14 @@ static void screen_user_menu_draw(const bContext *C, Menu *menu) wmOperatorType *ot = WM_operatortype_find(umi_op->op_idname, false); if (ot != NULL) { IDProperty *prop = umi_op->prop ? IDP_CopyProperty(umi_op->prop) : NULL; - uiItemFullO_ptr(menu->layout, ot, ui_name, ICON_NONE, prop, umi_op->opcontext, 0, NULL); + uiItemFullO_ptr(menu->layout, + ot, + CTX_IFACE_(ot->translation_context, ui_name), + ICON_NONE, + prop, + umi_op->opcontext, + 0, + NULL); is_empty = false; } else { diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc b/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc index 709ecc79967..408139d6653 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc @@ -4,8 +4,7 @@ #include "BLI_enumerable_thread_specific.hh" #include "BLI_float4x4.hh" -#include "BLI_kdtree.h" -#include "BLI_rand.hh" +#include "BLI_length_parameterize.hh" #include "BLI_vector.hh" #include "PIL_time.h" @@ -14,19 +13,13 @@ #include "BKE_attribute_math.hh" #include "BKE_brush.h" -#include "BKE_bvhutils.h" #include "BKE_context.h" #include "BKE_curves.hh" -#include "BKE_mesh.h" -#include "BKE_mesh_runtime.h" #include "BKE_paint.h" -#include "BKE_spline.hh" #include "DNA_brush_enums.h" #include "DNA_brush_types.h" #include "DNA_curves_types.h" -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" #include "DNA_object_types.h" #include "DNA_screen_types.h" #include "DNA_space_types.h" @@ -70,6 +63,24 @@ class ShrinkCurvesEffect : public CurvesEffect { private: const Brush &brush_; + /** Storage of per-curve parameterization data to avoid reallocation. */ + struct ParameterizationBuffers { + Array<float3> old_positions; + Array<float> old_lengths; + Array<float> sample_lengths; + Array<int> indices; + Array<float> factors; + + void reinitialize(const int points_num) + { + this->old_positions.reinitialize(points_num); + this->old_lengths.reinitialize(length_parameterize::segments_num(points_num, false)); + this->sample_lengths.reinitialize(points_num); + this->indices.reinitialize(points_num); + this->factors.reinitialize(points_num); + } + }; + public: ShrinkCurvesEffect(const Brush &brush) : brush_(brush) { @@ -81,46 +92,42 @@ class ShrinkCurvesEffect : public CurvesEffect { { MutableSpan<float3> positions_cu = curves.positions_for_write(); threading::parallel_for(curve_indices.index_range(), 256, [&](const IndexRange range) { + ParameterizationBuffers data; for (const int influence_i : range) { const int curve_i = curve_indices[influence_i]; const float move_distance_cu = move_distances_cu[influence_i]; - const IndexRange curve_points = curves.points_for_curve(curve_i); - this->shrink_curve(positions_cu, curve_points, move_distance_cu); + const IndexRange points = curves.points_for_curve(curve_i); + this->shrink_curve(positions_cu.slice(points), move_distance_cu, data); } }); } + private: void shrink_curve(MutableSpan<float3> positions, - const IndexRange curve_points, - const float shrink_length) const + const float shrink_length, + ParameterizationBuffers &data) const { - PolySpline spline; - spline.resize(curve_points.size()); - MutableSpan<float3> spline_positions = spline.positions(); - spline_positions.copy_from(positions.slice(curve_points)); - spline.mark_cache_invalid(); + namespace lp = length_parameterize; + data.reinitialize(positions.size()); + + /* Copy the old positions to facilitate mixing from neighbors for the resulting curve. */ + data.old_positions.as_mutable_span().copy_from(positions); + + lp::accumulate_lengths<float3>(data.old_positions, false, data.old_lengths); + const float min_length = brush_.curves_sculpt_settings->minimum_length; - const float old_length = spline.length(); + const float old_length = data.old_lengths.last(); const float new_length = std::max(min_length, old_length - shrink_length); const float length_factor = std::clamp(new_length / old_length, 0.0f, 1.0f); - Vector<float> old_point_lengths; - old_point_lengths.append(0.0f); - for (const int i : spline_positions.index_range().drop_back(1)) { - const float3 &p1 = spline_positions[i]; - const float3 &p2 = spline_positions[i + 1]; - const float length = math::distance(p1, p2); - old_point_lengths.append(old_point_lengths.last() + length); + data.sample_lengths.first() = 0.0f; + for (const int i : data.old_lengths.index_range()) { + data.sample_lengths[i + 1] = data.old_lengths[i] * length_factor; } - for (const int i : spline_positions.index_range()) { - const float eval_length = old_point_lengths[i] * length_factor; - const Spline::LookupResult lookup = spline.lookup_evaluated_length(eval_length); - const float index_factor = lookup.evaluated_index + lookup.factor; - float3 p; - spline.sample_with_index_factors<float3>(spline_positions, {&index_factor, 1}, {&p, 1}); - positions[curve_points[i]] = p; - } + lp::sample_at_lengths(data.old_lengths, data.sample_lengths, data.indices, data.factors); + + lp::linear_interpolation<float3>(data.old_positions, data.indices, data.factors, positions); } }; diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc index 5c73c7a37d3..47e0fe3a61a 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc @@ -147,7 +147,10 @@ struct SculptCurvesBrushStrokeData { PaintStroke *stroke; }; -static bool stroke_get_location(bContext *C, float out[3], const float mouse[2]) +static bool stroke_get_location(bContext *C, + float out[3], + const float mouse[2], + bool UNUSED(force_original)) { out[0] = mouse[0]; out[1] = mouse[1]; diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h index ea17114efa5..99c25953d50 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.h +++ b/source/blender/editors/sculpt_paint/paint_intern.h @@ -46,7 +46,10 @@ typedef struct CoNo { /* paint_stroke.c */ -typedef bool (*StrokeGetLocation)(struct bContext *C, float location[3], const float mouse[2]); +typedef bool (*StrokeGetLocation)(struct bContext *C, + float location[3], + const float mouse[2], + bool force_original); typedef bool (*StrokeTestStart)(struct bContext *C, struct wmOperator *op, const float mouse[2]); typedef void (*StrokeUpdateStep)(struct bContext *C, struct wmOperator *op, @@ -370,10 +373,12 @@ void PAINT_OT_face_select_linked(struct wmOperatorType *ot); void PAINT_OT_face_select_linked_pick(struct wmOperatorType *ot); void PAINT_OT_face_select_all(struct wmOperatorType *ot); void PAINT_OT_face_select_hide(struct wmOperatorType *ot); -void PAINT_OT_face_select_reveal(struct wmOperatorType *ot); + +void PAINT_OT_face_vert_reveal(struct wmOperatorType *ot); void PAINT_OT_vert_select_all(struct wmOperatorType *ot); void PAINT_OT_vert_select_ungrouped(struct wmOperatorType *ot); +void PAINT_OT_vert_select_hide(struct wmOperatorType *ot); bool vert_paint_poll(struct bContext *C); bool mask_paint_poll(struct bContext *C); diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c index ce6b397af15..994ae4011b4 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.c +++ b/source/blender/editors/sculpt_paint/paint_ops.c @@ -1454,6 +1454,7 @@ void ED_operatortypes_paint(void) /* vertex selection */ WM_operatortype_append(PAINT_OT_vert_select_all); WM_operatortype_append(PAINT_OT_vert_select_ungrouped); + WM_operatortype_append(PAINT_OT_vert_select_hide); /* vertex */ WM_operatortype_append(PAINT_OT_vertex_paint_toggle); @@ -1472,7 +1473,8 @@ void ED_operatortypes_paint(void) WM_operatortype_append(PAINT_OT_face_select_linked_pick); WM_operatortype_append(PAINT_OT_face_select_all); WM_operatortype_append(PAINT_OT_face_select_hide); - WM_operatortype_append(PAINT_OT_face_select_reveal); + + WM_operatortype_append(PAINT_OT_face_vert_reveal); /* partial visibility */ WM_operatortype_append(PAINT_OT_hide_show); diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index 1ee26935dc9..88e7a786a47 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -79,6 +79,8 @@ typedef struct PaintStroke { float last_mouse_position[2]; float last_world_space_position[3]; + float last_scene_spacing_delta[3]; + bool stroke_over_mesh; /* space distance covered so far */ float stroke_distance; @@ -120,6 +122,8 @@ typedef struct PaintStroke { StrokeUpdateStep update_step; StrokeRedraw redraw; StrokeDone done; + + bool original; /* Raycast original mesh at start of stroke */ } PaintStroke; /*** Cursors ***/ @@ -243,6 +247,11 @@ static bool paint_stroke_use_scene_spacing(Brush *brush, ePaintMode mode) return false; } +static bool paint_tool_raycast_original(Brush *brush, ePaintMode UNUSED(mode)) +{ + return brush->flag & BRUSH_ANCHORED; +} + static bool paint_tool_require_inbetween_mouse_events(Brush *brush, ePaintMode mode) { if (brush->flag & BRUSH_ANCHORED) { @@ -392,7 +401,7 @@ static bool paint_brush_update(bContext *C, halfway[1] = dy * 0.5f + stroke->initial_mouse[1]; if (stroke->get_location) { - if (stroke->get_location(C, r_location, halfway)) { + if (stroke->get_location(C, r_location, halfway, stroke->original)) { hit = true; location_sampled = true; location_success = true; @@ -466,7 +475,7 @@ static bool paint_brush_update(bContext *C, if (!location_sampled) { if (stroke->get_location) { - if (stroke->get_location(C, r_location, mouse)) { + if (stroke->get_location(C, r_location, mouse, stroke->original)) { location_success = true; *r_location_is_set = true; } @@ -550,8 +559,16 @@ static void paint_brush_stroke_add_step( stroke->last_pressure = pressure; if (paint_stroke_use_scene_spacing(brush, mode)) { - SCULPT_stroke_get_location(C, stroke->last_world_space_position, stroke->last_mouse_position); - mul_m4_v3(stroke->vc.obact->obmat, stroke->last_world_space_position); + float world_space_position[3]; + + if (SCULPT_stroke_get_location( + C, world_space_position, stroke->last_mouse_position, stroke->original)) { + copy_v3_v3(stroke->last_world_space_position, world_space_position); + mul_m4_v3(stroke->vc.obact->obmat, stroke->last_world_space_position); + } + else { + add_v3_v3(stroke->last_world_space_position, stroke->last_scene_spacing_delta); + } } if (paint_stroke_use_jitter(mode, brush, stroke->stroke_mode == BRUSH_STROKE_INVERT)) { @@ -698,7 +715,7 @@ static float paint_space_stroke_spacing(bContext *C, spacing *= stroke->zoom_2d; if (paint_stroke_use_scene_spacing(brush, mode)) { - return max_ff(0.001f, size_clamp * spacing / 50.0f); + return size_clamp * spacing / 50.0f; } return max_ff(stroke->zoom_2d, size_clamp * spacing / 50.0f); } @@ -807,7 +824,7 @@ static int paint_space_stroke(bContext *C, if (use_scene_spacing) { float world_space_position[3]; - bool hit = SCULPT_stroke_get_location(C, world_space_position, final_mouse); + bool hit = SCULPT_stroke_get_location(C, world_space_position, final_mouse, stroke->original); mul_m4_v3(stroke->vc.obact->obmat, world_space_position); if (hit && stroke->stroke_over_mesh) { sub_v3_v3v3(d_world_space_position, world_space_position, stroke->last_world_space_position); @@ -838,6 +855,8 @@ static int paint_space_stroke(bContext *C, stroke->last_world_space_position, final_world_space_position); ED_view3d_project_v2(region, final_world_space_position, mouse); + + mul_v3_v3fl(stroke->last_scene_spacing_delta, d_world_space_position, spacing); } else { mouse[0] = stroke->last_mouse_position[0] + dmouse[0] * spacing; @@ -896,6 +915,8 @@ PaintStroke *paint_stroke_new(bContext *C, stroke->ups = ups; stroke->stroke_mode = RNA_enum_get(op->ptr, "mode"); + stroke->original = paint_tool_raycast_original(br, BKE_paintmode_get_active_from_context(C)); + get_imapaint_zoom(C, &zoomx, &zoomy); stroke->zoom_2d = max_ff(zoomx, zoomy); @@ -1191,8 +1212,10 @@ static void paint_line_strokes_spacing(bContext *C, copy_v2_v2(stroke->last_mouse_position, old_pos); if (use_scene_spacing) { - bool hit_old = SCULPT_stroke_get_location(C, world_space_position_old, old_pos); - bool hit_new = SCULPT_stroke_get_location(C, world_space_position_new, new_pos); + bool hit_old = SCULPT_stroke_get_location( + C, world_space_position_old, old_pos, stroke->original); + bool hit_new = SCULPT_stroke_get_location( + C, world_space_position_new, new_pos, stroke->original); mul_m4_v3(stroke->vc.obact->obmat, world_space_position_old); mul_m4_v3(stroke->vc.obact->obmat, world_space_position_new); if (hit_old && hit_new && stroke->stroke_over_mesh) { @@ -1336,7 +1359,7 @@ static bool paint_stroke_curve_end(bContext *C, wmOperator *op, PaintStroke *str if (paint_stroke_use_scene_spacing(br, BKE_paintmode_get_active_from_context(C))) { stroke->stroke_over_mesh = SCULPT_stroke_get_location( - C, stroke->last_world_space_position, data + 2 * j); + C, stroke->last_world_space_position, data + 2 * j, stroke->original); mul_m4_v3(stroke->vc.obact->obmat, stroke->last_world_space_position); } @@ -1468,7 +1491,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event, PaintS copy_v2_v2(stroke->last_mouse_position, sample_average.mouse); if (paint_stroke_use_scene_spacing(br, mode)) { stroke->stroke_over_mesh = SCULPT_stroke_get_location( - C, stroke->last_world_space_position, sample_average.mouse); + C, stroke->last_world_space_position, sample_average.mouse, stroke->original); mul_m4_v3(stroke->vc.obact->obmat, stroke->last_world_space_position); } stroke->stroke_started = stroke->test_start(C, op, sample_average.mouse); diff --git a/source/blender/editors/sculpt_paint/paint_utils.c b/source/blender/editors/sculpt_paint/paint_utils.c index 3f26f590b70..1f272882100 100644 --- a/source/blender/editors/sculpt_paint/paint_utils.c +++ b/source/blender/editors/sculpt_paint/paint_utils.c @@ -749,25 +749,68 @@ void PAINT_OT_face_select_hide(wmOperatorType *ot) ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected objects"); } -static int face_select_reveal_exec(bContext *C, wmOperator *op) +static int vert_select_hide_exec(bContext *C, wmOperator *op) +{ + const bool unselected = RNA_boolean_get(op->ptr, "unselected"); + Object *ob = CTX_data_active_object(C); + paintvert_hide(C, ob, unselected); + ED_region_tag_redraw(CTX_wm_region(C)); + return OPERATOR_FINISHED; +} + +void PAINT_OT_vert_select_hide(wmOperatorType *ot) +{ + ot->name = "Vertex Select Hide"; + ot->description = "Hide selected vertices"; + ot->idname = "PAINT_OT_vert_select_hide"; + + ot->exec = vert_select_hide_exec; + ot->poll = vert_paint_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_boolean( + ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected vertices"); +} + +static int face_vert_reveal_exec(bContext *C, wmOperator *op) { const bool select = RNA_boolean_get(op->ptr, "select"); Object *ob = CTX_data_active_object(C); - paintface_reveal(C, ob, select); + + if (BKE_paint_select_vert_test(ob)) { + paintvert_reveal(C, ob, select); + } + else { + paintface_reveal(C, ob, select); + } + ED_region_tag_redraw(CTX_wm_region(C)); return OPERATOR_FINISHED; } -void PAINT_OT_face_select_reveal(wmOperatorType *ot) +static bool face_vert_reveal_poll(bContext *C) { - ot->name = "Face Select Reveal"; - ot->description = "Reveal hidden faces"; - ot->idname = "PAINT_OT_face_select_reveal"; + Object *ob = CTX_data_active_object(C); - ot->exec = face_select_reveal_exec; - ot->poll = facemask_paint_poll; + /* Allow using this operator when no selection is enabled but hiding is applied. */ + return BKE_paint_select_elem_test(ob) || BKE_paint_always_hide_test(ob); +} + +void PAINT_OT_face_vert_reveal(wmOperatorType *ot) +{ + ot->name = "Reveal Faces/Vertices"; + ot->description = "Reveal hidden faces and vertices"; + ot->idname = "PAINT_OT_face_vert_reveal"; + + ot->exec = face_vert_reveal_exec; + ot->poll = face_vert_reveal_poll; ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - RNA_def_boolean(ot->srna, "select", true, "Select", ""); + RNA_def_boolean(ot->srna, + "select", + true, + "Select", + "Specifies whether the newly revealed geometry should be selected"); } diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 906c4fd35fe..2951706ebd8 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -4952,7 +4952,10 @@ bool SCULPT_cursor_geometry_info_update(bContext *C, return true; } -bool SCULPT_stroke_get_location(bContext *C, float out[3], const float mval[2]) +bool SCULPT_stroke_get_location(bContext *C, + float out[3], + const float mval[2], + bool force_original) { Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); Object *ob; @@ -4968,7 +4971,7 @@ bool SCULPT_stroke_get_location(bContext *C, float out[3], const float mval[2]) ss = ob->sculpt; cache = ss->cache; - original = (cache) ? cache->original : false; + original = force_original || ((cache) ? cache->original : false); const Brush *brush = BKE_paint_brush(BKE_paint_get_active_from_context(C)); @@ -5284,7 +5287,7 @@ void SCULPT_flush_update_done(const bContext *C, Object *ob, SculptUpdateType up static bool over_mesh(bContext *C, struct wmOperator *UNUSED(op), const float mval[2]) { float co_dummy[3]; - return SCULPT_stroke_get_location(C, co_dummy, mval); + return SCULPT_stroke_get_location(C, co_dummy, mval, false); } bool SCULPT_handles_colors_report(SculptSession *ss, ReportList *reports) @@ -5499,10 +5502,23 @@ static int sculpt_brush_stroke_invoke(bContext *C, wmOperator *op, const wmEvent struct PaintStroke *stroke; int ignore_background_click; int retval; + Object *ob = CTX_data_active_object(C); + + /* Test that ob is visible; otherwise we won't be able to get evaluated data + * from the depsgraph. We do this here instead of SCULPT_mode_poll + * to avoid falling through to the translate operator in the + * global view3d keymap. + * + * Note: BKE_object_is_visible_in_viewport is not working here (it returns false + * if the object is in local view); instead, test for OB_HIDE_VIEWPORT directly. + */ + + if (ob->visibility_flag & OB_HIDE_VIEWPORT) { + return OPERATOR_CANCELLED; + } sculpt_brush_stroke_init(C, op); - Object *ob = CTX_data_active_object(C); Sculpt *sd = CTX_data_tool_settings(C)->sculpt; Brush *brush = BKE_paint_brush(&sd->paint); diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_color.c b/source/blender/editors/sculpt_paint/sculpt_filter_color.c index a5d9f5306e2..a9186010a9f 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_color.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.c @@ -93,7 +93,7 @@ static void color_filter_task_cb(void *__restrict userdata, const int mode = data->filter_type; SculptOrigVertData orig_data; - SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COLOR); PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index 8485c7fcbf9..83526006d7d 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -845,7 +845,10 @@ void SCULPT_tag_update_overlays(bContext *C); * (This allows us to ignore the GL depth buffer) * Returns 0 if the ray doesn't hit the mesh, non-zero otherwise. */ -bool SCULPT_stroke_get_location(struct bContext *C, float out[3], const float mouse[2]); +bool SCULPT_stroke_get_location(struct bContext *C, + float out[3], + const float mouse[2], + bool force_original); /** * Gets the normal, location and active vertex location of the geometry under the cursor. This also * updates the active vertex and cursor related data of the SculptSession using the mouse position diff --git a/source/blender/editors/sculpt_paint/sculpt_transform.c b/source/blender/editors/sculpt_paint/sculpt_transform.c index 365000ab163..8856e3bf3db 100644 --- a/source/blender/editors/sculpt_paint/sculpt_transform.c +++ b/source/blender/editors/sculpt_paint/sculpt_transform.c @@ -426,7 +426,7 @@ static int sculpt_set_pivot_position_exec(bContext *C, wmOperator *op) RNA_float_get(op->ptr, "mouse_x"), RNA_float_get(op->ptr, "mouse_y"), }; - if (SCULPT_stroke_get_location(C, stroke_location, mval)) { + if (SCULPT_stroke_get_location(C, stroke_location, mval, false)) { copy_v3_v3(ss->pivot_pos, stroke_location); } } diff --git a/source/blender/editors/sculpt_paint/sculpt_uv.c b/source/blender/editors/sculpt_paint/sculpt_uv.c index 8e1f4f4d495..5c2dff7b252 100644 --- a/source/blender/editors/sculpt_paint/sculpt_uv.c +++ b/source/blender/editors/sculpt_paint/sculpt_uv.c @@ -500,20 +500,10 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm if (do_island_optimization) { /* We will need island information */ - if (ts->uv_flag & UV_SYNC_SELECTION) { - data->elementMap = BM_uv_element_map_create(bm, scene, false, false, true, true); - } - else { - data->elementMap = BM_uv_element_map_create(bm, scene, true, false, true, true); - } + data->elementMap = BM_uv_element_map_create(bm, scene, false, true, true); } else { - if (ts->uv_flag & UV_SYNC_SELECTION) { - data->elementMap = BM_uv_element_map_create(bm, scene, false, false, true, false); - } - else { - data->elementMap = BM_uv_element_map_create(bm, scene, true, false, true, false); - } + data->elementMap = BM_uv_element_map_create(bm, scene, false, true, false); } if (!data->elementMap) { diff --git a/source/blender/editors/sound/sound_ops.c b/source/blender/editors/sound/sound_ops.c index 2a8a2be8b65..08b795db0c3 100644 --- a/source/blender/editors/sound/sound_ops.c +++ b/source/blender/editors/sound/sound_ops.c @@ -340,7 +340,8 @@ static int sound_mixdown_exec(bContext *C, wmOperator *op) AUD_DeviceSpecs specs; AUD_Container container; AUD_Codec codec; - const char *result; + int result; + char error_message[1024] = {'\0'}; sound_bake_animation_exec(C, op); @@ -372,7 +373,9 @@ static int sound_mixdown_exec(bContext *C, wmOperator *op) codec, bitrate, NULL, - NULL); + NULL, + error_message, + sizeof(error_message)); } else { result = AUD_mixdown(scene_eval->sound_scene, @@ -385,13 +388,15 @@ static int sound_mixdown_exec(bContext *C, wmOperator *op) codec, bitrate, NULL, - NULL); + NULL, + error_message, + sizeof(error_message)); } BKE_sound_reset_scene_specs(scene_eval); - if (result) { - BKE_report(op->reports, RPT_ERROR, result); + if (!result) { + BKE_report(op->reports, RPT_ERROR, error_message); return OPERATOR_CANCELLED; } #else /* WITH_AUDASPACE */ diff --git a/source/blender/editors/space_action/action_edit.c b/source/blender/editors/space_action/action_edit.c index e97b666810c..23c92cbdaa0 100644 --- a/source/blender/editors/space_action/action_edit.c +++ b/source/blender/editors/space_action/action_edit.c @@ -1006,7 +1006,7 @@ static bool delete_action_keys(bAnimContext *ac) AnimData *adt = ale->adt; /* delete selected keyframes only */ - changed = delete_fcurve_keys(fcu); + changed = BKE_fcurve_delete_keys_selected(fcu); /* Only delete curve too if it won't be doing anything anymore */ if (BKE_fcurve_is_empty(fcu)) { @@ -1473,7 +1473,7 @@ static void sethandles_action_keys(bAnimContext *ac, short mode) /* any selected keyframes for editing? */ if (ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, sel_cb, NULL)) { /* change type of selected handles */ - ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, edit_cb, calchandles_fcurve); + ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, edit_cb, BKE_fcurve_handles_recalc); ale->update |= ANIM_UPDATE_DEFAULT; } @@ -1790,11 +1790,11 @@ static void snap_action_keys(bAnimContext *ac, short mode) } else if (adt) { ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 0); - ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve); + ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, BKE_fcurve_handles_recalc); ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 0); } else { - ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve); + ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, BKE_fcurve_handles_recalc); } ale->update |= ANIM_UPDATE_DEFAULT; @@ -1914,11 +1914,11 @@ static void mirror_action_keys(bAnimContext *ac, short mode) } else if (adt) { ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 0); - ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve); + ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, BKE_fcurve_handles_recalc); ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 0); } else { - ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve); + ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, BKE_fcurve_handles_recalc); } ale->update |= ANIM_UPDATE_DEFAULT; diff --git a/source/blender/editors/space_api/spacetypes.c b/source/blender/editors/space_api/spacetypes.c index d53fe2efb03..3d964a95bc0 100644 --- a/source/blender/editors/space_api/spacetypes.c +++ b/source/blender/editors/space_api/spacetypes.c @@ -163,6 +163,7 @@ void ED_spacemacros_init(void) ED_operatormacros_sequencer(); ED_operatormacros_paint(); ED_operatormacros_gpencil(); + ED_operatormacros_nla(); /* Register dropboxes (can use macros). */ ED_dropboxes_ui(); diff --git a/source/blender/editors/space_buttons/CMakeLists.txt b/source/blender/editors/space_buttons/CMakeLists.txt index e2f1df74446..7d4f38b1841 100644 --- a/source/blender/editors/space_buttons/CMakeLists.txt +++ b/source/blender/editors/space_buttons/CMakeLists.txt @@ -35,7 +35,6 @@ endif() if(WITH_EXPERIMENTAL_FEATURES) add_definitions(-DWITH_SIMULATION_DATABLOCK) add_definitions(-DWITH_POINT_CLOUD) - add_definitions(-DWITH_NEW_CURVES_TYPE) endif() blender_add_lib(bf_editor_space_buttons "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/editors/space_buttons/buttons_context.c b/source/blender/editors/space_buttons/buttons_context.c index 5780b0c9df7..c3479409f0d 100644 --- a/source/blender/editors/space_buttons/buttons_context.c +++ b/source/blender/editors/space_buttons/buttons_context.c @@ -258,11 +258,9 @@ static bool buttons_context_path_data(ButsContextPath *path, int type) if (RNA_struct_is_a(ptr->type, &RNA_GreasePencil) && (ELEM(type, -1, OB_GPENCIL))) { return true; } -#ifdef WITH_NEW_CURVES_TYPE if (RNA_struct_is_a(ptr->type, &RNA_Curves) && (ELEM(type, -1, OB_CURVES))) { return true; } -#endif #ifdef WITH_POINT_CLOUD if (RNA_struct_is_a(ptr->type, &RNA_PointCloud) && (ELEM(type, -1, OB_POINTCLOUD))) { return true; @@ -830,9 +828,7 @@ const char *buttons_context_dir[] = { "line_style", "collection", "gpencil", -#ifdef WITH_NEW_CURVES_TYPE "curves", -#endif #ifdef WITH_POINT_CLOUD "pointcloud", #endif @@ -926,12 +922,10 @@ int /*eContextResult*/ buttons_context(const bContext *C, set_pointer_type(path, result, &RNA_LightProbe); return CTX_RESULT_OK; } -#ifdef WITH_NEW_CURVES_TYPE if (CTX_data_equals(member, "curves")) { set_pointer_type(path, result, &RNA_Curves); return CTX_RESULT_OK; } -#endif #ifdef WITH_POINT_CLOUD if (CTX_data_equals(member, "pointcloud")) { set_pointer_type(path, result, &RNA_PointCloud); diff --git a/source/blender/editors/space_console/console_ops.c b/source/blender/editors/space_console/console_ops.c index 17fbef23eac..ef22b1b9f0b 100644 --- a/source/blender/editors/space_console/console_ops.c +++ b/source/blender/editors/space_console/console_ops.c @@ -413,16 +413,8 @@ static int console_insert_invoke(bContext *C, wmOperator *op, const wmEvent *eve } char str[BLI_UTF8_MAX + 1]; - size_t len; - - if (event->utf8_buf[0]) { - len = BLI_str_utf8_size_safe(event->utf8_buf); - memcpy(str, event->utf8_buf, len); - } - else { - /* in theory, ghost can set value to extended ascii here */ - len = BLI_str_utf8_from_unicode(event->ascii, str, sizeof(str) - 1); - } + const size_t len = BLI_str_utf8_size_safe(event->utf8_buf); + memcpy(str, event->utf8_buf, len); str[len] = '\0'; RNA_string_set(op->ptr, "text", str); } diff --git a/source/blender/editors/space_file/asset_catalog_tree_view.cc b/source/blender/editors/space_file/asset_catalog_tree_view.cc index 87595ecdb88..1829f19bfd6 100644 --- a/source/blender/editors/space_file/asset_catalog_tree_view.cc +++ b/source/blender/editors/space_file/asset_catalog_tree_view.cc @@ -86,12 +86,12 @@ class AssetCatalogTreeViewItem : public ui::BasicTreeViewItem { bool rename(StringRefNull new_name) override; /** Add drag support for catalog items. */ - std::unique_ptr<ui::AbstractTreeViewItemDragController> create_drag_controller() const override; + std::unique_ptr<ui::AbstractViewItemDragController> create_drag_controller() const override; /** Add dropping support for catalog items. */ - std::unique_ptr<ui::AbstractTreeViewItemDropController> create_drop_controller() const override; + std::unique_ptr<ui::AbstractViewItemDropController> create_drop_controller() const override; }; -class AssetCatalogDragController : public ui::AbstractTreeViewItemDragController { +class AssetCatalogDragController : public ui::AbstractViewItemDragController { AssetCatalogTreeItem &catalog_item_; public: @@ -103,7 +103,7 @@ class AssetCatalogDragController : public ui::AbstractTreeViewItemDragController void on_drag_start() override; }; -class AssetCatalogDropController : public ui::AbstractTreeViewItemDropController { +class AssetCatalogDropController : public ui::AbstractViewItemDropController { AssetCatalogTreeItem &catalog_item_; public: @@ -142,7 +142,7 @@ class AssetCatalogTreeViewAllItem : public ui::BasicTreeViewItem { void build_row(uiLayout &row) override; - struct DropController : public ui::AbstractTreeViewItemDropController { + struct DropController : public ui::AbstractViewItemDropController { DropController(AssetCatalogTreeView &tree_view); bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const override; @@ -150,13 +150,13 @@ class AssetCatalogTreeViewAllItem : public ui::BasicTreeViewItem { bool on_drop(struct bContext *C, const wmDrag &drag) override; }; - std::unique_ptr<ui::AbstractTreeViewItemDropController> create_drop_controller() const override; + std::unique_ptr<ui::AbstractViewItemDropController> create_drop_controller() const override; }; class AssetCatalogTreeViewUnassignedItem : public ui::BasicTreeViewItem { using BasicTreeViewItem::BasicTreeViewItem; - struct DropController : public ui::AbstractTreeViewItemDropController { + struct DropController : public ui::AbstractViewItemDropController { DropController(AssetCatalogTreeView &tree_view); bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const override; @@ -164,7 +164,7 @@ class AssetCatalogTreeViewUnassignedItem : public ui::BasicTreeViewItem { bool on_drop(struct bContext *C, const wmDrag &drag) override; }; - std::unique_ptr<ui::AbstractTreeViewItemDropController> create_drop_controller() const override; + std::unique_ptr<ui::AbstractViewItemDropController> create_drop_controller() const override; }; /* ---------------------------------------------------------------------- */ @@ -272,11 +272,11 @@ void AssetCatalogTreeViewItem::build_row(uiLayout &row) return; } - uiButTreeRow *tree_row_but = tree_row_button(); + uiButViewItem *view_item_but = view_item_button(); PointerRNA *props; props = UI_but_extra_operator_icon_add( - (uiBut *)tree_row_but, "ASSET_OT_catalog_new", WM_OP_INVOKE_DEFAULT, ICON_ADD); + (uiBut *)view_item_but, "ASSET_OT_catalog_new", WM_OP_INVOKE_DEFAULT, ICON_ADD); RNA_string_set(props, "parent_path", catalog_item_.catalog_path().c_str()); } @@ -305,7 +305,7 @@ void AssetCatalogTreeViewItem::build_context_menu(bContext &C, uiLayout &column) 0, &props); RNA_string_set(&props, "catalog_id", catalog_id_str_buffer); - uiItemO(&column, "Rename", ICON_NONE, "UI_OT_tree_view_item_rename"); + uiItemO(&column, "Rename", ICON_NONE, "UI_OT_view_item_rename"); /* Doesn't actually exist right now, but could be defined in Python. Reason that this isn't done * in Python yet is that catalogs are not exposed in BPY, and we'd somehow pass the clicked on @@ -333,14 +333,14 @@ bool AssetCatalogTreeViewItem::rename(StringRefNull new_name) return true; } -std::unique_ptr<ui::AbstractTreeViewItemDropController> AssetCatalogTreeViewItem:: +std::unique_ptr<ui::AbstractViewItemDropController> AssetCatalogTreeViewItem:: create_drop_controller() const { return std::make_unique<AssetCatalogDropController>( static_cast<AssetCatalogTreeView &>(get_tree_view()), catalog_item_); } -std::unique_ptr<ui::AbstractTreeViewItemDragController> AssetCatalogTreeViewItem:: +std::unique_ptr<ui::AbstractViewItemDragController> AssetCatalogTreeViewItem:: create_drag_controller() const { return std::make_unique<AssetCatalogDragController>( @@ -351,7 +351,7 @@ std::unique_ptr<ui::AbstractTreeViewItemDragController> AssetCatalogTreeViewItem AssetCatalogDropController::AssetCatalogDropController(AssetCatalogTreeView &tree_view, AssetCatalogTreeItem &catalog_item) - : ui::AbstractTreeViewItemDropController(tree_view), catalog_item_(catalog_item) + : ui::AbstractViewItemDropController(tree_view), catalog_item_(catalog_item) { } @@ -422,10 +422,10 @@ bool AssetCatalogDropController::on_drop(struct bContext *C, const wmDrag &drag) { if (drag.type == WM_DRAG_ASSET_CATALOG) { return drop_asset_catalog_into_catalog( - drag, tree_view<AssetCatalogTreeView>(), catalog_item_.get_catalog_id()); + drag, get_view<AssetCatalogTreeView>(), catalog_item_.get_catalog_id()); } return drop_assets_into_catalog(C, - tree_view<AssetCatalogTreeView>(), + get_view<AssetCatalogTreeView>(), drag, catalog_item_.get_catalog_id(), catalog_item_.get_simple_name()); @@ -512,14 +512,14 @@ bool AssetCatalogDropController::has_droppable_asset(const wmDrag &drag, ::AssetLibrary &AssetCatalogDropController::get_asset_library() const { - return *tree_view<AssetCatalogTreeView>().asset_library_; + return *get_view<AssetCatalogTreeView>().asset_library_; } /* ---------------------------------------------------------------------- */ AssetCatalogDragController::AssetCatalogDragController(AssetCatalogTreeView &tree_view, AssetCatalogTreeItem &catalog_item) - : ui::AbstractTreeViewItemDragController(tree_view), catalog_item_(catalog_item) + : ui::AbstractViewItemDragController(tree_view), catalog_item_(catalog_item) { } @@ -538,7 +538,7 @@ void *AssetCatalogDragController::create_drag_data() const void AssetCatalogDragController::on_drag_start() { - AssetCatalogTreeView &tree_view_ = tree_view<AssetCatalogTreeView>(); + AssetCatalogTreeView &tree_view_ = get_view<AssetCatalogTreeView>(); tree_view_.activate_catalog_by_id(catalog_item_.get_catalog_id()); } @@ -551,15 +551,15 @@ void AssetCatalogTreeViewAllItem::build_row(uiLayout &row) PointerRNA *props; UI_but_extra_operator_icon_add( - (uiBut *)tree_row_button(), "ASSET_OT_catalogs_save", WM_OP_INVOKE_DEFAULT, ICON_FILE_TICK); + (uiBut *)view_item_button(), "ASSET_OT_catalogs_save", WM_OP_INVOKE_DEFAULT, ICON_FILE_TICK); props = UI_but_extra_operator_icon_add( - (uiBut *)tree_row_button(), "ASSET_OT_catalog_new", WM_OP_INVOKE_DEFAULT, ICON_ADD); + (uiBut *)view_item_button(), "ASSET_OT_catalog_new", WM_OP_INVOKE_DEFAULT, ICON_ADD); /* No parent path to use the root level. */ RNA_string_set(props, "parent_path", nullptr); } -std::unique_ptr<ui::AbstractTreeViewItemDropController> AssetCatalogTreeViewAllItem:: +std::unique_ptr<ui::AbstractViewItemDropController> AssetCatalogTreeViewAllItem:: create_drop_controller() const { return std::make_unique<AssetCatalogTreeViewAllItem::DropController>( @@ -567,7 +567,7 @@ std::unique_ptr<ui::AbstractTreeViewItemDropController> AssetCatalogTreeViewAllI } AssetCatalogTreeViewAllItem::DropController::DropController(AssetCatalogTreeView &tree_view) - : ui::AbstractTreeViewItemDropController(tree_view) + : ui::AbstractViewItemDropController(tree_view) { } @@ -579,7 +579,7 @@ bool AssetCatalogTreeViewAllItem::DropController::can_drop(const wmDrag &drag, } const AssetCatalog *drag_catalog = AssetCatalogDropController::get_drag_catalog( - drag, *tree_view<AssetCatalogTreeView>().asset_library_); + drag, *get_view<AssetCatalogTreeView>().asset_library_); if (drag_catalog->path.parent() == "") { *r_disabled_hint = "Catalog is already placed at the highest level"; return false; @@ -592,7 +592,7 @@ std::string AssetCatalogTreeViewAllItem::DropController::drop_tooltip(const wmDr { BLI_assert(drag.type == WM_DRAG_ASSET_CATALOG); const AssetCatalog *drag_catalog = AssetCatalogDropController::get_drag_catalog( - drag, *tree_view<AssetCatalogTreeView>().asset_library_); + drag, *get_view<AssetCatalogTreeView>().asset_library_); return std::string(TIP_("Move Catalog")) + " '" + drag_catalog->path.name() + "' " + TIP_("to the top level of the tree"); @@ -604,14 +604,14 @@ bool AssetCatalogTreeViewAllItem::DropController::on_drop(struct bContext *UNUSE BLI_assert(drag.type == WM_DRAG_ASSET_CATALOG); return AssetCatalogDropController::drop_asset_catalog_into_catalog( drag, - tree_view<AssetCatalogTreeView>(), + get_view<AssetCatalogTreeView>(), /* No value to drop into the root level. */ std::nullopt); } /* ---------------------------------------------------------------------- */ -std::unique_ptr<ui::AbstractTreeViewItemDropController> AssetCatalogTreeViewUnassignedItem:: +std::unique_ptr<ui::AbstractViewItemDropController> AssetCatalogTreeViewUnassignedItem:: create_drop_controller() const { return std::make_unique<AssetCatalogTreeViewUnassignedItem::DropController>( @@ -619,7 +619,7 @@ std::unique_ptr<ui::AbstractTreeViewItemDropController> AssetCatalogTreeViewUnas } AssetCatalogTreeViewUnassignedItem::DropController::DropController(AssetCatalogTreeView &tree_view) - : ui::AbstractTreeViewItemDropController(tree_view) + : ui::AbstractViewItemDropController(tree_view) { } @@ -647,7 +647,7 @@ bool AssetCatalogTreeViewUnassignedItem::DropController::on_drop(struct bContext { /* Assign to nil catalog ID. */ return AssetCatalogDropController::drop_assets_into_catalog( - C, tree_view<AssetCatalogTreeView>(), drag, CatalogID{}); + C, get_view<AssetCatalogTreeView>(), drag, CatalogID{}); } } // namespace blender::ed::asset_browser diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c index 62bdd583bc1..59d9a15fbab 100644 --- a/source/blender/editors/space_file/file_ops.c +++ b/source/blender/editors/space_file/file_ops.c @@ -1793,7 +1793,7 @@ static bool file_execute(bContext *C, SpaceFile *sfile) } ED_file_change_dir(C); } - /* opening file - sends events now, so things get handled on windowqueue level */ + /* Opening file, sends events now, so things get handled on window-queue level. */ else if (sfile->op) { wmOperator *op = sfile->op; char filepath[FILE_MAX]; diff --git a/source/blender/editors/space_graph/graph_buttons.c b/source/blender/editors/space_graph/graph_buttons.c index bc2705df314..3b8c6cbd1d0 100644 --- a/source/blender/editors/space_graph/graph_buttons.c +++ b/source/blender/editors/space_graph/graph_buttons.c @@ -277,7 +277,7 @@ static void graphedit_activekey_update_cb(bContext *UNUSED(C), /* make sure F-Curve and its handles are still valid after this editing */ sort_time_fcurve(fcu); - calchandles_fcurve(fcu); + BKE_fcurve_handles_recalc(fcu); } /* update callback for active keyframe properties - handle-editing wrapper */ diff --git a/source/blender/editors/space_graph/graph_edit.c b/source/blender/editors/space_graph/graph_edit.c index 7e8bca88744..64a3c603e73 100644 --- a/source/blender/editors/space_graph/graph_edit.c +++ b/source/blender/editors/space_graph/graph_edit.c @@ -723,7 +723,7 @@ static bool delete_graph_keys(bAnimContext *ac) bool changed; /* Delete selected keyframes only. */ - changed = delete_fcurve_keys(fcu); + changed = BKE_fcurve_delete_keys_selected(fcu); if (changed) { ale->update |= ANIM_UPDATE_DEFAULT; @@ -1488,7 +1488,7 @@ static void setipo_graph_keys(bAnimContext *ac, short mode) * Currently that's not necessary here. */ for (ale = anim_data.first; ale; ale = ale->next) { - ANIM_fcurve_keyframes_loop(NULL, ale->key_data, NULL, set_cb, calchandles_fcurve); + ANIM_fcurve_keyframes_loop(NULL, ale->key_data, NULL, set_cb, BKE_fcurve_handles_recalc); ale->update |= ANIM_UPDATE_DEFAULT_NOHANDLES; } @@ -1566,7 +1566,7 @@ static void seteasing_graph_keys(bAnimContext *ac, short mode) * Currently that's not necessary here. */ for (ale = anim_data.first; ale; ale = ale->next) { - ANIM_fcurve_keyframes_loop(NULL, ale->key_data, NULL, set_cb, calchandles_fcurve); + ANIM_fcurve_keyframes_loop(NULL, ale->key_data, NULL, set_cb, BKE_fcurve_handles_recalc); ale->update |= ANIM_UPDATE_DEFAULT_NOHANDLES; } @@ -1649,7 +1649,7 @@ static void sethandles_graph_keys(bAnimContext *ac, short mode) /* Any selected keyframes for editing? */ if (ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, sel_cb, NULL)) { /* Change type of selected handles. */ - ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, edit_cb, calchandles_fcurve); + ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, edit_cb, BKE_fcurve_handles_recalc); ale->update |= ANIM_UPDATE_DEFAULT; } @@ -2295,11 +2295,11 @@ static void snap_graph_keys(bAnimContext *ac, short mode) /* Perform snapping. */ if (adt) { ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 0); - ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve); + ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, BKE_fcurve_handles_recalc); ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 0); } else { - ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve); + ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, BKE_fcurve_handles_recalc); } ale->update |= ANIM_UPDATE_DEFAULT; @@ -2555,11 +2555,11 @@ static void mirror_graph_keys(bAnimContext *ac, short mode) /* Perform actual mirroring. */ if (adt) { ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 0); - ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve); + ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, BKE_fcurve_handles_recalc); ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 0); } else { - ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve); + ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, BKE_fcurve_handles_recalc); } ale->update |= ANIM_UPDATE_DEFAULT; diff --git a/source/blender/editors/space_image/image_edit.c b/source/blender/editors/space_image/image_edit.c index 950acd77f6a..0de50474ab8 100644 --- a/source/blender/editors/space_image/image_edit.c +++ b/source/blender/editors/space_image/image_edit.c @@ -20,6 +20,7 @@ #include "BKE_image.h" #include "BKE_lib_id.h" #include "BKE_main.h" +#include "BKE_scene.h" #include "IMB_imbuf_types.h" @@ -212,13 +213,7 @@ void ED_space_image_get_size(SpaceImage *sima, int *r_width, int *r_height) } else if (sima->image && sima->image->type == IMA_TYPE_R_RESULT && scene) { /* not very important, just nice */ - *r_width = (scene->r.xsch * scene->r.size) / 100; - *r_height = (scene->r.ysch * scene->r.size) / 100; - - if ((scene->r.mode & R_BORDER) && (scene->r.mode & R_CROP)) { - *r_width *= BLI_rctf_size_x(&scene->r.border); - *r_height *= BLI_rctf_size_y(&scene->r.border); - } + BKE_render_resolution(&scene->r, true, r_width, r_height); } /* I know a bit weak... but preview uses not actual image size */ // XXX else if (image_preview_active(sima, r_width, r_height)); diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index 49489b696ef..4036f859231 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -45,6 +45,7 @@ #include "BKE_main.h" #include "BKE_packedFile.h" #include "BKE_report.h" +#include "BKE_scene.h" #include "DEG_depsgraph.h" @@ -1830,7 +1831,7 @@ static void image_save_options_from_op(Main *bmain, ImageSaveOptions *opts, wmOp } static bool save_image_op( - Main *bmain, Image *ima, ImageUser *iuser, wmOperator *op, ImageSaveOptions *opts) + Main *bmain, Image *ima, ImageUser *iuser, wmOperator *op, const ImageSaveOptions *opts) { WM_cursor_wait(true); @@ -3732,38 +3733,52 @@ static int render_border_exec(bContext *C, wmOperator *op) ARegion *region = CTX_wm_region(C); Scene *scene = CTX_data_scene(C); Render *re = RE_GetSceneRender(scene); - RenderData *rd; - rctf border; + SpaceImage *sima = CTX_wm_space_image(C); if (re == NULL) { /* Shouldn't happen, but better be safe close to the release. */ return OPERATOR_CANCELLED; } - rd = RE_engine_get_render_data(re); - if ((rd->mode & (R_BORDER | R_CROP)) == (R_BORDER | R_CROP)) { - BKE_report(op->reports, RPT_INFO, "Can not set border from a cropped render"); - return OPERATOR_CANCELLED; - } + /* Get information about the previous render, or current scene if no render yet. */ + int width, height; + BKE_render_resolution(&scene->r, false, &width, &height); + const RenderData *rd = ED_space_image_has_buffer(sima) ? RE_engine_get_render_data(re) : + &scene->r; - /* get rectangle from operator */ + /* Get rectangle from the operator. */ + rctf border; WM_operator_properties_border_to_rctf(op, &border); UI_view2d_region_to_view_rctf(®ion->v2d, &border, &border); - /* actually set border */ + /* Adjust for cropping. */ + if ((rd->mode & (R_BORDER | R_CROP)) == (R_BORDER | R_CROP)) { + border.xmin = rd->border.xmin + border.xmin * (rd->border.xmax - rd->border.xmin); + border.xmax = rd->border.xmin + border.xmax * (rd->border.xmax - rd->border.xmin); + border.ymin = rd->border.ymin + border.ymin * (rd->border.ymax - rd->border.ymin); + border.ymax = rd->border.ymin + border.ymax * (rd->border.ymax - rd->border.ymin); + } + CLAMP(border.xmin, 0.0f, 1.0f); CLAMP(border.ymin, 0.0f, 1.0f); CLAMP(border.xmax, 0.0f, 1.0f); CLAMP(border.ymax, 0.0f, 1.0f); - scene->r.border = border; - /* drawing a border surrounding the entire camera view switches off border rendering - * or the border covers no pixels */ + /* Drawing a border surrounding the entire camera view switches off border rendering + * or the border covers no pixels. */ if ((border.xmin <= 0.0f && border.xmax >= 1.0f && border.ymin <= 0.0f && border.ymax >= 1.0f) || (border.xmin == border.xmax || border.ymin == border.ymax)) { scene->r.mode &= ~R_BORDER; } else { + /* Snap border to pixel boundaries, so drawing a border within a pixel selects that pixel. */ + border.xmin = floorf(border.xmin * width) / width; + border.xmax = ceilf(border.xmax * width) / width; + border.ymin = floorf(border.ymin * height) / height; + border.ymax = ceilf(border.ymax * height) / height; + + /* Set border. */ + scene->r.border = border; scene->r.mode |= R_BORDER; } diff --git a/source/blender/editors/space_nla/nla_edit.c b/source/blender/editors/space_nla/nla_edit.c index cc45c86ddcd..801d032a861 100644 --- a/source/blender/editors/space_nla/nla_edit.c +++ b/source/blender/editors/space_nla/nla_edit.c @@ -1216,13 +1216,10 @@ static int nlaedit_duplicate_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } -static int nlaedit_duplicate_invoke(bContext *C, wmOperator *op, const wmEvent *event) +static int nlaedit_duplicate_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { nlaedit_duplicate_exec(C, op); - RNA_enum_set(op->ptr, "mode", TFM_TRANSLATION); - WM_operator_name_call(C, "TRANSFORM_OT_transform", WM_OP_INVOKE_REGION_WIN, op->ptr, event); - return OPERATOR_FINISHED; } @@ -1248,9 +1245,6 @@ void NLA_OT_duplicate(wmOperatorType *ot) false, "Linked", "When duplicating strips, assign new copies of the actions they use"); - - /* to give to transform */ - RNA_def_enum(ot->srna, "mode", rna_enum_transform_mode_types, TFM_TRANSLATION, "Mode", ""); } /** \} */ @@ -2204,8 +2198,13 @@ static int nlaedit_apply_scale_exec(bContext *C, wmOperator *UNUSED(op)) /* setup iterator, and iterate over all the keyframes in the action, * applying this scaling */ ked.data = strip; - ANIM_animchanneldata_keyframes_loop( - &ked, ac.ads, strip->act, ALE_ACT, NULL, bezt_apply_nlamapping, calchandles_fcurve); + ANIM_animchanneldata_keyframes_loop(&ked, + ac.ads, + strip->act, + ALE_ACT, + NULL, + bezt_apply_nlamapping, + BKE_fcurve_handles_recalc); /* clear scale of strip now that it has been applied, * and recalculate the extents of the action now that it has been scaled diff --git a/source/blender/editors/space_nla/nla_ops.c b/source/blender/editors/space_nla/nla_ops.c index 902e7a176a3..3ae73282230 100644 --- a/source/blender/editors/space_nla/nla_ops.c +++ b/source/blender/editors/space_nla/nla_ops.c @@ -16,6 +16,8 @@ #include "ED_anim_api.h" #include "ED_screen.h" +#include "RNA_access.h" + #include "WM_api.h" #include "WM_types.h" @@ -138,6 +140,28 @@ void nla_operatortypes(void) WM_operatortype_append(NLA_OT_fmodifier_paste); } +void ED_operatormacros_nla() +{ + wmOperatorType *ot; + wmOperatorTypeMacro *otmacro; + + ot = WM_operatortype_append_macro("NLA_OT_duplicate_move", + "Duplicate", + "Duplicate selected strips and their Actions and move them", + OPTYPE_UNDO | OPTYPE_REGISTER); + otmacro = WM_operatortype_macro_define(ot, "NLA_OT_duplicate"); + RNA_boolean_set(otmacro->ptr, "linked", false); + WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate"); + + ot = WM_operatortype_append_macro("NLA_OT_duplicate_linked_move", + "Duplicate Linked", + "Duplicate selected strips and move them", + OPTYPE_UNDO | OPTYPE_REGISTER); + otmacro = WM_operatortype_macro_define(ot, "NLA_OT_duplicate"); + RNA_boolean_set(otmacro->ptr, "linked", true); + WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate"); +} + /* ************************** registration - keymaps **********************************/ void nla_keymap(wmKeyConfig *keyconf) diff --git a/source/blender/editors/space_node/node_add.cc b/source/blender/editors/space_node/node_add.cc index 975d4eda7e3..04bf5ef469e 100644 --- a/source/blender/editors/space_node/node_add.cc +++ b/source/blender/editors/space_node/node_add.cc @@ -382,7 +382,7 @@ static bool node_add_group_poll(bContext *C) if (snode->edittree->type == NTREE_CUSTOM) { CTX_wm_operator_poll_msg_set(C, "This node editor displays a custom (Python defined) node tree. " - "Dropping node groups isn't supported for this."); + "Dropping node groups isn't supported for this"); return false; } return true; diff --git a/source/blender/editors/space_outliner/outliner_dragdrop.cc b/source/blender/editors/space_outliner/outliner_dragdrop.cc index c72080be811..7435fa50a93 100644 --- a/source/blender/editors/space_outliner/outliner_dragdrop.cc +++ b/source/blender/editors/space_outliner/outliner_dragdrop.cc @@ -1455,7 +1455,7 @@ static int outliner_item_drag_drop_invoke(bContext *C, TSE_GPENCIL_EFFECT_BASE); const int wm_drag_type = use_datastack_drag ? WM_DRAG_DATASTACK : WM_DRAG_ID; - wmDrag *drag = WM_event_start_drag(C, data.icon, wm_drag_type, nullptr, 0.0, WM_DRAG_NOP); + wmDrag *drag = WM_drag_data_create(C, data.icon, wm_drag_type, nullptr, 0.0, WM_DRAG_NOP); if (use_datastack_drag) { TreeElement *te_bone = nullptr; @@ -1545,6 +1545,8 @@ static int outliner_item_drag_drop_invoke(bContext *C, WM_drag_add_local_ID(drag, data.drag_id, data.drag_parent); } + WM_event_start_prepared_drag(C, drag); + ED_outliner_select_sync_from_outliner(C, space_outliner); return (OPERATOR_FINISHED | OPERATOR_PASS_THROUGH); diff --git a/source/blender/editors/space_outliner/outliner_tools.cc b/source/blender/editors/space_outliner/outliner_tools.cc index 7b7182eb106..9d52103a266 100644 --- a/source/blender/editors/space_outliner/outliner_tools.cc +++ b/source/blender/editors/space_outliner/outliner_tools.cc @@ -875,7 +875,7 @@ static void id_override_library_create_fn(bContext *C, /* For now, remap all local usages of linked ID to local override one here. */ ID *id_iter; FOREACH_MAIN_ID_BEGIN (bmain, id_iter) { - if (ID_IS_LINKED(id_iter)) { + if (ID_IS_LINKED(id_iter) || ID_IS_OVERRIDE_LIBRARY(id_iter)) { id_iter->tag &= ~LIB_TAG_DOIT; } else { diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index 4b9ff1e170e..eb2e4ef05e5 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -241,329 +241,296 @@ typedef struct WaveVizData { float pos[2]; float rms_pos; bool clip; - bool end; + bool draw_line; /* Draw triangle otherwise. */ + bool final_sample; /* There are no more samples. */ } WaveVizData; -static int get_section_len(WaveVizData *start, WaveVizData *end) +static bool seq_draw_waveforms_poll(const bContext *UNUSED(C), SpaceSeq *sseq, Sequence *seq) { - int len = 0; - while (start != end) { - len++; - if (start->end) { - return len; - } - start++; - } - return len; -} + const bool strip_is_valid = seq->type == SEQ_TYPE_SOUND_RAM && seq->sound != NULL; + const bool overlays_enabled = (sseq->flag & SEQ_SHOW_OVERLAY) != 0; + const bool ovelay_option = ((sseq->timeline_overlay.flag & SEQ_TIMELINE_ALL_WAVEFORMS) != 0 || + (seq->flag & SEQ_AUDIO_DRAW_WAVEFORM)); -static void draw_waveform(WaveVizData *iter, WaveVizData *end, GPUPrimType prim_type, bool use_rms) -{ - int strip_len = get_section_len(iter, end); - if (strip_len > 1) { - GPU_blend(GPU_BLEND_ALPHA); - GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + if ((sseq->timeline_overlay.flag & SEQ_TIMELINE_NO_WAVEFORMS) != 0) { + return false; + } - immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR); - immBegin(prim_type, strip_len); + if (strip_is_valid && overlays_enabled && ovelay_option) { + return true; + } - while (iter != end) { - if (iter->clip) { - immAttr4f(col, 1.0f, 0.0f, 0.0f, 0.5f); - } - else if (use_rms) { - immAttr4f(col, 1.0f, 1.0f, 1.0f, 0.8f); - } - else { - immAttr4f(col, 1.0f, 1.0f, 1.0f, 0.5f); - } + return false; +} - if (use_rms) { - immVertex2f(pos, iter->pos[0], iter->rms_pos); - } - else { - immVertex2f(pos, iter->pos[0], iter->pos[1]); - } +static void waveform_job_start_if_needed(const bContext *C, Sequence *seq) +{ + bSound *sound = seq->sound; - if (iter->end) { - /* End of line. */ - iter++; - strip_len = get_section_len(iter, end); - if (strip_len != 0) { - immEnd(); - immUnbindProgram(); - immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR); - immBegin(prim_type, strip_len); - } - } - else { - iter++; - } + BLI_spin_lock(sound->spinlock); + if (!sound->waveform) { + /* Load the waveform data if it hasn't been loaded and cached already. */ + if (!(sound->tags & SOUND_TAGS_WAVEFORM_LOADING)) { + /* Prevent sounds from reloading. */ + sound->tags |= SOUND_TAGS_WAVEFORM_LOADING; + BLI_spin_unlock(sound->spinlock); + sequencer_preview_add_sound(C, seq); + } + else { + BLI_spin_unlock(sound->spinlock); } - immEnd(); - immUnbindProgram(); - - GPU_blend(GPU_BLEND_NONE); } + BLI_spin_unlock(sound->spinlock); } -static float clamp_frame_coord_to_pixel(float frame_coord, - float pixel_frac, - float frames_per_pixel) +static size_t get_vertex_count(WaveVizData *waveform_data) { - float cur_pixel = (frame_coord / frames_per_pixel); - float new_pixel = (int)(frame_coord / frames_per_pixel) + pixel_frac; - if (cur_pixel > new_pixel) { - new_pixel += 1.0f; + bool draw_line = waveform_data->draw_line; + size_t length = 0; + + while (waveform_data->draw_line == draw_line && !waveform_data->final_sample) { + waveform_data++; + length++; } - return new_pixel * frames_per_pixel; + + return length; } -/** - * \param x1, x2, y1, y2: The starting and end X value to draw the wave, same for y1 and y2. - * \param frames_per_pixel: The amount of pixels a whole frame takes up (x-axis direction). - */ -static void draw_seq_waveform_overlay(View2D *v2d, - const bContext *C, - SpaceSeq *sseq, - Scene *scene, - Sequence *seq, - float x1, - float y1, - float x2, - float y2, - float frames_per_pixel) +static size_t draw_waveform_segment(WaveVizData *waveform_data, bool use_rms) { - if (seq->sound && ((sseq->timeline_overlay.flag & SEQ_TIMELINE_ALL_WAVEFORMS) || - (seq->flag & SEQ_AUDIO_DRAW_WAVEFORM))) { - /* Make sure that the start drawing position is aligned to the pixels on the screen to avoid - * flickering when moving around the strip. - * To do this we figure out the fractional offset in pixel space by checking where the - * window starts. - * We then append this pixel offset to our strip start coordinate to ensure we are aligned to - * the screen pixel grid. */ - float pixel_frac = v2d->cur.xmin / frames_per_pixel - floor(v2d->cur.xmin / frames_per_pixel); - float x1_adj = clamp_frame_coord_to_pixel(x1, pixel_frac, frames_per_pixel); - - /* Offset x1 and x2 values, to match view min/max, if strip is out of bounds. */ - float x1_offset = max_ff(v2d->cur.xmin, x1_adj); - float x2_offset = min_ff(v2d->cur.xmax, x2); - - /* Calculate how long the strip that is in view is in pixels. */ - int pix_strip_len = round((x2_offset - x1_offset) / frames_per_pixel); - - if (pix_strip_len < 2) { - return; - } + size_t vertices_done = 0; + size_t vertex_count = get_vertex_count(waveform_data); - bSound *sound = seq->sound; + /* Not enough data to draw. */ + if (vertex_count <= 2) { + return vertex_count; + } - BLI_spin_lock(sound->spinlock); - if (!sound->waveform) { - /* Load the waveform data if it hasn't been loaded and cached already. */ - if (!(sound->tags & SOUND_TAGS_WAVEFORM_LOADING)) { - /* Prevent sounds from reloading. */ - sound->tags |= SOUND_TAGS_WAVEFORM_LOADING; - BLI_spin_unlock(sound->spinlock); - sequencer_preview_add_sound(C, seq); - } - else { - BLI_spin_unlock(sound->spinlock); - } - return; /* Nothing to draw. */ - } - BLI_spin_unlock(sound->spinlock); + GPU_blend(GPU_BLEND_ALPHA); + GPUVertFormat *format = immVertexFormat(); + GPUPrimType prim_type = waveform_data->draw_line ? GPU_PRIM_LINE_STRIP : GPU_PRIM_TRI_STRIP; + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR); + immBegin(prim_type, vertex_count); - SoundWaveform *waveform = sound->waveform; + while (vertices_done < vertex_count && !waveform_data->final_sample) { + /* Color. */ + if (waveform_data->clip) { + immAttr4f(col, 1.0f, 0.0f, 0.0f, 0.5f); + } + else if (use_rms) { + immAttr4f(col, 1.0f, 1.0f, 1.0f, 0.8f); + } + else { + immAttr4f(col, 1.0f, 1.0f, 1.0f, 0.5f); + } - /* Waveform could not be built. */ - if (waveform->length == 0) { - return; + /* Vertices. */ + if (use_rms) { + immVertex2f(pos, waveform_data->pos[0], waveform_data->rms_pos); + } + else { + immVertex2f(pos, waveform_data->pos[0], waveform_data->pos[1]); } - /* F-Curve lookup is quite expensive, so do this after precondition. */ - FCurve *fcu = id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "volume", 0, NULL); + vertices_done++; + waveform_data++; + } - WaveVizData *tri_strip_arr = MEM_callocN(sizeof(*tri_strip_arr) * pix_strip_len * 2, - "tri_strip"); - WaveVizData *line_strip_arr = MEM_callocN(sizeof(*line_strip_arr) * pix_strip_len, - "line_strip"); + immEnd(); + immUnbindProgram(); - WaveVizData *tri_strip_iter = tri_strip_arr; - WaveVizData *line_strip_iter = line_strip_arr; + GPU_blend(GPU_BLEND_NONE); - /* The y coordinate for the middle of the strip. */ - float y_mid = (y1 + y2) / 2.0f; - /* The length from the middle of the strip to the top/bottom. */ - float y_scale = (y2 - y1) / 2.0f; - float volume = seq->volume; + return vertices_done; +} - /* Value to keep track if the previous item to be drawn was a line strip. */ - int8_t was_line_strip = -1; /* -1 == no previous value. */ +static void draw_waveform(WaveVizData *waveform_data, size_t wave_data_len) +{ + size_t items_done = 0; + while (items_done < wave_data_len) { + if (!waveform_data[items_done].draw_line) { /* Draw RMS. */ + draw_waveform_segment(&waveform_data[items_done], true); + } + items_done += draw_waveform_segment(&waveform_data[items_done], false); + } +} - float samples_per_frame = SOUND_WAVE_SAMPLES_PER_SECOND / FPS; +static float align_frame_with_pixel(float frame_coord, float frames_per_pixel) +{ + return round_fl_to_int(frame_coord / frames_per_pixel) * frames_per_pixel; +} - /* How many samples do we have for each pixel? */ - float samples_per_pix = samples_per_frame * frames_per_pixel; +static void write_waveform_data(WaveVizData *waveform_data, + const vec2f pos, + const float rms, + const bool is_clipping, + const bool draw_line) +{ + waveform_data->pos[0] = pos.x; + waveform_data->pos[1] = pos.y; + waveform_data->clip = is_clipping; + waveform_data->rms_pos = rms; + waveform_data->draw_line = draw_line; +} - float strip_start_offset = seq->startofs + seq->anim_startofs; - float start_sample = 0; +static size_t waveform_append_sample(WaveVizData *waveform_data, + vec2f pos, + const float value_min, + const float value_max, + const float y_mid, + const float y_scale, + const float rms, + const bool is_clipping, + const bool is_line_strip) +{ + size_t data_written = 0; + pos.y = y_mid + value_min * y_scale; + float rms_value = y_mid + max_ff(-rms, value_min) * y_scale; + write_waveform_data(&waveform_data[0], pos, rms_value, is_clipping, is_line_strip); + data_written++; + + /* Use `value_max` as second vertex for triangle drawing. */ + if (!is_line_strip) { + pos.y = y_mid + value_max * y_scale; + rms_value = y_mid + min_ff(rms, value_max) * y_scale; + write_waveform_data(&waveform_data[1], pos, rms_value, is_clipping, is_line_strip); + data_written++; + } + return data_written; +} - if (strip_start_offset != 0) { - /* If start offset is not zero, we need to make sure that we pick the same start sample as if - * we simply scrolled the start of the strip off-screen. Otherwise we will get flickering - * when changing start offset as the pixel alignment will not be the same for the drawn - * samples. */ - strip_start_offset = clamp_frame_coord_to_pixel( - x1 - strip_start_offset, pixel_frac, frames_per_pixel); - start_sample = fabsf(strip_start_offset - x1_adj) * samples_per_frame; - } +/** + * \param x1, x2, y1, y2: The starting and end X value to draw the wave, same for y1 and y2. + * \param frames_per_pixel: The amount of pixels a whole frame takes up (x-axis direction). + */ +static void draw_seq_waveform_overlay( + const bContext *C, ARegion *region, Sequence *seq, float x1, float y1, float x2, float y2) +{ + const View2D *v2d = ®ion->v2d; + Scene *scene = CTX_data_scene(C); - start_sample += seq->sound->offset_time * SOUND_WAVE_SAMPLES_PER_SECOND; - /* If we scrolled the start off-screen, then the start sample should be at the first visible - * sample. */ - start_sample += (x1_offset - x1_adj) * samples_per_frame; + const float frames_per_pixel = BLI_rctf_size_x(®ion->v2d.cur) / region->winx; + const float samples_per_frame = SOUND_WAVE_SAMPLES_PER_SECOND / FPS; + float samples_per_pixel = samples_per_frame * frames_per_pixel; - for (int i = 0; i < pix_strip_len; i++) { - float sample_offset = start_sample + i * samples_per_pix; - int p = sample_offset; + /* Align strip start with nearest pixel to prevent waveform flickering. */ + const float x1_aligned = align_frame_with_pixel(x1, frames_per_pixel); + /* Offset x1 and x2 values, to match view min/max, if strip is out of bounds. */ + const float frame_start = max_ff(v2d->cur.xmin, x1_aligned); + const float frame_end = min_ff(v2d->cur.xmax, x2); + const int pixels_to_draw = round_fl_to_int((frame_end - frame_start) / frames_per_pixel); - if (p < 0) { - continue; - } + if (pixels_to_draw < 2) { + return; /* Not much to draw, exit before running job. */ + } - if (p >= waveform->length) { - break; - } + waveform_job_start_if_needed(C, seq); - float value_min = waveform->data[p * 3]; - float value_max = waveform->data[p * 3 + 1]; - float rms = waveform->data[p * 3 + 2]; - - if (p + 1 < waveform->length) { - /* Use simple linear interpolation. */ - float f = sample_offset - p; - value_min = (1.0f - f) * value_min + f * waveform->data[p * 3 + 3]; - value_max = (1.0f - f) * value_max + f * waveform->data[p * 3 + 4]; - rms = (1.0f - f) * rms + f * waveform->data[p * 3 + 5]; - if (samples_per_pix > 1.0f) { - /* We need to sum up the values we skip over until the next step. */ - float next_pos = sample_offset + samples_per_pix; - int end_idx = next_pos; - - for (int j = p + 1; (j < waveform->length) && (j < end_idx); j++) { - value_min = min_ff(value_min, waveform->data[j * 3]); - value_max = max_ff(value_max, waveform->data[j * 3 + 1]); - rms = max_ff(rms, waveform->data[j * 3 + 2]); - } - } - } + SoundWaveform *waveform = seq->sound->waveform; + if (waveform == NULL || waveform->length == 0) { + return; /* Waveform was not built. */ + } - if (fcu && !BKE_fcurve_is_empty(fcu)) { - float evaltime = x1_offset + (i * frames_per_pixel); - volume = evaluate_fcurve(fcu, evaltime); - CLAMP_MIN(volume, 0.0f); - } + /* F-Curve lookup is quite expensive, so do this after precondition. */ + FCurve *fcu = id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "volume", 0, NULL); + WaveVizData *waveform_data = MEM_callocN(sizeof(WaveVizData) * pixels_to_draw * 3, __func__); + size_t wave_data_len = 0; - value_min *= volume; - value_max *= volume; - rms *= volume; + /* Offset must be also aligned, otherwise waveform flickers when moving left handle. */ + const float strip_offset = align_frame_with_pixel(seq->startofs + seq->anim_startofs, + frames_per_pixel); + float start_sample = strip_offset * samples_per_frame; + start_sample += seq->sound->offset_time * SOUND_WAVE_SAMPLES_PER_SECOND; + /* Add off-screen part of strip to offset. */ + start_sample += (frame_start - x1_aligned) * samples_per_frame; - bool clipping = false; + for (int i = 0; i < pixels_to_draw; i++) { + float sample = start_sample + i * samples_per_pixel; + int sample_index = round_fl_to_int(sample); - if (value_max > 1 || value_min < -1) { - clipping = true; + if (sample_index < 0) { + continue; + } - CLAMP_MAX(value_max, 1.0f); - CLAMP_MIN(value_min, -1.0f); - } + if (sample_index >= waveform->length) { + break; + } - bool is_line_strip = (value_max - value_min < 0.05f); - - if (!ELEM(was_line_strip, -1, is_line_strip)) { - /* If the previously added strip type isn't the same as the current one, - * add transition areas so they transition smoothly between each other. */ - if (is_line_strip) { - /* This will be a line strip, end the tri strip. */ - tri_strip_iter->pos[0] = x1_offset + i * frames_per_pixel; - tri_strip_iter->pos[1] = y_mid + value_min * y_scale; - tri_strip_iter->clip = clipping; - tri_strip_iter->rms_pos = tri_strip_iter->pos[1]; - tri_strip_iter->end = true; - - /* End of section. */ - tri_strip_iter++; - - /* Check if we are at the end. - * If so, skip one point line. */ - if (i + 1 == pix_strip_len) { - continue; - } - } - else { - /* This will be a tri strip. */ - line_strip_iter--; - tri_strip_iter->pos[0] = line_strip_iter->pos[0]; - tri_strip_iter->pos[1] = line_strip_iter->pos[1]; - tri_strip_iter->clip = line_strip_iter->clip; - tri_strip_iter->rms_pos = line_strip_iter->pos[1]; - tri_strip_iter++; - - /* Check if line had only one point. */ - line_strip_iter--; - if (line_strip_iter < line_strip_arr || line_strip_iter->end) { - /* Only one point, skip it. */ - line_strip_iter++; - } - else { - /* End of section. */ - line_strip_iter++; - line_strip_iter->end = true; - line_strip_iter++; - } + float value_min = waveform->data[sample_index * 3]; + float value_max = waveform->data[sample_index * 3 + 1]; + float rms = waveform->data[sample_index * 3 + 2]; + + if (sample_index + 1 < waveform->length) { + /* Use simple linear interpolation. */ + float f = sample - sample_index; + value_min = (1.0f - f) * value_min + f * waveform->data[sample_index * 3 + 3]; + value_max = (1.0f - f) * value_max + f * waveform->data[sample_index * 3 + 4]; + rms = (1.0f - f) * rms + f * waveform->data[sample_index * 3 + 5]; + if (samples_per_pixel > 1.0f) { + /* We need to sum up the values we skip over until the next step. */ + float next_pos = sample + samples_per_pixel; + int end_idx = next_pos; + + for (int j = sample_index + 1; (j < waveform->length) && (j < end_idx); j++) { + value_min = min_ff(value_min, waveform->data[j * 3]); + value_max = max_ff(value_max, waveform->data[j * 3 + 1]); + rms = max_ff(rms, waveform->data[j * 3 + 2]); } } + } - was_line_strip = is_line_strip; - - if (is_line_strip) { - line_strip_iter->pos[0] = x1_offset + i * frames_per_pixel; - line_strip_iter->pos[1] = y_mid + value_min * y_scale; - line_strip_iter->clip = clipping; - line_strip_iter++; - } - else { - tri_strip_iter->pos[0] = x1_offset + i * frames_per_pixel; - tri_strip_iter->pos[1] = y_mid + value_min * y_scale; - tri_strip_iter->clip = clipping; - tri_strip_iter->rms_pos = y_mid + max_ff(-rms, value_min) * y_scale; - tri_strip_iter++; - - tri_strip_iter->pos[0] = x1_offset + i * frames_per_pixel; - tri_strip_iter->pos[1] = y_mid + value_max * y_scale; - tri_strip_iter->clip = clipping; - tri_strip_iter->rms_pos = y_mid + min_ff(rms, value_max) * y_scale; - tri_strip_iter++; - } + float volume = seq->volume; + if (fcu && !BKE_fcurve_is_empty(fcu)) { + float evaltime = frame_start + (i * frames_per_pixel); + volume = evaluate_fcurve(fcu, evaltime); + CLAMP_MIN(volume, 0.0f); } - WaveVizData *tri_strip_end = tri_strip_iter; - WaveVizData *line_strip_end = line_strip_iter; + value_min *= volume; + value_max *= volume; + rms *= volume; + + bool is_clipping = false; + + if (value_max > 1 || value_min < -1) { + is_clipping = true; - tri_strip_iter = tri_strip_arr; - line_strip_iter = line_strip_arr; + CLAMP_MAX(value_max, 1.0f); + CLAMP_MIN(value_min, -1.0f); + } - draw_waveform(line_strip_iter, line_strip_end, GPU_PRIM_LINE_STRIP, false); - draw_waveform(tri_strip_iter, tri_strip_end, GPU_PRIM_TRI_STRIP, false); - draw_waveform(tri_strip_iter, tri_strip_end, GPU_PRIM_TRI_STRIP, true); + bool is_line_strip = (value_max - value_min < 0.05f); + /* The y coordinate for the middle of the strip. */ + float y_mid = (y1 + y2) / 2.0f; + /* The length from the middle of the strip to the top/bottom. */ + float y_scale = (y2 - y1) / 2.0f; - MEM_freeN(tri_strip_arr); - MEM_freeN(line_strip_arr); + vec2f pos = {frame_start + i * frames_per_pixel, y_mid + value_min * y_scale}; + WaveVizData *new_data = &waveform_data[wave_data_len]; + wave_data_len += waveform_append_sample( + new_data, pos, value_min, value_max, y_mid, y_scale, rms, is_clipping, is_line_strip); } + + /* Terminate array, so `get_segment_length()` can know when to stop. */ + waveform_data[wave_data_len].final_sample = true; + draw_waveform(waveform_data, wave_data_len); + MEM_freeN(waveform_data); } +/* +static size_t *waveform_append(WaveVizData *waveform_data, + vec2f pos, + const float value_min, + const float value_max, + const float y_mid, + const float y_scale, + const float rms, + const bool is_clipping, + const bool is_line_strip) +*/ + static void drawmeta_contents(Scene *scene, Sequence *seqm, float x1, @@ -1430,18 +1397,9 @@ static void draw_seq_strip(const bContext *C, } /* Draw sound strip waveform. */ - if ((seq->type == SEQ_TYPE_SOUND_RAM) && ((sseq->flag & SEQ_SHOW_OVERLAY)) && - (sseq->timeline_overlay.flag & SEQ_TIMELINE_NO_WAVEFORMS) == 0) { - draw_seq_waveform_overlay(v2d, - C, - sseq, - scene, - seq, - x1, - y_threshold ? y1 + 0.05f : y1, - x2, - y_threshold ? text_margin_y : y2, - BLI_rctf_size_x(®ion->v2d.cur) / region->winx); + if (seq_draw_waveforms_poll(C, sseq, seq)) { + draw_seq_waveform_overlay( + C, region, seq, x1, y_threshold ? y1 + 0.05f : y1, x2, y_threshold ? text_margin_y : y2); } /* Draw locked state. */ if (SEQ_transform_is_locked(channels, seq)) { @@ -1762,8 +1720,7 @@ void sequencer_draw_maskedit(const bContext *C, Scene *scene, ARegion *region, S // ED_mask_get_size(C, &width, &height); //Scene *scene = CTX_data_scene(C); - width = (scene->r.size * scene->r.xsch) / 100; - height = (scene->r.size * scene->r.ysch) / 100; + BKE_render_resolution(&scene->r, false, &width, &height); ED_mask_draw_region(mask, region, diff --git a/source/blender/editors/space_sequencer/sequencer_view.c b/source/blender/editors/space_sequencer/sequencer_view.c index 4d32c00109a..78fa8c379d9 100644 --- a/source/blender/editors/space_sequencer/sequencer_view.c +++ b/source/blender/editors/space_sequencer/sequencer_view.c @@ -12,6 +12,7 @@ #include "DNA_scene_types.h" #include "BKE_context.h" +#include "BKE_scene.h" #include "WM_api.h" #include "WM_types.h" @@ -174,8 +175,7 @@ static int sequencer_view_all_preview_exec(bContext *C, wmOperator *UNUSED(op)) seq_reset_imageofs(sseq); - imgwidth = (scene->r.size * scene->r.xsch) / 100; - imgheight = (scene->r.size * scene->r.ysch) / 100; + BKE_render_resolution(&scene->r, false, &imgwidth, &imgheight); /* Apply aspect, doesn't need to be that accurate. */ imgwidth = (int)(imgwidth * (scene->r.xasp / scene->r.yasp)); @@ -227,11 +227,11 @@ static int sequencer_view_zoom_ratio_exec(bContext *C, wmOperator *op) float ratio = RNA_float_get(op->ptr, "ratio"); - float winx = (int)(rd->size * rd->xsch) / 100; - float winy = (int)(rd->size * rd->ysch) / 100; + int winx, winy; + BKE_render_resolution(rd, false, &winx, &winy); - float facx = BLI_rcti_size_x(&v2d->mask) / winx; - float facy = BLI_rcti_size_y(&v2d->mask) / winy; + float facx = BLI_rcti_size_x(&v2d->mask) / (float)winx; + float facy = BLI_rcti_size_y(&v2d->mask) / (float)winy; BLI_rctf_resize(&v2d->cur, ceilf(winx * facx / ratio + 0.5f), ceilf(winy * facy / ratio + 0.5f)); diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc index aa9b867264a..146b6091bde 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc @@ -145,7 +145,7 @@ void GeometryDataSetTreeViewItem::build_row(uiLayout &row) * to the right side of the number, which it didn't have with the button. */ char element_count[7]; BLI_str_format_decimal_unit(element_count, *count); - UI_but_hint_drawstr_set((uiBut *)this->tree_row_button(), element_count); + UI_but_hint_drawstr_set((uiBut *)this->view_item_button(), element_count); } } diff --git a/source/blender/editors/space_text/text_format_py.c b/source/blender/editors/space_text/text_format_py.c index aa361f07932..28f536ffa98 100644 --- a/source/blender/editors/space_text/text_format_py.c +++ b/source/blender/editors/space_text/text_format_py.c @@ -59,9 +59,9 @@ static int txtfmt_py_find_builtinfunc(const char *string) /* clang-format off */ if (STR_LITERAL_STARTSWITH(string, "and", len)) { i = len; - } else if (STR_LITERAL_STARTSWITH(string, "as", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "assert", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "async", len)) { i = len; + } else if (STR_LITERAL_STARTSWITH(string, "as", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "await", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "break", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "case", len)) { i = len; diff --git a/source/blender/editors/space_text/text_ops.c b/source/blender/editors/space_text/text_ops.c index 05d51cf6362..33219092d20 100644 --- a/source/blender/editors/space_text/text_ops.c +++ b/source/blender/editors/space_text/text_ops.c @@ -3396,7 +3396,8 @@ static int text_line_number_invoke(bContext *C, wmOperator *UNUSED(op), const wm return OPERATOR_PASS_THROUGH; } - if (!(event->ascii >= '0' && event->ascii <= '9')) { + const char event_ascii = WM_event_utf8_to_ascii(event); + if (!(event_ascii >= '0' && event_ascii <= '9')) { return OPERATOR_PASS_THROUGH; } @@ -3406,7 +3407,7 @@ static int text_line_number_invoke(bContext *C, wmOperator *UNUSED(op), const wm } jump_to *= 10; - jump_to += (int)(event->ascii - '0'); + jump_to += (int)(event_ascii - '0'); txt_move_toline(text, jump_to - 1, 0); last_jump = time; @@ -3495,16 +3496,8 @@ static int text_insert_invoke(bContext *C, wmOperator *op, const wmEvent *event) } char str[BLI_UTF8_MAX + 1]; - size_t len; - - if (event->utf8_buf[0]) { - len = BLI_str_utf8_size_safe(event->utf8_buf); - memcpy(str, event->utf8_buf, len); - } - else { - /* in theory, ghost can set value to extended ascii here */ - len = BLI_str_utf8_from_unicode(event->ascii, str, sizeof(str) - 1); - } + const size_t len = BLI_str_utf8_size_safe(event->utf8_buf); + memcpy(str, event->utf8_buf, len); str[len] = '\0'; RNA_string_set(op->ptr, "text", str); diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index 041663b4a00..d6ddd6d044e 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -413,7 +413,9 @@ static void view3d_set_1_to_1_viewborder(Scene *scene, { RegionView3D *rv3d = region->regiondata; float size[2]; - int im_width = (scene->r.size * scene->r.xsch) / 100; + + int im_width, im_height; + BKE_render_resolution(&scene->r, false, &im_width, &im_height); ED_view3d_calc_camera_border_size(scene, depsgraph, region, v3d, rv3d, size); diff --git a/source/blender/editors/space_view3d/view3d_utils.c b/source/blender/editors/space_view3d/view3d_utils.c index 1fabdef8da2..85b1af8e55d 100644 --- a/source/blender/editors/space_view3d/view3d_utils.c +++ b/source/blender/editors/space_view3d/view3d_utils.c @@ -1492,10 +1492,12 @@ static bool view3d_camera_to_view_selected_impl(struct Main *bmain, depsgraph, scene, camera_ob_eval, co, &scale, r_clip_start, r_clip_end)) { ObjectTfmProtectedChannels obtfm; float obmat_new[4][4]; + bool is_ortho_camera = false; if ((camera_ob_eval->type == OB_CAMERA) && (((Camera *)camera_ob_eval->data)->type == CAM_ORTHO)) { ((Camera *)camera_ob->data)->ortho_scale = scale; + is_ortho_camera = true; } copy_m4_m4(obmat_new, camera_ob_eval->obmat); @@ -1508,6 +1510,9 @@ static bool view3d_camera_to_view_selected_impl(struct Main *bmain, /* notifiers */ DEG_id_tag_update_ex(bmain, &camera_ob->id, ID_RECALC_TRANSFORM); + if (is_ortho_camera) { + DEG_id_tag_update_ex(bmain, camera_ob->data, ID_RECALC_PARAMETERS); + } return true; } diff --git a/source/blender/editors/transform/CMakeLists.txt b/source/blender/editors/transform/CMakeLists.txt index 68c4f4e76ca..6984dcb18d4 100644 --- a/source/blender/editors/transform/CMakeLists.txt +++ b/source/blender/editors/transform/CMakeLists.txt @@ -39,6 +39,7 @@ set(SRC transform_convert_mesh_edge.c transform_convert_mesh_skin.c transform_convert_mesh_uv.c + transform_convert_mesh_vert_cdata.c transform_convert_nla.c transform_convert_node.c transform_convert_object.c diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index 049d1b6a90e..b01affc7307 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -222,6 +222,7 @@ typedef enum { TC_MESH_EDGES, TC_MESH_SKIN, TC_MESH_UV, + TC_MESH_VERT_CDATA, TC_NLA_DATA, TC_NODE_DATA, TC_OBJECT, diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c index d9b971c5478..b0148ce508c 100644 --- a/source/blender/editors/transform/transform_convert.c +++ b/source/blender/editors/transform/transform_convert.c @@ -479,132 +479,6 @@ TransDataCurveHandleFlags *initTransDataCurveHandles(TransData *td, struct BezTr /** \name UV Coordinates * \{ */ -/** - * Find the correction for the scaling factor when "Constrain to Bounds" is active. - * \param numerator: How far the UV boundary (unit square) is from the origin of the scale. - * \param denominator: How far the AABB is from the origin of the scale. - * \param scale: Scale parameter to update. - */ -static void constrain_scale_to_boundary(const float numerator, - const float denominator, - float *scale) -{ - if (denominator == 0.0f) { - /* The origin of the scale is on the edge of the boundary. */ - if (numerator < 0.0f) { - /* Negative scale will wrap around and put us outside the boundary. */ - *scale = 0.0f; /* Hold at the boundary instead. */ - } - return; /* Nothing else we can do without more info. */ - } - - const float correction = numerator / denominator; - if (correction < 0.0f || !isfinite(correction)) { - /* TODO: Correction is negative or invalid, but we lack context to fix `*scale`. */ - return; - } - - if (denominator < 0.0f) { - /* Scale origin is outside boundary, only make scale bigger. */ - if (*scale < correction) { - *scale = correction; - } - return; - } - - /* Scale origin is inside boundary, the "regular" case, limit maximum scale. */ - if (*scale > correction) { - *scale = correction; - } -} - -bool clipUVTransform(TransInfo *t, float vec[2], const bool resize) -{ - bool clipx = true, clipy = true; - float min[2], max[2]; - - /* Check if the current image in UV editor is a tiled image or not. */ - const SpaceImage *sima = t->area->spacedata.first; - const Image *image = sima->image; - const bool is_tiled_image = image && (image->source == IMA_SRC_TILED); - /* Stores the coordinates of the closest UDIM tile. - * Also acts as an offset to the tile from the origin of UV space. */ - float base_offset[2] = {0.0f, 0.0f}; - - /* If tiled image then constrain to correct/closest UDIM tile, else 0-1 UV space. */ - if (is_tiled_image) { - int nearest_tile_index = BKE_image_find_nearest_tile(image, t->center_global); - if (nearest_tile_index != -1) { - nearest_tile_index -= 1001; - /* Getting coordinates of nearest tile from the tile index. */ - base_offset[0] = nearest_tile_index % 10; - base_offset[1] = nearest_tile_index / 10; - } - } - - min[0] = min[1] = FLT_MAX; - max[0] = max[1] = FLT_MIN; - - FOREACH_TRANS_DATA_CONTAINER (t, tc) { - - TransData *td; - int a; - - for (a = 0, td = tc->data; a < tc->data_len; a++, td++) { - minmax_v2v2_v2(min, max, td->loc); - } - } - - if (resize) { - /* Assume no change is required. */ - float scalex = 1.0f; - float scaley = 1.0f; - - /* Update U against the left border. */ - constrain_scale_to_boundary( - t->center_global[0] - base_offset[0], t->center_global[0] - min[0], &scalex); - /* Now the right border, negated, because `-1.0 / -1.0 = 1.0` */ - constrain_scale_to_boundary(base_offset[0] + t->aspect[0] - t->center_global[0], - max[0] - t->center_global[0], - &scalex); - - /* Do the same for the V co-ordinate, which is called `y`. */ - constrain_scale_to_boundary( - t->center_global[1] - base_offset[1], t->center_global[1] - min[1], &scaley); - constrain_scale_to_boundary(base_offset[1] + t->aspect[1] - t->center_global[1], - max[1] - t->center_global[1], - &scaley); - - clipx = (scalex != 1.0f); - clipy = (scaley != 1.0f); - vec[0] *= scalex; - vec[1] *= scaley; - } - else { - if (min[0] < base_offset[0]) { - vec[0] += base_offset[0] - min[0]; - } - else if (max[0] > base_offset[0] + t->aspect[0]) { - vec[0] -= max[0] - base_offset[0] - t->aspect[0]; - } - else { - clipx = 0; - } - - if (min[1] < base_offset[1]) { - vec[1] += base_offset[1] - min[1]; - } - else if (max[1] > base_offset[1] + t->aspect[1]) { - vec[1] -= max[1] - base_offset[1] - t->aspect[1]; - } - else { - clipy = 0; - } - } - - return (clipx || clipy); -} - void clipUVData(TransInfo *t) { FOREACH_TRANS_DATA_CONTAINER (t, tc) { @@ -769,7 +643,7 @@ void posttrans_fcurve_clean(FCurve *fcu, const int sel_flag, const bool use_hand } else { /* Delete Keyframe */ - delete_fcurve_key(fcu, i, 0); + BKE_fcurve_delete_key(fcu, i); } /* Update count of how many we've deleted @@ -779,7 +653,7 @@ void posttrans_fcurve_clean(FCurve *fcu, const int sel_flag, const bool use_hand } else { /* Always delete - Unselected keys don't matter */ - delete_fcurve_key(fcu, i, 0); + BKE_fcurve_delete_key(fcu, i); } /* Stop the RK search... we've found our match now */ @@ -949,6 +823,7 @@ void special_aftertrans_update(bContext *C, TransInfo *t) case TC_GPENCIL: case TC_LATTICE_VERTS: case TC_MBALL_VERTS: + case TC_MESH_VERT_CDATA: case TC_MESH_UV: case TC_MESH_SKIN: case TC_OBJECT_TEXSPACE: @@ -1031,6 +906,7 @@ static void init_proportional_edit(TransInfo *t) case TC_MESH_EDGES: case TC_MESH_SKIN: case TC_MESH_UV: + case TC_MESH_VERT_CDATA: case TC_NODE_DATA: case TC_OBJECT: case TC_PARTICLE_VERTS: @@ -1065,7 +941,7 @@ static void init_proportional_edit(TransInfo *t) if (ELEM(convert_type, TC_ACTION_DATA, TC_GRAPH_EDIT_DATA)) { /* Distance has already been set. */ } - else if (ELEM(convert_type, TC_MESH_VERTS, TC_MESH_SKIN)) { + else if (ELEM(convert_type, TC_MESH_VERTS, TC_MESH_SKIN, TC_MESH_VERT_CDATA)) { if (t->flag & T_PROP_CONNECTED) { /* Already calculated by transform_convert_mesh_connectivity_distance. */ } @@ -1110,6 +986,7 @@ static void init_TransDataContainers(TransInfo *t, case TC_MESH_EDGES: case TC_MESH_SKIN: case TC_MESH_UV: + case TC_MESH_VERT_CDATA: break; case TC_ACTION_DATA: case TC_GRAPH_EDIT_DATA: @@ -1221,6 +1098,7 @@ static eTFlag flags_from_data_type(eTConvertType data_type) case TC_MBALL_VERTS: case TC_MESH_VERTS: case TC_MESH_SKIN: + case TC_MESH_VERT_CDATA: return T_EDIT | T_POINTS; case TC_MESH_EDGES: return T_EDIT; @@ -1320,6 +1198,9 @@ static eTConvertType convert_type_get(const TransInfo *t, Object **r_obj_armatur if (t->mode == TFM_SKIN_RESIZE) { convert_type = TC_MESH_SKIN; } + else if (ELEM(t->mode, TFM_BWEIGHT, TFM_VERT_CREASE)) { + convert_type = TC_MESH_VERT_CDATA; + } else { convert_type = TC_MESH_VERTS; } @@ -1443,6 +1324,9 @@ void createTransData(bContext *C, TransInfo *t) case TC_NLA_DATA: createTransNlaData(C, t); break; + case TC_MESH_VERT_CDATA: + createTransMeshVertCData(t); + break; case TC_NODE_DATA: createTransNodeData(t); break; @@ -1749,6 +1633,9 @@ void recalcData(TransInfo *t) case TC_MESH_UV: recalcData_uv(t); break; + case TC_MESH_VERT_CDATA: + recalcData_mesh_cdata(t); + break; case TC_NLA_DATA: recalcData_nla(t); break; diff --git a/source/blender/editors/transform/transform_convert.h b/source/blender/editors/transform/transform_convert.h index 037fbe26c77..1b7c33e443c 100644 --- a/source/blender/editors/transform/transform_convert.h +++ b/source/blender/editors/transform/transform_convert.h @@ -34,7 +34,6 @@ int special_transform_moving(TransInfo *t); void special_aftertrans_update(struct bContext *C, TransInfo *t); void sort_trans_data_dist(TransInfo *t); void createTransData(struct bContext *C, TransInfo *t); -bool clipUVTransform(TransInfo *t, float vec[2], bool resize); void clipUVData(TransInfo *t); void transform_convert_flush_handle2D(TransData *td, TransData2D *td2d, float y_fac); /** @@ -253,6 +252,11 @@ void createTransUVs(bContext *C, TransInfo *t); /* helper for recalcData() - for Image Editor transforms */ void recalcData_uv(TransInfo *t); +/* transform_convert_mesh_vert_cdata.c */ + +void createTransMeshVertCData(TransInfo *t); +void recalcData_mesh_cdata(TransInfo *t); + /* transform_convert_nla.c */ void createTransNlaData(bContext *C, TransInfo *t); diff --git a/source/blender/editors/transform/transform_convert_graph.c b/source/blender/editors/transform/transform_convert_graph.c index d93fff72de6..b57f5b78939 100644 --- a/source/blender/editors/transform/transform_convert_graph.c +++ b/source/blender/editors/transform/transform_convert_graph.c @@ -934,7 +934,7 @@ void recalcData_graphedit(TransInfo *t) dosort++; } else { - calchandles_fcurve_ex(fcu, BEZT_FLAG_TEMP_TAG); + BKE_fcurve_handles_recalc_ex(fcu, BEZT_FLAG_TEMP_TAG); } /* set refresh tags for objects using this animation, diff --git a/source/blender/editors/transform/transform_convert_mesh.c b/source/blender/editors/transform/transform_convert_mesh.c index c8d943df3fa..de736fad94e 100644 --- a/source/blender/editors/transform/transform_convert_mesh.c +++ b/source/blender/editors/transform/transform_convert_mesh.c @@ -1408,7 +1408,6 @@ static void VertsToTransData(TransInfo *t, TransDataExtension *tx, BMEditMesh *em, BMVert *eve, - float *bweight, const struct TransIslandData *island_data, const int island_index) { @@ -1449,11 +1448,7 @@ static void VertsToTransData(TransInfo *t, td->ext = NULL; td->val = NULL; td->extra = eve; - if (ELEM(t->mode, TFM_BWEIGHT, TFM_VERT_CREASE)) { - td->val = bweight; - td->ival = *bweight; - } - else if (t->mode == TFM_SHRINKFATTEN) { + if (t->mode == TFM_SHRINKFATTEN) { td->ext = tx; tx->isize[0] = BM_vert_calc_shell_factor_ex(eve, no, BM_ELEM_SELECT); } @@ -1589,17 +1584,6 @@ void createTransEditVerts(TransInfo *t) "TransObData ext"); } - int cd_vert_bweight_offset = -1; - int cd_vert_crease_offset = -1; - if (t->mode == TFM_BWEIGHT) { - BM_mesh_cd_flag_ensure(bm, BKE_mesh_from_object(tc->obedit), ME_CDFLAG_VERT_BWEIGHT); - cd_vert_bweight_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT); - } - else if (t->mode == TFM_VERT_CREASE) { - BM_mesh_cd_flag_ensure(bm, BKE_mesh_from_object(tc->obedit), ME_CDFLAG_VERT_CREASE); - cd_vert_crease_offset = CustomData_get_offset(&bm->vdata, CD_CREASE); - } - TransData *tob = tc->data; TransDataMirror *td_mirror = tc->data_mirror; BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, a) { @@ -1632,15 +1616,9 @@ void createTransEditVerts(TransInfo *t) td_mirror++; } else if (prop_mode || BM_elem_flag_test(eve, BM_ELEM_SELECT)) { - float *bweight = (cd_vert_bweight_offset != -1) ? - BM_ELEM_CD_GET_VOID_P(eve, cd_vert_bweight_offset) : - (cd_vert_crease_offset != -1) ? - BM_ELEM_CD_GET_VOID_P(eve, cd_vert_crease_offset) : - NULL; - /* Do not use the island center in case we are using islands * only to get axis for snap/rotate to normal... */ - VertsToTransData(t, tob, tx, em, eve, bweight, &island_data, island_index); + VertsToTransData(t, tob, tx, em, eve, &island_data, island_index); if (tx) { tx++; } diff --git a/source/blender/editors/transform/transform_convert_mesh_uv.c b/source/blender/editors/transform/transform_convert_mesh_uv.c index 18868643b6d..bb550909407 100644 --- a/source/blender/editors/transform/transform_convert_mesh_uv.c +++ b/source/blender/editors/transform/transform_convert_mesh_uv.c @@ -238,7 +238,6 @@ void createTransUVs(bContext *C, TransInfo *t) { SpaceImage *sima = CTX_wm_space_image(C); Scene *scene = t->scene; - ToolSettings *ts = CTX_data_tool_settings(C); const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0; const bool is_prop_connected = (t->flag & T_PROP_CONNECTED) != 0; @@ -266,8 +265,7 @@ void createTransUVs(bContext *C, TransInfo *t) /* count */ if (is_island_center) { /* create element map with island information */ - const bool use_facesel = (ts->uv_flag & UV_SYNC_SELECTION) == 0; - elementmap = BM_uv_element_map_create(em->bm, scene, use_facesel, true, false, true); + elementmap = BM_uv_element_map_create(em->bm, scene, true, false, true); if (elementmap == NULL) { continue; } diff --git a/source/blender/editors/transform/transform_convert_mesh_vert_cdata.c b/source/blender/editors/transform/transform_convert_mesh_vert_cdata.c new file mode 100644 index 00000000000..104b45de138 --- /dev/null +++ b/source/blender/editors/transform/transform_convert_mesh_vert_cdata.c @@ -0,0 +1,289 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2001-2002 NaN Holding BV. All rights reserved. */ + +/** \file + * \ingroup edtransform + */ + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_math.h" + +#include "BKE_context.h" +#include "BKE_crazyspace.h" +#include "BKE_editmesh.h" +#include "BKE_modifier.h" +#include "BKE_scene.h" + +#include "ED_mesh.h" + +#include "DEG_depsgraph_query.h" + +#include "transform.h" +#include "transform_orientations.h" + +#include "transform_convert.h" + +/* -------------------------------------------------------------------- */ +/** \name Edit Mesh #CD_BWEIGHT and #CD_CREASE Transform Creation + * \{ */ + +static float *tc_mesh_cdata_transdata_center(const struct TransIslandData *island_data, + const int island_index, + BMVert *eve) +{ + if (island_data->center && island_index != -1) { + return island_data->center[island_index]; + } + return eve->co; +} + +static void tc_mesh_cdata_transdata_create(TransDataBasic *td, + BMVert *eve, + float *weight, + const struct TransIslandData *island_data, + const int island_index) +{ + BLI_assert(BM_elem_flag_test(eve, BM_ELEM_HIDDEN) == 0); + + td->loc = weight; + td->iloc[0] = *weight; + + if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) { + td->flag |= TD_SELECTED; + } + + copy_v3_v3(td->center, tc_mesh_cdata_transdata_center(island_data, island_index, eve)); + td->extra = eve; +} + +void createTransMeshVertCData(TransInfo *t) +{ + BLI_assert(ELEM(t->mode, TFM_BWEIGHT, TFM_VERT_CREASE)); + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); + Mesh *me = tc->obedit->data; + BMesh *bm = em->bm; + BMVert *eve; + BMIter iter; + float mtx[3][3], smtx[3][3]; + int a; + const int prop_mode = (t->flag & T_PROP_EDIT) ? (t->flag & T_PROP_EDIT_ALL) : 0; + + struct TransIslandData island_data = {NULL}; + struct TransMirrorData mirror_data = {NULL}; + struct TransMeshDataCrazySpace crazyspace_data = {NULL}; + + /* Support other objects using PET to adjust these, unless connected is enabled. */ + if ((!prop_mode || (prop_mode & T_PROP_CONNECTED)) && (bm->totvertsel == 0)) { + continue; + } + + int cd_offset = -1; + if (t->mode == TFM_BWEIGHT) { + BM_mesh_cd_flag_ensure(bm, me, ME_CDFLAG_VERT_BWEIGHT); + cd_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT); + } + else { + BM_mesh_cd_flag_ensure(bm, me, ME_CDFLAG_VERT_CREASE); + cd_offset = CustomData_get_offset(&bm->vdata, CD_CREASE); + } + + if (cd_offset == -1) { + continue; + } + + int data_len = 0; + if (prop_mode) { + BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) { + if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) { + data_len++; + } + } + } + else { + data_len = bm->totvertsel; + } + + if (data_len == 0) { + continue; + } + + const bool is_island_center = (t->around == V3D_AROUND_LOCAL_ORIGINS); + if (is_island_center) { + /* In this specific case, near-by vertices will need to know + * the island of the nearest connected vertex. */ + const bool calc_single_islands = ((prop_mode & T_PROP_CONNECTED) && + (t->around == V3D_AROUND_LOCAL_ORIGINS) && + (em->selectmode & SCE_SELECT_VERTEX)); + + const bool calc_island_center = false; + const bool calc_island_axismtx = false; + + transform_convert_mesh_islands_calc( + em, calc_single_islands, calc_island_center, calc_island_axismtx, &island_data); + } + + copy_m3_m4(mtx, tc->obedit->obmat); + /* we use a pseudo-inverse so that when one of the axes is scaled to 0, + * matrix inversion still works and we can still moving along the other */ + pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON); + + /* Original index of our connected vertex when connected distances are calculated. + * Optional, allocate if needed. */ + int *dists_index = NULL; + float *dists = NULL; + if (prop_mode & T_PROP_CONNECTED) { + dists = MEM_mallocN(bm->totvert * sizeof(float), __func__); + if (is_island_center) { + dists_index = MEM_mallocN(bm->totvert * sizeof(int), __func__); + } + transform_convert_mesh_connectivity_distance(em->bm, mtx, dists, dists_index); + } + + /* Create TransDataMirror. */ + if (tc->use_mirror_axis_any) { + bool use_topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0; + bool use_select = (t->flag & T_PROP_EDIT) == 0; + const bool mirror_axis[3] = { + tc->use_mirror_axis_x, tc->use_mirror_axis_y, tc->use_mirror_axis_z}; + transform_convert_mesh_mirrordata_calc( + em, use_select, use_topology, mirror_axis, &mirror_data); + + if (mirror_data.vert_map) { + tc->data_mirror_len = mirror_data.mirror_elem_len; + tc->data_mirror = MEM_mallocN(mirror_data.mirror_elem_len * sizeof(*tc->data_mirror), + __func__); + + BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, a) { + if (prop_mode || BM_elem_flag_test(eve, BM_ELEM_SELECT)) { + if (mirror_data.vert_map[a].index != -1) { + data_len--; + } + } + } + } + } + + /* Detect CrazySpace [tm]. */ + transform_convert_mesh_crazyspace_detect(t, tc, em, &crazyspace_data); + + /* Create TransData. */ + BLI_assert(data_len >= 1); + tc->data_len = data_len; + tc->data = MEM_callocN(data_len * sizeof(TransData), "TransObData(Mesh EditMode)"); + + TransData *td = tc->data; + TransDataMirror *td_mirror = tc->data_mirror; + BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, a) { + if (BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) { + continue; + } + + int island_index = -1; + if (island_data.island_vert_map) { + const int connected_index = (dists_index && dists_index[a] != -1) ? dists_index[a] : a; + island_index = island_data.island_vert_map[connected_index]; + } + + float *weight = BM_ELEM_CD_GET_VOID_P(eve, cd_offset); + if (mirror_data.vert_map && mirror_data.vert_map[a].index != -1) { + tc_mesh_cdata_transdata_create( + (TransDataBasic *)td_mirror, eve, weight, &island_data, island_index); + + int elem_index = mirror_data.vert_map[a].index; + BMVert *v_src = BM_vert_at_index(bm, elem_index); + + td_mirror->flag |= mirror_data.vert_map[a].flag; + td_mirror->loc_src = BM_ELEM_CD_GET_VOID_P(v_src, cd_offset); + td_mirror++; + } + else if (prop_mode || BM_elem_flag_test(eve, BM_ELEM_SELECT)) { + tc_mesh_cdata_transdata_create( + (TransDataBasic *)td, eve, weight, &island_data, island_index); + + if (t->around == V3D_AROUND_LOCAL_ORIGINS) { + createSpaceNormal(td->axismtx, eve->no); + } + else { + /* Setting normals */ + copy_v3_v3(td->axismtx[2], eve->no); + td->axismtx[0][0] = td->axismtx[0][1] = td->axismtx[0][2] = td->axismtx[1][0] = + td->axismtx[1][1] = td->axismtx[1][2] = 0.0f; + } + + if (prop_mode) { + if (prop_mode & T_PROP_CONNECTED) { + td->dist = dists[a]; + } + else { + td->flag |= TD_NOTCONNECTED; + td->dist = FLT_MAX; + } + } + + /* CrazySpace */ + transform_convert_mesh_crazyspace_transdata_set( + mtx, + smtx, + crazyspace_data.defmats ? crazyspace_data.defmats[a] : NULL, + crazyspace_data.quats && BM_elem_flag_test(eve, BM_ELEM_TAG) ? + crazyspace_data.quats[a] : + NULL, + td); + + td++; + } + } + + transform_convert_mesh_islanddata_free(&island_data); + transform_convert_mesh_mirrordata_free(&mirror_data); + transform_convert_mesh_crazyspace_free(&crazyspace_data); + if (dists) { + MEM_freeN(dists); + } + if (dists_index) { + MEM_freeN(dists_index); + } + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Recalc Mesh Data + * \{ */ + +static void tc_mesh_cdata_apply_to_mirror(TransInfo *t) +{ + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + if (tc->use_mirror_axis_any) { + TransDataMirror *td_mirror = tc->data_mirror; + for (int i = 0; i < tc->data_mirror_len; i++, td_mirror++) { + td_mirror->loc[0] = td_mirror->loc_src[0]; + } + } + } +} + +void recalcData_mesh_cdata(TransInfo *t) +{ + bool is_canceling = t->state == TRANS_CANCEL; + /* mirror modifier clipping? */ + if (!is_canceling) { + if (!(t->flag & T_NO_MIRROR)) { + tc_mesh_cdata_apply_to_mirror(t); + } + } + + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + DEG_id_tag_update(tc->obedit->data, ID_RECALC_GEOMETRY); + BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); + BKE_editmesh_looptri_and_normals_calc(em); + } +} + +/** \} */ diff --git a/source/blender/editors/transform/transform_mode_curveshrinkfatten.c b/source/blender/editors/transform/transform_mode_curveshrinkfatten.c index aa4d608de04..f7f9e14b8ac 100644 --- a/source/blender/editors/transform/transform_mode_curveshrinkfatten.c +++ b/source/blender/editors/transform/transform_mode_curveshrinkfatten.c @@ -64,10 +64,8 @@ static void applyCurveShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) if (td->val) { *td->val = td->ival * ratio; /* apply PET */ - *td->val = (*td->val * td->factor) + ((1.0f - td->factor) * td->ival); - if (*td->val <= 0.0f) { - *td->val = 0.001f; - } + *td->val = interpf(*td->val, td->ival, td->factor); + CLAMP_MIN(*td->val, 0.0f); } } } @@ -93,10 +91,6 @@ void initCurveShrinkFatten(TransInfo *t) t->num.unit_sys = t->scene->unit.system; t->num.unit_type[0] = B_UNIT_NONE; -#ifdef USE_NUM_NO_ZERO - t->num.val_flag[0] |= NUM_NO_ZERO; -#endif - t->flag |= T_NO_CONSTRAINT; } diff --git a/source/blender/editors/transform/transform_mode_edge_bevelweight.c b/source/blender/editors/transform/transform_mode_edge_bevelweight.c index 987d8396907..e96e74b596c 100644 --- a/source/blender/editors/transform/transform_mode_edge_bevelweight.c +++ b/source/blender/editors/transform/transform_mode_edge_bevelweight.c @@ -44,16 +44,11 @@ static void transdata_elem_bevel_weight(const TransInfo *UNUSED(t), TransData *td, const float weight) { - if (td->val == NULL) { + if (td->loc == NULL) { return; } - *td->val = td->ival + weight * td->factor; - if (*td->val < 0.0f) { - *td->val = 0.0f; - } - if (*td->val > 1.0f) { - *td->val = 1.0f; - } + *td->loc = td->iloc[0] + weight * td->factor; + CLAMP(*td->loc, 0.0f, 1.0f); } static void transdata_elem_bevel_weight_fn(void *__restrict iter_data_v, diff --git a/source/blender/editors/transform/transform_mode_edge_crease.c b/source/blender/editors/transform/transform_mode_edge_crease.c index f1acc2a4c9a..1a3ccf30387 100644 --- a/source/blender/editors/transform/transform_mode_edge_crease.c +++ b/source/blender/editors/transform/transform_mode_edge_crease.c @@ -44,17 +44,12 @@ static void transdata_elem_crease(const TransInfo *UNUSED(t), TransData *td, const float crease) { - if (td->val == NULL) { + if (td->loc == NULL) { return; } - *td->val = td->ival + crease * td->factor; - if (*td->val < 0.0f) { - *td->val = 0.0f; - } - if (*td->val > 1.0f) { - *td->val = 1.0f; - } + *td->loc = td->iloc[0] + crease * td->factor; + CLAMP(*td->loc, 0.0f, 1.0f); } static void transdata_elem_crease_fn(void *__restrict iter_data_v, diff --git a/source/blender/editors/transform/transform_mode_gpopacity.c b/source/blender/editors/transform/transform_mode_gpopacity.c index 83dce17d104..8b9431b65ea 100644 --- a/source/blender/editors/transform/transform_mode_gpopacity.c +++ b/source/blender/editors/transform/transform_mode_gpopacity.c @@ -74,7 +74,7 @@ static void applyGPOpacity(TransInfo *t, const int UNUSED(mval[2])) if (td->val) { *td->val = td->ival * ratio; /* apply PET */ - *td->val = (*td->val * td->factor) + ((1.0f - td->factor) * td->ival); + *td->val = interpf(*td->val, td->ival, td->factor); CLAMP(*td->val, 0.0f, 1.0f); } } diff --git a/source/blender/editors/transform/transform_mode_gpshrinkfatten.c b/source/blender/editors/transform/transform_mode_gpshrinkfatten.c index 796d5c7ae9c..d8ec7d4ff50 100644 --- a/source/blender/editors/transform/transform_mode_gpshrinkfatten.c +++ b/source/blender/editors/transform/transform_mode_gpshrinkfatten.c @@ -74,7 +74,7 @@ static void applyGPShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) if (td->val) { *td->val = td->ival * ratio; /* apply PET */ - *td->val = (*td->val * td->factor) + ((1.0f - td->factor) * td->ival); + *td->val = interpf(*td->val, td->ival, td->factor); if (*td->val <= 0.0f) { *td->val = 0.001f; } diff --git a/source/blender/editors/transform/transform_mode_maskshrinkfatten.c b/source/blender/editors/transform/transform_mode_maskshrinkfatten.c index 19a3deade63..e2ccf61796b 100644 --- a/source/blender/editors/transform/transform_mode_maskshrinkfatten.c +++ b/source/blender/editors/transform/transform_mode_maskshrinkfatten.c @@ -90,7 +90,7 @@ static void applyMaskShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) } /* apply PET */ - *td->val = (*td->val * td->factor) + ((1.0f - td->factor) * td->ival); + *td->val = interpf(*td->val, td->ival, td->factor); if (*td->val <= 0.0f) { *td->val = 0.001f; } diff --git a/source/blender/editors/transform/transform_mode_resize.c b/source/blender/editors/transform/transform_mode_resize.c index 31d40486afc..bbe1cfdf521 100644 --- a/source/blender/editors/transform/transform_mode_resize.c +++ b/source/blender/editors/transform/transform_mode_resize.c @@ -11,6 +11,7 @@ #include "BLI_task.h" #include "BKE_context.h" +#include "BKE_image.h" #include "BKE_unit.h" #include "ED_screen.h" @@ -84,6 +85,109 @@ static void ApplySnapResize(TransInfo *t, float vec[3]) } } +/** + * Find the correction for the scaling factor when "Constrain to Bounds" is active. + * \param numerator: How far the UV boundary (unit square) is from the origin of the scale. + * \param denominator: How far the AABB is from the origin of the scale. + * \param scale: Scale parameter to update. + */ +static void constrain_scale_to_boundary(const float numerator, + const float denominator, + float *scale) +{ + if (denominator == 0.0f) { + /* The origin of the scale is on the edge of the boundary. */ + if (numerator < 0.0f) { + /* Negative scale will wrap around and put us outside the boundary. */ + *scale = 0.0f; /* Hold at the boundary instead. */ + } + return; /* Nothing else we can do without more info. */ + } + + const float correction = numerator / denominator; + if (correction < 0.0f || !isfinite(correction)) { + /* TODO: Correction is negative or invalid, but we lack context to fix `*scale`. */ + return; + } + + if (denominator < 0.0f) { + /* Scale origin is outside boundary, only make scale bigger. */ + if (*scale < correction) { + *scale = correction; + } + return; + } + + /* Scale origin is inside boundary, the "regular" case, limit maximum scale. */ + if (*scale > correction) { + *scale = correction; + } +} + +static bool clip_uv_transform_resize(TransInfo *t, float vec[2]) +{ + /* Check if the current image in UV editor is a tiled image or not. */ + const SpaceImage *sima = t->area->spacedata.first; + const Image *image = sima->image; + const bool is_tiled_image = image && (image->source == IMA_SRC_TILED); + + /* Stores the coordinates of the closest UDIM tile. + * Also acts as an offset to the tile from the origin of UV space. */ + float base_offset[2] = {0.0f, 0.0f}; + + /* If tiled image then constrain to correct/closest UDIM tile, else 0-1 UV space. */ + if (is_tiled_image) { + int nearest_tile_index = BKE_image_find_nearest_tile(image, t->center_global); + if (nearest_tile_index != -1) { + nearest_tile_index -= 1001; + /* Getting coordinates of nearest tile from the tile index. */ + base_offset[0] = nearest_tile_index % 10; + base_offset[1] = nearest_tile_index / 10; + } + } + + /* Assume no change is required. */ + float scale = 1.0f; + + /* Are we scaling U and V together, or just one axis? */ + const bool adjust_u = !(t->con.mode & CON_AXIS1); + const bool adjust_v = !(t->con.mode & CON_AXIS0); + const bool use_local_center = transdata_check_local_center(t, t->around); + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + for (TransData *td = tc->data; td < tc->data + tc->data_len; td++) { + + /* Get scale origin. */ + const float *scale_origin = use_local_center ? td->center : t->center_global; + + /* Alias td->loc as min and max just in case we need to optimize later. */ + const float *min = td->loc; + const float *max = td->loc; + + if (adjust_u) { + /* Update U against the left border. */ + constrain_scale_to_boundary( + scale_origin[0] - base_offset[0], scale_origin[0] - min[0], &scale); + + /* Now the right border, negated, because `-1.0 / -1.0 = 1.0` */ + constrain_scale_to_boundary( + base_offset[0] + t->aspect[0] - scale_origin[0], max[0] - scale_origin[0], &scale); + } + + /* Do the same for the V co-ordinate. */ + if (adjust_v) { + constrain_scale_to_boundary( + scale_origin[1] - base_offset[1], scale_origin[1] - min[1], &scale); + + constrain_scale_to_boundary( + base_offset[1] + t->aspect[1] - scale_origin[1], max[1] - scale_origin[1], &scale); + } + } + } + vec[0] *= scale; + vec[1] *= scale; + return scale != 1.0f; +} + static void applyResize(TransInfo *t, const int UNUSED(mval[2])) { float mat[3][3]; @@ -157,7 +261,7 @@ static void applyResize(TransInfo *t, const int UNUSED(mval[2])) } /* Evil hack - redo resize if clipping needed. */ - if (t->flag & T_CLIP_UV && clipUVTransform(t, t->values_final, 1)) { + if (t->flag & T_CLIP_UV && clip_uv_transform_resize(t, t->values_final)) { size_to_mat3(mat, t->values_final); if (t->con.mode & CON_APPLY) { diff --git a/source/blender/editors/transform/transform_mode_translate.c b/source/blender/editors/transform/transform_mode_translate.c index 65690f9069d..67bdeb3fed0 100644 --- a/source/blender/editors/transform/transform_mode_translate.c +++ b/source/blender/editors/transform/transform_mode_translate.c @@ -16,6 +16,7 @@ #include "BLI_task.h" #include "BKE_context.h" +#include "BKE_image.h" #include "BKE_report.h" #include "BKE_unit.h" @@ -434,6 +435,60 @@ static void applyTranslationValue(TransInfo *t, const float vec[3]) custom_data->prev.rotate_mode = rotate_mode; } +static bool clip_uv_transform_translation(TransInfo *t, float vec[2]) +{ + /* Check if the current image in UV editor is a tiled image or not. */ + const SpaceImage *sima = t->area->spacedata.first; + const Image *image = sima->image; + const bool is_tiled_image = image && (image->source == IMA_SRC_TILED); + + /* Stores the coordinates of the closest UDIM tile. + * Also acts as an offset to the tile from the origin of UV space. */ + float base_offset[2] = {0.0f, 0.0f}; + + /* If tiled image then constrain to correct/closest UDIM tile, else 0-1 UV space. */ + if (is_tiled_image) { + int nearest_tile_index = BKE_image_find_nearest_tile(image, t->center_global); + if (nearest_tile_index != -1) { + nearest_tile_index -= 1001; + /* Getting coordinates of nearest tile from the tile index. */ + base_offset[0] = nearest_tile_index % 10; + base_offset[1] = nearest_tile_index / 10; + } + } + + float min[2], max[2]; + min[0] = min[1] = FLT_MAX; + max[0] = max[1] = -FLT_MAX; + + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + for (TransData *td = tc->data; td < tc->data + tc->data_len; td++) { + minmax_v2v2_v2(min, max, td->loc); + } + } + + bool result = false; + if (min[0] < base_offset[0]) { + vec[0] += base_offset[0] - min[0]; + result = true; + } + else if (max[0] > base_offset[0] + t->aspect[0]) { + vec[0] -= max[0] - base_offset[0] - t->aspect[0]; + result = true; + } + + if (min[1] < base_offset[1]) { + vec[1] += base_offset[1] - min[1]; + result = true; + } + else if (max[1] > base_offset[1] + t->aspect[1]) { + vec[1] -= max[1] - base_offset[1] - t->aspect[1]; + result = true; + } + + return result; +} + static void applyTranslation(TransInfo *t, const int UNUSED(mval[2])) { char str[UI_MAX_DRAW_STR]; @@ -498,7 +553,7 @@ static void applyTranslation(TransInfo *t, const int UNUSED(mval[2])) applyTranslationValue(t, global_dir); /* evil hack - redo translation if clipping needed */ - if (t->flag & T_CLIP_UV && clipUVTransform(t, global_dir, 0)) { + if (t->flag & T_CLIP_UV && clip_uv_transform_translation(t, global_dir)) { applyTranslationValue(t, global_dir); /* In proportional edit it can happen that */ diff --git a/source/blender/editors/transform/transform_ops.c b/source/blender/editors/transform/transform_ops.c index a64eff8f981..7c94241f3e3 100644 --- a/source/blender/editors/transform/transform_ops.c +++ b/source/blender/editors/transform/transform_ops.c @@ -566,6 +566,17 @@ static bool transform_poll_property(const bContext *UNUSED(C), } } + /* Snapping. */ + { + PropertyRNA *prop_snap = RNA_struct_find_property(op->ptr, "snap"); + if (prop_snap && (prop_snap != prop) && + (RNA_property_boolean_get(op->ptr, prop_snap) == false)) { + if (STRPREFIX(prop_id, "snap") || STRPREFIX(prop_id, "use_snap")) { + return false; + } + } + } + return true; } @@ -644,28 +655,63 @@ void Transform_Properties(struct wmOperatorType *ot, int flags) } if (flags & P_SNAP) { - prop = RNA_def_boolean(ot->srna, "snap", 0, "Use Snapping Options", ""); + prop = RNA_def_boolean(ot->srna, "snap", false, "Use Snapping Options", ""); RNA_def_property_flag(prop, PROP_HIDDEN); + prop = RNA_def_enum(ot->srna, + "snap_elements", + rna_enum_snap_element_items, + SCE_SNAP_MODE_INCREMENT, + "Snap to Elements", + ""); + RNA_def_property_flag(prop, PROP_ENUM_FLAG); + + RNA_def_boolean(ot->srna, "use_snap_project", false, "Project Individual Elements", ""); + if (flags & P_GEO_SNAP) { - /* TODO(@gfxcoder): Rename `snap_target` to `snap_source` to avoid - * previous ambiguity of "target" (now, "source" is geometry to be moved and "target" is - * geometry to which moved geometry is snapped). Use "Source snap point" and "Point on - * source that will snap to target" for name and description, respectively. */ - prop = RNA_def_enum(ot->srna, "snap_target", rna_enum_snap_source_items, 0, "Target", ""); + /* TODO(@gfxcoder): Rename `snap_target` to `snap_source` to avoid previous ambiguity of + * "target" (now, "source" is geometry to be moved and "target" is geometry to which moved + * geometry is snapped). Use "Source snap point" and "Point on source that will snap to + * target" for name and description, respectively. */ + prop = RNA_def_enum(ot->srna, "snap_target", rna_enum_snap_source_items, 0, "Snap With", ""); + RNA_def_property_flag(prop, PROP_HIDDEN); + + /* Target selection. */ + prop = RNA_def_boolean(ot->srna, "use_snap_self", true, "Target: Include Active", ""); RNA_def_property_flag(prop, PROP_HIDDEN); + prop = RNA_def_boolean(ot->srna, "use_snap_edit", true, "Target: Include Edit", ""); + RNA_def_property_flag(prop, PROP_HIDDEN); + prop = RNA_def_boolean(ot->srna, "use_snap_nonedit", true, "Target: Include Non-Edited", ""); + RNA_def_property_flag(prop, PROP_HIDDEN); + prop = RNA_def_boolean( + ot->srna, "use_snap_selectable_only", false, "Target: Exclude Non-Selectable", ""); + RNA_def_property_flag(prop, PROP_HIDDEN); + + /* Face Nearest options */ + prop = RNA_def_boolean( + ot->srna, "use_snap_to_same_target", false, "Snap to Same Target", ""); + RNA_def_property_flag(prop, PROP_HIDDEN); + prop = RNA_def_int( + ot->srna, "snap_face_nearest_steps", 1, 1, 32767, "Face Nearest Steps", "", 1, 32767); + RNA_def_property_flag(prop, PROP_HIDDEN); + prop = RNA_def_float_vector( ot->srna, "snap_point", 3, NULL, -FLT_MAX, FLT_MAX, "Point", "", -FLT_MAX, FLT_MAX); RNA_def_property_flag(prop, PROP_HIDDEN); if (flags & P_ALIGN_SNAP) { - prop = RNA_def_boolean(ot->srna, "snap_align", 0, "Align with Point Normal", ""); + prop = RNA_def_boolean(ot->srna, "snap_align", false, "Align with Point Normal", ""); RNA_def_property_flag(prop, PROP_HIDDEN); prop = RNA_def_float_vector( ot->srna, "snap_normal", 3, NULL, -FLT_MAX, FLT_MAX, "Normal", "", -FLT_MAX, FLT_MAX); RNA_def_property_flag(prop, PROP_HIDDEN); } } + else { + prop = RNA_def_boolean( + ot->srna, "use_snap_selectable_only", false, "Target: Exclude Non-Selectable", ""); + RNA_def_property_flag(prop, PROP_HIDDEN); + } } if (flags & P_GPENCIL_EDIT) { diff --git a/source/blender/editors/util/numinput.c b/source/blender/editors/util/numinput.c index be6ac6e13e6..60cbc2a2df6 100644 --- a/source/blender/editors/util/numinput.c +++ b/source/blender/editors/util/numinput.c @@ -311,6 +311,7 @@ static bool editstr_is_simple_numinput(const char ascii) bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event) { const char *utf8_buf = NULL; + const char event_ascii = WM_event_utf8_to_ascii(event); char ascii[2] = {'\0', '\0'}; bool updated = false; short idx = n->idx, idx_max = n->idx_max; @@ -321,8 +322,8 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event) if (U.flag & USER_FLAG_NUMINPUT_ADVANCED) #endif { - if (((event->modifier & (KM_CTRL | KM_ALT)) == 0) && (event->ascii != '\0') && - strchr("01234567890@%^&*-+/{}()[]<>.|", event->ascii)) { + if (((event->modifier & (KM_CTRL | KM_ALT)) == 0) && (event_ascii != '\0') && + strchr("01234567890@%^&*-+/{}()[]<>.|", event_ascii)) { if (!(n->flag & NUM_EDIT_FULL)) { n->flag |= NUM_EDITED; n->flag |= NUM_EDIT_FULL; @@ -333,7 +334,7 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event) #ifdef USE_FAKE_EDIT /* XXX Hack around keyboards without direct access to '=' nor '*'... */ - if (ELEM(event->ascii, '=', '*')) { + if (ELEM(event_ascii, '=', '*')) { if (!(n->flag & NUM_EDIT_FULL)) { n->flag |= NUM_EDIT_FULL; n->val_flag[idx] |= NUM_EDITED; @@ -357,7 +358,7 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event) else { /* might be a char too... */ utf8_buf = event->utf8_buf; - ascii[0] = event->ascii; + ascii[0] = event_ascii; } break; case EVT_BACKSPACEKEY: @@ -523,9 +524,9 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event) break; } - if (!updated && !utf8_buf && (event->utf8_buf[0] || event->ascii)) { + if (!updated && !utf8_buf && event->utf8_buf[0]) { utf8_buf = event->utf8_buf; - ascii[0] = event->ascii; + ascii[0] = event_ascii; } /* Up to this point, if we have a ctrl modifier, skip. diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c index 4844ff22b68..74a9989f550 100644 --- a/source/blender/editors/uvedit/uvedit_ops.c +++ b/source/blender/editors/uvedit/uvedit_ops.c @@ -544,7 +544,7 @@ static bool uvedit_uv_straighten(Scene *scene, BMesh *bm, eUVWeldAlign tool) return false; } - UvElementMap *element_map = BM_uv_element_map_create(bm, scene, false, true, false, true); + UvElementMap *element_map = BM_uv_element_map_create(bm, scene, true, false, true); if (element_map == NULL) { return false; } @@ -1036,6 +1036,12 @@ static bool uv_snap_cursor_to_selection(Scene *scene, return ED_uvedit_center_multi(scene, objects_edit, objects_len, sima->cursor, sima->around); } +static void uv_snap_cursor_to_origin(float uvco[2]) +{ + uvco[0] = 0; + uvco[1] = 0; +} + static int uv_snap_cursor_exec(bContext *C, wmOperator *op) { SpaceImage *sima = CTX_wm_space_image(C); @@ -1058,6 +1064,10 @@ static int uv_snap_cursor_exec(bContext *C, wmOperator *op) MEM_freeN(objects); break; } + case 2: + uv_snap_cursor_to_origin(sima->cursor); + changed = true; + break; } if (!changed) { @@ -1074,6 +1084,7 @@ static void UV_OT_snap_cursor(wmOperatorType *ot) static const EnumPropertyItem target_items[] = { {0, "PIXELS", 0, "Pixels", ""}, {1, "SELECTED", 0, "Selected", ""}, + {2, "ORIGIN", 0, "Origin", ""}, {0, NULL, 0, NULL, NULL}, }; diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c index db834f6a0fd..d59dcb4f4ed 100644 --- a/source/blender/editors/uvedit/uvedit_select.c +++ b/source/blender/editors/uvedit/uvedit_select.c @@ -3582,6 +3582,7 @@ static int uv_box_select_exec(bContext *C, wmOperator *op) } } else if (use_edge && !pinned) { + bool do_second_pass = true; BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { if (!uvedit_face_visible_test(scene, efa)) { continue; @@ -3596,11 +3597,35 @@ static int uv_box_select_exec(bContext *C, wmOperator *op) uvedit_edge_select_set_with_sticky( scene, em, l_prev, select, false, cd_loop_uv_offset); changed = true; + do_second_pass = false; } l_prev = l; luv_prev = luv; } } + /* Do a second pass if no complete edges could be selected. + * This matches wire-frame edit-mesh selection in the 3D view. */ + if (do_second_pass) { + /* Second pass to check if edges partially overlap with the selection area (box). */ + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + if (!uvedit_face_visible_test(scene, efa)) { + continue; + } + BMLoop *l_prev = BM_FACE_FIRST_LOOP(efa)->prev; + MLoopUV *luv_prev = BM_ELEM_CD_GET_VOID_P(l_prev, cd_loop_uv_offset); + + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + if (BLI_rctf_isect_segment(&rectf, luv_prev->uv, luv->uv)) { + uvedit_edge_select_set_with_sticky( + scene, em, l_prev, select, false, cd_loop_uv_offset); + changed = true; + } + l_prev = l; + luv_prev = luv; + } + } + } } else { /* other selection modes */ @@ -3920,6 +3945,24 @@ static bool do_lasso_select_mesh_uv_is_point_inside(const ARegion *region, return false; } +static bool do_lasso_select_mesh_uv_is_edge_inside(const ARegion *region, + const rcti *clip_rect, + const int mcoords[][2], + const int mcoords_len, + const float co_test_a[2], + const float co_test_b[2]) +{ + int co_screen_a[2], co_screen_b[2]; + if (UI_view2d_view_to_region_segment_clip( + ®ion->v2d, co_test_a, co_test_b, co_screen_a, co_screen_b) && + BLI_rcti_isect_segment(clip_rect, co_screen_a, co_screen_b) && + BLI_lasso_is_edge_inside( + mcoords, mcoords_len, UNPACK2(co_screen_a), UNPACK2(co_screen_b), V2D_IS_CLIPPED)) { + return true; + } + return false; +} + static bool do_lasso_select_mesh_uv(bContext *C, const int mcoords[][2], const int mcoords_len, @@ -3988,6 +4031,7 @@ static bool do_lasso_select_mesh_uv(bContext *C, } } else if (use_edge) { + bool do_second_pass = true; BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { if (!uvedit_face_visible_test(scene, efa)) { continue; @@ -4004,12 +4048,37 @@ static bool do_lasso_select_mesh_uv(bContext *C, region, &rect, mcoords, mcoords_len, luv_prev->uv)) { uvedit_edge_select_set_with_sticky( scene, em, l_prev, select, false, cd_loop_uv_offset); + do_second_pass = false; changed = true; } l_prev = l; luv_prev = luv; } } + /* Do a second pass if no complete edges could be selected. + * This matches wire-frame edit-mesh selection in the 3D view. */ + if (do_second_pass) { + /* Second pass to check if edges partially overlap with the selection area (lasso). */ + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + if (!uvedit_face_visible_test(scene, efa)) { + continue; + } + BMLoop *l_prev = BM_FACE_FIRST_LOOP(efa)->prev; + MLoopUV *luv_prev = BM_ELEM_CD_GET_VOID_P(l_prev, cd_loop_uv_offset); + + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + if (do_lasso_select_mesh_uv_is_edge_inside( + region, &rect, mcoords, mcoords_len, luv->uv, luv_prev->uv)) { + uvedit_edge_select_set_with_sticky( + scene, em, l_prev, select, false, cd_loop_uv_offset); + changed = true; + } + l_prev = l; + luv_prev = luv; + } + } + } } else { /* Vert Selection. */ BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT, BM_ELEM_TAG, false); @@ -5154,7 +5223,7 @@ static void uv_isolate_selected_islands(const Scene *scene, BLI_assert((scene->toolsettings->uv_flag & UV_SYNC_SELECTION) == 0); BMFace *efa; BMIter iter, liter; - UvElementMap *elementmap = BM_uv_element_map_create(em->bm, scene, true, false, false, true); + UvElementMap *elementmap = BM_uv_element_map_create(em->bm, scene, false, false, true); if (elementmap == NULL) { return; } diff --git a/source/blender/editors/uvedit/uvedit_smart_stitch.c b/source/blender/editors/uvedit/uvedit_smart_stitch.c index 55e44607f6f..579674930a6 100644 --- a/source/blender/editors/uvedit/uvedit_smart_stitch.c +++ b/source/blender/editors/uvedit/uvedit_smart_stitch.c @@ -1902,15 +1902,7 @@ static StitchState *stitch_init(bContext *C, * for stitch this isn't useful behavior, see T86924. */ const int selectmode_orig = scene->toolsettings->selectmode; scene->toolsettings->selectmode = SCE_SELECT_VERTEX; - - /* in uv synch selection, all uv's are visible */ - if (ts->uv_flag & UV_SYNC_SELECTION) { - state->element_map = BM_uv_element_map_create(state->em->bm, scene, false, false, true, true); - } - else { - state->element_map = BM_uv_element_map_create(state->em->bm, scene, true, false, true, true); - } - + state->element_map = BM_uv_element_map_create(state->em->bm, scene, false, true, true); scene->toolsettings->selectmode = selectmode_orig; if (!state->element_map) { diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c index ae81aaffeb2..2c7ad012dd2 100644 --- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c +++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c @@ -1191,7 +1191,7 @@ void UV_OT_pack_islands(wmOperatorType *ot) /** \name Average UV Islands Scale Operator * \{ */ -static int average_islands_scale_exec(bContext *C, wmOperator *UNUSED(op)) +static int average_islands_scale_exec(bContext *C, wmOperator *op) { const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); @@ -1215,8 +1215,12 @@ static int average_islands_scale_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_CANCELLED; } + /* RNA props */ + const bool scale_uv = RNA_boolean_get(op->ptr, "scale_uv"); + const bool shear = RNA_boolean_get(op->ptr, "shear"); + ParamHandle *handle = construct_param_handle_multi(scene, objects, objects_len, &options); - GEO_uv_parametrizer_average(handle, false); + GEO_uv_parametrizer_average(handle, false, scale_uv, shear); GEO_uv_parametrizer_flush(handle); GEO_uv_parametrizer_delete(handle); @@ -1247,6 +1251,10 @@ void UV_OT_average_islands_scale(wmOperatorType *ot) /* api callbacks */ ot->exec = average_islands_scale_exec; ot->poll = ED_operator_uvedit; + + /* properties */ + RNA_def_boolean(ot->srna, "scale_uv", false, "Non-Uniform", "Scale U and V independently"); + RNA_def_boolean(ot->srna, "shear", false, "Shear", "Reduce shear within islands"); } /** \} */ @@ -1845,7 +1853,7 @@ static void uvedit_unwrap(const Scene *scene, result_info ? &result_info->count_failed : NULL); GEO_uv_parametrizer_lscm_end(handle); - GEO_uv_parametrizer_average(handle, true); + GEO_uv_parametrizer_average(handle, true, false, false); GEO_uv_parametrizer_flush(handle); @@ -1907,7 +1915,7 @@ static int unwrap_exec(bContext *C, wmOperator *op) UnwrapOptions options = { .topology_from_uvs = false, - .only_selected_faces = false, + .only_selected_faces = true, .only_selected_uvs = false, .fill_holes = RNA_boolean_get(op->ptr, "fill_holes"), .correct_aspect = RNA_boolean_get(op->ptr, "correct_aspect"), @@ -1918,7 +1926,6 @@ static int unwrap_exec(bContext *C, wmOperator *op) if (CTX_wm_space_image(C)) { /* Inside the UV Editor, only unwrap selected UVs. */ options.only_selected_uvs = true; - options.only_selected_faces = true; options.pin_unselected = true; } diff --git a/source/blender/geometry/CMakeLists.txt b/source/blender/geometry/CMakeLists.txt index df66a806c16..da83d9e8957 100644 --- a/source/blender/geometry/CMakeLists.txt +++ b/source/blender/geometry/CMakeLists.txt @@ -16,6 +16,7 @@ set(INC set(SRC intern/add_curves_on_mesh.cc + intern/fillet_curves.cc intern/mesh_merge_by_distance.cc intern/mesh_primitive_cuboid.cc intern/mesh_to_curve_convert.cc @@ -29,6 +30,7 @@ set(SRC intern/uv_parametrizer.c GEO_add_curves_on_mesh.hh + GEO_fillet_curves.hh GEO_mesh_merge_by_distance.hh GEO_mesh_primitive_cuboid.hh GEO_mesh_to_curve.hh diff --git a/source/blender/geometry/GEO_fillet_curves.hh b/source/blender/geometry/GEO_fillet_curves.hh new file mode 100644 index 00000000000..1f832f8b6cc --- /dev/null +++ b/source/blender/geometry/GEO_fillet_curves.hh @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_function_ref.hh" +#include "BLI_index_mask.hh" + +#include "BKE_curves.hh" + +namespace blender::geometry { + +bke::CurvesGeometry fillet_curves_poly(const bke::CurvesGeometry &src_curves, + IndexMask curve_selection, + const VArray<float> &radius, + const VArray<int> &counts, + bool limit_radius); + +bke::CurvesGeometry fillet_curves_bezier(const bke::CurvesGeometry &src_curves, + IndexMask curve_selection, + const VArray<float> &radius, + bool limit_radius); + +} // namespace blender::geometry diff --git a/source/blender/geometry/GEO_mesh_to_curve.hh b/source/blender/geometry/GEO_mesh_to_curve.hh index c480e4178cf..f619aaff217 100644 --- a/source/blender/geometry/GEO_mesh_to_curve.hh +++ b/source/blender/geometry/GEO_mesh_to_curve.hh @@ -1,12 +1,12 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#pragma once + #include "BLI_index_mask.hh" -#pragma once +#include "BKE_curves.hh" struct Mesh; -struct Curves; -class MeshComponent; /** \file * \ingroup geo @@ -15,10 +15,10 @@ class MeshComponent; namespace blender::geometry { /** - * Convert the mesh into one or many poly splines. Since splines cannot have branches, - * intersections of more than three edges will become breaks in splines. Attributes that + * Convert the mesh into one or many poly curves. Since curves cannot have branches, + * intersections of more than three edges will become breaks in curves. Attributes that * are not built-in on meshes and not curves are transferred to the result curve. */ -Curves *mesh_to_curve_convert(const MeshComponent &mesh_component, const IndexMask selection); +bke::CurvesGeometry mesh_to_curve_convert(const Mesh &mesh, const IndexMask selection); } // namespace blender::geometry diff --git a/source/blender/geometry/GEO_set_curve_type.hh b/source/blender/geometry/GEO_set_curve_type.hh index 6a75450006b..f38e63b1fc8 100644 --- a/source/blender/geometry/GEO_set_curve_type.hh +++ b/source/blender/geometry/GEO_set_curve_type.hh @@ -2,17 +2,10 @@ #pragma once -#include "DNA_curves_types.h" - #include "BLI_function_ref.hh" #include "BLI_index_mask.hh" -struct Curves; -class CurveComponent; - -namespace blender::bke { -class CurvesGeometry; -} +#include "BKE_curves.hh" namespace blender::geometry { @@ -27,14 +20,13 @@ namespace blender::geometry { */ bool try_curves_conversion_in_place(IndexMask selection, CurveType dst_type, - FunctionRef<Curves &()> get_writable_curves_fn); + FunctionRef<bke::CurvesGeometry &()> get_writable_curves_fn); /** * Change the types of the selected curves, potentially changing the total point count. */ -Curves *convert_curves(const CurveComponent &src_component, - const bke::CurvesGeometry &src_curves, - IndexMask selection, - CurveType dst_type); +bke::CurvesGeometry convert_curves(const bke::CurvesGeometry &src_curves, + IndexMask selection, + CurveType dst_type); } // namespace blender::geometry diff --git a/source/blender/geometry/GEO_subdivide_curves.hh b/source/blender/geometry/GEO_subdivide_curves.hh index 66c2eb53496..ba55118baa4 100644 --- a/source/blender/geometry/GEO_subdivide_curves.hh +++ b/source/blender/geometry/GEO_subdivide_curves.hh @@ -4,11 +4,10 @@ #include "BLI_function_ref.hh" #include "BLI_index_mask.hh" +#include "BLI_virtual_array.hh" #include "BKE_curves.hh" -class CurveComponent; - namespace blender::geometry { /** @@ -18,9 +17,8 @@ namespace blender::geometry { * * \param selection: A selection of curves to consider when subdividing. */ -Curves *subdivide_curves(const CurveComponent &src_component, - const bke::CurvesGeometry &src_curves, - IndexMask selection, - const VArray<int> &cuts); +bke::CurvesGeometry subdivide_curves(const bke::CurvesGeometry &src_curves, + IndexMask selection, + const VArray<int> &cuts); } // namespace blender::geometry diff --git a/source/blender/geometry/GEO_uv_parametrizer.h b/source/blender/geometry/GEO_uv_parametrizer.h index 2181f95945e..5285aefbd4c 100644 --- a/source/blender/geometry/GEO_uv_parametrizer.h +++ b/source/blender/geometry/GEO_uv_parametrizer.h @@ -103,7 +103,10 @@ void GEO_uv_parametrizer_pack(ParamHandle *handle, /** \name Average area for all charts * \{ */ -void GEO_uv_parametrizer_average(ParamHandle *handle, bool ignore_pinned); +void GEO_uv_parametrizer_average(ParamHandle *handle, + bool ignore_pinned, + bool scale_uv, + bool shear); /** \} */ diff --git a/source/blender/geometry/intern/add_curves_on_mesh.cc b/source/blender/geometry/intern/add_curves_on_mesh.cc index 34551bd474f..a69073af207 100644 --- a/source/blender/geometry/intern/add_curves_on_mesh.cc +++ b/source/blender/geometry/intern/add_curves_on_mesh.cc @@ -1,7 +1,9 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BLI_length_parameterize.hh" + +#include "BKE_attribute_math.hh" #include "BKE_mesh_sample.hh" -#include "BKE_spline.hh" #include "GEO_add_curves_on_mesh.hh" @@ -145,16 +147,16 @@ static void interpolate_position_with_interpolation(CurvesGeometry &curves, const int added_curves_num = root_positions_cu.size(); threading::parallel_for(IndexRange(added_curves_num), 256, [&](const IndexRange range) { - for (const int i : range) { - const NeighborCurves &neighbors = neighbors_per_curve[i]; - const int curve_i = old_curves_num + i; + for (const int added_curve_i : range) { + const NeighborCurves &neighbors = neighbors_per_curve[added_curve_i]; + const int curve_i = old_curves_num + added_curve_i; const IndexRange points = curves.points_for_curve(curve_i); - const float length_cu = new_lengths_cu[i]; - const float3 &normal_su = new_normals_su[i]; + const float length_cu = new_lengths_cu[added_curve_i]; + const float3 &normal_su = new_normals_su[added_curve_i]; const float3 normal_cu = math::normalize(surface_to_curves_normal_mat * normal_su); - const float3 &root_cu = root_positions_cu[i]; + const float3 &root_cu = root_positions_cu[added_curve_i]; if (neighbors.is_empty()) { /* If there are no neighbors, just make a straight line. */ @@ -197,30 +199,41 @@ static void interpolate_position_with_interpolation(CurvesGeometry &curves, const IndexRange neighbor_points = curves.points_for_curve(neighbor_curve_i); const float3 &neighbor_root_cu = positions_cu[neighbor_points[0]]; - /* Use a temporary #PolySpline, because that's the easiest way to resample an - * existing curve right now. Resampling is necessary if the length of the new curve - * does not match the length of the neighbors or the number of handle points is - * different. */ - PolySpline neighbor_spline; - neighbor_spline.resize(neighbor_points.size()); - neighbor_spline.positions().copy_from(positions_cu.slice(neighbor_points)); - neighbor_spline.mark_cache_invalid(); + /* Sample the positions on neighbors and mix them into the final positions of the curve. + * Resampling is necessary if the length of the new curve does not match the length of the + * neighbors or the number of handle points is different. + * + * TODO: The lengths can be cached so they aren't recomputed if a curve is a neighbor for + * multiple new curves. Also, allocations could be avoided by reusing some arrays. */ + + const Span<float3> neighbor_positions_cu = positions_cu.slice(neighbor_points); + if (neighbor_positions_cu.size() == 1) { + /* Skip interpolating positions from neighbors with only one point. */ + continue; + } + Array<float, 32> lengths(length_parameterize::segments_num(neighbor_points.size(), false)); + length_parameterize::accumulate_lengths<float3>(neighbor_positions_cu, false, lengths); + const float neighbor_length_cu = lengths.last(); - const float neighbor_length_cu = neighbor_spline.length(); + Array<float, 32> sample_lengths(points.size()); const float length_factor = std::min(1.0f, length_cu / neighbor_length_cu); - const float resample_factor = (1.0f / (points.size() - 1.0f)) * length_factor; - for (const int j : IndexRange(points.size())) { - const Spline::LookupResult lookup = neighbor_spline.lookup_evaluated_factor( - j * resample_factor); - const float index_factor = lookup.evaluated_index + lookup.factor; - float3 p; - neighbor_spline.sample_with_index_factors<float3>( - neighbor_spline.positions(), {&index_factor, 1}, {&p, 1}); - const float3 relative_coord = p - neighbor_root_cu; - float3 rotated_relative_coord = relative_coord; + for (const int i : sample_lengths.index_range()) { + sample_lengths[i] = i * resample_factor * neighbor_length_cu; + } + + Array<int, 32> indices(points.size()); + Array<float, 32> factors(points.size()); + length_parameterize::sample_at_lengths(lengths, sample_lengths, indices, factors); + + for (const int i : IndexRange(points.size())) { + const float3 sample_cu = math::interpolate(neighbor_positions_cu[indices[i]], + neighbor_positions_cu[indices[i] + 1], + factors[i]); + const float3 relative_to_root_cu = sample_cu - neighbor_root_cu; + float3 rotated_relative_coord = relative_to_root_cu; mul_m3_v3(normal_rotation_cu, rotated_relative_coord); - positions_cu[points[j]] += neighbor.weight * rotated_relative_coord; + positions_cu[points[i]] += neighbor.weight * rotated_relative_coord; } } } diff --git a/source/blender/geometry/intern/fillet_curves.cc b/source/blender/geometry/intern/fillet_curves.cc new file mode 100644 index 00000000000..2cca91f40ae --- /dev/null +++ b/source/blender/geometry/intern/fillet_curves.cc @@ -0,0 +1,561 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BKE_attribute_math.hh" +#include "BKE_curves.hh" +#include "BKE_curves_utils.hh" +#include "BKE_geometry_set.hh" + +#include "BLI_devirtualize_parameters.hh" +#include "BLI_math_geom.h" +#include "BLI_math_rotation.hh" +#include "BLI_task.hh" + +#include "GEO_fillet_curves.hh" + +namespace blender::geometry { + +/** + * Return a range used to retrieve values from an array of values stored per point, but with an + * extra element at the end of each curve. This is useful for offsets within curves, where it is + * convenient to store the first 0 and have the last offset be the total result curve size. + */ +static IndexRange curve_dst_offsets(const IndexRange points, const int curve_index) +{ + return {curve_index + points.start(), points.size() + 1}; +} + +template<typename T> +static void threaded_slice_fill(const Span<T> src, const Span<int> offsets, MutableSpan<T> dst) +{ + threading::parallel_for(src.index_range(), 512, [&](IndexRange range) { + for (const int i : range) { + dst.slice(bke::offsets_to_range(offsets, i)).fill(src[i]); + } + }); +} + +template<typename T> +static void duplicate_fillet_point_data(const bke::CurvesGeometry &src_curves, + const bke::CurvesGeometry &dst_curves, + const IndexMask curve_selection, + const Span<int> point_offsets, + const Span<T> src, + MutableSpan<T> dst) +{ + threading::parallel_for(curve_selection.index_range(), 512, [&](IndexRange range) { + for (const int curve_i : curve_selection.slice(range)) { + const IndexRange src_points = src_curves.points_for_curve(curve_i); + const IndexRange dst_points = dst_curves.points_for_curve(curve_i); + const Span<int> offsets = point_offsets.slice(curve_dst_offsets(src_points, curve_i)); + threaded_slice_fill(src.slice(src_points), offsets, dst.slice(dst_points)); + } + }); +} + +static void duplicate_fillet_point_data(const bke::CurvesGeometry &src_curves, + const bke::CurvesGeometry &dst_curves, + const IndexMask selection, + const Span<int> point_offsets, + const GSpan src, + GMutableSpan dst) +{ + attribute_math::convert_to_static_type(dst.type(), [&](auto dummy) { + using T = decltype(dummy); + duplicate_fillet_point_data( + src_curves, dst_curves, selection, point_offsets, src.typed<T>(), dst.typed<T>()); + }); +} + +static void calculate_result_offsets(const bke::CurvesGeometry &src_curves, + const IndexMask selection, + const Span<IndexRange> unselected_ranges, + const VArray<float> &radii, + const VArray<int> &counts, + const Span<bool> cyclic, + MutableSpan<int> dst_curve_offsets, + MutableSpan<int> dst_point_offsets) +{ + /* Fill the offsets array with the curve point counts, then accumulate them to form offsets. */ + bke::curves::fill_curve_counts(src_curves, unselected_ranges, dst_curve_offsets); + threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) { + for (const int curve_i : selection.slice(range)) { + const IndexRange src_points = src_curves.points_for_curve(curve_i); + const IndexRange offsets_range = curve_dst_offsets(src_points, curve_i); + + MutableSpan<int> point_offsets = dst_point_offsets.slice(offsets_range); + MutableSpan<int> point_counts = point_offsets.drop_back(1); + + counts.materialize_compressed(src_points, point_counts); + for (int &count : point_counts) { + /* Make sure the number of cuts is greater than zero and add one for the existing point. */ + count = std::max(count, 0) + 1; + } + if (!cyclic[curve_i]) { + /* Endpoints on non-cyclic curves cannot be filleted. */ + point_counts.first() = 1; + point_counts.last() = 1; + } + /* Implicitly "deselect" points with zero radius. */ + devirtualize_varray(radii, [&](const auto radii) { + for (const int i : IndexRange(src_points.size())) { + if (radii[i] == 0.0f) { + point_counts[i] = 1; + } + } + }); + + bke::curves::accumulate_counts_to_offsets(point_offsets); + + dst_curve_offsets[curve_i] = point_offsets.last(); + } + }); + bke::curves::accumulate_counts_to_offsets(dst_curve_offsets); +} + +static void calculate_directions(const Span<float3> positions, MutableSpan<float3> directions) +{ + for (const int i : positions.index_range().drop_back(1)) { + directions[i] = math::normalize(positions[i + 1] - positions[i]); + } + directions.last() = math::normalize(positions.first() - positions.last()); +} + +static void calculate_angles(const Span<float3> directions, MutableSpan<float> angles) +{ + angles.first() = M_PI - angle_v3v3(-directions.last(), directions.first()); + for (const int i : directions.index_range().drop_front(1)) { + angles[i] = M_PI - angle_v3v3(-directions[i - 1], directions[i]); + } +} + +/** + * Find the portion of the previous and next segments used by the current and next point fillets. + * If more than the total length of the segment would be used, scale the current point's radius + * just enough to make the two points meet in the middle. + */ +static float limit_radius(const float3 &position_prev, + const float3 &position, + const float3 &position_next, + const float angle_prev, + const float angle, + const float angle_next, + const float radius_prev, + const float radius, + const float radius_next) +{ + const float displacement = radius * std::tan(angle / 2.0f); + + const float displacement_prev = radius_prev * std::tan(angle_prev / 2.0f); + const float segment_length_prev = math::distance(position, position_prev); + const float total_displacement_prev = displacement_prev + displacement; + const float factor_prev = std::clamp(segment_length_prev / total_displacement_prev, 0.0f, 1.0f); + + const float displacement_next = radius_next * std::tan(angle_next / 2.0f); + const float segment_length_next = math::distance(position, position_next); + const float total_displacement_next = displacement_next + displacement; + const float factor_next = std::clamp(segment_length_next / total_displacement_next, 0.0f, 1.0f); + + return radius * std::min(factor_prev, factor_next); +} + +static void limit_radii(const Span<float3> positions, + const Span<float> angles, + const Span<float> radii, + const bool cyclic, + MutableSpan<float> radii_clamped) +{ + if (cyclic) { + /* First point. */ + radii_clamped.first() = limit_radius(positions.last(), + positions.first(), + positions[1], + angles.last(), + angles.first(), + angles[1], + radii.last(), + radii.first(), + radii[1]); + /* All middle points. */ + for (const int i : positions.index_range().drop_back(1).drop_front(1)) { + const int i_prev = i - 1; + const int i_next = i + 1; + radii_clamped[i] = limit_radius(positions[i_prev], + positions[i], + positions[i_next], + angles[i_prev], + angles[i], + angles[i_next], + radii[i_prev], + radii[i], + radii[i_next]); + } + /* Last point. */ + radii_clamped.last() = limit_radius(positions.last(1), + positions.last(), + positions.first(), + angles.last(1), + angles.last(), + angles.first(), + radii.last(1), + radii.last(), + radii.first()); + } + else { + const int i_last = positions.index_range().last(); + /* First point. */ + radii_clamped.first() = 0.0f; + /* All middle points. */ + for (const int i : positions.index_range().drop_back(1).drop_front(1)) { + const int i_prev = i - 1; + const int i_next = i + 1; + /* Use a zero radius for the first and last points, because they don't have fillets. + * This logic could potentially be unrolled, but it doesn't seem worth it. */ + const float radius_prev = i_prev == 0 ? 0.0f : radii[i_prev]; + const float radius_next = i_next == i_last ? 0.0f : radii[i_next]; + radii_clamped[i] = limit_radius(positions[i_prev], + positions[i], + positions[i_next], + angles[i_prev], + angles[i], + angles[i_next], + radius_prev, + radii[i], + radius_next); + } + /* Last point. */ + radii_clamped.last() = 0.0f; + } +} + +static void calculate_fillet_positions(const Span<float3> src_positions, + const Span<float> angles, + const Span<float> radii, + const Span<float3> directions, + const Span<int> dst_offsets, + MutableSpan<float3> dst) +{ + const int i_src_last = src_positions.index_range().last(); + threading::parallel_for(src_positions.index_range(), 512, [&](IndexRange range) { + for (const int i_src : range) { + const IndexRange arc = bke::offsets_to_range(dst_offsets, i_src); + const float3 &src = src_positions[i_src]; + if (arc.size() == 1) { + dst[arc.first()] = src; + continue; + } + + const int i_src_prev = i_src == 0 ? i_src_last : i_src - 1; + const float angle = angles[i_src]; + const float radius = radii[i_src]; + const float displacement = radius * std::tan(angle / 2.0f); + const float3 prev_dir = -directions[i_src_prev]; + const float3 &next_dir = directions[i_src]; + const float3 arc_start = src + prev_dir * displacement; + const float3 arc_end = src + next_dir * displacement; + + dst[arc.first()] = arc_start; + dst[arc.last()] = arc_end; + + const IndexRange middle = arc.drop_front(1).drop_back(1); + if (middle.is_empty()) { + continue; + } + + const float3 axis = -math::normalize(math::cross(prev_dir, next_dir)); + const float3 center_direction = math::normalize(math::midpoint(next_dir, prev_dir)); + const float distance_to_center = std::sqrt(pow2f(radius) + pow2f(displacement)); + const float3 center = src + center_direction * distance_to_center; + + /* Rotate each middle fillet point around the center. */ + const float segment_angle = angle / (middle.size() + 1); + for (const int i : IndexRange(middle.size())) { + const int point_i = middle[i]; + dst[point_i] = math::rotate_around_axis(arc_start, center, axis, segment_angle * (i + 1)); + } + } + }); +} + +/** + * Set handles for the "Bezier" mode where we rely on setting the inner handles to approximate a + * circular arc. The outer (previous and next) handles outside the result fillet segment are set + * to vector handles. + */ +static void calculate_bezier_handles_bezier_mode(const Span<float3> src_handles_l, + const Span<float3> src_handles_r, + const Span<int8_t> src_types_l, + const Span<int8_t> src_types_r, + const Span<float> angles, + const Span<float> radii, + const Span<float3> directions, + const Span<int> dst_offsets, + const Span<float3> dst_positions, + MutableSpan<float3> dst_handles_l, + MutableSpan<float3> dst_handles_r, + MutableSpan<int8_t> dst_types_l, + MutableSpan<int8_t> dst_types_r) +{ + const int i_src_last = src_handles_l.index_range().last(); + const int i_dst_last = dst_positions.index_range().last(); + threading::parallel_for(src_handles_l.index_range(), 512, [&](IndexRange range) { + for (const int i_src : range) { + const IndexRange arc = bke::offsets_to_range(dst_offsets, i_src); + if (arc.size() == 1) { + dst_handles_l[arc.first()] = src_handles_l[i_src]; + dst_handles_r[arc.first()] = src_handles_r[i_src]; + dst_types_l[arc.first()] = src_types_l[i_src]; + dst_types_r[arc.first()] = src_types_r[i_src]; + continue; + } + BLI_assert(arc.size() == 2); + const int i_dst_a = arc.first(); + const int i_dst_b = arc.last(); + + const int i_src_prev = i_src == 0 ? i_src_last : i_src - 1; + const float angle = angles[i_src]; + const float radius = radii[i_src]; + const float3 prev_dir = -directions[i_src_prev]; + const float3 &next_dir = directions[i_src]; + + const float3 &arc_start = dst_positions[arc.first()]; + const float3 &arc_end = dst_positions[arc.last()]; + + /* Calculate the point's handles on the outside of the fillet segment, + * connecting to the next or previous result points. */ + const int i_dst_prev = i_dst_a == 0 ? i_dst_last : i_dst_a - 1; + const int i_dst_next = i_dst_b == i_dst_last ? 0 : i_dst_b + 1; + dst_handles_l[i_dst_a] = bke::curves::bezier::calculate_vector_handle( + dst_positions[i_dst_a], dst_positions[i_dst_prev]); + dst_handles_r[i_dst_b] = bke::curves::bezier::calculate_vector_handle( + dst_positions[i_dst_b], dst_positions[i_dst_next]); + dst_types_l[i_dst_a] = BEZIER_HANDLE_VECTOR; + dst_types_r[i_dst_b] = BEZIER_HANDLE_VECTOR; + + /* The inner handles are aligned with the aligned with the outer vector + * handles, but have a specific length to best approximate a circle. */ + const float handle_length = (4.0f / 3.0f) * radius * std::tan(angle / 4.0f); + dst_handles_r[i_dst_a] = arc_start - prev_dir * handle_length; + dst_handles_l[i_dst_b] = arc_end - next_dir * handle_length; + dst_types_r[i_dst_a] = BEZIER_HANDLE_ALIGN; + dst_types_l[i_dst_b] = BEZIER_HANDLE_ALIGN; + } + }); +} + +/** + * In the poly fillet mode, all the inner handles are set to vector handles, along with the "outer" + * (previous and next) handles at each fillet. + */ +static void calculate_bezier_handles_poly_mode(const Span<float3> src_handles_l, + const Span<float3> src_handles_r, + const Span<int8_t> src_types_l, + const Span<int8_t> src_types_r, + const Span<int> dst_offsets, + const Span<float3> dst_positions, + MutableSpan<float3> dst_handles_l, + MutableSpan<float3> dst_handles_r, + MutableSpan<int8_t> dst_types_l, + MutableSpan<int8_t> dst_types_r) +{ + const int i_dst_last = dst_positions.index_range().last(); + threading::parallel_for(src_handles_l.index_range(), 512, [&](IndexRange range) { + for (const int i_src : range) { + const IndexRange arc = bke::offsets_to_range(dst_offsets, i_src); + if (arc.size() == 1) { + dst_handles_l[arc.first()] = src_handles_l[i_src]; + dst_handles_r[arc.first()] = src_handles_r[i_src]; + dst_types_l[arc.first()] = src_types_l[i_src]; + dst_types_r[arc.first()] = src_types_r[i_src]; + continue; + } + + /* The fillet's next and previous handles are vector handles, as are the inner handles. */ + dst_types_l.slice(arc).fill(BEZIER_HANDLE_VECTOR); + dst_types_r.slice(arc).fill(BEZIER_HANDLE_VECTOR); + + /* Calculate the point's handles on the outside of the fillet segment. This point + * won't be selected for a fillet if it is the first or last in a non-cyclic curve. */ + + const int i_dst_prev = arc.first() == 0 ? i_dst_last : arc.one_before_start(); + const int i_dst_next = arc.last() == i_dst_last ? 0 : arc.one_after_last(); + dst_handles_l[arc.first()] = bke::curves::bezier::calculate_vector_handle( + dst_positions[arc.first()], dst_positions[i_dst_prev]); + dst_handles_r[arc.last()] = bke::curves::bezier::calculate_vector_handle( + dst_positions[arc.last()], dst_positions[i_dst_next]); + + /* Set the values for the inner handles. */ + const IndexRange middle = arc.drop_front(1).drop_back(1); + for (const int i : middle) { + dst_handles_r[i] = bke::curves::bezier::calculate_vector_handle(dst_positions[i], + dst_positions[i - 1]); + dst_handles_l[i] = bke::curves::bezier::calculate_vector_handle(dst_positions[i], + dst_positions[i + 1]); + } + } + }); +} + +static bke::CurvesGeometry fillet_curves(const bke::CurvesGeometry &src_curves, + const IndexMask curve_selection, + const VArray<float> &radius_input, + const VArray<int> &counts, + const bool limit_radius, + const bool use_bezier_mode) +{ + const Vector<IndexRange> unselected_ranges = curve_selection.extract_ranges_invert( + src_curves.curves_range()); + + const Span<float3> positions = src_curves.positions(); + const VArraySpan<bool> cyclic{src_curves.cyclic()}; + const bke::AttributeAccessor src_attributes = src_curves.attributes(); + + bke::CurvesGeometry dst_curves = bke::curves::copy_only_curve_domain(src_curves); + /* Stores the offset of every result point for every original point. + * The extra length is used in order to store an extra zero for every curve. */ + Array<int> dst_point_offsets(src_curves.points_num() + src_curves.curves_num()); + calculate_result_offsets(src_curves, + curve_selection, + unselected_ranges, + radius_input, + counts, + cyclic, + dst_curves.offsets_for_write(), + dst_point_offsets); + const Span<int> point_offsets = dst_point_offsets.as_span(); + + dst_curves.resize(dst_curves.offsets().last(), dst_curves.curves_num()); + bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write(); + MutableSpan<float3> dst_positions = dst_curves.positions_for_write(); + + VArraySpan<int8_t> src_types_l; + VArraySpan<int8_t> src_types_r; + Span<float3> src_handles_l; + Span<float3> src_handles_r; + MutableSpan<int8_t> dst_types_l; + MutableSpan<int8_t> dst_types_r; + MutableSpan<float3> dst_handles_l; + MutableSpan<float3> dst_handles_r; + if (src_curves.has_curve_with_type(CURVE_TYPE_BEZIER)) { + src_types_l = src_curves.handle_types_left(); + src_types_r = src_curves.handle_types_right(); + src_handles_l = src_curves.handle_positions_left(); + src_handles_r = src_curves.handle_positions_right(); + + dst_types_l = dst_curves.handle_types_left_for_write(); + dst_types_r = dst_curves.handle_types_right_for_write(); + dst_handles_l = dst_curves.handle_positions_left_for_write(); + dst_handles_r = dst_curves.handle_positions_right_for_write(); + } + + threading::parallel_for(curve_selection.index_range(), 512, [&](IndexRange range) { + Array<float3> directions; + Array<float> angles; + Array<float> radii; + Array<float> input_radii_buffer; + + for (const int curve_i : curve_selection.slice(range)) { + const IndexRange src_points = src_curves.points_for_curve(curve_i); + const Span<int> offsets = point_offsets.slice(curve_dst_offsets(src_points, curve_i)); + const IndexRange dst_points = dst_curves.points_for_curve(curve_i); + const Span<float3> src_positions = positions.slice(src_points); + + directions.reinitialize(src_points.size()); + calculate_directions(src_positions, directions); + + angles.reinitialize(src_points.size()); + calculate_angles(directions, angles); + + radii.reinitialize(src_points.size()); + if (limit_radius) { + input_radii_buffer.reinitialize(src_points.size()); + radius_input.materialize_compressed(src_points, input_radii_buffer); + limit_radii(src_positions, angles, input_radii_buffer, cyclic[curve_i], radii); + } + else { + radius_input.materialize_compressed(src_points, radii); + } + + calculate_fillet_positions(positions.slice(src_points), + angles, + radii, + directions, + offsets, + dst_positions.slice(dst_points)); + + if (src_curves.has_curve_with_type(CURVE_TYPE_BEZIER)) { + if (use_bezier_mode) { + calculate_bezier_handles_bezier_mode(src_handles_l.slice(src_points), + src_handles_r.slice(src_points), + src_types_l.slice(src_points), + src_types_r.slice(src_points), + angles, + radii, + directions, + offsets, + dst_positions.slice(dst_points), + dst_handles_l.slice(dst_points), + dst_handles_r.slice(dst_points), + dst_types_l.slice(dst_points), + dst_types_r.slice(dst_points)); + } + else { + calculate_bezier_handles_poly_mode(src_handles_l.slice(src_points), + src_handles_r.slice(src_points), + src_types_l.slice(src_points), + src_types_r.slice(src_points), + offsets, + dst_positions.slice(dst_points), + dst_handles_l.slice(dst_points), + dst_handles_r.slice(dst_points), + dst_types_l.slice(dst_points), + dst_types_r.slice(dst_points)); + } + } + } + }); + + for (auto &attribute : bke::retrieve_attributes_for_transfer( + src_attributes, + dst_attributes, + ATTR_DOMAIN_MASK_POINT, + {"position", "handle_type_left", "handle_type_right", "handle_right", "handle_left"})) { + duplicate_fillet_point_data( + src_curves, dst_curves, curve_selection, point_offsets, attribute.src, attribute.dst.span); + attribute.dst.finish(); + } + + if (!unselected_ranges.is_empty()) { + for (auto &attribute : bke::retrieve_attributes_for_transfer( + src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT)) { + bke::curves::copy_point_data( + src_curves, dst_curves, unselected_ranges, attribute.src, attribute.dst.span); + attribute.dst.finish(); + } + } + + return dst_curves; +} + +bke::CurvesGeometry fillet_curves_poly(const bke::CurvesGeometry &src_curves, + const IndexMask curve_selection, + const VArray<float> &radius, + const VArray<int> &count, + const bool limit_radius) +{ + return fillet_curves(src_curves, curve_selection, radius, count, limit_radius, false); +} + +bke::CurvesGeometry fillet_curves_bezier(const bke::CurvesGeometry &src_curves, + const IndexMask curve_selection, + const VArray<float> &radius, + const bool limit_radius) +{ + return fillet_curves(src_curves, + curve_selection, + radius, + VArray<int>::ForSingle(1, src_curves.points_num()), + limit_radius, + true); +} + +} // namespace blender::geometry diff --git a/source/blender/geometry/intern/mesh_to_curve_convert.cc b/source/blender/geometry/intern/mesh_to_curve_convert.cc index 681dfd15137..fdacb174462 100644 --- a/source/blender/geometry/intern/mesh_to_curve_convert.cc +++ b/source/blender/geometry/intern/mesh_to_curve_convert.cc @@ -30,13 +30,12 @@ static void copy_with_map(const VArray<T> &src, Span<int> map, MutableSpan<T> ds }); } -static Curves *create_curve_from_vert_indices(const MeshComponent &mesh_component, - const Span<int> vert_indices, - const Span<int> curve_offsets, - const IndexRange cyclic_curves) +static bke::CurvesGeometry create_curve_from_vert_indices(const Mesh &mesh, + const Span<int> vert_indices, + const Span<int> curve_offsets, + const IndexRange cyclic_curves) { - Curves *curves_id = bke::curves_new_nomain(vert_indices.size(), curve_offsets.size()); - bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry); + bke::CurvesGeometry curves(vert_indices.size(), curve_offsets.size()); curves.offsets_for_write().drop_back(1).copy_from(curve_offsets); curves.offsets_for_write().last() = vert_indices.size(); curves.fill_curve_types(CURVE_TYPE_POLY); @@ -44,8 +43,8 @@ static Curves *create_curve_from_vert_indices(const MeshComponent &mesh_componen curves.cyclic_for_write().fill(false); curves.cyclic_for_write().slice(cyclic_curves).fill(true); + const bke::AttributeAccessor mesh_attributes = bke::mesh_attributes(mesh); bke::MutableAttributeAccessor curves_attributes = curves.attributes_for_write(); - const bke::AttributeAccessor mesh_attributes = *mesh_component.attributes(); Set<bke::AttributeIDRef> source_attribute_ids = mesh_attributes.all_ids(); @@ -76,7 +75,7 @@ static Curves *create_curve_from_vert_indices(const MeshComponent &mesh_componen }); } - return curves_id; + return curves; } struct CurveFromEdgesOutput { @@ -220,16 +219,14 @@ static Vector<std::pair<int, int>> get_selected_edges(const Mesh &mesh, const In return selected_edges; } -Curves *mesh_to_curve_convert(const MeshComponent &mesh_component, const IndexMask selection) +bke::CurvesGeometry mesh_to_curve_convert(const Mesh &mesh, const IndexMask selection) { - const Mesh &mesh = *mesh_component.get_for_read(); - Vector<std::pair<int, int>> selected_edges = get_selected_edges(*mesh_component.get_for_read(), - selection); + Vector<std::pair<int, int>> selected_edges = get_selected_edges(mesh, selection); CurveFromEdgesOutput output = edges_to_curve_point_indices({mesh.mvert, mesh.totvert}, selected_edges); return create_curve_from_vert_indices( - mesh_component, output.vert_indices, output.curve_offsets, output.cyclic_curves); + mesh, output.vert_indices, output.curve_offsets, output.cyclic_curves); } } // namespace blender::geometry diff --git a/source/blender/geometry/intern/point_merge_by_distance.cc b/source/blender/geometry/intern/point_merge_by_distance.cc index 40bc02cbd34..ac10f4ef00c 100644 --- a/source/blender/geometry/intern/point_merge_by_distance.cc +++ b/source/blender/geometry/intern/point_merge_by_distance.cc @@ -18,8 +18,10 @@ PointCloud *point_merge_by_distance(const PointCloudComponent &src_points, const IndexMask selection) { const PointCloud &src_pointcloud = *src_points.get_for_read(); - const int src_size = src_pointcloud.totpoint; - Span<float3> positions{reinterpret_cast<float3 *>(src_pointcloud.co), src_size}; + bke::AttributeAccessor attributes = bke::pointcloud_attributes(src_pointcloud); + VArraySpan<float3> positions = attributes.lookup_or_default<float3>( + "position", ATTR_DOMAIN_POINT, float3(0)); + const int src_size = positions.size(); /* Create the KD tree based on only the selected points, to speed up merge detection and * balancing. */ @@ -106,10 +108,10 @@ PointCloud *point_merge_by_distance(const PointCloudComponent &src_points, const bke::AttributeAccessor src_attributes = *src_points.attributes(); bke::MutableAttributeAccessor dst_attributes = *dst_points.attributes_for_write(); - Set<bke::AttributeIDRef> attributes = src_attributes.all_ids(); + Set<bke::AttributeIDRef> attribute_ids = src_attributes.all_ids(); /* Transfer the ID attribute if it exists, using the ID of the first merged point. */ - if (attributes.contains("id")) { + if (attribute_ids.contains("id")) { VArraySpan<int> src = src_attributes.lookup_or_default<int>("id", ATTR_DOMAIN_POINT, 0); bke::SpanAttributeWriter<int> dst = dst_attributes.lookup_or_add_for_write_only_span<int>( "id", ATTR_DOMAIN_POINT); @@ -122,11 +124,11 @@ PointCloud *point_merge_by_distance(const PointCloudComponent &src_points, }); dst.finish(); - attributes.remove_contained("id"); + attribute_ids.remove_contained("id"); } /* Transfer all other attributes. */ - for (const bke::AttributeIDRef &id : attributes) { + for (const bke::AttributeIDRef &id : attribute_ids) { if (!id.should_be_kept()) { continue; } diff --git a/source/blender/geometry/intern/realize_instances.cc b/source/blender/geometry/intern/realize_instances.cc index 25bf00c5783..0544f304283 100644 --- a/source/blender/geometry/intern/realize_instances.cc +++ b/source/blender/geometry/intern/realize_instances.cc @@ -69,6 +69,7 @@ struct PointCloudRealizeInfo { /** Matches the order stored in #AllPointCloudsInfo.attributes. */ Array<std::optional<GVArraySpan>> attributes; /** Id attribute on the point cloud. If there are no ids, this #Span is empty. */ + Span<float3> positions; Span<int> stored_ids; }; @@ -665,6 +666,9 @@ static AllPointCloudsInfo preprocess_pointclouds(const GeometrySet &geometry_set pointcloud_info.stored_ids = ids_attribute.varray.get_internal_span().typed<int>(); } } + const VArray<float3> position_attribute = attributes.lookup_or_default<float3>( + "position", ATTR_DOMAIN_POINT, float3(0)); + pointcloud_info.positions = position_attribute.get_internal_span(); } return info; } @@ -673,18 +677,16 @@ static void execute_realize_pointcloud_task( const RealizeInstancesOptions &options, const RealizePointCloudTask &task, const OrderedAttributes &ordered_attributes, - PointCloud &dst_pointcloud, MutableSpan<GSpanAttributeWriter> dst_attribute_writers, - MutableSpan<int> all_dst_ids) + MutableSpan<int> all_dst_ids, + MutableSpan<float3> all_dst_positions) { const PointCloudRealizeInfo &pointcloud_info = *task.pointcloud_info; const PointCloud &pointcloud = *pointcloud_info.pointcloud; - const Span<float3> src_positions{(float3 *)pointcloud.co, pointcloud.totpoint}; const IndexRange point_slice{task.start_index, pointcloud.totpoint}; - MutableSpan<float3> dst_positions{(float3 *)dst_pointcloud.co + task.start_index, - pointcloud.totpoint}; - copy_transformed_positions(src_positions, task.transform, dst_positions); + copy_transformed_positions( + pointcloud_info.positions, task.transform, all_dst_positions.slice(point_slice)); /* Create point ids. */ if (!all_dst_ids.is_empty()) { @@ -726,6 +728,9 @@ static void execute_realize_pointcloud_tasks(const RealizeInstancesOptions &opti bke::MutableAttributeAccessor dst_attributes = bke::pointcloud_attributes_for_write( *dst_pointcloud); + SpanAttributeWriter<float3> positions = dst_attributes.lookup_or_add_for_write_only_span<float3>( + "position", ATTR_DOMAIN_POINT); + /* Prepare id attribute. */ SpanAttributeWriter<int> point_ids; if (all_pointclouds_info.create_id_attribute) { @@ -748,9 +753,9 @@ static void execute_realize_pointcloud_tasks(const RealizeInstancesOptions &opti execute_realize_pointcloud_task(options, task, ordered_attributes, - *dst_pointcloud, dst_attribute_writers, - point_ids.span); + point_ids.span, + positions.span); } }); @@ -758,6 +763,7 @@ static void execute_realize_pointcloud_tasks(const RealizeInstancesOptions &opti for (GSpanAttributeWriter &dst_attribute : dst_attribute_writers) { dst_attribute.finish(); } + positions.finish(); if (point_ids) { point_ids.finish(); } @@ -1238,6 +1244,11 @@ static void execute_realize_curve_tasks(const RealizeInstancesOptions &options, dst_component.replace(dst_curves_id); bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write(); + /* Copy settings from the first input geometry set with curves. */ + const RealizeCurveTask &first_task = tasks.first(); + const Curves &first_curves_id = *first_task.curve_info->curves; + bke::curves_copy_parameters(first_curves_id, *dst_curves_id); + /* Prepare id attribute. */ SpanAttributeWriter<int> point_ids; if (all_curves_info.create_id_attribute) { @@ -1295,6 +1306,15 @@ static void execute_realize_curve_tasks(const RealizeInstancesOptions &options, } }); + /* Type counts have to be updated eagerly. */ + dst_curves.runtime->type_counts.fill(0); + for (const RealizeCurveTask &task : tasks) { + for (const int i : IndexRange(CURVE_TYPES_NUM)) { + dst_curves.runtime->type_counts[i] += + task.curve_info->curves->geometry.runtime->type_counts[i]; + } + } + /* Tag modified attributes. */ for (GSpanAttributeWriter &dst_attribute : dst_attribute_writers) { dst_attribute.finish(); diff --git a/source/blender/geometry/intern/resample_curves.cc b/source/blender/geometry/intern/resample_curves.cc index c9b8a032ce6..2c3a6c6e0cf 100644 --- a/source/blender/geometry/intern/resample_curves.cc +++ b/source/blender/geometry/intern/resample_curves.cc @@ -162,8 +162,6 @@ static void gather_point_attributes_to_interpolate(const CurveComponent &src_com result.src_no_interpolation, result.dst_no_interpolation, result.dst_attributes); - - dst_curves.update_customdata_pointers(); } static Curves *resample_to_uniform(const CurveComponent &src_component, diff --git a/source/blender/geometry/intern/set_curve_type.cc b/source/blender/geometry/intern/set_curve_type.cc index 08888a74973..40ee2488a4b 100644 --- a/source/blender/geometry/intern/set_curve_type.cc +++ b/source/blender/geometry/intern/set_curve_type.cc @@ -1,9 +1,9 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BKE_attribute.hh" #include "BKE_attribute_math.hh" #include "BKE_curves.hh" #include "BKE_curves_utils.hh" -#include "BKE_geometry_set.hh" #include "BLI_task.hh" @@ -322,39 +322,16 @@ static void retrieve_generic_point_attributes(const bke::AttributeAccessor &src_ }); } -static Curves *create_result_curves(const bke::CurvesGeometry &src_curves, - const IndexMask selection, - const CurveType dst_type) -{ - Curves *dst_curves_id = bke::curves_new_nomain(0, src_curves.curves_num()); - bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id->geometry); - CurveComponent dst_component; - dst_component.replace(dst_curves_id, GeometryOwnershipType::Editable); - /* Directly copy curve attributes, since they stay the same (except for curve types). */ - CustomData_copy(&src_curves.curve_data, - &dst_curves.curve_data, - CD_MASK_ALL, - CD_DUPLICATE, - src_curves.curves_num()); - - dst_curves.fill_curve_types(selection, dst_type); - - return dst_curves_id; -} - -static Curves *convert_curves_to_bezier(const CurveComponent &src_component, - const bke::CurvesGeometry &src_curves, - const IndexMask selection) +static bke::CurvesGeometry convert_curves_to_bezier(const bke::CurvesGeometry &src_curves, + const IndexMask selection) { const VArray<int8_t> src_knot_modes = src_curves.nurbs_knots_modes(); const VArray<int8_t> src_types = src_curves.curve_types(); const VArray<bool> src_cyclic = src_curves.cyclic(); const Span<float3> src_positions = src_curves.positions(); - Curves *dst_curves_id = create_result_curves(src_curves, selection, CURVE_TYPE_BEZIER); - bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id->geometry); - CurveComponent dst_component; - dst_component.replace(dst_curves_id, GeometryOwnershipType::Editable); + bke::CurvesGeometry dst_curves = bke::curves::copy_only_curve_domain(src_curves); + dst_curves.fill_curve_types(selection, CURVE_TYPE_BEZIER); MutableSpan<int> dst_offsets = dst_curves.offsets_for_write(); retrieve_curve_sizes(src_curves, dst_curves.offsets_for_write()); @@ -367,8 +344,8 @@ static Curves *convert_curves_to_bezier(const CurveComponent &src_component, bke::curves::accumulate_counts_to_offsets(dst_offsets); dst_curves.resize(dst_offsets.last(), dst_curves.curves_num()); - const bke::AttributeAccessor src_attributes = *src_component.attributes(); - bke::MutableAttributeAccessor dst_attributes = *dst_component.attributes_for_write(); + const bke::AttributeAccessor src_attributes = src_curves.attributes(); + bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write(); GenericAttributes attributes; retrieve_generic_point_attributes(src_attributes, dst_attributes, attributes); @@ -501,21 +478,18 @@ static Curves *convert_curves_to_bezier(const CurveComponent &src_component, attribute.finish(); } - return dst_curves_id; + return dst_curves; } -static Curves *convert_curves_to_nurbs(const CurveComponent &src_component, - const bke::CurvesGeometry &src_curves, - const IndexMask selection) +static bke::CurvesGeometry convert_curves_to_nurbs(const bke::CurvesGeometry &src_curves, + const IndexMask selection) { const VArray<int8_t> src_types = src_curves.curve_types(); const VArray<bool> src_cyclic = src_curves.cyclic(); const Span<float3> src_positions = src_curves.positions(); - Curves *dst_curves_id = create_result_curves(src_curves, selection, CURVE_TYPE_NURBS); - bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id->geometry); - CurveComponent dst_component; - dst_component.replace(dst_curves_id, GeometryOwnershipType::Editable); + bke::CurvesGeometry dst_curves = bke::curves::copy_only_curve_domain(src_curves); + dst_curves.fill_curve_types(selection, CURVE_TYPE_NURBS); MutableSpan<int> dst_offsets = dst_curves.offsets_for_write(); retrieve_curve_sizes(src_curves, dst_curves.offsets_for_write()); @@ -527,8 +501,8 @@ static Curves *convert_curves_to_nurbs(const CurveComponent &src_component, bke::curves::accumulate_counts_to_offsets(dst_offsets); dst_curves.resize(dst_offsets.last(), dst_curves.curves_num()); - const bke::AttributeAccessor src_attributes = *src_component.attributes(); - bke::MutableAttributeAccessor dst_attributes = *dst_component.attributes_for_write(); + const bke::AttributeAccessor src_attributes = src_curves.attributes(); + bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write(); GenericAttributes attributes; retrieve_generic_point_attributes(src_attributes, dst_attributes, attributes); @@ -669,7 +643,7 @@ static Curves *convert_curves_to_nurbs(const CurveComponent &src_component, attribute.finish(); } - return dst_curves_id; + return dst_curves; } static bke::CurvesGeometry convert_curves_trivial(const bke::CurvesGeometry &src_curves, @@ -682,33 +656,31 @@ static bke::CurvesGeometry convert_curves_trivial(const bke::CurvesGeometry &src return dst_curves; } -Curves *convert_curves(const CurveComponent &src_component, - const bke::CurvesGeometry &src_curves, - const IndexMask selection, - const CurveType dst_type) +bke::CurvesGeometry convert_curves(const bke::CurvesGeometry &src_curves, + const IndexMask selection, + const CurveType dst_type) { switch (dst_type) { case CURVE_TYPE_CATMULL_ROM: case CURVE_TYPE_POLY: - return bke::curves_new_nomain(convert_curves_trivial(src_curves, selection, dst_type)); + return convert_curves_trivial(src_curves, selection, dst_type); case CURVE_TYPE_BEZIER: - return convert_curves_to_bezier(src_component, src_curves, selection); + return convert_curves_to_bezier(src_curves, selection); case CURVE_TYPE_NURBS: - return convert_curves_to_nurbs(src_component, src_curves, selection); + return convert_curves_to_nurbs(src_curves, selection); } BLI_assert_unreachable(); - return nullptr; + return {}; } bool try_curves_conversion_in_place(const IndexMask selection, const CurveType dst_type, - FunctionRef<Curves &()> get_writable_curves_fn) + FunctionRef<bke::CurvesGeometry &()> get_writable_curves_fn) { if (conversion_can_change_point_num(dst_type)) { return false; } - Curves &curves_id = get_writable_curves_fn(); - bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); + bke::CurvesGeometry &curves = get_writable_curves_fn(); curves.fill_curve_types(selection, dst_type); curves.remove_attributes_based_on_types(); return true; diff --git a/source/blender/geometry/intern/subdivide_curves.cc b/source/blender/geometry/intern/subdivide_curves.cc index 914174235cd..8a6f3cbd5e3 100644 --- a/source/blender/geometry/intern/subdivide_curves.cc +++ b/source/blender/geometry/intern/subdivide_curves.cc @@ -12,26 +12,6 @@ namespace blender::geometry { /** - * \warning Only the curve domain of the input is copied, so the result is invalid! - */ -static Curves *create_result_curves(const bke::CurvesGeometry &src_curves) -{ - Curves *dst_curves_id = bke::curves_new_nomain(0, src_curves.curves_num()); - bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id->geometry); - CurveComponent dst_component; - dst_component.replace(dst_curves_id, GeometryOwnershipType::Editable); - /* Directly copy curve attributes, since they stay the same. */ - CustomData_copy(&src_curves.curve_data, - &dst_curves.curve_data, - CD_MASK_ALL, - CD_DUPLICATE, - src_curves.curves_num()); - dst_curves.runtime->type_counts = src_curves.runtime->type_counts; - - return dst_curves_id; -} - -/** * Return a range used to retrieve values from an array of values stored per point, but with an * extra element at the end of each curve. This is useful for offsets within curves, where it is * convenient to store the first 0 and have the last offset be the total result curve size. @@ -76,40 +56,6 @@ static void calculate_result_offsets(const bke::CurvesGeometry &src_curves, bke::curves::accumulate_counts_to_offsets(dst_curve_offsets); } -struct AttributeTransferData { - /* Expect that if an attribute exists, it is stored as a contiguous array internally anyway. */ - GVArraySpan src; - bke::GSpanAttributeWriter dst; -}; - -static Vector<AttributeTransferData> retrieve_point_attributes( - const bke::AttributeAccessor &src_attributes, - bke::MutableAttributeAccessor &dst_attributes, - const Set<std::string> &skip = {}) -{ - Vector<AttributeTransferData> attributes; - src_attributes.for_all( - [&](const bke::AttributeIDRef &id, const bke::AttributeMetaData meta_data) { - if (meta_data.domain != ATTR_DOMAIN_POINT) { - /* Curve domain attributes are all copied directly to the result in one step. */ - return true; - } - if (id.is_named() && skip.contains(id.name())) { - return true; - } - - GVArray src = src_attributes.lookup(id, ATTR_DOMAIN_POINT); - BLI_assert(src); - bke::GSpanAttributeWriter dst = dst_attributes.lookup_or_add_for_write_only_span( - id, ATTR_DOMAIN_POINT, meta_data.data_type); - BLI_assert(dst); - attributes.append({std::move(src), std::move(dst)}); - - return true; - }); - return attributes; -} - template<typename T> static inline void linear_interpolation(const T &a, const T &b, MutableSpan<T> dst) { @@ -342,10 +288,9 @@ static void subdivide_bezier_positions(const Span<float3> src_positions, cyclic, dst_types_l, dst_types_r, dst_positions, dst_handles_l, dst_handles_r); } -Curves *subdivide_curves(const CurveComponent &src_component, - const bke::CurvesGeometry &src_curves, - const IndexMask selection, - const VArray<int> &cuts) +bke::CurvesGeometry subdivide_curves(const bke::CurvesGeometry &src_curves, + const IndexMask selection, + const VArray<int> &cuts) { const Vector<IndexRange> unselected_ranges = selection.extract_ranges_invert( src_curves.curves_range()); @@ -353,10 +298,7 @@ Curves *subdivide_curves(const CurveComponent &src_component, /* Cyclic is accessed a lot, it's probably worth it to make sure it's a span. */ const VArraySpan<bool> cyclic{src_curves.cyclic()}; - Curves *dst_curves_id = create_result_curves(src_curves); - bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id->geometry); - CurveComponent dst_component; - dst_component.replace(dst_curves_id, GeometryOwnershipType::Editable); + bke::CurvesGeometry dst_curves = bke::curves::copy_only_curve_domain(src_curves); /* For each point, this contains the point offset in the corresponding result curve, * starting at zero. For example for two curves with four points each, the values might @@ -385,11 +327,12 @@ Curves *subdivide_curves(const CurveComponent &src_component, dst_curves.resize(dst_curves.offsets().last(), dst_curves.curves_num()); - const bke::AttributeAccessor src_attributes = *src_component.attributes(); - bke::MutableAttributeAccessor dst_attributes = *dst_component.attributes_for_write(); + const bke::AttributeAccessor src_attributes = src_curves.attributes(); + bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write(); auto subdivide_catmull_rom = [&](IndexMask selection) { - for (auto &attribute : retrieve_point_attributes(src_attributes, dst_attributes)) { + for (auto &attribute : bke::retrieve_attributes_for_transfer( + src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT)) { subdivide_attribute_catmull_rom(src_curves, dst_curves, selection, @@ -402,7 +345,8 @@ Curves *subdivide_curves(const CurveComponent &src_component, }; auto subdivide_poly = [&](IndexMask selection) { - for (auto &attribute : retrieve_point_attributes(src_attributes, dst_attributes)) { + for (auto &attribute : bke::retrieve_attributes_for_transfer( + src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT)) { subdivide_attribute_linear( src_curves, dst_curves, selection, point_offsets, attribute.src, attribute.dst.span); attribute.dst.finish(); @@ -443,13 +387,14 @@ Curves *subdivide_curves(const CurveComponent &src_component, } }); - for (auto &attribute : retrieve_point_attributes(src_attributes, - dst_attributes, - {"position", - "handle_type_left", - "handle_type_right", - "handle_right", - "handle_left"})) { + for (auto &attribute : bke::retrieve_attributes_for_transfer(src_attributes, + dst_attributes, + ATTR_DOMAIN_MASK_POINT, + {"position", + "handle_type_left", + "handle_type_right", + "handle_right", + "handle_left"})) { subdivide_attribute_linear( src_curves, dst_curves, selection, point_offsets, attribute.src, attribute.dst.span); attribute.dst.finish(); @@ -469,14 +414,15 @@ Curves *subdivide_curves(const CurveComponent &src_component, subdivide_nurbs); if (!unselected_ranges.is_empty()) { - for (auto &attribute : retrieve_point_attributes(src_attributes, dst_attributes)) { + for (auto &attribute : bke::retrieve_attributes_for_transfer( + src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT)) { bke::curves::copy_point_data( src_curves, dst_curves, unselected_ranges, attribute.src, attribute.dst.span); attribute.dst.finish(); } } - return dst_curves_id; + return dst_curves; } } // namespace blender::geometry diff --git a/source/blender/geometry/intern/uv_parametrizer.c b/source/blender/geometry/intern/uv_parametrizer.c index c75a302f8dc..38924c718c3 100644 --- a/source/blender/geometry/intern/uv_parametrizer.c +++ b/source/blender/geometry/intern/uv_parametrizer.c @@ -148,7 +148,8 @@ typedef struct PChart { } lscm; struct PChartPack { float rescale, area; - float size[2] /* , trans[2] */; + float size[2]; + float origin[2]; } pack; } u; @@ -3306,8 +3307,10 @@ static float p_face_stretch(PFace *f) area = p_face_uv_area_signed(f); - if (area <= 0.0f) { /* flipped face -> infinite stretch */ - return 1e10f; + if (area <= 0.0f) { + /* When a face is flipped, provide a large penalty. + * Add on a slight gradient to unflip the face, see also: T99781. */ + return 1e8f * (1.0f + p_edge_uv_length(e1) + p_edge_uv_length(e2) + p_edge_uv_length(e3)); } w = 1.0f / (2.0f * area); @@ -4243,7 +4246,10 @@ void GEO_uv_parametrizer_pack(ParamHandle *handle, } } -void GEO_uv_parametrizer_average(ParamHandle *phandle, bool ignore_pinned) +void GEO_uv_parametrizer_average(ParamHandle *phandle, + bool ignore_pinned, + bool scale_uv, + bool shear) { PChart *chart; int i; @@ -4256,17 +4262,93 @@ void GEO_uv_parametrizer_average(ParamHandle *phandle, bool ignore_pinned) } for (i = 0; i < phandle->ncharts; i++) { - PFace *f; chart = phandle->charts[i]; if (ignore_pinned && (chart->flag & PCHART_HAS_PINS)) { continue; } + p_chart_uv_bbox(chart, minv, maxv); + mid_v2_v2v2(chart->u.pack.origin, minv, maxv); + + if (scale_uv || shear) { + /* It's possible that for some "bad" inputs, the following iteration will converge slowly or + * perhaps even diverge. Rather than infinite loop, we only iterate a maximum of `max_iter` + * times. (Also useful when making changes to the calculation.) */ + int max_iter = 10; + for (int j = 0; j < max_iter; j++) { + /* An island could contain millions of polygons. When summing many small values, we need to + * use double precision in the accumulator to maintain accuracy. Note that the individual + * calculations only need to be at single precision.*/ + double scale_cou = 0; + double scale_cov = 0; + double scale_cross = 0; + double weight_sum = 0; + for (PFace *f = chart->faces; f; f = f->nextlink) { + float m[2][2], s[2][2]; + PVert *va = f->edge->vert; + PVert *vb = f->edge->next->vert; + PVert *vc = f->edge->next->next->vert; + s[0][0] = va->uv[0] - vc->uv[0]; + s[0][1] = va->uv[1] - vc->uv[1]; + s[1][0] = vb->uv[0] - vc->uv[0]; + s[1][1] = vb->uv[1] - vc->uv[1]; + /* Find the "U" axis and "V" axis in triangle co-ordinates. Normally this would require + * SVD, but in 2D we can use a cheaper matrix inversion instead.*/ + if (!invert_m2_m2(m, s)) { + continue; + } + float cou[3], cov[3]; /* i.e. Texture "U" and texture "V" in 3D co-ordinates.*/ + for (int k = 0; k < 3; k++) { + cou[k] = m[0][0] * (va->co[k] - vc->co[k]) + m[0][1] * (vb->co[k] - vc->co[k]); + cov[k] = m[1][0] * (va->co[k] - vc->co[k]) + m[1][1] * (vb->co[k] - vc->co[k]); + } + const float weight = p_face_area(f); + scale_cou += len_v3(cou) * weight; + scale_cov += len_v3(cov) * weight; + if (shear) { + normalize_v3(cov); + normalize_v3(cou); + + /* Why is scale_cross called `cross` when we call `dot`? The next line calculates: + * `scale_cross += length(cross(cross(cou, face_normal), cov))` + * By construction, both `cou` and `cov` are orthogonal to the face normal. + * By definition, the normal vector has unit length. */ + scale_cross += dot_v3v3(cou, cov) * weight; + } + weight_sum += weight; + } + if (scale_cou * scale_cov < 1e-10f) { + break; + } + const float scale_factor_u = scale_uv ? sqrtf(scale_cou / scale_cov) : 1.0f; + + /* Compute correction transform. */ + float t[2][2]; + t[0][0] = scale_factor_u; + t[1][0] = clamp_f((float)(scale_cross / weight_sum), -0.5f, 0.5f); + t[0][1] = 0; + t[1][1] = 1.0f / scale_factor_u; + + /* Apply the correction. */ + p_chart_uv_transform(chart, t); + + /* How far from the identity transform are we? [[1,0],[0,1]] */ + const float err = fabsf(t[0][0] - 1.0f) + fabsf(t[1][0]) + fabsf(t[0][1]) + + fabsf(t[1][1] - 1.0f); + + const float tolerance = 1e-6f; /* Trade accuracy for performance. */ + if (err < tolerance) { + /* Too slow? Use Richardson Extrapolation to accelerate the convergence.*/ + break; + } + } + } + chart->u.pack.area = 0.0f; /* 3d area */ chart->u.pack.rescale = 0.0f; /* UV area, abusing rescale for tmp storage, oh well :/ */ - for (f = chart->faces; f; f = f->nextlink) { + for (PFace *f = chart->faces; f; f = f->nextlink) { chart->u.pack.area += p_face_area(f); chart->u.pack.rescale += fabsf(p_face_uv_area_signed(f)); } @@ -4292,18 +4374,16 @@ void GEO_uv_parametrizer_average(ParamHandle *phandle, bool ignore_pinned) if (chart->u.pack.area != 0.0f && chart->u.pack.rescale != 0.0f) { fac = chart->u.pack.area / chart->u.pack.rescale; - /* Get the island center */ - p_chart_uv_bbox(chart, minv, maxv); - trans[0] = (minv[0] + maxv[0]) / -2.0f; - trans[1] = (minv[1] + maxv[1]) / -2.0f; - - /* Move center to 0,0 */ - p_chart_uv_translate(chart, trans); + /* Average scale. */ p_chart_uv_scale(chart, sqrtf(fac / tot_fac)); - /* Move to original center */ - trans[0] = -trans[0]; - trans[1] = -trans[1]; + /* Get the current island center. */ + p_chart_uv_bbox(chart, minv, maxv); + + /* Move to original center. */ + mid_v2_v2v2(trans, minv, maxv); + negate_v2(trans); + add_v2_v2(trans, chart->u.pack.origin); p_chart_uv_translate(chart, trans); } } diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c index 4f00b997902..77616ae13b6 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c @@ -448,7 +448,7 @@ static void options_light_reference_draw(const bContext *UNUSED(C), Panel *panel uiItemR(col, ptr, "shadow_camera_near", 0, "Near", ICON_NONE); uiItemR(col, ptr, "shadow_camera_far", 0, "Far", ICON_NONE); - uiItemR(layout, ptr, "use_shadow_enclosed_shapes", 0, IFACE_("Eclosed Shapes"), ICON_NONE); + uiItemR(layout, ptr, "use_shadow_enclosed_shapes", 0, IFACE_("Enclosed Shapes"), ICON_NONE); } static void options_panel_draw(const bContext *UNUSED(C), Panel *panel) diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c index 1d62e930caa..1a8d1e75746 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c @@ -23,6 +23,7 @@ #include "BKE_context.h" #include "BKE_gpencil.h" +#include "BKE_gpencil_geom.h" #include "BKE_gpencil_modifier.h" #include "BKE_lib_query.h" #include "BKE_main.h" @@ -100,9 +101,11 @@ static void update_position(Object *ob, MirrorGpencilModifierData *mmd, bGPDstro } } -static void generate_geometry(GpencilModifierData *md, Object *ob, bGPDlayer *gpl, bGPDframe *gpf) +static void generate_geometry( + GpencilModifierData *md, Object *ob, bGPDlayer *gpl, bGPDframe *gpf, const bool update) { MirrorGpencilModifierData *mmd = (MirrorGpencilModifierData *)md; + bGPdata *gpd = ob->data; bGPDstroke *gps, *gps_new = NULL; int tot_strokes; int i; @@ -129,6 +132,9 @@ static void generate_geometry(GpencilModifierData *md, Object *ob, bGPDlayer *gp mmd->flag & GP_MIRROR_INVERT_MATERIAL)) { gps_new = BKE_gpencil_stroke_duplicate(gps, true, true); update_position(ob, mmd, gps_new, xi); + if (update) { + BKE_gpencil_stroke_geometry_update(gpd, gps_new); + } BLI_addtail(&gpf->strokes, gps_new); } } @@ -147,7 +153,7 @@ static void generateStrokes(GpencilModifierData *md, Depsgraph *depsgraph, Objec if (gpf == NULL) { continue; } - generate_geometry(md, ob, gpl, gpf); + generate_geometry(md, ob, gpl, gpf, false); } } @@ -167,7 +173,7 @@ static void bakeModifier(Main *UNUSED(bmain), BKE_scene_graph_update_for_newframe(depsgraph); /* compute mirror effects on this frame */ - generate_geometry(md, ob, gpl, gpf); + generate_geometry(md, ob, gpl, gpf, true); } } diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 0f539d4b30a..5e97909a2b8 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -5,7 +5,7 @@ # to more easily highlight code-paths in other libraries that need to be refactored, # bf_gpu is allowed to have opengl regardless of this option. -if(NOT WITH_OPENGL AND NOT WITH_METAL_BACKEND) +if(NOT WITH_OPENGL AND NOT WITH_METAL_BACKEND AND NOT WITH_HEADLESS) add_definitions(-DWITH_OPENGL) endif() @@ -195,9 +195,11 @@ set(METAL_SRC metal/mtl_debug.mm metal/mtl_framebuffer.mm metal/mtl_memory.mm + metal/mtl_query.mm metal/mtl_state.mm metal/mtl_texture.mm metal/mtl_texture_util.mm + metal/mtl_uniform_buffer.mm metal/mtl_backend.hh metal/mtl_capabilities.hh @@ -206,8 +208,10 @@ set(METAL_SRC metal/mtl_debug.hh metal/mtl_framebuffer.hh metal/mtl_memory.hh + metal/mtl_query.hh metal/mtl_state.hh metal/mtl_texture.hh + metal/mtl_uniform_buffer.hh ) # Select Backend source based on availability @@ -561,7 +565,7 @@ endif() -if(WITH_GPU_SHADER_BUILDER) +if(WITH_GPU_BUILDTIME_SHADER_BUILDER) # TODO(@fclem) Fix this mess. if(APPLE) add_executable(shader_builder @@ -570,11 +574,7 @@ if(WITH_GPU_SHADER_BUILDER) ) setup_platform_linker_flags(shader_builder) - - target_link_libraries(shader_builder PUBLIC - bf_blenkernel - buildinfoobj - ) + target_link_libraries(shader_builder PUBLIC buildinfoobj) else() if(WIN32) # We can re-use the manifest from tests.exe here since it's @@ -589,12 +589,14 @@ if(WITH_GPU_SHADER_BUILDER) ${MANIFEST} ) - target_link_libraries(shader_builder PUBLIC - bf_blenkernel - ${PLATFORM_LINKLIBS} - ) endif() - + target_link_libraries(shader_builder PUBLIC + bf_gpu + bf_intern_clog + bf_blenlib + bf_intern_ghost + ${PLATFORM_LINKLIBS} + ) target_include_directories(shader_builder PRIVATE ${INC} ${CMAKE_CURRENT_BINARY_DIR}) set(SRC_BAKED_CREATE_INFOS_FILE ${CMAKE_CURRENT_BINARY_DIR}/shader_baked.hh) diff --git a/source/blender/gpu/GPU_context.h b/source/blender/gpu/GPU_context.h index 1fcd94c48fc..9d92ea2cad9 100644 --- a/source/blender/gpu/GPU_context.h +++ b/source/blender/gpu/GPU_context.h @@ -17,10 +17,10 @@ extern "C" { #endif -void GPU_backend_init(eGPUBackendType backend); -void GPU_backend_exit(void); -bool GPU_backend_supported(eGPUBackendType type); - +/* GPU backends abstract the differences between different APIs. GPU_context_create + * automatically initializes the backend, and GPU_context_discard frees it when there + * are no more contexts. */ +bool GPU_backend_supported(void); eGPUBackendType GPU_backend_get_type(void); /** Opaque type hiding blender::gpu::Context. */ diff --git a/source/blender/gpu/intern/gpu_backend.hh b/source/blender/gpu/intern/gpu_backend.hh index 6e07e6c3229..d2890efee72 100644 --- a/source/blender/gpu/intern/gpu_backend.hh +++ b/source/blender/gpu/intern/gpu_backend.hh @@ -30,6 +30,7 @@ class VertBuf; class GPUBackend { public: virtual ~GPUBackend() = default; + virtual void delete_resources() = 0; static GPUBackend *get(); diff --git a/source/blender/gpu/intern/gpu_context.cc b/source/blender/gpu/intern/gpu_context.cc index 4a0a9ecc7f6..e29b0d5801d 100644 --- a/source/blender/gpu/intern/gpu_context.cc +++ b/source/blender/gpu/intern/gpu_context.cc @@ -27,6 +27,7 @@ #include "gpu_batch_private.hh" #include "gpu_context_private.hh" #include "gpu_matrix_private.h" +#include "gpu_private.h" #ifdef WITH_OPENGL_BACKEND # include "gl_backend.hh" @@ -43,6 +44,12 @@ using namespace blender::gpu; static thread_local Context *active_ctx = nullptr; +static std::mutex backend_users_mutex; +static int num_backend_users = 0; + +static void gpu_backend_create(); +static void gpu_backend_discard(); + /* -------------------------------------------------------------------- */ /** \name gpu::Context methods * \{ */ @@ -85,9 +92,13 @@ Context *Context::get() GPUContext *GPU_context_create(void *ghost_window) { - if (GPUBackend::get() == nullptr) { - /* TODO: move where it make sense. */ - GPU_backend_init(GPU_BACKEND_OPENGL); + { + std::scoped_lock lock(backend_users_mutex); + if (num_backend_users == 0) { + /* Automatically create backend when first context is created. */ + gpu_backend_create(); + } + num_backend_users++; } Context *ctx = GPUBackend::get()->context_alloc(ghost_window); @@ -101,6 +112,16 @@ void GPU_context_discard(GPUContext *ctx_) Context *ctx = unwrap(ctx_); delete ctx; active_ctx = nullptr; + + { + std::scoped_lock lock(backend_users_mutex); + num_backend_users--; + BLI_assert(num_backend_users >= 0); + if (num_backend_users == 0) { + /* Discard backend when last context is discarded. */ + gpu_backend_discard(); + } + } } void GPU_context_active_set(GPUContext *ctx_) @@ -191,11 +212,12 @@ void GPU_render_step() /** \name Backend selection * \{ */ +static const eGPUBackendType g_backend_type = GPU_BACKEND_OPENGL; static GPUBackend *g_backend = nullptr; -bool GPU_backend_supported(eGPUBackendType type) +bool GPU_backend_supported(void) { - switch (type) { + switch (g_backend_type) { case GPU_BACKEND_OPENGL: #ifdef WITH_OPENGL_BACKEND return true; @@ -214,12 +236,12 @@ bool GPU_backend_supported(eGPUBackendType type) } } -void GPU_backend_init(eGPUBackendType backend_type) +static void gpu_backend_create() { BLI_assert(g_backend == nullptr); - BLI_assert(GPU_backend_supported(backend_type)); + BLI_assert(GPU_backend_supported()); - switch (backend_type) { + switch (g_backend_type) { #ifdef WITH_OPENGL_BACKEND case GPU_BACKEND_OPENGL: g_backend = new GLBackend; @@ -236,10 +258,15 @@ void GPU_backend_init(eGPUBackendType backend_type) } } -void GPU_backend_exit() +void gpu_backend_delete_resources() +{ + BLI_assert(g_backend); + g_backend->delete_resources(); +} + +void gpu_backend_discard() { - /* TODO: assert no resource left. Currently UI textures are still not freed in their context - * correctly. */ + /* TODO: assert no resource left. */ delete g_backend; g_backend = nullptr; } diff --git a/source/blender/gpu/intern/gpu_context_private.hh b/source/blender/gpu/intern/gpu_context_private.hh index 9cdf0075632..f823a92893c 100644 --- a/source/blender/gpu/intern/gpu_context_private.hh +++ b/source/blender/gpu/intern/gpu_context_private.hh @@ -28,11 +28,11 @@ namespace blender::gpu { class Context { public: /** State management */ - Shader *shader = NULL; - FrameBuffer *active_fb = NULL; - GPUMatrixState *matrix_state = NULL; - StateManager *state_manager = NULL; - Immediate *imm = NULL; + Shader *shader = nullptr; + FrameBuffer *active_fb = nullptr; + GPUMatrixState *matrix_state = nullptr; + StateManager *state_manager = nullptr; + Immediate *imm = nullptr; /** * All 4 window frame-buffers. @@ -41,10 +41,10 @@ class Context { * Front frame-buffers contains (in principle, but not always) the last frame color. * Default frame-buffer is back_left. */ - FrameBuffer *back_left = NULL; - FrameBuffer *front_left = NULL; - FrameBuffer *back_right = NULL; - FrameBuffer *front_right = NULL; + FrameBuffer *back_left = nullptr; + FrameBuffer *front_left = nullptr; + FrameBuffer *back_right = nullptr; + FrameBuffer *front_right = nullptr; DebugStack debug_stack; @@ -52,7 +52,7 @@ class Context { /** Thread on which this context is active. */ pthread_t thread_; bool is_active_; - /** Avoid including GHOST headers. Can be NULL for off-screen contexts. */ + /** Avoid including GHOST headers. Can be nullptr for off-screen contexts. */ void *ghost_window_; public: diff --git a/source/blender/gpu/intern/gpu_immediate_private.hh b/source/blender/gpu/intern/gpu_immediate_private.hh index 6c50fa01071..74ebbdc7ae3 100644 --- a/source/blender/gpu/intern/gpu_immediate_private.hh +++ b/source/blender/gpu/intern/gpu_immediate_private.hh @@ -19,7 +19,7 @@ namespace blender::gpu { class Immediate { public: /** Pointer to the mapped buffer data for the current vertex. */ - uchar *vertex_data = NULL; + uchar *vertex_data = nullptr; /** Current vertex index. */ uint vertex_idx = 0; /** Length of the buffer in vertices. */ @@ -32,12 +32,12 @@ class Immediate { /** Current draw call specification. */ GPUPrimType prim_type = GPU_PRIM_NONE; GPUVertFormat vertex_format = {}; - GPUShader *shader = NULL; + GPUShader *shader = nullptr; /** Enforce strict vertex count (disabled when using #immBeginAtMost). */ bool strict_vertex_len = true; /** Batch in construction when using #immBeginBatch. */ - GPUBatch *batch = NULL; + GPUBatch *batch = nullptr; /** Wide Line workaround. */ diff --git a/source/blender/gpu/intern/gpu_init_exit.c b/source/blender/gpu/intern/gpu_init_exit.c index 062614fb5cb..34b355eefaf 100644 --- a/source/blender/gpu/intern/gpu_init_exit.c +++ b/source/blender/gpu/intern/gpu_init_exit.c @@ -55,6 +55,8 @@ void GPU_exit(void) gpu_shader_dependency_exit(); gpu_shader_create_info_exit(); + gpu_backend_delete_resources(); + initialized = false; } diff --git a/source/blender/gpu/intern/gpu_private.h b/source/blender/gpu/intern/gpu_private.h index a8ee5187d98..0e293302086 100644 --- a/source/blender/gpu/intern/gpu_private.h +++ b/source/blender/gpu/intern/gpu_private.h @@ -10,6 +10,10 @@ extern "C" { #endif +/* gpu_backend.cc */ + +void gpu_backend_delete_resources(void); + /* gpu_pbvh.c */ void gpu_pbvh_init(void); diff --git a/source/blender/gpu/intern/gpu_shader_builder_stubs.cc b/source/blender/gpu/intern/gpu_shader_builder_stubs.cc index 515f65adb73..d8af2fc584d 100644 --- a/source/blender/gpu/intern/gpu_shader_builder_stubs.cc +++ b/source/blender/gpu/intern/gpu_shader_builder_stubs.cc @@ -12,6 +12,7 @@ #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" +#include "BKE_attribute.h" #include "BKE_customdata.h" #include "BKE_global.h" #include "BKE_material.h" @@ -101,6 +102,38 @@ void UI_GetThemeColorShadeAlpha4ubv(int UNUSED(colorid), /** \} */ /* -------------------------------------------------------------------- */ +/** \name Stubs of BKE_attribute.h + * \{ */ + +void BKE_id_attribute_copy_domains_temp(short UNUSED(id_type), + const struct CustomData *UNUSED(vdata), + const struct CustomData *UNUSED(edata), + const struct CustomData *UNUSED(ldata), + const struct CustomData *UNUSED(pdata), + const struct CustomData *UNUSED(cdata), + struct ID *UNUSED(i_id)) +{ +} + +struct CustomDataLayer *BKE_id_attributes_active_color_get(const struct ID *UNUSED(id)) +{ + return nullptr; +} + +struct CustomDataLayer *BKE_id_attributes_render_color_get(const struct ID *UNUSED(id)) +{ + return nullptr; +} + +eAttrDomain BKE_id_attribute_domain(const struct ID *UNUSED(id), + const struct CustomDataLayer *UNUSED(layer)) +{ + return ATTR_DOMAIN_AUTO; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Stubs of BKE_paint.h * \{ */ bool paint_is_face_hidden(const struct MLoopTri *UNUSED(lt), @@ -170,6 +203,28 @@ int CustomData_get_offset(const struct CustomData *UNUSED(data), int UNUSED(type return 0; } +int CustomData_get_named_layer_index(const struct CustomData *UNUSED(data), + int UNUSED(type), + const char *UNUSED(name)) +{ + return -1; +} + +int CustomData_get_active_layer_index(const struct CustomData *UNUSED(data), int UNUSED(type)) +{ + return -1; +} + +int CustomData_get_render_layer_index(const struct CustomData *UNUSED(data), int UNUSED(type)) +{ + return -1; +} + +bool CustomData_has_layer(const struct CustomData *UNUSED(data), int UNUSED(type)) +{ + return false; +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -237,5 +292,14 @@ void DRW_deferred_shader_remove(struct GPUMaterial *UNUSED(mat)) BLI_assert_unreachable(); } +void DRW_cdlayer_attr_aliases_add(struct GPUVertFormat *UNUSED(format), + const char *UNUSED(base_name), + const struct CustomData *UNUSED(data), + const struct CustomDataLayer *UNUSED(cl), + bool UNUSED(is_active_render), + bool UNUSED(is_active_layer)) +{ +} + /** \} */ } diff --git a/source/blender/gpu/intern/gpu_shader_create_info.cc b/source/blender/gpu/intern/gpu_shader_create_info.cc index 16ce4f7723e..bc0731862cb 100644 --- a/source/blender/gpu/intern/gpu_shader_create_info.cc +++ b/source/blender/gpu/intern/gpu_shader_create_info.cc @@ -333,8 +333,11 @@ bool gpu_shader_create_info_compile_all() int skipped = 0; int total = 0; for (ShaderCreateInfo *info : g_create_infos->values()) { + info->finalize(); if (info->do_static_compilation_) { - if (GPU_compute_shader_support() == false && info->compute_source_ != nullptr) { + if ((GPU_compute_shader_support() == false && info->compute_source_ != nullptr) || + (GPU_shader_image_load_store_support() == false && info->has_resource_image()) || + (GPU_shader_storage_buffer_objects_support() == false && info->has_resource_storage())) { skipped++; continue; } diff --git a/source/blender/gpu/intern/gpu_shader_create_info.hh b/source/blender/gpu/intern/gpu_shader_create_info.hh index 4927ef75a75..8e05412d0ee 100644 --- a/source/blender/gpu/intern/gpu_shader_create_info.hh +++ b/source/blender/gpu/intern/gpu_shader_create_info.hh @@ -872,6 +872,31 @@ struct ShaderCreateInfo { return stream; } + bool has_resource_type(Resource::BindType bind_type) const + { + for (auto &res : batch_resources_) { + if (res.bind_type == bind_type) { + return true; + } + } + for (auto &res : pass_resources_) { + if (res.bind_type == bind_type) { + return true; + } + } + return false; + } + + bool has_resource_image() const + { + return has_resource_type(Resource::BindType::IMAGE); + } + + bool has_resource_storage() const + { + return has_resource_type(Resource::BindType::STORAGE_BUFFER); + } + /** \} */ #undef TEST_EQUAL diff --git a/source/blender/gpu/intern/gpu_shader_interface.hh b/source/blender/gpu/intern/gpu_shader_interface.hh index ac78af38fcc..60344757b43 100644 --- a/source/blender/gpu/intern/gpu_shader_interface.hh +++ b/source/blender/gpu/intern/gpu_shader_interface.hh @@ -39,9 +39,9 @@ class ShaderInterface { /* TODO(fclem): should be protected. */ public: /** Flat array. In this order: Attributes, Ubos, Uniforms. */ - ShaderInput *inputs_ = NULL; + ShaderInput *inputs_ = nullptr; /** Buffer containing all inputs names separated by '\0'. */ - char *name_buffer_ = NULL; + char *name_buffer_ = nullptr; /** Input counts inside input array. */ uint attr_len_ = 0; uint ubo_len_ = 0; @@ -187,7 +187,7 @@ inline const char *ShaderInterface::builtin_uniform_name(GPUUniformBuiltin u) return "srgbTarget"; default: - return NULL; + return nullptr; } } @@ -208,7 +208,7 @@ inline const char *ShaderInterface::builtin_uniform_block_name(GPUUniformBlockBu case GPU_UNIFORM_BLOCK_DRW_INFOS: return "drw_infos"; default: - return NULL; + return nullptr; } } @@ -258,7 +258,7 @@ inline const ShaderInput *ShaderInterface::input_lookup(const ShaderInput *const return inputs + i; /* not found */ } } - return NULL; /* not found */ + return nullptr; /* not found */ } /* This is a bit dangerous since we could have a hash collision. @@ -268,7 +268,7 @@ inline const ShaderInput *ShaderInterface::input_lookup(const ShaderInput *const return inputs + i; } } - return NULL; /* not found */ + return nullptr; /* not found */ } inline const ShaderInput *ShaderInterface::input_lookup(const ShaderInput *const inputs, @@ -281,7 +281,7 @@ inline const ShaderInput *ShaderInterface::input_lookup(const ShaderInput *const return inputs + i; } } - return NULL; /* not found */ + return nullptr; /* not found */ } } // namespace blender::gpu diff --git a/source/blender/gpu/intern/gpu_storage_buffer_private.hh b/source/blender/gpu/intern/gpu_storage_buffer_private.hh index 091e6c2d386..9baec0c2a77 100644 --- a/source/blender/gpu/intern/gpu_storage_buffer_private.hh +++ b/source/blender/gpu/intern/gpu_storage_buffer_private.hh @@ -29,7 +29,7 @@ class StorageBuf { /** Data size in bytes. */ size_t size_in_bytes_; /** Continuous memory block to copy to GPU. This data is owned by the StorageBuf. */ - void *data_ = NULL; + void *data_ = nullptr; /** Debugging name */ char name_[DEBUG_NAME_LEN]; diff --git a/source/blender/gpu/intern/gpu_uniform_buffer_private.hh b/source/blender/gpu/intern/gpu_uniform_buffer_private.hh index 6e3285b6fef..e3d70634ce1 100644 --- a/source/blender/gpu/intern/gpu_uniform_buffer_private.hh +++ b/source/blender/gpu/intern/gpu_uniform_buffer_private.hh @@ -29,7 +29,7 @@ class UniformBuf { /** Data size in bytes. */ size_t size_in_bytes_; /** Continuous memory block to copy to GPU. This data is owned by the UniformBuf. */ - void *data_ = NULL; + void *data_ = nullptr; /** Debugging name */ char name_[DEBUG_NAME_LEN]; diff --git a/source/blender/gpu/intern/gpu_vertex_buffer_private.hh b/source/blender/gpu/intern/gpu_vertex_buffer_private.hh index 7a0b53cf958..a7920bacaec 100644 --- a/source/blender/gpu/intern/gpu_vertex_buffer_private.hh +++ b/source/blender/gpu/intern/gpu_vertex_buffer_private.hh @@ -29,7 +29,7 @@ class VertBuf { /** Status flag. */ GPUVertBufStatus flag = GPU_VERTBUF_INVALID; /** NULL indicates data in VRAM (unmapped) */ - uchar *data = NULL; + uchar *data = nullptr; protected: /** Usage hint for GL optimization. */ diff --git a/source/blender/gpu/metal/mtl_backend.hh b/source/blender/gpu/metal/mtl_backend.hh index 7228a5f7596..3e09408e43e 100644 --- a/source/blender/gpu/metal/mtl_backend.hh +++ b/source/blender/gpu/metal/mtl_backend.hh @@ -40,6 +40,11 @@ class MTLBackend : public GPUBackend { MTLBackend::platform_exit(); } + void delete_resources() + { + /* Delete any resources with context active. */ + } + static bool metal_is_supported(); static MTLBackend *get() { diff --git a/source/blender/gpu/metal/mtl_backend.mm b/source/blender/gpu/metal/mtl_backend.mm index d2524cc16e3..3328855bbf8 100644 --- a/source/blender/gpu/metal/mtl_backend.mm +++ b/source/blender/gpu/metal/mtl_backend.mm @@ -10,6 +10,8 @@ #include "mtl_backend.hh" #include "mtl_context.hh" #include "mtl_framebuffer.hh" +#include "mtl_uniform_buffer.hh" +#include "mtl_query.hh" #include "gpu_capabilities_private.hh" #include "gpu_platform_private.hh" @@ -64,8 +66,7 @@ IndexBuf *MTLBackend::indexbuf_alloc() QueryPool *MTLBackend::querypool_alloc() { - /* TODO(Metal): Implement MTLQueryPool. */ - return nullptr; + return new MTLQueryPool(); }; Shader *MTLBackend::shader_alloc(const char *name) @@ -81,8 +82,7 @@ Texture *MTLBackend::texture_alloc(const char *name) UniformBuf *MTLBackend::uniformbuf_alloc(int size, const char *name) { - /* TODO(Metal): Implement MTLUniformBuf. */ - return nullptr; + return new MTLUniformBuf(size, name); }; StorageBuf *MTLBackend::storagebuf_alloc(int size, GPUUsageType usage, const char *name) diff --git a/source/blender/gpu/metal/mtl_command_buffer.mm b/source/blender/gpu/metal/mtl_command_buffer.mm index 7b6066b3d86..9a9a2d55103 100644 --- a/source/blender/gpu/metal/mtl_command_buffer.mm +++ b/source/blender/gpu/metal/mtl_command_buffer.mm @@ -308,6 +308,12 @@ id<MTLRenderCommandEncoder> MTLCommandBufferManager::ensure_begin_render_command active_pass_descriptor_ = active_frame_buffer_->bake_render_pass_descriptor( is_rebind && (!active_frame_buffer_->get_pending_clear())); + /* Determine if there is a visibility buffer assigned to the context. */ + gpu::MTLBuffer *visibility_buffer = context_.get_visibility_buffer(); + this->active_pass_descriptor_.visibilityResultBuffer = + (visibility_buffer) ? visibility_buffer->get_metal_buffer() : nil; + context_.clear_visibility_dirty(); + /* Ensure we have already cleaned up our previous render command encoder. */ BLI_assert(active_render_command_encoder_ == nil); diff --git a/source/blender/gpu/metal/mtl_context.hh b/source/blender/gpu/metal/mtl_context.hh index 4b87b994a3d..0db87bf5da5 100644 --- a/source/blender/gpu/metal/mtl_context.hh +++ b/source/blender/gpu/metal/mtl_context.hh @@ -3,6 +3,9 @@ /** \file * \ingroup gpu */ + +#pragma once + #include "MEM_guardedalloc.h" #include "gpu_context_private.hh" @@ -588,6 +591,10 @@ class MTLContext : public Context { bool is_inside_frame_ = false; uint current_frame_index_; + /* Visibility buffer for MTLQuery results. */ + gpu::MTLBuffer *visibility_buffer_ = nullptr; + bool visibility_is_dirty_ = false; + public: /* Shaders and Pipeline state. */ MTLContextGlobalShaderPipelineState pipeline_state; @@ -619,6 +626,11 @@ class MTLContext : public Context { void memory_statistics_get(int *total_mem, int *free_mem) override; + static MTLContext *get() + { + return static_cast<MTLContext *>(Context::get()); + } + void debug_group_begin(const char *name, int index) override; void debug_group_end() override; @@ -660,6 +672,18 @@ class MTLContext : public Context { void set_scissor(int scissor_x, int scissor_y, int scissor_width, int scissor_height); void set_scissor_enabled(bool scissor_enabled); + /* Visibility buffer control. */ + void set_visibility_buffer(gpu::MTLBuffer *buffer); + gpu::MTLBuffer *get_visibility_buffer() const; + + /* Flag whether the visibility buffer for query results + * has changed. This requires a new RenderPass in order + * to update.*/ + bool is_visibility_dirty() const; + + /* Reset dirty flag state for visibility buffer. */ + void clear_visibility_dirty(); + /* Texture utilities. */ MTLContextTextureUtils &get_texture_utils() { diff --git a/source/blender/gpu/metal/mtl_context.mm b/source/blender/gpu/metal/mtl_context.mm index 2ab2e20158a..26cfe6632ef 100644 --- a/source/blender/gpu/metal/mtl_context.mm +++ b/source/blender/gpu/metal/mtl_context.mm @@ -188,7 +188,8 @@ id<MTLRenderCommandEncoder> MTLContext::ensure_begin_render_pass() * framebuffer state has been modified (is_dirty). */ if (!this->main_command_buffer.is_inside_render_pass() || this->active_fb != this->main_command_buffer.get_active_framebuffer() || - this->main_command_buffer.get_active_framebuffer()->get_dirty()) { + this->main_command_buffer.get_active_framebuffer()->get_dirty() || + this->is_visibility_dirty()) { /* Validate bound framebuffer before beginning render pass. */ if (!static_cast<MTLFrameBuffer *>(this->active_fb)->validate_render_pass()) { @@ -372,6 +373,45 @@ void MTLContext::set_scissor_enabled(bool scissor_enabled) /** \} */ /* -------------------------------------------------------------------- */ +/** \name Visibility buffer control for MTLQueryPool. + * \{ */ + +void MTLContext::set_visibility_buffer(gpu::MTLBuffer *buffer) +{ + /* Flag visibility buffer as dirty if the buffer being used for visibility has changed -- + * This is required by the render pass, and we will break the pass if the results destination + * buffer is modified. */ + if (buffer) { + visibility_is_dirty_ = (buffer != visibility_buffer_) || visibility_is_dirty_; + visibility_buffer_ = buffer; + visibility_buffer_->debug_ensure_used(); + } + else { + /* If buffer is null, reset visibility state, mark dirty to break render pass if results are no + * longer needed. */ + visibility_is_dirty_ = (visibility_buffer_ != nullptr) || visibility_is_dirty_; + visibility_buffer_ = nullptr; + } +} + +gpu::MTLBuffer *MTLContext::get_visibility_buffer() const +{ + return visibility_buffer_; +} + +void MTLContext::clear_visibility_dirty() +{ + visibility_is_dirty_ = false; +} + +bool MTLContext::is_visibility_dirty() const +{ + return visibility_is_dirty_; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Texture State Management * \{ */ diff --git a/source/blender/gpu/metal/mtl_memory.hh b/source/blender/gpu/metal/mtl_memory.hh index daa049e78af..dc5417dc11a 100644 --- a/source/blender/gpu/metal/mtl_memory.hh +++ b/source/blender/gpu/metal/mtl_memory.hh @@ -78,8 +78,10 @@ * Usage: * MTLContext::get_global_memory_manager(); - static routine to fetch global memory manager. * - * gpu::MTLBuffer *allocate_buffer(size, is_cpu_visibile, bytes=nullptr) - * gpu::MTLBuffer *allocate_buffer_aligned(size, alignment, is_cpu_visibile, bytes=nullptr) + * gpu::MTLBuffer *allocate(size, is_cpu_visibile) + * gpu::MTLBuffer *allocate_aligned(size, alignment, is_cpu_visibile) + * gpu::MTLBuffer *allocate_with_data(size, is_cpu_visibile, data_ptr) + * gpu::MTLBuffer *allocate_aligned_with_data(size, alignment, is_cpu_visibile, data_ptr) */ /* Debug memory statistics: Disabled by Macro rather than guarded for @@ -389,11 +391,13 @@ class MTLBufferPool { void init(id<MTLDevice> device); ~MTLBufferPool(); - gpu::MTLBuffer *allocate_buffer(uint64_t size, bool cpu_visible, const void *bytes = nullptr); - gpu::MTLBuffer *allocate_buffer_aligned(uint64_t size, - uint alignment, - bool cpu_visible, - const void *bytes = nullptr); + gpu::MTLBuffer *allocate(uint64_t size, bool cpu_visible); + gpu::MTLBuffer *allocate_aligned(uint64_t size, uint alignment, bool cpu_visible); + gpu::MTLBuffer *allocate_with_data(uint64_t size, bool cpu_visible, const void *data = nullptr); + gpu::MTLBuffer *allocate_aligned_with_data(uint64_t size, + uint alignment, + bool cpu_visible, + const void *data = nullptr); bool free_buffer(gpu::MTLBuffer *buffer); /* Flush MTLSafeFreeList buffers, for completed lists in `completed_safelist_queue_`, diff --git a/source/blender/gpu/metal/mtl_memory.mm b/source/blender/gpu/metal/mtl_memory.mm index e5db32ed1b1..48e27dd2bb6 100644 --- a/source/blender/gpu/metal/mtl_memory.mm +++ b/source/blender/gpu/metal/mtl_memory.mm @@ -57,17 +57,23 @@ void MTLBufferPool::free() buffer_pools_.clear(); } -gpu::MTLBuffer *MTLBufferPool::allocate_buffer(uint64_t size, bool cpu_visible, const void *bytes) +gpu::MTLBuffer *MTLBufferPool::allocate(uint64_t size, bool cpu_visible) { /* Allocate buffer with default HW-compatible alignment of 256 bytes. * See https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf for more. */ - return this->allocate_buffer_aligned(size, 256, cpu_visible, bytes); + return this->allocate_aligned(size, 256, cpu_visible); } -gpu::MTLBuffer *MTLBufferPool::allocate_buffer_aligned(uint64_t size, - uint alignment, - bool cpu_visible, - const void *bytes) +gpu::MTLBuffer *MTLBufferPool::allocate_with_data(uint64_t size, + bool cpu_visible, + const void *data) +{ + /* Allocate buffer with default HW-compatible alignemnt of 256 bytes. + * See https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf for more. */ + return this->allocate_aligned_with_data(size, 256, cpu_visible, data); +} + +gpu::MTLBuffer *MTLBufferPool::allocate_aligned(uint64_t size, uint alignment, bool cpu_visible) { /* Check not required. Main GPU module usage considered thread-safe. */ // BLI_assert(BLI_thread_is_main()); @@ -153,15 +159,6 @@ gpu::MTLBuffer *MTLBufferPool::allocate_buffer_aligned(uint64_t size, /* Flag buffer as actively in-use. */ new_buffer->flag_in_use(true); - /* Upload initial data if provided -- Size based on original size param, not aligned size*/ - if (bytes) { - BLI_assert(!(options & MTLResourceStorageModePrivate)); - BLI_assert(size <= aligned_alloc_size); - BLI_assert(size <= [new_buffer->get_metal_buffer() length]); - memcpy(new_buffer->get_host_ptr(), bytes, size); - new_buffer->flush_range(0, size); - } - #if MTL_DEBUG_MEMORY_STATISTICS == 1 this->per_frame_allocation_count++; #endif @@ -169,6 +166,23 @@ gpu::MTLBuffer *MTLBufferPool::allocate_buffer_aligned(uint64_t size, return new_buffer; } +gpu::MTLBuffer *MTLBufferPool::allocate_aligned_with_data(uint64_t size, + uint alignment, + bool cpu_visible, + const void *data) +{ + gpu::MTLBuffer *buf = this->allocate_aligned(size, 256, cpu_visible); + + /* Upload initial data. */ + BLI_assert(data != nullptr); + BLI_assert(!(buf->get_resource_options() & MTLResourceStorageModePrivate)); + BLI_assert(size <= buf->get_size()); + BLI_assert(size <= [buf->get_metal_buffer() length]); + memcpy(buf->get_host_ptr(), data, size); + buf->flush_range(0, size); + return buf; +} + bool MTLBufferPool::free_buffer(gpu::MTLBuffer *buffer) { /* Ensure buffer is flagged as in-use. I.e. has not already been returned to memory pools. */ @@ -356,7 +370,7 @@ void MTLBufferPool::insert_buffer_into_pool(MTLResourceOptions options, gpu::MTL #if MTL_DEBUG_MEMORY_STATISTICS == 1 /* Debug statistics. */ - allocations_in_pool_ += buffer->size; + allocations_in_pool_ += buffer->get_size(); buffers_in_pool_++; #endif } @@ -413,7 +427,7 @@ void MTLSafeFreeList::decrement_reference() { lock_.lock(); BLI_assert(in_free_queue_ == false); - int ref_count = reference_count_--; + int ref_count = --reference_count_; if (ref_count == 0) { MTLContext::get_global_memory_manager().push_completed_safe_list(this); diff --git a/source/blender/gpu/metal/mtl_query.hh b/source/blender/gpu/metal/mtl_query.hh new file mode 100644 index 00000000000..c1ec9a2a0f5 --- /dev/null +++ b/source/blender/gpu/metal/mtl_query.hh @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup gpu + */ + +#pragma once + +#include "BLI_vector.hh" + +#include "gpu_query.hh" +#include "mtl_context.hh" + +namespace blender::gpu { + +class MTLQueryPool : public QueryPool { + private: + /** Number of queries that have been issued since last initialization. + * Should be equal to query_ids_.size(). */ + uint32_t query_issued_; + /** Type of this query pool. */ + GPUQueryType type_; + /** Can only be initialized once. */ + bool initialized_ = false; + MTLVisibilityResultMode mtl_type_; + Vector<gpu::MTLBuffer *> buffer_; + + void allocate_buffer(); + + public: + MTLQueryPool(); + ~MTLQueryPool(); + + void init(GPUQueryType type) override; + + void begin_query() override; + void end_query() override; + + void get_occlusion_result(MutableSpan<uint32_t> r_values) override; +}; +} // namespace blender::gpu diff --git a/source/blender/gpu/metal/mtl_query.mm b/source/blender/gpu/metal/mtl_query.mm new file mode 100644 index 00000000000..33ae8f554c3 --- /dev/null +++ b/source/blender/gpu/metal/mtl_query.mm @@ -0,0 +1,122 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup gpu + */ + +#include "mtl_query.hh" + +namespace blender::gpu { + +static const size_t VISIBILITY_COUNT_PER_BUFFER = 512; +/* defined in the documentation but not queryable programmatically: + * https://developer.apple.com/documentation/metal/mtlvisibilityresultmode/mtlvisibilityresultmodeboolean?language=objc + */ +static const size_t VISIBILITY_RESULT_SIZE_IN_BYTES = 8; + +MTLQueryPool::MTLQueryPool() +{ + allocate_buffer(); +} +MTLQueryPool::~MTLQueryPool() +{ + for (gpu::MTLBuffer *buf : buffer_) { + BLI_assert(buf); + buf->free(); + } +} + +void MTLQueryPool::allocate_buffer() +{ + /* Allocate Metal buffer for visibility results. */ + size_t buffer_size_in_bytes = VISIBILITY_COUNT_PER_BUFFER * VISIBILITY_RESULT_SIZE_IN_BYTES; + gpu::MTLBuffer *buffer = MTLContext::get_global_memory_manager().allocate_buffer( + buffer_size_in_bytes, true); + BLI_assert(buffer); + buffer_.append(buffer); +} + +static inline MTLVisibilityResultMode to_mtl_type(GPUQueryType type) +{ + if (type == GPU_QUERY_OCCLUSION) { + return MTLVisibilityResultModeBoolean; + } + BLI_assert(0); + return MTLVisibilityResultModeBoolean; +} + +void MTLQueryPool::init(GPUQueryType type) +{ + BLI_assert(initialized_ == false); + initialized_ = true; + type_ = type; + mtl_type_ = to_mtl_type(type); + query_issued_ = 0; +} + +void MTLQueryPool::begin_query() +{ + MTLContext *ctx = reinterpret_cast<MTLContext *>(GPU_context_active_get()); + + /* Ensure our allocated buffer pool has enough space for the current queries. */ + int query_id = query_issued_; + int requested_buffer = query_id / VISIBILITY_COUNT_PER_BUFFER; + if (requested_buffer >= buffer_.size()) { + allocate_buffer(); + } + + BLI_assert(requested_buffer < buffer_.size()); + gpu::MTLBuffer *buffer = buffer_[requested_buffer]; + + /* Ensure visibility buffer is set on the context. If visibility buffer changes, + * we need to begin a new render pass with an updated reference in the + * MTLRenderPassDescriptor. */ + ctx->set_visibility_buffer(buffer); + + ctx->ensure_begin_render_pass(); + id<MTLRenderCommandEncoder> rec = ctx->main_command_buffer.get_active_render_command_encoder(); + [rec setVisibilityResultMode:mtl_type_ + offset:(query_id % VISIBILITY_COUNT_PER_BUFFER) * + VISIBILITY_RESULT_SIZE_IN_BYTES]; + query_issued_ += 1; +} + +void MTLQueryPool::end_query() +{ + MTLContext *ctx = reinterpret_cast<MTLContext *>(GPU_context_active_get()); + + id<MTLRenderCommandEncoder> rec = ctx->main_command_buffer.get_active_render_command_encoder(); + [rec setVisibilityResultMode:MTLVisibilityResultModeDisabled offset:0]; +} + +void MTLQueryPool::get_occlusion_result(MutableSpan<uint32_t> r_values) +{ + MTLContext *ctx = reinterpret_cast<MTLContext *>(GPU_context_active_get()); + + /* Create a blit encoder to synchronize the query buffer results between + * GPU and CPU when not using shared-memory. */ + if ([ctx->device hasUnifiedMemory] == false) { + id<MTLBlitCommandEncoder> blit_encoder = ctx->main_command_buffer.ensure_begin_blit_encoder(); + BLI_assert(blit_encoder); + for (gpu::MTLBuffer *buf : buffer_) { + [blit_encoder synchronizeResource:buf->get_metal_buffer()]; + } + BLI_assert(ctx->get_inside_frame()); + } + + /* Wait for GPU operatiosn to complete and for query buffer contents + * to be synchronised back to host memory. */ + GPU_finish(); + + /* Iterate through all possible visibility buffers and copy results into provided + * container. */ + for (const int i : IndexRange(query_issued_)) { + int requested_buffer = i / VISIBILITY_COUNT_PER_BUFFER; + const uint64_t *queries = static_cast<const uint64_t *>( + buffer_[requested_buffer]->get_host_ptr()); + r_values[i] = static_cast<uint32_t>(queries[i % VISIBILITY_COUNT_PER_BUFFER]); + } + ctx->set_visibility_buffer(nullptr); +} + +} // namespace blender::gpu diff --git a/source/blender/gpu/metal/mtl_texture.hh b/source/blender/gpu/metal/mtl_texture.hh index 9387d5af814..82a7a20a310 100644 --- a/source/blender/gpu/metal/mtl_texture.hh +++ b/source/blender/gpu/metal/mtl_texture.hh @@ -93,7 +93,7 @@ struct TextureReadRoutineSpecialisation { * 0 = Not a Depth format, * 1 = FLOAT DEPTH, * 2 = 24Bit Integer Depth, - * 4 = 32bit uinteger Depth. */ + * 4 = 32bit Unsigned-Integer Depth. */ int depth_format_mode; bool operator==(const TextureReadRoutineSpecialisation &other) const diff --git a/source/blender/gpu/metal/mtl_uniform_buffer.hh b/source/blender/gpu/metal/mtl_uniform_buffer.hh new file mode 100644 index 00000000000..722d819cf96 --- /dev/null +++ b/source/blender/gpu/metal/mtl_uniform_buffer.hh @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup gpu + */ + +#pragma once + +#include "MEM_guardedalloc.h" +#include "gpu_uniform_buffer_private.hh" + +#include "mtl_context.hh" + +namespace blender::gpu { + +/** + * Implementation of Uniform Buffers using Metal. + **/ +class MTLUniformBuf : public UniformBuf { + private: + /* Allocation Handle. */ + gpu::MTLBuffer *metal_buffer_ = nullptr; + + /* Whether buffer has contents, if false, no GPU buffer will + * have yet been allocated. */ + bool has_data_ = false; + + /* Bindstate tracking. */ + int bind_slot_ = -1; + MTLContext *bound_ctx_ = nullptr; + + public: + MTLUniformBuf(size_t size, const char *name); + ~MTLUniformBuf(); + + void update(const void *data) override; + void bind(int slot) override; + void unbind() override; + + id<MTLBuffer> get_metal_buffer(int *r_offset); + int get_size(); + const char *get_name() + { + return name_; + } + + MEM_CXX_CLASS_ALLOC_FUNCS("MTLUniformBuf"); +}; + +} // namespace blender::gpu diff --git a/source/blender/gpu/metal/mtl_uniform_buffer.mm b/source/blender/gpu/metal/mtl_uniform_buffer.mm new file mode 100644 index 00000000000..3415c41f2cc --- /dev/null +++ b/source/blender/gpu/metal/mtl_uniform_buffer.mm @@ -0,0 +1,162 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup gpu + */ + +#include "BKE_global.h" + +#include "BLI_string.h" + +#include "gpu_backend.hh" +#include "gpu_context_private.hh" + +#include "mtl_backend.hh" +#include "mtl_context.hh" +#include "mtl_debug.hh" +#include "mtl_uniform_buffer.hh" + +namespace blender::gpu { + +MTLUniformBuf::MTLUniformBuf(size_t size, const char *name) : UniformBuf(size, name) +{ +} + +MTLUniformBuf::~MTLUniformBuf() +{ + if (metal_buffer_ != nullptr) { + metal_buffer_->free(); + metal_buffer_ = nullptr; + } + has_data_ = false; + + /* Ensure UBO is not bound to active CTX. + * UBO bindings are reset upon Context-switch so we do not need + * to check deactivated context's. */ + MTLContext *ctx = MTLContext::get(); + if (ctx) { + for (int i = 0; i < MTL_MAX_UNIFORM_BUFFER_BINDINGS; i++) { + MTLUniformBufferBinding &slot = ctx->pipeline_state.ubo_bindings[i]; + if (slot.bound && slot.ubo == this) { + slot.bound = false; + slot.ubo = nullptr; + } + } + } +} + +void MTLUniformBuf::update(const void *data) +{ + BLI_assert(this); + BLI_assert(size_in_bytes_ > 0); + + /* Free existing allocation. + * The previous UBO resource will be tracked by the memory manager, + * in case dependent GPU work is still executing. */ + if (metal_buffer_ != nullptr) { + metal_buffer_->free(); + metal_buffer_ = nullptr; + } + + /* Allocate MTL buffer */ + MTLContext *ctx = static_cast<MTLContext *>(unwrap(GPU_context_active_get())); + BLI_assert(ctx); + BLI_assert(ctx->device); + UNUSED_VARS_NDEBUG(ctx); + + if (data != nullptr) { + metal_buffer_ = MTLContext::get_global_memory_manager().allocate_with_data( + size_in_bytes_, true, data); + has_data_ = true; + + metal_buffer_->set_label(@"Uniform Buffer"); + BLI_assert(metal_buffer_ != nullptr); + BLI_assert(metal_buffer_->get_metal_buffer() != nil); + } + else { + /* If data is not yet present, no buffer will be allocated and MTLContext will use an empty + * null buffer, containing zeroes, if the UBO is bound. */ + metal_buffer_ = nullptr; + has_data_ = false; + } +} + +void MTLUniformBuf::bind(int slot) +{ + if (slot < 0) { + MTL_LOG_WARNING("Failed to bind UBO %p. uniform location %d invalid.\n", this, slot); + return; + } + + BLI_assert(slot < MTL_MAX_UNIFORM_BUFFER_BINDINGS); + + /* Bind current UBO to active context. */ + MTLContext *ctx = MTLContext::get(); + BLI_assert(ctx); + + MTLUniformBufferBinding &ctx_ubo_bind_slot = ctx->pipeline_state.ubo_bindings[slot]; + ctx_ubo_bind_slot.ubo = this; + ctx_ubo_bind_slot.bound = true; + + bind_slot_ = slot; + bound_ctx_ = ctx; + + /* Check if we have any deferred data to upload. */ + if (data_ != nullptr) { + this->update(data_); + MEM_SAFE_FREE(data_); + } + + /* Ensure there is atleast an empty dummy buffer. */ + if (metal_buffer_ == nullptr) { + this->update(nullptr); + } +} + +void MTLUniformBuf::unbind() +{ + /* Unbind in debug mode to validate missing binds. + * Otherwise, only perform a full unbind upon destruction + * to ensure no lingering references. */ +#ifndef NDEBUG + if (true) { +#else + if (G.debug & G_DEBUG_GPU) { +#endif + if (bound_ctx_ != nullptr && bind_slot_ > -1) { + MTLUniformBufferBinding &ctx_ubo_bind_slot = + bound_ctx_->pipeline_state.ubo_bindings[bind_slot_]; + if (ctx_ubo_bind_slot.bound && ctx_ubo_bind_slot.ubo == this) { + ctx_ubo_bind_slot.bound = false; + ctx_ubo_bind_slot.ubo = nullptr; + } + } + } + + /* Reset bind index. */ + bind_slot_ = -1; + bound_ctx_ = nullptr; +} + +id<MTLBuffer> MTLUniformBuf::get_metal_buffer(int *r_offset) +{ + BLI_assert(this); + *r_offset = 0; + if (metal_buffer_ != nullptr && has_data_) { + *r_offset = 0; + metal_buffer_->debug_ensure_used(); + return metal_buffer_->get_metal_buffer(); + } + else { + *r_offset = 0; + return nil; + } +} + +int MTLUniformBuf::get_size() +{ + BLI_assert(this); + return size_in_bytes_; +} + +} // blender::gpu diff --git a/source/blender/gpu/opengl/gl_backend.hh b/source/blender/gpu/opengl/gl_backend.hh index 29249111294..e425b87afe8 100644 --- a/source/blender/gpu/opengl/gl_backend.hh +++ b/source/blender/gpu/opengl/gl_backend.hh @@ -42,11 +42,15 @@ class GLBackend : public GPUBackend { } ~GLBackend() { - GLTexture::samplers_free(); - GLBackend::platform_exit(); } + void delete_resources() override + { + /* Delete any resources with context active. */ + GLTexture::samplers_free(); + } + static GLBackend *get() { return static_cast<GLBackend *>(GPUBackend::get()); diff --git a/source/blender/gpu/opengl/gl_batch.hh b/source/blender/gpu/opengl/gl_batch.hh index a25e495b3b1..1a18572c683 100644 --- a/source/blender/gpu/opengl/gl_batch.hh +++ b/source/blender/gpu/opengl/gl_batch.hh @@ -35,9 +35,9 @@ class GLShaderInterface; class GLVaoCache { private: /** Context for which the vao_cache_ was generated. */ - GLContext *context_ = NULL; + GLContext *context_ = nullptr; /** Last interface this batch was drawn with. */ - GLShaderInterface *interface_ = NULL; + GLShaderInterface *interface_ = nullptr; /** Cached VAO for the last interface. */ GLuint vao_id_ = 0; /** Used when arb_base_instance is not supported. */ diff --git a/source/blender/gpu/opengl/gl_debug.cc b/source/blender/gpu/opengl/gl_debug.cc index f82138e0d65..79b28642a67 100644 --- a/source/blender/gpu/opengl/gl_debug.cc +++ b/source/blender/gpu/opengl/gl_debug.cc @@ -189,7 +189,7 @@ void check_gl_error(const char *info) case err: { \ char msg[256]; \ SNPRINTF(msg, "%s : %s", #err, info); \ - debug_callback(0, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, 0, msg, NULL); \ + debug_callback(0, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, 0, msg, nullptr); \ break; \ } diff --git a/source/blender/gpu/opengl/gl_framebuffer.hh b/source/blender/gpu/opengl/gl_framebuffer.hh index 224c7a92c0a..2dc0936d0fe 100644 --- a/source/blender/gpu/opengl/gl_framebuffer.hh +++ b/source/blender/gpu/opengl/gl_framebuffer.hh @@ -30,9 +30,9 @@ class GLFrameBuffer : public FrameBuffer { /** OpenGL handle. */ GLuint fbo_id_ = 0; /** Context the handle is from. Frame-buffers are not shared across contexts. */ - GLContext *context_ = NULL; + GLContext *context_ = nullptr; /** State Manager of the same contexts. */ - GLStateManager *state_manager_ = NULL; + GLStateManager *state_manager_ = nullptr; /** Copy of the GL state. Contains ONLY color attachments enums for slot binding. */ GLenum gl_attachments_[GPU_FB_MAX_COLOR_ATTACHMENT]; /** Internal frame-buffers are immutable. */ diff --git a/source/blender/gpu/opengl/gl_texture.cc b/source/blender/gpu/opengl/gl_texture.cc index 055c8d104e2..cfb3184c4a5 100644 --- a/source/blender/gpu/opengl/gl_texture.cc +++ b/source/blender/gpu/opengl/gl_texture.cc @@ -310,6 +310,12 @@ void GLTexture::update_sub( */ void GLTexture::generate_mipmap() { + /* Allow users to provide mipmaps stored in compressed textures. + * Skip generating mipmaps to avoid overriding the existing ones. */ + if (format_flag_ & GPU_FORMAT_COMPRESSED) { + return; + } + /* Some drivers have bugs when using #glGenerateMipmap with depth textures (see T56789). * In this case we just create a complete texture with mipmaps manually without * down-sampling. You must initialize the texture levels using other methods like diff --git a/source/blender/gpu/opengl/gl_texture.hh b/source/blender/gpu/opengl/gl_texture.hh index e5b879f1f15..aeb9fc0e6b7 100644 --- a/source/blender/gpu/opengl/gl_texture.hh +++ b/source/blender/gpu/opengl/gl_texture.hh @@ -33,7 +33,7 @@ class GLTexture : public Texture { /** opengl identifier for texture. */ GLuint tex_id_ = 0; /** Legacy workaround for texture copy. Created when using framebuffer_get(). */ - struct GPUFrameBuffer *framebuffer_ = NULL; + struct GPUFrameBuffer *framebuffer_ = nullptr; /** True if this texture is bound to at least one texture unit. */ /* TODO(fclem): How do we ensure thread safety here? */ bool is_bound_ = false; diff --git a/source/blender/gpu/tests/gpu_testing.cc b/source/blender/gpu/tests/gpu_testing.cc index 4e93e062b50..224a9afcf59 100644 --- a/source/blender/gpu/tests/gpu_testing.cc +++ b/source/blender/gpu/tests/gpu_testing.cc @@ -26,7 +26,6 @@ void GPUTest::SetUp() void GPUTest::TearDown() { GPU_exit(); - GPU_backend_exit(); GPU_context_discard(context); GHOST_DisposeOpenGLContext(ghost_system, ghost_context); GHOST_DisposeSystem(ghost_system); diff --git a/source/blender/imbuf/intern/colormanagement.c b/source/blender/imbuf/intern/colormanagement.c index b95ec1cd92b..b62bdd5521d 100644 --- a/source/blender/imbuf/intern/colormanagement.c +++ b/source/blender/imbuf/intern/colormanagement.c @@ -2213,10 +2213,11 @@ void IMB_colormanagement_imbuf_to_byte_texture(unsigned char *out_buffer, const struct ImBuf *ibuf, const bool store_premultiplied) { - /* Byte buffer storage, only for sRGB and data texture since other + /* Byte buffer storage, only for sRGB, scene linear and data texture since other * color space conversions can't be done on the GPU. */ BLI_assert(ibuf->rect && ibuf->rect_float == NULL); BLI_assert(IMB_colormanagement_space_is_srgb(ibuf->rect_colorspace) || + IMB_colormanagement_space_is_scene_linear(ibuf->rect_colorspace) || IMB_colormanagement_space_is_data(ibuf->rect_colorspace)); const unsigned char *in_buffer = (unsigned char *)ibuf->rect; diff --git a/source/blender/imbuf/intern/divers.c b/source/blender/imbuf/intern/divers.c index 588c92d748d..13c8f0887b3 100644 --- a/source/blender/imbuf/intern/divers.c +++ b/source/blender/imbuf/intern/divers.c @@ -695,9 +695,6 @@ void IMB_buffer_byte_from_byte(uchar *rect_to, void IMB_rect_from_float(ImBuf *ibuf) { - float *buffer; - const char *from_colorspace; - /* verify we have a float buffer */ if (ibuf->rect_float == NULL) { return; @@ -710,24 +707,21 @@ void IMB_rect_from_float(ImBuf *ibuf) } } - if (ibuf->float_colorspace == NULL) { - from_colorspace = IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_SCENE_LINEAR); - } - else { - from_colorspace = ibuf->float_colorspace->name; - } + const char *from_colorspace = (ibuf->float_colorspace == NULL) ? + IMB_colormanagement_role_colorspace_name_get( + COLOR_ROLE_SCENE_LINEAR) : + ibuf->float_colorspace->name; + const char *to_colorspace = (ibuf->rect_colorspace == NULL) ? + IMB_colormanagement_role_colorspace_name_get( + COLOR_ROLE_DEFAULT_BYTE) : + ibuf->rect_colorspace->name; - buffer = MEM_dupallocN(ibuf->rect_float); + float *buffer = MEM_dupallocN(ibuf->rect_float); /* first make float buffer in byte space */ const bool predivide = IMB_alpha_affects_rgb(ibuf); - IMB_colormanagement_transform(buffer, - ibuf->x, - ibuf->y, - ibuf->channels, - from_colorspace, - ibuf->rect_colorspace->name, - predivide); + IMB_colormanagement_transform( + buffer, ibuf->x, ibuf->y, ibuf->channels, from_colorspace, to_colorspace, predivide); /* convert from float's premul alpha to byte's straight alpha */ if (IMB_alpha_affects_rgb(ibuf)) { diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c index cbc5d984755..00396c01d99 100644 --- a/source/blender/imbuf/intern/indexer.c +++ b/source/blender/imbuf/intern/indexer.c @@ -1098,6 +1098,7 @@ static int indexer_performance_get_decode_rate(FFmpegIndexBuilderContext *contex while (av_read_frame(context->iFormatCtx, packet) >= 0) { if (packet->stream_index != context->videoStream) { + av_packet_unref(packet); continue; } @@ -1121,6 +1122,7 @@ static int indexer_performance_get_decode_rate(FFmpegIndexBuilderContext *contex if (end > start + time_period) { break; } + av_packet_unref(packet); } av_packet_free(&packet); @@ -1145,6 +1147,7 @@ static int indexer_performance_get_max_gop_size(FFmpegIndexBuilderContext *conte while (av_read_frame(context->iFormatCtx, packet) >= 0) { if (packet->stream_index != context->videoStream) { + av_packet_unref(packet); continue; } packet_index++; @@ -1158,6 +1161,7 @@ static int indexer_performance_get_max_gop_size(FFmpegIndexBuilderContext *conte if (packet_index > packets_max) { break; } + av_packet_unref(packet); } av_packet_free(&packet); diff --git a/source/blender/io/collada/AnimationImporter.cpp b/source/blender/io/collada/AnimationImporter.cpp index 923a392dbde..cc91c3eeac9 100644 --- a/source/blender/io/collada/AnimationImporter.cpp +++ b/source/blender/io/collada/AnimationImporter.cpp @@ -64,7 +64,7 @@ void AnimationImporter::add_bezt(FCurve *fcu, bez.f1 = bez.f2 = bez.f3 = SELECT; bez.h1 = bez.h2 = HD_AUTO; insert_bezt_fcurve(fcu, &bez, INSERTKEY_NOFLAGS); - calchandles_fcurve(fcu); + BKE_fcurve_handles_recalc(fcu); } void AnimationImporter::animation_to_fcurves(COLLADAFW::AnimationCurve *curve) @@ -132,7 +132,7 @@ void AnimationImporter::animation_to_fcurves(COLLADAFW::AnimationCurve *curve) insert_bezt_fcurve(fcu, &bez, INSERTKEY_NOFLAGS); } - calchandles_fcurve(fcu); + BKE_fcurve_handles_recalc(fcu); fcurves.push_back(fcu); unused_curves.push_back(fcu); diff --git a/source/blender/io/collada/BCAnimationCurve.cpp b/source/blender/io/collada/BCAnimationCurve.cpp index fbb2ba499a5..04a7a81c0a6 100644 --- a/source/blender/io/collada/BCAnimationCurve.cpp +++ b/source/blender/io/collada/BCAnimationCurve.cpp @@ -96,7 +96,7 @@ void BCAnimationCurve::create_bezt(float frame, float output) bez.f1 = bez.f2 = bez.f3 = SELECT; bez.h1 = bez.h2 = HD_AUTO; insert_bezt_fcurve(fcu, &bez, INSERTKEY_NOFLAGS); - calchandles_fcurve(fcu); + BKE_fcurve_handles_recalc(fcu); } BCAnimationCurve::~BCAnimationCurve() diff --git a/source/blender/io/gpencil/intern/gpencil_io_base.cc b/source/blender/io/gpencil/intern/gpencil_io_base.cc index 05f1158c57d..6db3eccedbe 100644 --- a/source/blender/io/gpencil/intern/gpencil_io_base.cc +++ b/source/blender/io/gpencil/intern/gpencil_io_base.cc @@ -91,8 +91,7 @@ void GpencilIO::prepare_camera_params(Scene *scene, const GpencilIOParams *ipara /* Camera rectangle. */ if ((rv3d_->persp == RV3D_CAMOB) || (force_camera_view)) { - render_x_ = (scene_->r.xsch * scene_->r.size) / 100; - render_y_ = (scene_->r.ysch * scene_->r.size) / 100; + BKE_render_resolution(&scene->r, false, &render_x_, &render_y_); ED_view3d_calc_camera_border(CTX_data_scene(params_.C), depsgraph_, diff --git a/source/blender/io/gpencil/intern/gpencil_io_base.hh b/source/blender/io/gpencil/intern/gpencil_io_base.hh index a89b723ed6c..4987ab34ffc 100644 --- a/source/blender/io/gpencil/intern/gpencil_io_base.hh +++ b/source/blender/io/gpencil/intern/gpencil_io_base.hh @@ -58,8 +58,8 @@ class GpencilIO { struct Scene *scene_; struct RegionView3D *rv3d_; - int16_t winx_, winy_; - int16_t render_x_, render_y_; + int winx_, winy_; + int render_x_, render_y_; float camera_ratio_; rctf camera_rect_; diff --git a/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc b/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc index c2aa96713b1..a32fd90594d 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc @@ -22,32 +22,24 @@ using std::string; /** * Based on the properties of the given Geometry instance, create a new Geometry instance * or return the previous one. - * - * Also update index offsets which should always happen if a new Geometry instance is created. */ static Geometry *create_geometry(Geometry *const prev_geometry, const eGeometryType new_type, StringRef name, - const GlobalVertices &global_vertices, - Vector<std::unique_ptr<Geometry>> &r_all_geometries, - VertexIndexOffset &r_offset) + Vector<std::unique_ptr<Geometry>> &r_all_geometries) { auto new_geometry = [&]() { r_all_geometries.append(std::make_unique<Geometry>()); Geometry *g = r_all_geometries.last().get(); g->geom_type_ = new_type; g->geometry_name_ = name.is_empty() ? "New object" : name; - g->vertex_start_ = global_vertices.vertices.size(); - g->vertex_color_start_ = global_vertices.vertex_colors.size(); - r_offset.set_index_offset(g->vertex_start_); return g; }; if (prev_geometry && prev_geometry->geom_type_ == GEOM_MESH) { /* After the creation of a Geometry instance, at least one element has been found in the OBJ - * file that indicates that it is a mesh (basically anything but the vertex positions). */ - if (!prev_geometry->face_elements_.is_empty() || prev_geometry->has_vertex_normals_ || - !prev_geometry->edges_.is_empty()) { + * file that indicates that it is a mesh (faces or edges). */ + if (!prev_geometry->face_elements_.is_empty() || !prev_geometry->edges_.is_empty()) { return new_geometry(); } if (new_type == GEOM_MESH) { @@ -70,15 +62,11 @@ static Geometry *create_geometry(Geometry *const prev_geometry, return new_geometry(); } -static void geom_add_vertex(Geometry *geom, - const char *p, - const char *end, - GlobalVertices &r_global_vertices) +static void geom_add_vertex(const char *p, const char *end, GlobalVertices &r_global_vertices) { float3 vert; p = parse_floats(p, end, 0.0f, vert, 3); r_global_vertices.vertices.append(vert); - geom->vertex_count_++; /* OBJ extension: `xyzrgb` vertex colors, when the vertex position * is followed by 3 more RGB color components. See * http://paulbourke.net/dataformats/obj/colour.html */ @@ -88,16 +76,22 @@ static void geom_add_vertex(Geometry *geom, if (srgb.x >= 0 && srgb.y >= 0 && srgb.z >= 0) { float3 linear; srgb_to_linearrgb_v3_v3(linear, srgb); - r_global_vertices.vertex_colors.append(linear); - geom->vertex_color_count_++; + + auto &blocks = r_global_vertices.vertex_colors; + /* If we don't have vertex colors yet, or the previous vertex + * was without color, we need to start a new vertex colors block. */ + if (blocks.is_empty() || (blocks.last().start_vertex_index + blocks.last().colors.size() != + r_global_vertices.vertices.size() - 1)) { + GlobalVertices::VertexColorsBlock block; + block.start_vertex_index = r_global_vertices.vertices.size() - 1; + blocks.append(block); + } + blocks.last().colors.append(linear); } } } -static void geom_add_mrgb_colors(Geometry *geom, - const char *p, - const char *end, - GlobalVertices &r_global_vertices) +static void geom_add_mrgb_colors(const char *p, const char *end, GlobalVertices &r_global_vertices) { /* MRGB color extension, in the form of * "#MRGB MMRRGGBBMMRRGGBB ..." @@ -117,14 +111,26 @@ static void geom_add_mrgb_colors(Geometry *geom, srgb[3] = 0xFF; float linear[4]; srgb_to_linearrgb_uchar4(linear, srgb); - r_global_vertices.vertex_colors.append({linear[0], linear[1], linear[2]}); - geom->vertex_color_count_++; + + auto &blocks = r_global_vertices.vertex_colors; + /* If we don't have vertex colors yet, or the previous vertex + * was without color, we need to start a new vertex colors block. */ + if (blocks.is_empty() || (blocks.last().start_vertex_index + blocks.last().colors.size() != + r_global_vertices.vertices.size())) { + GlobalVertices::VertexColorsBlock block; + block.start_vertex_index = r_global_vertices.vertices.size(); + blocks.append(block); + } + blocks.last().colors.append({linear[0], linear[1], linear[2]}); + /* MRGB colors are specified after vertex positions; each new color + * "pushes" the vertex colors block further back into which vertices it is for. */ + blocks.last().start_vertex_index--; + p += mrgb_length; } } -static void geom_add_vertex_normal(Geometry *geom, - const char *p, +static void geom_add_vertex_normal(const char *p, const char *end, GlobalVertices &r_global_vertices) { @@ -135,7 +141,6 @@ static void geom_add_vertex_normal(Geometry *geom, * normalized. */ normalize_v3(normal); r_global_vertices.vertex_normals.append(normal); - geom->has_vertex_normals_ = true; } static void geom_add_uv_vertex(const char *p, const char *end, GlobalVertices &r_global_vertices) @@ -148,24 +153,24 @@ static void geom_add_uv_vertex(const char *p, const char *end, GlobalVertices &r static void geom_add_edge(Geometry *geom, const char *p, const char *end, - const VertexIndexOffset &offsets, GlobalVertices &r_global_vertices) { int edge_v1, edge_v2; p = parse_int(p, end, -1, edge_v1); p = parse_int(p, end, -1, edge_v2); /* Always keep stored indices non-negative and zero-based. */ - edge_v1 += edge_v1 < 0 ? r_global_vertices.vertices.size() : -offsets.get_index_offset() - 1; - edge_v2 += edge_v2 < 0 ? r_global_vertices.vertices.size() : -offsets.get_index_offset() - 1; + edge_v1 += edge_v1 < 0 ? r_global_vertices.vertices.size() : -1; + edge_v2 += edge_v2 < 0 ? r_global_vertices.vertices.size() : -1; BLI_assert(edge_v1 >= 0 && edge_v2 >= 0); geom->edges_.append({static_cast<uint>(edge_v1), static_cast<uint>(edge_v2)}); + geom->track_vertex_index(edge_v1); + geom->track_vertex_index(edge_v2); } static void geom_add_polygon(Geometry *geom, const char *p, const char *end, const GlobalVertices &global_vertices, - const VertexIndexOffset &offsets, const int material_index, const int group_index, const bool shaded_smooth) @@ -204,8 +209,7 @@ static void geom_add_polygon(Geometry *geom, } } /* Always keep stored indices non-negative and zero-based. */ - corner.vert_index += corner.vert_index < 0 ? global_vertices.vertices.size() : - -offsets.get_index_offset() - 1; + corner.vert_index += corner.vert_index < 0 ? global_vertices.vertices.size() : -1; if (corner.vert_index < 0 || corner.vert_index >= global_vertices.vertices.size()) { fprintf(stderr, "Invalid vertex index %i (valid range [0, %zu)), ignoring face\n", @@ -213,6 +217,9 @@ static void geom_add_polygon(Geometry *geom, (size_t)global_vertices.vertices.size()); face_valid = false; } + else { + geom->track_vertex_index(corner.vert_index); + } if (got_uv) { corner.uv_vert_index += corner.uv_vert_index < 0 ? global_vertices.uv_vertices.size() : -1; if (corner.uv_vert_index < 0 || corner.uv_vert_index >= global_vertices.uv_vertices.size()) { @@ -226,7 +233,7 @@ static void geom_add_polygon(Geometry *geom, /* Ignore corner normal index, if the geometry does not have any normals. * Some obj files out there do have face definitions that refer to normal indices, * without any normals being present (T98782). */ - if (got_normal && geom->has_vertex_normals_) { + if (got_normal && !global_vertices.vertex_normals.is_empty()) { corner.vertex_normal_index += corner.vertex_normal_index < 0 ? global_vertices.vertex_normals.size() : -1; @@ -260,9 +267,7 @@ static void geom_add_polygon(Geometry *geom, static Geometry *geom_set_curve_type(Geometry *geom, const char *p, const char *end, - const GlobalVertices &global_vertices, const StringRef group_name, - VertexIndexOffset &r_offsets, Vector<std::unique_ptr<Geometry>> &r_all_geometries) { p = drop_whitespace(p, end); @@ -270,8 +275,7 @@ static Geometry *geom_set_curve_type(Geometry *geom, std::cerr << "Curve type not supported: '" << std::string(p, end) << "'" << std::endl; return geom; } - geom = create_geometry( - geom, GEOM_CURVE, group_name, global_vertices, r_all_geometries, r_offsets); + geom = create_geometry(geom, GEOM_CURVE, group_name, r_all_geometries); geom->nurbs_element_.group_ = group_name; return geom; } @@ -402,9 +406,7 @@ void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries, BLI_strncpy(ob_name, BLI_path_basename(import_params_.filepath), FILE_MAXFILE); BLI_path_extension_replace(ob_name, FILE_MAXFILE, ""); - VertexIndexOffset offsets; - Geometry *curr_geom = create_geometry( - nullptr, GEOM_MESH, ob_name, r_global_vertices, r_all_geometries, offsets); + Geometry *curr_geom = create_geometry(nullptr, GEOM_MESH, ob_name, r_all_geometries); /* State variables: once set, they remain the same for the remaining * elements in the object. */ @@ -427,6 +429,11 @@ void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries, break; /* No more data to read. */ } + /* Take care of line continuations now (turn them into spaces); + * the rest of the parsing code does not need to worry about them anymore. */ + fixup_line_continuations(buffer.data() + buffer_offset, + buffer.data() + buffer_offset + bytes_read); + /* Ensure buffer ends in a newline. */ if (bytes_read < read_buffer_size_) { if (bytes_read == 0 || buffer[buffer_offset + bytes_read - 1] != '\n') { @@ -445,9 +452,7 @@ void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries, while (last_nl > 0) { --last_nl; if (buffer[last_nl] == '\n') { - if (last_nl < 1 || buffer[last_nl - 1] != '\\') { - break; - } + break; } } if (buffer[last_nl] != '\n') { @@ -474,10 +479,10 @@ void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries, /* Most common things that start with 'v': vertices, normals, UVs. */ if (*p == 'v') { if (parse_keyword(p, end, "v")) { - geom_add_vertex(curr_geom, p, end, r_global_vertices); + geom_add_vertex(p, end, r_global_vertices); } else if (parse_keyword(p, end, "vn")) { - geom_add_vertex_normal(curr_geom, p, end, r_global_vertices); + geom_add_vertex_normal(p, end, r_global_vertices); } else if (parse_keyword(p, end, "vt")) { geom_add_uv_vertex(p, end, r_global_vertices); @@ -489,26 +494,21 @@ void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries, p, end, r_global_vertices, - offsets, state_material_index, - state_group_index, /* TODO was wrongly material name! */ + state_group_index, state_shaded_smooth); } /* Faces. */ else if (parse_keyword(p, end, "l")) { - geom_add_edge(curr_geom, p, end, offsets, r_global_vertices); + geom_add_edge(curr_geom, p, end, r_global_vertices); } /* Objects. */ else if (parse_keyword(p, end, "o")) { state_shaded_smooth = false; state_group_name = ""; state_material_name = ""; - curr_geom = create_geometry(curr_geom, - GEOM_MESH, - StringRef(p, end).trim(), - r_global_vertices, - r_all_geometries, - offsets); + curr_geom = create_geometry( + curr_geom, GEOM_MESH, StringRef(p, end).trim(), r_all_geometries); } /* Groups. */ else if (parse_keyword(p, end, "g")) { @@ -537,7 +537,7 @@ void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries, add_mtl_library(StringRef(p, end).trim()); } else if (parse_keyword(p, end, "#MRGB")) { - geom_add_mrgb_colors(curr_geom, p, end, r_global_vertices); + geom_add_mrgb_colors(p, end, r_global_vertices); } /* Comments. */ else if (*p == '#') { @@ -545,8 +545,7 @@ void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries, } /* Curve related things. */ else if (parse_keyword(p, end, "cstype")) { - curr_geom = geom_set_curve_type( - curr_geom, p, end, r_global_vertices, state_group_name, offsets, r_all_geometries); + curr_geom = geom_set_curve_type(curr_geom, p, end, state_group_name, r_all_geometries); } else if (parse_keyword(p, end, "deg")) { geom_set_curve_degree(curr_geom, p, end); diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc index 8cef0700b3b..01f05466b3a 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc @@ -31,13 +31,17 @@ Object *MeshFromGeometry::create_mesh(Main *bmain, Map<std::string, Material *> &created_materials, const OBJImportParams &import_params) { + const int64_t tot_verts_object{mesh_geometry_.get_vertex_count()}; + if (tot_verts_object <= 0) { + /* Empty mesh */ + return nullptr; + } std::string ob_name{mesh_geometry_.geometry_name_}; if (ob_name.empty()) { ob_name = "Untitled"; } fixup_invalid_faces(); - const int64_t tot_verts_object{mesh_geometry_.vertex_count_}; /* Total explicitly imported edges, not the ones belonging the polygons to be created. */ const int64_t tot_edges{mesh_geometry_.edges_.size()}; const int64_t tot_face_elems{mesh_geometry_.face_elements_.size()}; @@ -153,9 +157,9 @@ void MeshFromGeometry::fixup_invalid_faces() void MeshFromGeometry::create_vertices(Mesh *mesh) { - const int tot_verts_object{mesh_geometry_.vertex_count_}; + const int tot_verts_object{mesh_geometry_.get_vertex_count()}; for (int i = 0; i < tot_verts_object; ++i) { - int vi = mesh_geometry_.vertex_start_ + i; + int vi = mesh_geometry_.vertex_index_min_ + i; if (vi < global_vertices_.vertices.size()) { copy_v3_v3(mesh->mvert[i].co, global_vertices_.vertices[vi]); } @@ -170,7 +174,7 @@ void MeshFromGeometry::create_vertices(Mesh *mesh) void MeshFromGeometry::create_polys_loops(Mesh *mesh, bool use_vertex_groups) { mesh->dvert = nullptr; - const int64_t total_verts = mesh_geometry_.vertex_count_; + const int64_t total_verts = mesh_geometry_.get_vertex_count(); if (use_vertex_groups && total_verts && mesh_geometry_.has_vertex_groups_) { mesh->dvert = static_cast<MDeformVert *>( CustomData_add_layer(&mesh->vdata, CD_MDEFORMVERT, CD_CALLOC, nullptr, total_verts)); @@ -204,7 +208,7 @@ void MeshFromGeometry::create_polys_loops(Mesh *mesh, bool use_vertex_groups) const PolyCorner &curr_corner = mesh_geometry_.face_corners_[curr_face.start_index_ + idx]; MLoop &mloop = mesh->mloop[tot_loop_idx]; tot_loop_idx++; - mloop.v = curr_corner.vert_index; + mloop.v = curr_corner.vert_index - mesh_geometry_.vertex_index_min_; /* Setup vertex group data, if needed. */ if (!mesh->dvert) { @@ -231,14 +235,14 @@ void MeshFromGeometry::create_vertex_groups(Object *obj) void MeshFromGeometry::create_edges(Mesh *mesh) { const int64_t tot_edges{mesh_geometry_.edges_.size()}; - const int64_t total_verts{mesh_geometry_.vertex_count_}; + const int64_t total_verts{mesh_geometry_.get_vertex_count()}; UNUSED_VARS_NDEBUG(total_verts); for (int i = 0; i < tot_edges; ++i) { const MEdge &src_edge = mesh_geometry_.edges_[i]; MEdge &dst_edge = mesh->medge[i]; - BLI_assert(src_edge.v1 < total_verts && src_edge.v2 < total_verts); - dst_edge.v1 = src_edge.v1; - dst_edge.v2 = src_edge.v2; + dst_edge.v1 = src_edge.v1 - mesh_geometry_.vertex_index_min_; + dst_edge.v2 = src_edge.v2 - mesh_geometry_.vertex_index_min_; + BLI_assert(dst_edge.v1 < total_verts && dst_edge.v2 < total_verts); dst_edge.flag = ME_LOOSEEDGE; } @@ -312,10 +316,8 @@ void MeshFromGeometry::create_materials(Main *bmain, void MeshFromGeometry::create_normals(Mesh *mesh) { - /* NOTE: Needs more clarity about what is expected in the viewport if the function works. */ - /* No normal data: nothing to do. */ - if (global_vertices_.vertex_normals.is_empty() || !mesh_geometry_.has_vertex_normals_) { + if (global_vertices_.vertex_normals.is_empty()) { return; } @@ -341,23 +343,26 @@ void MeshFromGeometry::create_normals(Mesh *mesh) void MeshFromGeometry::create_colors(Mesh *mesh) { - /* Nothing to do if we don't have vertex colors. */ - if (mesh_geometry_.vertex_color_count_ < 1) { - return; - } - if (mesh_geometry_.vertex_color_count_ != mesh_geometry_.vertex_count_) { - std::cerr << "Mismatching number of vertices (" << mesh_geometry_.vertex_count_ - << ") and colors (" << mesh_geometry_.vertex_color_count_ << ") on object '" - << mesh_geometry_.geometry_name_ << "', ignoring colors." << std::endl; + /* Nothing to do if we don't have vertex colors at all. */ + if (global_vertices_.vertex_colors.is_empty()) { return; } - CustomDataLayer *color_layer = BKE_id_attribute_new( - &mesh->id, "Color", CD_PROP_COLOR, ATTR_DOMAIN_POINT, nullptr); - float4 *colors = (float4 *)color_layer->data; - for (int i = 0; i < mesh_geometry_.vertex_color_count_; ++i) { - float3 c = global_vertices_.vertex_colors[mesh_geometry_.vertex_color_start_ + i]; - colors[i] = float4(c.x, c.y, c.z, 1.0f); + /* Find which vertex color block is for this mesh (if any). */ + for (const auto &block : global_vertices_.vertex_colors) { + if (mesh_geometry_.vertex_index_min_ >= block.start_vertex_index && + mesh_geometry_.vertex_index_max_ < block.start_vertex_index + block.colors.size()) { + /* This block is suitable, use colors from it. */ + CustomDataLayer *color_layer = BKE_id_attribute_new( + &mesh->id, "Color", CD_PROP_COLOR, ATTR_DOMAIN_POINT, nullptr); + float4 *colors = (float4 *)color_layer->data; + int offset = mesh_geometry_.vertex_index_min_ - block.start_vertex_index; + for (int i = 0, n = mesh_geometry_.get_vertex_count(); i != n; ++i) { + float3 c = block.colors[offset + i]; + colors[i] = float4(c.x, c.y, c.z, 1.0f); + } + return; + } } } diff --git a/source/blender/io/wavefront_obj/importer/obj_import_objects.hh b/source/blender/io/wavefront_obj/importer/obj_import_objects.hh index 3d6733d661e..9f0079d7c53 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_objects.hh +++ b/source/blender/io/wavefront_obj/importer/obj_import_objects.hh @@ -19,35 +19,24 @@ namespace blender::io::obj { /** - * List of all vertex and UV vertex coordinates in an OBJ file accessible to any - * Geometry instance at any time. + * All vertex positions, normals, UVs, colors in the OBJ file. */ struct GlobalVertices { Vector<float3> vertices; Vector<float2> uv_vertices; Vector<float3> vertex_normals; - Vector<float3> vertex_colors; -}; - -/** - * Keeps track of the vertices that belong to other Geometries. - * Needed only for MLoop.v and MEdge.v1 which needs vertex indices ranging from (0 to total - * vertices in the mesh) as opposed to the other OBJ indices ranging from (0 to total vertices - * in the global list). - */ -struct VertexIndexOffset { - private: - int offset_ = 0; - public: - void set_index_offset(const int64_t total_vertices) - { - offset_ = total_vertices; - } - int64_t get_index_offset() const - { - return offset_; - } + /** + * Vertex colors might not be present in the file at all, or only + * provided for some meshes. Store them in chunks as they are + * spelled out in the file, e.g. if there are 10 vertices in sequence, all + * with `xyzrgb` colors, they will be one block. + */ + struct VertexColorsBlock { + Vector<float3> colors; + int start_vertex_index; + }; + Vector<VertexColorsBlock> vertex_colors; }; /** @@ -101,10 +90,8 @@ struct Geometry { Map<std::string, int> material_indices_; Vector<std::string> material_order_; - int vertex_start_ = 0; - int vertex_count_ = 0; - int vertex_color_start_ = 0; - int vertex_color_count_ = 0; + int vertex_index_min_ = INT_MAX; + int vertex_index_max_ = -1; /** Edges written in the file in addition to (or even without polygon) elements. */ Vector<MEdge> edges_; @@ -112,10 +99,21 @@ struct Geometry { Vector<PolyElem> face_elements_; bool has_invalid_polys_ = false; - bool has_vertex_normals_ = false; bool has_vertex_groups_ = false; NurbsElement nurbs_element_; int total_loops_ = 0; + + int get_vertex_count() const + { + if (vertex_index_max_ < vertex_index_min_) + return 0; + return vertex_index_max_ - vertex_index_min_ + 1; + } + void track_vertex_index(int index) + { + vertex_index_min_ = std::min(vertex_index_min_, index); + vertex_index_max_ = std::max(vertex_index_max_, index); + } }; } // namespace blender::io::obj diff --git a/source/blender/io/wavefront_obj/importer/obj_import_string_utils.cc b/source/blender/io/wavefront_obj/importer/obj_import_string_utils.cc index bc9006e1051..9a457167fca 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_string_utils.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_string_utils.cc @@ -18,14 +18,12 @@ StringRef read_next_line(StringRef &buffer) const char *start = buffer.begin(); const char *end = buffer.end(); size_t len = 0; - char prev = 0; const char *ptr = start; while (ptr < end) { char c = *ptr++; - if (c == '\n' && prev != '\\') { + if (c == '\n') { break; } - prev = c; ++len; } @@ -35,7 +33,27 @@ StringRef read_next_line(StringRef &buffer) static bool is_whitespace(char c) { - return c <= ' ' || c == '\\'; + return c <= ' '; +} + +void fixup_line_continuations(char *p, char *end) +{ + while (true) { + /* Find next backslash, if any. */ + char *backslash = std::find(p, end, '\\'); + if (backslash == end) + break; + /* Skip over possible whitespace right after it. */ + p = backslash + 1; + while (p < end && is_whitespace(*p) && *p != '\n') + ++p; + /* If then we have a newline, turn both backslash + * and the newline into regular spaces. */ + if (p < end && *p == '\n') { + *backslash = ' '; + *p = ' '; + } + } } const char *drop_whitespace(const char *p, const char *end) diff --git a/source/blender/io/wavefront_obj/importer/obj_import_string_utils.hh b/source/blender/io/wavefront_obj/importer/obj_import_string_utils.hh index f6dd1a6b675..e42f5080d25 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_string_utils.hh +++ b/source/blender/io/wavefront_obj/importer/obj_import_string_utils.hh @@ -6,9 +6,6 @@ /* * Various text parsing utilities used by OBJ importer. - * The utilities are not directly usable by other formats, since - * they treat backslash (\) as a whitespace character (OBJ format - * allows backslashes to function as a line-continuation character). * * Many of these functions take two pointers (p, end) indicating * which part of a string to operate on, and return a possibly @@ -27,21 +24,22 @@ namespace blender::io::obj { * The returned line will not have '\n' characters at the end; * the `buffer` is modified to contain remaining text without * the input line. - * - * Note that backslash (\) character is treated as a line - * continuation. */ StringRef read_next_line(StringRef &buffer); /** + * Fix up OBJ line continuations by replacing backslash (\) and the + * following newline with spaces. + */ +void fixup_line_continuations(char *p, char *end); + +/** * Drop leading white-space from a string part. - * Note that backslash character is considered white-space. */ const char *drop_whitespace(const char *p, const char *end); /** * Drop leading non-white-space from a string part. - * Note that backslash character is considered white-space. */ const char *drop_non_whitespace(const char *p, const char *end); diff --git a/source/blender/io/wavefront_obj/tests/obj_import_string_utils_tests.cc b/source/blender/io/wavefront_obj/tests/obj_import_string_utils_tests.cc index e9747b437cc..dc1cfd2b449 100644 --- a/source/blender/io/wavefront_obj/tests/obj_import_string_utils_tests.cc +++ b/source/blender/io/wavefront_obj/tests/obj_import_string_utils_tests.cc @@ -10,17 +10,34 @@ namespace blender::io::obj { TEST(obj_import_string_utils, read_next_line) { - std::string str = "abc\n \n\nline with \\\ncontinuation\nCRLF ending:\r\na"; + std::string str = "abc\n \n\nline with \t spaces\nCRLF ending:\r\na"; StringRef s = str; EXPECT_STRREF_EQ("abc", read_next_line(s)); EXPECT_STRREF_EQ(" ", read_next_line(s)); EXPECT_STRREF_EQ("", read_next_line(s)); - EXPECT_STRREF_EQ("line with \\\ncontinuation", read_next_line(s)); + EXPECT_STRREF_EQ("line with \t spaces", read_next_line(s)); EXPECT_STRREF_EQ("CRLF ending:\r", read_next_line(s)); EXPECT_STRREF_EQ("a", read_next_line(s)); EXPECT_TRUE(s.is_empty()); } +TEST(obj_import_string_utils, fixup_line_continuations) +{ + const char *str = + "backslash \\\n eol\n" + "backslash spaces \\ \n eol\n" + "without eol \\ is \\\\ \\ left intact\n" + "\\"; + const char *exp = + "backslash eol\n" + "backslash spaces eol\n" + "without eol \\ is \\\\ \\ left intact\n" + "\\"; + std::string buf(str); + fixup_line_continuations(buf.data(), buf.data() + buf.size()); + EXPECT_STRREF_EQ(exp, buf); +} + static StringRef drop_whitespace(StringRef s) { return StringRef(drop_whitespace(s.begin(), s.end()), s.end()); @@ -54,7 +71,7 @@ TEST(obj_import_string_utils, drop_whitespace) /* No leading whitespace */ EXPECT_STRREF_EQ("c", drop_whitespace("c")); /* Case with backslash, should be treated as whitespace */ - EXPECT_STRREF_EQ("d", drop_whitespace(" \\ d")); + EXPECT_STRREF_EQ("d", drop_whitespace(" \t d")); } TEST(obj_import_string_utils, parse_int_valid) diff --git a/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc b/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc index eeb81f5e23e..02565556c37 100644 --- a/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc +++ b/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc @@ -158,6 +158,36 @@ TEST_F(obj_importer_test, import_cube) import_and_check("cube.obj", expect, std::size(expect), 1); } +TEST_F(obj_importer_test, import_cube_o_after_verts) +{ + Expectation expect[] = { + {"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)}, + { + "OBActualCube", + OB_MESH, + 8, + 12, + 6, + 24, + float3(-1, -1, 1), + float3(1, -1, -1), + float3(0, 0, 1), + }, + { + "OBSparseTri", + OB_MESH, + 6, + 3, + 1, + 3, + float3(1, -1, 1), + float3(-2, -2, 2), + float3(-0.2357f, 0.9428f, 0.2357f), + }, + }; + import_and_check("cube_o_after_verts.obj", expect, std::size(expect), 2); +} + TEST_F(obj_importer_test, import_suzanne_all_data) { Expectation expect[] = { @@ -293,13 +323,13 @@ TEST_F(obj_importer_test, import_faces_invalid_or_with_holes) float3(1, 0, -1)}, {"OBFaceQuadDupSomeVerts_BecomesOneQuadUsing4Verts", OB_MESH, - 8, + 4, 4, 1, 4, float3(3, 0, -2), - float3(6, 0, -1)}, - {"OBFaceTriDupVert_Becomes1Tri", OB_MESH, 8, 3, 1, 3, float3(-2, 0, 3), float3(1, 0, 4)}, + float3(7, 0, -2)}, + {"OBFaceTriDupVert_Becomes1Tri", OB_MESH, 3, 3, 1, 3, float3(-2, 0, 3), float3(2, 0, 7)}, {"OBFaceAllVertsDup_BecomesOneOverlappingFaceUsingAllVerts", OB_MESH, 8, @@ -316,7 +346,7 @@ TEST_F(obj_importer_test, import_faces_invalid_or_with_holes) 8, float3(8, 0, -2), float3(11, 0, -1)}, - {"OBFaceJustTwoVerts_IsSkipped", OB_MESH, 8, 0, 0, 0, float3(8, 0, 3), float3(11, 0, 4)}, + {"OBFaceJustTwoVerts_IsSkipped", OB_MESH, 2, 0, 0, 0, float3(8, 0, 3), float3(8, 0, 7)}, }; import_and_check("faces_invalid_or_with_holes.obj", expect, std::size(expect), 0); } @@ -327,12 +357,12 @@ TEST_F(obj_importer_test, import_invalid_indices) {"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)}, {"OBQuad", OB_MESH, - 4, + 3, 3, 1, 3, float3(-2, 0, -2), - float3(2, 0, -2), + float3(2, 0, 2), float3(0, 1, 0), float2(0.5f, 0.25f)}, }; @@ -345,12 +375,12 @@ TEST_F(obj_importer_test, import_invalid_syntax) {"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)}, {"OBObjectWithAReallyLongNameToCheckHowImportHandlesNamesThatAreLon", OB_MESH, - 10, /* NOTE: right now parses some invalid obj syntax as valid vertices. */ + 3, 3, 1, 3, float3(1, 2, 3), - float3(10, 11, 12), + float3(7, 8, 9), float3(0, 1, 0), float2(0.5f, 0.25f)}, }; @@ -585,29 +615,52 @@ TEST_F(obj_importer_test, import_cubes_vertex_colors) TEST_F(obj_importer_test, import_cubes_vertex_colors_mrgb) { - Expectation expect[] = {{"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)}, - {"OBCubeXYZRGB", - OB_MESH, - 8, - 12, - 6, - 24, - float3(1, 1, -1), - float3(-1, -1, 1), - float3(0, 0, 0), - float2(0, 0), - float4(0.6038f, 0.3185f, 0.1329f, 1.0f)}, - {"OBCubeMRGB", - OB_MESH, - 8, - 12, - 6, - 24, - float3(4, 1, -1), - float3(2, -1, 1), - float3(0, 0, 0), - float2(0, 0), - float4(0.8714f, 0.6308f, 0.5271f, 1.0f)}}; + Expectation expect[] = { + {"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)}, + {"OBCubeXYZRGB", + OB_MESH, + 8, + 12, + 6, + 24, + float3(1, 1, -1), + float3(-1, -1, 1), + float3(0, 0, 0), + float2(0, 0), + float4(0.6038f, 0.3185f, 0.1329f, 1.0f)}, + {"OBCubeMRGB", + OB_MESH, + 8, + 12, + 6, + 24, + float3(4, 1, -1), + float3(2, -1, 1), + float3(0, 0, 0), + float2(0, 0), + float4(0.8714f, 0.6308f, 0.5271f, 1.0f)}, + { + "OBTriNoColors", + OB_MESH, + 3, + 3, + 1, + 3, + float3(8, 1, -1), + float3(6, 0, -1), + }, + {"OBTriMRGB", + OB_MESH, + 3, + 3, + 1, + 3, + float3(12, 1, -1), + float3(10, 0, -1), + float3(0, 0, 0), + float2(0, 0), + float4(1.0f, 0.0f, 0.0f, 1.0f)}, + }; import_and_check("cubes_vertex_colors_mrgb.obj", expect, std::size(expect), 0); } diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index 451c921c4ef..d3fc279381f 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -923,6 +923,10 @@ typedef enum IDRecalcFlag { #define FILTER_ID_PT (1ULL << 33) #define FILTER_ID_VO (1ULL << 34) #define FILTER_ID_SIM (1ULL << 35) +#define FILTER_ID_KE (1ULL << 36) +#define FILTER_ID_SCR (1ULL << 37) +#define FILTER_ID_WM (1ULL << 38) +#define FILTER_ID_LI (1ULL << 39) #define FILTER_ID_ALL \ (FILTER_ID_AC | FILTER_ID_AR | FILTER_ID_BR | FILTER_ID_CA | FILTER_ID_CU_LEGACY | \ @@ -930,7 +934,8 @@ typedef enum IDRecalcFlag { FILTER_ID_MA | FILTER_ID_MB | FILTER_ID_MC | FILTER_ID_ME | FILTER_ID_MSK | FILTER_ID_NT | \ FILTER_ID_OB | FILTER_ID_PA | FILTER_ID_PAL | FILTER_ID_PC | FILTER_ID_SCE | FILTER_ID_SPK | \ FILTER_ID_SO | FILTER_ID_TE | FILTER_ID_TXT | FILTER_ID_VF | FILTER_ID_WO | FILTER_ID_CF | \ - FILTER_ID_WS | FILTER_ID_LP | FILTER_ID_CV | FILTER_ID_PT | FILTER_ID_VO | FILTER_ID_SIM) + FILTER_ID_WS | FILTER_ID_LP | FILTER_ID_CV | FILTER_ID_PT | FILTER_ID_VO | FILTER_ID_SIM | \ + FILTER_ID_KE | FILTER_ID_SCR | FILTER_ID_WM | FILTER_ID_LI) /** * This enum defines the index assigned to each type of IDs in the array returned by diff --git a/source/blender/makesdna/DNA_camera_types.h b/source/blender/makesdna/DNA_camera_types.h index e0aec298cd0..10a6c936be1 100644 --- a/source/blender/makesdna/DNA_camera_types.h +++ b/source/blender/makesdna/DNA_camera_types.h @@ -54,6 +54,7 @@ typedef struct CameraBGImage { typedef struct CameraDOFSettings { /** Focal distance for depth of field. */ struct Object *focus_object; + char focus_subtarget[64]; float focus_distance; float aperture_fstop; float aperture_rotation; diff --git a/source/blender/makesdna/DNA_curves_types.h b/source/blender/makesdna/DNA_curves_types.h index ed909c283c4..89deeec898b 100644 --- a/source/blender/makesdna/DNA_curves_types.h +++ b/source/blender/makesdna/DNA_curves_types.h @@ -67,23 +67,6 @@ typedef enum NormalMode { */ typedef struct CurvesGeometry { /** - * A runtime pointer to the "position" attribute data. - * \note This data is owned by #point_data. - */ - float (*position)[3]; - /** - * A runtime pointer to the "radius" attribute data. - * \note This data is owned by #point_data. - */ - float *radius; - - /** - * The type of each curve. #CurveType. - * \note This data is owned by #curve_data. - */ - int8_t *curve_type; - - /** * The start index of each curve in the point data. The size of each curve can be calculated by * subtracting the offset from the next offset. That is valid even for the last curve because * this array is allocated with a length one larger than the number of curves. This is allowed diff --git a/source/blender/makesdna/DNA_pointcloud_types.h b/source/blender/makesdna/DNA_pointcloud_types.h index 1cb490190c3..ee829ebcf6e 100644 --- a/source/blender/makesdna/DNA_pointcloud_types.h +++ b/source/blender/makesdna/DNA_pointcloud_types.h @@ -18,13 +18,9 @@ typedef struct PointCloud { struct AnimData *adt; /* animation data (must be immediately after id) */ int flag; - int _pad1[1]; /* Geometry */ - float (*co)[3]; - float *radius; int totpoint; - int _pad2[1]; /* Custom Data */ struct CustomData pdata; diff --git a/source/blender/makesdna/DNA_screen_types.h b/source/blender/makesdna/DNA_screen_types.h index 8560f8a454e..e9178c0cbf5 100644 --- a/source/blender/makesdna/DNA_screen_types.h +++ b/source/blender/makesdna/DNA_screen_types.h @@ -305,12 +305,6 @@ typedef struct uiList { /* some list UI data need to be saved in file */ int filter_flag; int filter_sort_flag; - /** Operator executed when activating an item. */ - const char *custom_activate_opname; - /** Operator executed when dragging an item (item gets activated too, without running - * custom_activate_opname above). */ - const char *custom_drag_opname; - /* Custom sub-classes properties. */ IDProperty *properties; diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 2ceef4f623e..74fb1c3ac96 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -913,8 +913,7 @@ typedef struct UserDef { /** Pie menu distance from center before a direction is set. */ short pie_menu_threshold; - short opensubdiv_compute_type; - short _pad6; + short _pad6[2]; char factor_display_type; diff --git a/source/blender/makesdna/DNA_windowmanager_types.h b/source/blender/makesdna/DNA_windowmanager_types.h index a3d9b5fc7b6..116ea4821cb 100644 --- a/source/blender/makesdna/DNA_windowmanager_types.h +++ b/source/blender/makesdna/DNA_windowmanager_types.h @@ -407,13 +407,13 @@ enum { KMI_USER_MODIFIED = (1 << 2), KMI_UPDATE = (1 << 3), /** - * When set, ignore events with #wmEvent.is_repeat enabled. + * When set, ignore events with `wmEvent.flag & WM_EVENT_IS_REPEAT` enabled. * * \note this flag isn't cleared when editing/loading the key-map items, * so it may be set in cases which don't make sense (modifier-keys or mouse-motion for example). * * Knowing if an event may repeat is something set at the operating-systems event handling level - * so rely on #wmEvent.is_repeat being false non keyboard events instead of checking if this + * so rely on #WM_EVENT_IS_REPEAT being false non keyboard events instead of checking if this * flag makes sense. * * Only used when: `ISKEYBOARD(kmi->type) || (kmi->type == KM_TEXTINPUT)` diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt index af8767a1220..778c6a6bfdd 100644 --- a/source/blender/makesrna/intern/CMakeLists.txt +++ b/source/blender/makesrna/intern/CMakeLists.txt @@ -84,7 +84,6 @@ set(DEFSRC if(WITH_EXPERIMENTAL_FEATURES) add_definitions(-DWITH_SIMULATION_DATABLOCK) - add_definitions(-DWITH_NEW_CURVES_TYPE) list(APPEND DEFSRC rna_curves.c rna_simulation.c diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c index b5354514205..2b24bd0b39c 100644 --- a/source/blender/makesrna/intern/makesrna.c +++ b/source/blender/makesrna/intern/makesrna.c @@ -1117,8 +1117,10 @@ static char *rna_def_property_set_func( fprintf( f, " if (data->%s != NULL) { MEM_freeN(data->%s); }\n", dp->dnaname, dp->dnaname); fprintf(f, " const int length = strlen(value);\n"); - fprintf(f, " data->%s = MEM_mallocN(length + 1, __func__);\n", dp->dnaname); - fprintf(f, " %s(data->%s, value, length + 1);\n", string_copy_func, dp->dnaname); + fprintf(f, " if (length > 0) {\n"); + fprintf(f, " data->%s = MEM_mallocN(length + 1, __func__);\n", dp->dnaname); + fprintf(f, " %s(data->%s, value, length + 1);\n", string_copy_func, dp->dnaname); + fprintf(f, " } else { data->%s = NULL; }\n", dp->dnaname); } else { /* Handle char array properties. */ @@ -4433,9 +4435,7 @@ static RNAProcessItem PROCESS_ITEMS[] = { {"rna_dynamicpaint.c", NULL, RNA_def_dynamic_paint}, {"rna_fcurve.c", "rna_fcurve_api.c", RNA_def_fcurve}, {"rna_gpencil.c", NULL, RNA_def_gpencil}, -#ifdef WITH_NEW_CURVES_TYPE {"rna_curves.c", NULL, RNA_def_curves}, -#endif {"rna_image.c", "rna_image_api.c", RNA_def_image}, {"rna_key.c", NULL, RNA_def_key}, {"rna_light.c", NULL, RNA_def_light}, diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index 8d2ee0ab534..4003df4d685 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -375,11 +375,9 @@ short RNA_type_to_ID_code(const StructRNA *type) if (base_type == &RNA_FreestyleLineStyle) { return ID_LS; } -# ifdef WITH_NEW_CURVES_TYPE if (base_type == &RNA_Curves) { return ID_CV; } -# endif if (base_type == &RNA_Lattice) { return ID_LT; } @@ -483,11 +481,7 @@ StructRNA *ID_code_to_RNA_type(short idcode) case ID_GR: return &RNA_Collection; case ID_CV: -# ifdef WITH_NEW_CURVES_TYPE return &RNA_Curves; -# else - return &RNA_ID; -# endif case ID_IM: return &RNA_Image; case ID_KE: diff --git a/source/blender/makesrna/intern/rna_camera.c b/source/blender/makesrna/intern/rna_camera.c index 9628c6b2d65..99f8c263da6 100644 --- a/source/blender/makesrna/intern/rna_camera.c +++ b/source/blender/makesrna/intern/rna_camera.c @@ -519,6 +519,12 @@ static void rna_def_camera_dof_settings_data(BlenderRNA *brna) prop, "Focus Object", "Use this object to define the depth of field focal point"); RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Camera_dependency_update"); + prop = RNA_def_property(srna, "focus_subtarget", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "focus_subtarget"); + RNA_def_property_ui_text( + prop, "Focus Bone", "Use this armature bone to define the depth of field focal point"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Camera_dependency_update"); + prop = RNA_def_property(srna, "focus_distance", PROP_FLOAT, PROP_DISTANCE); // RNA_def_property_pointer_sdna(prop, NULL, "focus_distance"); RNA_def_property_range(prop, 0.0f, FLT_MAX); diff --git a/source/blender/makesrna/intern/rna_curves.c b/source/blender/makesrna/intern/rna_curves.c index bc3e5203ed0..cb8b36f41d2 100644 --- a/source/blender/makesrna/intern/rna_curves.c +++ b/source/blender/makesrna/intern/rna_curves.c @@ -64,7 +64,24 @@ static int rna_CurvePoint_index_get_const(const PointerRNA *ptr) { const Curves *curves = rna_curves(ptr); const float(*co)[3] = ptr->data; - return (int)(co - curves->geometry.position); + const float(*positions)[3] = (const float(*)[3])CustomData_get_layer_named( + &curves->geometry.point_data, CD_PROP_FLOAT3, "position"); + return (int)(co - positions); +} + +static int rna_Curves_position_data_length(PointerRNA *ptr) +{ + const Curves *curves = rna_curves(ptr); + return curves->geometry.point_num; +} + +static void rna_Curves_position_data_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) +{ + const Curves *curves = rna_curves(ptr); + const float(*positions)[3] = (const float(*)[3])CustomData_get_layer_named( + &curves->geometry.point_data, CD_PROP_FLOAT3, "position"); + rna_iterator_array_begin( + iter, (void *)positions, sizeof(float[3]), curves->geometry.point_num, false, NULL); } static int rna_CurvePoint_index_get(PointerRNA *ptr) @@ -85,21 +102,23 @@ static void rna_CurvePoint_location_set(PointerRNA *ptr, const float value[3]) static float rna_CurvePoint_radius_get(PointerRNA *ptr) { const Curves *curves = rna_curves(ptr); - if (curves->geometry.radius == NULL) { + const float *radii = (const float *)CustomData_get_layer_named( + &curves->geometry.point_data, CD_PROP_FLOAT, "radius"); + if (radii == NULL) { return 0.0f; } - const float(*co)[3] = ptr->data; - return curves->geometry.radius[co - curves->geometry.position]; + return radii[rna_CurvePoint_index_get_const(ptr)]; } static void rna_CurvePoint_radius_set(PointerRNA *ptr, float value) { const Curves *curves = rna_curves(ptr); - if (curves->geometry.radius == NULL) { + float *radii = (float *)CustomData_get_layer_named( + &curves->geometry.point_data, CD_PROP_FLOAT, "radius"); + if (radii == NULL) { return; } - const float(*co)[3] = ptr->data; - curves->geometry.radius[co - curves->geometry.position] = value; + radii[rna_CurvePoint_index_get_const(ptr)] = value; } static char *rna_CurvePoint_path(const PointerRNA *ptr) @@ -123,16 +142,6 @@ static char *rna_CurveSlice_path(const PointerRNA *ptr) return BLI_sprintfN("curves[%d]", rna_CurveSlice_index_get_const(ptr)); } -static void rna_CurveSlice_points_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) -{ - Curves *curves = rna_curves(ptr); - const int *offset_ptr = (int *)ptr->data; - const int offset = *offset_ptr; - const int size = *(offset_ptr + 1) - offset; - float(*co)[3] = curves->geometry.position + *offset_ptr; - rna_iterator_array_begin(iter, co, sizeof(float[3]), size, 0, NULL); -} - static int rna_CurveSlice_first_point_index_get(PointerRNA *ptr) { const int *offset_ptr = (int *)ptr->data; @@ -146,6 +155,17 @@ static int rna_CurveSlice_points_length_get(PointerRNA *ptr) return *(offset_ptr + 1) - offset; } +static void rna_CurveSlice_points_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) +{ + Curves *curves = rna_curves(ptr); + const int offset = rna_CurveSlice_first_point_index_get(ptr); + const int size = rna_CurveSlice_points_length_get(ptr); + float(*positions)[3] = (float(*)[3])CustomData_get_layer_named( + &curves->geometry.point_data, CD_PROP_FLOAT3, "position"); + float(*co)[3] = positions + offset; + rna_iterator_array_begin(iter, co, sizeof(float[3]), size, 0, NULL); +} + static void rna_Curves_update_data(struct Main *UNUSED(bmain), struct Scene *UNUSED(scene), PointerRNA *ptr) @@ -252,20 +272,32 @@ static void rna_def_curves(BlenderRNA *brna) RNA_def_property_struct_type(prop, "CurveSlice"); RNA_def_property_ui_text(prop, "Curves", "All curves in the data-block"); - /* TODO: better solution for (*co)[3] parsing issue. */ - - RNA_define_verify_sdna(0); prop = RNA_def_property(srna, "points", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_sdna(prop, NULL, "geometry.position", "geometry.point_num"); RNA_def_property_struct_type(prop, "CurvePoint"); + RNA_def_property_collection_funcs(prop, + "rna_Curves_position_data_begin", + "rna_iterator_array_next", + "rna_iterator_array_end", + "rna_iterator_array_get", + "rna_Curves_position_data_length", + NULL, + NULL, + NULL); RNA_def_property_ui_text(prop, "Points", "Control points of all curves"); - RNA_define_verify_sdna(1); /* Direct access to built-in attributes. */ RNA_define_verify_sdna(0); prop = RNA_def_property(srna, "position_data", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_sdna(prop, NULL, "geometry.position", "geometry.point_num"); + RNA_def_property_collection_funcs(prop, + "rna_Curves_position_data_begin", + "rna_iterator_array_next", + "rna_iterator_array_end", + "rna_iterator_array_get", + "rna_Curves_position_data_length", + NULL, + NULL, + NULL); RNA_def_property_struct_type(prop, "FloatVectorAttributeValue"); RNA_def_property_update(prop, 0, "rna_Curves_update_data"); RNA_define_verify_sdna(1); diff --git a/source/blender/makesrna/intern/rna_fcurve.c b/source/blender/makesrna/intern/rna_fcurve.c index ac8aebd2fdd..727d329781d 100644 --- a/source/blender/makesrna/intern/rna_fcurve.c +++ b/source/blender/makesrna/intern/rna_fcurve.c @@ -612,7 +612,7 @@ static void rna_tag_animation_update(Main *bmain, ID *id) static void rna_FCurve_update_data_ex(ID *id, FCurve *fcu, Main *bmain) { sort_time_fcurve(fcu); - calchandles_fcurve(fcu); + BKE_fcurve_handles_recalc(fcu); rna_tag_animation_update(bmain, id); } @@ -752,7 +752,7 @@ static void rna_FModifier_update(Main *bmain, Scene *UNUSED(scene), PointerRNA * FModifier *fcm = (FModifier *)ptr->data; if (fcm->curve && fcm->type == FMODIFIER_TYPE_CYCLES) { - calchandles_fcurve(fcm->curve); + BKE_fcurve_handles_recalc(fcm->curve); } rna_tag_animation_update(bmain, id); @@ -1021,9 +1021,20 @@ static void rna_FKeyframe_points_remove( return; } - delete_fcurve_key(fcu, index, !do_fast); + BKE_fcurve_delete_key(fcu, index); RNA_POINTER_INVALIDATE(bezt_ptr); + if (!do_fast) { + BKE_fcurve_handles_recalc(fcu); + } + + rna_tag_animation_update(bmain, id); +} + +static void rna_FKeyframe_points_clear(ID *id, FCurve *fcu, Main *bmain) +{ + BKE_fcurve_delete_keys_all(fcu); + rna_tag_animation_update(bmain, id); } @@ -2306,6 +2317,10 @@ static void rna_def_fcurve_keyframe_points(BlenderRNA *brna, PropertyRNA *cprop) /* optional */ RNA_def_boolean( func, "fast", 0, "Fast", "Fast keyframe removal to avoid recalculating the curve each time"); + + func = RNA_def_function(srna, "clear", "rna_FKeyframe_points_clear"); + RNA_def_function_ui_description(func, "Remove all keyframes from an F-Curve"); + RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN); } static void rna_def_fcurve(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index dccf7d7a7a9..0647bc62081 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -774,7 +774,7 @@ static bool dash_segment_name_exists_fn(void *arg, const char *name) { const DashGpencilModifierData *dmd = (const DashGpencilModifierData *)arg; for (int i = 0; i < dmd->segments_len; i++) { - if (STREQ(dmd->segments[i].name, name)) { + if (STREQ(dmd->segments[i].name, name) && dmd->segments[i].name != name) { return true; } } @@ -3452,7 +3452,7 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna) prop = RNA_def_property(srna, "use_shadow", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "edge_types", LRT_EDGE_FLAG_PROJECTED_SHADOW); RNA_def_property_ui_text( - prop, "Use Shadow", "Project contour lines using a light shource object"); + prop, "Use Shadow", "Project contour lines using a light source object"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "shadow_region_filtering", PROP_ENUM, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index 6ca8e668fa0..833060e40f8 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -504,9 +504,7 @@ void RNA_def_main_cachefiles(BlenderRNA *brna, PropertyRNA *cprop); void RNA_def_main_paintcurves(BlenderRNA *brna, PropertyRNA *cprop); void RNA_def_main_workspaces(BlenderRNA *brna, PropertyRNA *cprop); void RNA_def_main_lightprobes(BlenderRNA *brna, PropertyRNA *cprop); -#ifdef WITH_NEW_CURVES_TYPE void RNA_def_main_hair_curves(BlenderRNA *brna, PropertyRNA *cprop); -#endif void RNA_def_main_pointclouds(BlenderRNA *brna, PropertyRNA *cprop); void RNA_def_main_volumes(BlenderRNA *brna, PropertyRNA *cprop); #ifdef WITH_SIMULATION_DATABLOCK diff --git a/source/blender/makesrna/intern/rna_main.c b/source/blender/makesrna/intern/rna_main.c index 7200bcaa2a6..4aedb1fc611 100644 --- a/source/blender/makesrna/intern/rna_main.c +++ b/source/blender/makesrna/intern/rna_main.c @@ -96,9 +96,7 @@ RNA_MAIN_LISTBASE_FUNCS_DEF(collections) RNA_MAIN_LISTBASE_FUNCS_DEF(curves) RNA_MAIN_LISTBASE_FUNCS_DEF(fonts) RNA_MAIN_LISTBASE_FUNCS_DEF(gpencils) -# ifdef WITH_NEW_CURVES_TYPE RNA_MAIN_LISTBASE_FUNCS_DEF(hair_curves) -# endif RNA_MAIN_LISTBASE_FUNCS_DEF(images) RNA_MAIN_LISTBASE_FUNCS_DEF(lattices) RNA_MAIN_LISTBASE_FUNCS_DEF(libraries) @@ -375,7 +373,6 @@ void RNA_def_main(BlenderRNA *brna) "Light Probes", "Light Probe data-blocks", RNA_def_main_lightprobes}, -# ifdef WITH_NEW_CURVES_TYPE /** * \note The name `hair_curves` is chosen to be different than `curves`, * but they are generic curve data-blocks, not just for hair. @@ -386,7 +383,6 @@ void RNA_def_main(BlenderRNA *brna) "Hair Curves", "Hair curve data-blocks", RNA_def_main_hair_curves}, -# endif {"pointclouds", "PointCloud", "rna_Main_pointclouds_begin", diff --git a/source/blender/makesrna/intern/rna_main_api.c b/source/blender/makesrna/intern/rna_main_api.c index 6c621604e40..1f21fa3fab9 100644 --- a/source/blender/makesrna/intern/rna_main_api.c +++ b/source/blender/makesrna/intern/rna_main_api.c @@ -749,7 +749,6 @@ static bGPdata *rna_Main_gpencils_new(Main *bmain, const char *name) return gpd; } -# ifdef WITH_NEW_CURVES_TYPE static Curves *rna_Main_hair_curves_new(Main *bmain, const char *name) { char safe_name[MAX_ID_NAME - 2]; @@ -762,7 +761,6 @@ static Curves *rna_Main_hair_curves_new(Main *bmain, const char *name) return curves; } -# endif static PointCloud *rna_Main_pointclouds_new(Main *bmain, const char *name) { @@ -847,9 +845,7 @@ RNA_MAIN_ID_TAG_FUNCS_DEF(cachefiles, cachefiles, ID_CF) RNA_MAIN_ID_TAG_FUNCS_DEF(paintcurves, paintcurves, ID_PC) RNA_MAIN_ID_TAG_FUNCS_DEF(workspaces, workspaces, ID_WS) RNA_MAIN_ID_TAG_FUNCS_DEF(lightprobes, lightprobes, ID_LP) -# ifdef WITH_NEW_CURVES_TYPE RNA_MAIN_ID_TAG_FUNCS_DEF(hair_curves, hair_curves, ID_CV) -# endif RNA_MAIN_ID_TAG_FUNCS_DEF(pointclouds, pointclouds, ID_PT) RNA_MAIN_ID_TAG_FUNCS_DEF(volumes, volumes, ID_VO) # ifdef WITH_SIMULATION_DATABLOCK @@ -2255,7 +2251,6 @@ void RNA_def_main_lightprobes(BlenderRNA *brna, PropertyRNA *cprop) RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); } -# ifdef WITH_NEW_CURVES_TYPE void RNA_def_main_hair_curves(BlenderRNA *brna, PropertyRNA *cprop) { StructRNA *srna; @@ -2299,7 +2294,6 @@ void RNA_def_main_hair_curves(BlenderRNA *brna, PropertyRNA *cprop) parm = RNA_def_boolean(func, "value", 0, "Value", ""); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); } -# endif void RNA_def_main_pointclouds(BlenderRNA *brna, PropertyRNA *cprop) { diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 386ef3f74a3..80217decb13 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -5282,7 +5282,7 @@ static void def_sh_tex_sky(StructRNA *srna) prop = RNA_def_property(srna, "sun_elevation", PROP_FLOAT, PROP_ANGLE); RNA_def_property_ui_text(prop, "Sun Elevation", "Sun angle from horizon"); - RNA_def_property_range(prop, -M_PI_2, M_PI_2); + RNA_def_property_ui_range(prop, -M_PI, M_PI, 1, 2); RNA_def_property_float_default(prop, M_PI_2); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index a68ef361f04..103c77fa808 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -607,11 +607,7 @@ static StructRNA *rna_Object_data_typef(PointerRNA *ptr) case OB_GPENCIL: return &RNA_GreasePencil; case OB_CURVES: -# ifdef WITH_NEW_CURVES_TYPE return &RNA_Curves; -# else - return &RNA_ID; -# endif case OB_POINTCLOUD: return &RNA_PointCloud; case OB_VOLUME: diff --git a/source/blender/makesrna/intern/rna_pointcloud.c b/source/blender/makesrna/intern/rna_pointcloud.c index 4c5dcd5a587..df09bff1aea 100644 --- a/source/blender/makesrna/intern/rna_pointcloud.c +++ b/source/blender/makesrna/intern/rna_pointcloud.c @@ -20,6 +20,7 @@ # include "BLI_math_vector.h" +# include "BKE_customdata.h" # include "BKE_pointcloud.h" # include "DEG_depsgraph.h" @@ -36,7 +37,9 @@ static int rna_Point_index_get_const(const PointerRNA *ptr) { const PointCloud *pointcloud = rna_pointcloud(ptr); const float(*co)[3] = ptr->data; - return (int)(co - pointcloud->co); + const float(*positions)[3] = (const float(*)[3])CustomData_get_layer_named( + &pointcloud->pdata, CD_PROP_FLOAT3, "position"); + return (int)(co - positions); } static int rna_Point_index_get(PointerRNA *ptr) @@ -44,6 +47,21 @@ static int rna_Point_index_get(PointerRNA *ptr) return rna_Point_index_get_const(ptr); } +static int rna_PointCloud_points_length(PointerRNA *ptr) +{ + const PointCloud *pointcloud = rna_pointcloud(ptr); + return pointcloud->totpoint; +} + +static void rna_PointCloud_points_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) +{ + const PointCloud *pointcloud = rna_pointcloud(ptr); + const float(*positions)[3] = (const float(*)[3])CustomData_get_layer_named( + &pointcloud->pdata, CD_PROP_FLOAT3, "position"); + rna_iterator_array_begin( + iter, (void *)positions, sizeof(float[3]), pointcloud->totpoint, false, NULL); +} + static void rna_Point_location_get(PointerRNA *ptr, float value[3]) { copy_v3_v3(value, (const float *)ptr->data); @@ -57,21 +75,22 @@ static void rna_Point_location_set(PointerRNA *ptr, const float value[3]) static float rna_Point_radius_get(PointerRNA *ptr) { const PointCloud *pointcloud = rna_pointcloud(ptr); - if (pointcloud->radius == NULL) { + const float *radii = (const float *)CustomData_get_layer_named( + &pointcloud->pdata, CD_PROP_FLOAT, "radius"); + if (radii == NULL) { return 0.0f; } - const float(*co)[3] = ptr->data; - return pointcloud->radius[co - pointcloud->co]; + return radii[rna_Point_index_get_const(ptr)]; } static void rna_Point_radius_set(PointerRNA *ptr, float value) { - const PointCloud *pointcloud = rna_pointcloud(ptr); - if (pointcloud->radius == NULL) { + PointCloud *pointcloud = rna_pointcloud(ptr); + float *radii = (float *)CustomData_get_layer_named(&pointcloud->pdata, CD_PROP_FLOAT, "radius"); + if (radii == NULL) { return; } - const float(*co)[3] = ptr->data; - pointcloud->radius[co - pointcloud->co] = value; + radii[rna_Point_index_get_const(ptr)] = value; } static char *rna_Point_path(const PointerRNA *ptr) @@ -130,13 +149,18 @@ static void rna_def_pointcloud(BlenderRNA *brna) RNA_def_struct_ui_icon(srna, ICON_POINTCLOUD_DATA); /* geometry */ - /* TODO: better solution for (*co)[3] parsing issue. */ - RNA_define_verify_sdna(0); prop = RNA_def_property(srna, "points", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_sdna(prop, NULL, "co", "totpoint"); RNA_def_property_struct_type(prop, "Point"); + RNA_def_property_collection_funcs(prop, + "rna_PointCloud_points_begin", + "rna_iterator_array_next", + "rna_iterator_array_end", + "rna_iterator_array_get", + "rna_PointCloud_points_length", + NULL, + NULL, + NULL); RNA_def_property_ui_text(prop, "Points", ""); - RNA_define_verify_sdna(1); /* materials */ prop = RNA_def_property(srna, "materials", PROP_COLLECTION, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c index 3de9e632ff6..2e1fa8db7fe 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.c +++ b/source/blender/makesrna/intern/rna_sculpt_paint.c @@ -1303,6 +1303,7 @@ static void rna_def_particle_edit(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "flag", PE_INTERPOLATE_ADDED); RNA_def_property_ui_text( prop, "Interpolate", "Interpolate new particles from the existing ones"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); prop = RNA_def_property(srna, "default_key_count", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "totaddkey"); diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 969d1f2075e..e67d840eeac 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -6585,7 +6585,7 @@ static void rna_def_fileselect_params(BlenderRNA *brna) static const EnumPropertyItem display_size_items[] = { {64, "TINY", 0, "Tiny", ""}, {96, "SMALL", 0, "Small", ""}, - {128, "NORMAL", 0, "Regular", ""}, + {128, "NORMAL", 0, "Medium", ""}, {192, "LARGE", 0, "Large", ""}, {0, NULL, 0, NULL, NULL}, }; diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 25eacdaab30..cfc72791123 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -38,23 +38,6 @@ #include "BLT_lang.h" -#ifdef WITH_OPENSUBDIV -static const EnumPropertyItem opensubdiv_compute_type_items[] = { - {USER_OPENSUBDIV_COMPUTE_NONE, "NONE", 0, "None", ""}, - {USER_OPENSUBDIV_COMPUTE_CPU, "CPU", 0, "CPU", ""}, - {USER_OPENSUBDIV_COMPUTE_OPENMP, "OPENMP", 0, "OpenMP", ""}, - {USER_OPENSUBDIV_COMPUTE_OPENCL, "OPENCL", 0, "OpenCL", ""}, - {USER_OPENSUBDIV_COMPUTE_CUDA, "CUDA", 0, "CUDA", ""}, - {USER_OPENSUBDIV_COMPUTE_GLSL_TRANSFORM_FEEDBACK, - "GLSL_TRANSFORM_FEEDBACK", - 0, - "GLSL Transform Feedback", - ""}, - {USER_OPENSUBDIV_COMPUTE_GLSL_COMPUTE, "GLSL_COMPUTE", 0, "GLSL Compute", ""}, - {0, NULL, 0, NULL, NULL}, -}; -#endif - const EnumPropertyItem rna_enum_preference_section_items[] = { {USER_SECTION_INTERFACE, "INTERFACE", 0, "Interface", ""}, {USER_SECTION_THEME, "THEMES", 0, "Themes", ""}, @@ -189,10 +172,6 @@ static const EnumPropertyItem rna_enum_userdef_viewport_aa_items[] = { # include "UI_interface.h" -# ifdef WITH_OPENSUBDIV -# include "opensubdiv_capi.h" -# endif - # ifdef WITH_SDL_DYNLOAD # include "sdlew.h" # endif @@ -728,55 +707,6 @@ static PointerRNA rna_Theme_space_list_generic_get(PointerRNA *ptr) return rna_pointer_inherit_refine(ptr, &RNA_ThemeSpaceListGeneric, ptr->data); } -# ifdef WITH_OPENSUBDIV -static const EnumPropertyItem *rna_userdef_opensubdiv_compute_type_itemf(bContext *UNUSED(C), - PointerRNA *UNUSED(ptr), - PropertyRNA *UNUSED(prop), - bool *r_free) -{ - EnumPropertyItem *item = NULL; - int totitem = 0; - int evaluators = openSubdiv_getAvailableEvaluators(); - - RNA_enum_items_add_value( - &item, &totitem, opensubdiv_compute_type_items, USER_OPENSUBDIV_COMPUTE_NONE); - -# define APPEND_COMPUTE(compute) \ - if (evaluators & OPENSUBDIV_EVALUATOR_##compute) { \ - RNA_enum_items_add_value( \ - &item, &totitem, opensubdiv_compute_type_items, USER_OPENSUBDIV_COMPUTE_##compute); \ - } \ - ((void)0) - - APPEND_COMPUTE(CPU); - APPEND_COMPUTE(OPENMP); - APPEND_COMPUTE(OPENCL); - APPEND_COMPUTE(CUDA); - APPEND_COMPUTE(GLSL_TRANSFORM_FEEDBACK); - APPEND_COMPUTE(GLSL_COMPUTE); - -# undef APPEND_COMPUTE - - RNA_enum_item_end(&item, &totitem); - *r_free = true; - - return item; -} - -static void rna_userdef_opensubdiv_update(Main *bmain, - Scene *UNUSED(scene), - PointerRNA *UNUSED(ptr)) -{ - Object *object; - - for (object = bmain->objects.first; object; object = object->id.next) { - DEG_id_tag_update(&object->id, ID_RECALC_TRANSFORM); - } - USERDEF_TAG_DIRTY; -} - -# endif - static const EnumPropertyItem *rna_userdef_audio_device_itemf(bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), @@ -5699,17 +5629,6 @@ static void rna_def_userdef_system(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Audio Channels", "Audio channel count"); RNA_def_property_update(prop, 0, "rna_UserDef_audio_update"); -# ifdef WITH_OPENSUBDIV - prop = RNA_def_property(srna, "opensubdiv_compute_type", PROP_ENUM, PROP_NONE); - RNA_def_property_flag(prop, PROP_ENUM_NO_CONTEXT); - RNA_def_property_enum_sdna(prop, NULL, "opensubdiv_compute_type"); - RNA_def_property_enum_items(prop, opensubdiv_compute_type_items); - RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_userdef_opensubdiv_compute_type_itemf"); - RNA_def_property_ui_text( - prop, "OpenSubdiv Compute Type", "Type of computer back-end used with OpenSubdiv"); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_PROPERTIES, "rna_userdef_opensubdiv_update"); -# endif - # ifdef WITH_CYCLES prop = RNA_def_property(srna, "legacy_compute_device_type", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "compute_device_type"); diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c index 7893a2f4bc0..2009f51e1f2 100644 --- a/source/blender/makesrna/intern/rna_wm.c +++ b/source/blender/makesrna/intern/rna_wm.c @@ -607,14 +607,14 @@ static PointerRNA rna_OperatorMacro_properties_get(PointerRNA *ptr) static void rna_Event_ascii_get(PointerRNA *ptr, char *value) { const wmEvent *event = ptr->data; - value[0] = event->ascii; + value[0] = WM_event_utf8_to_ascii(event); value[1] = '\0'; } static int rna_Event_ascii_length(PointerRNA *ptr) { const wmEvent *event = ptr->data; - return (event->ascii) ? 1 : 0; + return WM_event_utf8_to_ascii(event) ? 1 : 0; } static void rna_Event_unicode_get(PointerRNA *ptr, char *value) diff --git a/source/blender/makesrna/intern/rna_wm_api.c b/source/blender/makesrna/intern/rna_wm_api.c index b9f36d35ee8..eb5e2549b1d 100644 --- a/source/blender/makesrna/intern/rna_wm_api.c +++ b/source/blender/makesrna/intern/rna_wm_api.c @@ -639,16 +639,12 @@ static wmEvent *rna_Window_event_add_simulate(wmWindow *win, } /* TODO: validate NDOF. */ - char ascii = 0; if (unicode != NULL) { int len = BLI_str_utf8_size(unicode); if (len == -1 || unicode[len] != '\0') { BKE_report(reports, RPT_ERROR, "Only a single character supported"); return NULL; } - if (len == 1 && isascii(unicode[0])) { - ascii = unicode[0]; - } } wmEvent e = *win->eventstate; @@ -672,10 +668,8 @@ static wmEvent *rna_Window_event_add_simulate(wmWindow *win, e.modifier |= KM_OSKEY; } - e.ascii = '\0'; e.utf8_buf[0] = '\0'; if (unicode != NULL) { - e.ascii = ascii; STRNCPY(e.utf8_buf, unicode); } diff --git a/source/blender/makesrna/intern/rna_wm_gizmo.c b/source/blender/makesrna/intern/rna_wm_gizmo.c index 6c3d96726bb..0e307f5b424 100644 --- a/source/blender/makesrna/intern/rna_wm_gizmo.c +++ b/source/blender/makesrna/intern/rna_wm_gizmo.c @@ -1398,6 +1398,11 @@ static void rna_def_gizmogroup(BlenderRNA *brna) 0, "Tool Init", "Postpone running until tool operator run (when used with a tool)"}, + {WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP, + "TOOL_FALLBACK_KEYMAP", + 0, + "Use fallback tools keymap", + "Add fallback tools keymap to this gizmo type."}, {WM_GIZMOGROUPTYPE_VR_REDRAWS, "VR_REDRAWS", 0, diff --git a/source/blender/makesrna/intern/rna_wm_gizmo_api.c b/source/blender/makesrna/intern/rna_wm_gizmo_api.c index 419dfa68305..760121d2279 100644 --- a/source/blender/makesrna/intern/rna_wm_gizmo_api.c +++ b/source/blender/makesrna/intern/rna_wm_gizmo_api.c @@ -211,7 +211,15 @@ void RNA_api_gizmo(StructRNA *srna) RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); RNA_def_property_multi_array(parm, 2, rna_matrix_dimsize_4x4); RNA_def_property_ui_text(parm, "", "The matrix to transform"); - RNA_def_int(func, "select_id", -1, -1, INT_MAX, "Zero when not selecting", "", -1, INT_MAX); + RNA_def_int(func, + "select_id", + -1, + -1, + INT_MAX, + "ID to use when gizmo is selectable. Use -1 when not selecting", + "", + -1, + INT_MAX); /* draw_preset_box */ func = RNA_def_function(srna, "draw_preset_arrow", "rna_gizmo_draw_preset_arrow"); @@ -221,7 +229,15 @@ void RNA_api_gizmo(StructRNA *srna) RNA_def_property_multi_array(parm, 2, rna_matrix_dimsize_4x4); RNA_def_property_ui_text(parm, "", "The matrix to transform"); RNA_def_enum(func, "axis", rna_enum_object_axis_items, 2, "", "Arrow Orientation"); - RNA_def_int(func, "select_id", -1, -1, INT_MAX, "Zero when not selecting", "", -1, INT_MAX); + RNA_def_int(func, + "select_id", + -1, + -1, + INT_MAX, + "ID to use when gizmo is selectable. Use -1 when not selecting", + "", + -1, + INT_MAX); func = RNA_def_function(srna, "draw_preset_circle", "rna_gizmo_draw_preset_circle"); RNA_def_function_ui_description(func, "Draw a box"); @@ -230,7 +246,15 @@ void RNA_api_gizmo(StructRNA *srna) RNA_def_property_multi_array(parm, 2, rna_matrix_dimsize_4x4); RNA_def_property_ui_text(parm, "", "The matrix to transform"); RNA_def_enum(func, "axis", rna_enum_object_axis_items, 2, "", "Arrow Orientation"); - RNA_def_int(func, "select_id", -1, -1, INT_MAX, "Zero when not selecting", "", -1, INT_MAX); + RNA_def_int(func, + "select_id", + -1, + -1, + INT_MAX, + "ID to use when gizmo is selectable. Use -1 when not selecting", + "", + -1, + INT_MAX); /* -------------------------------------------------------------------- */ /* Other Shapes */ @@ -243,7 +267,15 @@ void RNA_api_gizmo(StructRNA *srna) RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); parm = RNA_def_int(func, "face_map", 0, 0, INT_MAX, "Face map index", "", 0, INT_MAX); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); - RNA_def_int(func, "select_id", -1, -1, INT_MAX, "Zero when not selecting", "", -1, INT_MAX); + RNA_def_int(func, + "select_id", + -1, + -1, + INT_MAX, + "ID to use when gizmo is selectable. Use -1 when not selecting", + "", + -1, + INT_MAX); /* -------------------------------------------------------------------- */ /* Property API */ diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt index 1aac3c2191d..73daabec9b3 100644 --- a/source/blender/modifiers/CMakeLists.txt +++ b/source/blender/modifiers/CMakeLists.txt @@ -217,7 +217,6 @@ endif() if(WITH_EXPERIMENTAL_FEATURES) add_definitions(-DWITH_SIMULATION_DATABLOCK) - add_definitions(-DWITH_NEW_CURVES_TYPE) endif() # So we can have special tricks in modifier system. diff --git a/source/blender/modifiers/intern/MOD_armature.c b/source/blender/modifiers/intern/MOD_armature.c index f6e8a26f0e1..15ad361a262 100644 --- a/source/blender/modifiers/intern/MOD_armature.c +++ b/source/blender/modifiers/intern/MOD_armature.c @@ -266,7 +266,7 @@ static void blendRead(BlendDataReader *UNUSED(reader), ModifierData *md) } ModifierTypeInfo modifierType_Armature = { - /* name */ "Armature", + /* name */ N_("Armature"), /* structName */ "ArmatureModifierData", /* structSize */ sizeof(ArmatureModifierData), /* srna */ &RNA_ArmatureModifier, diff --git a/source/blender/modifiers/intern/MOD_array.c b/source/blender/modifiers/intern/MOD_array.c index 77915702ee4..a7361f6d0b6 100644 --- a/source/blender/modifiers/intern/MOD_array.c +++ b/source/blender/modifiers/intern/MOD_array.c @@ -1002,7 +1002,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_Array = { - /* name */ "Array", + /* name */ N_("Array"), /* structName */ "ArrayModifierData", /* structSize */ sizeof(ArrayModifierData), /* srna */ &RNA_ArrayModifier, diff --git a/source/blender/modifiers/intern/MOD_bevel.c b/source/blender/modifiers/intern/MOD_bevel.c index c634873cfe4..94f2090e081 100644 --- a/source/blender/modifiers/intern/MOD_bevel.c +++ b/source/blender/modifiers/intern/MOD_bevel.c @@ -417,7 +417,7 @@ static void blendRead(BlendDataReader *reader, ModifierData *md) } ModifierTypeInfo modifierType_Bevel = { - /* name */ "Bevel", + /* name */ N_("Bevel"), /* structName */ "BevelModifierData", /* structSize */ sizeof(BevelModifierData), /* srna */ &RNA_BevelModifier, diff --git a/source/blender/modifiers/intern/MOD_boolean.cc b/source/blender/modifiers/intern/MOD_boolean.cc index 09676d0e9ee..c9dc14b3b20 100644 --- a/source/blender/modifiers/intern/MOD_boolean.cc +++ b/source/blender/modifiers/intern/MOD_boolean.cc @@ -639,7 +639,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_Boolean = { - /* name */ "Boolean", + /* name */ N_("Boolean"), /* structName */ "BooleanModifierData", /* structSize */ sizeof(BooleanModifierData), /* srna */ &RNA_BooleanModifier, diff --git a/source/blender/modifiers/intern/MOD_build.c b/source/blender/modifiers/intern/MOD_build.c index 687ff04cedf..a9b6af967be 100644 --- a/source/blender/modifiers/intern/MOD_build.c +++ b/source/blender/modifiers/intern/MOD_build.c @@ -13,6 +13,8 @@ #include "BLI_math_vector.h" #include "BLI_rand.h" +#include "BLT_translation.h" + #include "DNA_defaults.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" @@ -311,7 +313,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_Build = { - /* name */ "Build", + /* name */ N_("Build"), /* structName */ "BuildModifierData", /* structSize */ sizeof(BuildModifierData), /* srna */ &RNA_BuildModifier, diff --git a/source/blender/modifiers/intern/MOD_cast.c b/source/blender/modifiers/intern/MOD_cast.c index 3916551bc90..9aaf7fead36 100644 --- a/source/blender/modifiers/intern/MOD_cast.c +++ b/source/blender/modifiers/intern/MOD_cast.c @@ -559,7 +559,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_Cast = { - /* name */ "Cast", + /* name */ N_("Cast"), /* structName */ "CastModifierData", /* structSize */ sizeof(CastModifierData), /* srna */ &RNA_CastModifier, diff --git a/source/blender/modifiers/intern/MOD_cloth.c b/source/blender/modifiers/intern/MOD_cloth.c index 185b05b4cf9..cc0bd87d614 100644 --- a/source/blender/modifiers/intern/MOD_cloth.c +++ b/source/blender/modifiers/intern/MOD_cloth.c @@ -275,7 +275,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_Cloth = { - /* name */ "Cloth", + /* name */ N_("Cloth"), /* structName */ "ClothModifierData", /* structSize */ sizeof(ClothModifierData), /* srna */ &RNA_ClothModifier, diff --git a/source/blender/modifiers/intern/MOD_collision.c b/source/blender/modifiers/intern/MOD_collision.c index f9cd3d5937d..74cb4ac700a 100644 --- a/source/blender/modifiers/intern/MOD_collision.c +++ b/source/blender/modifiers/intern/MOD_collision.c @@ -284,7 +284,7 @@ static void blendRead(BlendDataReader *UNUSED(reader), ModifierData *md) } ModifierTypeInfo modifierType_Collision = { - /* name */ "Collision", + /* name */ N_("Collision"), /* structName */ "CollisionModifierData", /* structSize */ sizeof(CollisionModifierData), /* srna */ &RNA_CollisionModifier, diff --git a/source/blender/modifiers/intern/MOD_correctivesmooth.c b/source/blender/modifiers/intern/MOD_correctivesmooth.c index 3698f4403a1..2beb1be6749 100644 --- a/source/blender/modifiers/intern/MOD_correctivesmooth.c +++ b/source/blender/modifiers/intern/MOD_correctivesmooth.c @@ -835,7 +835,7 @@ static void blendRead(BlendDataReader *reader, ModifierData *md) } ModifierTypeInfo modifierType_CorrectiveSmooth = { - /* name */ "CorrectiveSmooth", + /* name */ N_("CorrectiveSmooth"), /* structName */ "CorrectiveSmoothModifierData", /* structSize */ sizeof(CorrectiveSmoothModifierData), /* srna */ &RNA_CorrectiveSmoothModifier, diff --git a/source/blender/modifiers/intern/MOD_curve.c b/source/blender/modifiers/intern/MOD_curve.c index a82b999f4dc..48a59f4d949 100644 --- a/source/blender/modifiers/intern/MOD_curve.c +++ b/source/blender/modifiers/intern/MOD_curve.c @@ -203,7 +203,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_Curve = { - /* name */ "Curve", + /* name */ N_("Curve"), /* structName */ "CurveModifierData", /* structSize */ sizeof(CurveModifierData), /* srna */ &RNA_CurveModifier, diff --git a/source/blender/modifiers/intern/MOD_datatransfer.c b/source/blender/modifiers/intern/MOD_datatransfer.c index a3b088799cc..e9f1cf47e38 100644 --- a/source/blender/modifiers/intern/MOD_datatransfer.c +++ b/source/blender/modifiers/intern/MOD_datatransfer.c @@ -474,7 +474,7 @@ static void panelRegister(ARegionType *region_type) #undef DT_TYPES_AFFECT_MESH ModifierTypeInfo modifierType_DataTransfer = { - /* name */ "DataTransfer", + /* name */ N_("DataTransfer"), /* structName */ "DataTransferModifierData", /* structSize */ sizeof(DataTransferModifierData), /* srna */ &RNA_DataTransferModifier, diff --git a/source/blender/modifiers/intern/MOD_decimate.c b/source/blender/modifiers/intern/MOD_decimate.c index 70f028d6907..3df4fbcbea8 100644 --- a/source/blender/modifiers/intern/MOD_decimate.c +++ b/source/blender/modifiers/intern/MOD_decimate.c @@ -271,7 +271,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_Decimate = { - /* name */ "Decimate", + /* name */ N_("Decimate"), /* structName */ "DecimateModifierData", /* structSize */ sizeof(DecimateModifierData), /* srna */ &RNA_DecimateModifier, diff --git a/source/blender/modifiers/intern/MOD_displace.c b/source/blender/modifiers/intern/MOD_displace.c index 1e2224e3a65..5289fc42e21 100644 --- a/source/blender/modifiers/intern/MOD_displace.c +++ b/source/blender/modifiers/intern/MOD_displace.c @@ -473,7 +473,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_Displace = { - /* name */ "Displace", + /* name */ N_("Displace"), /* structName */ "DisplaceModifierData", /* structSize */ sizeof(DisplaceModifierData), /* srna */ &RNA_DisplaceModifier, diff --git a/source/blender/modifiers/intern/MOD_dynamicpaint.c b/source/blender/modifiers/intern/MOD_dynamicpaint.c index 6b0578c77f1..4afb81c04a9 100644 --- a/source/blender/modifiers/intern/MOD_dynamicpaint.c +++ b/source/blender/modifiers/intern/MOD_dynamicpaint.c @@ -189,7 +189,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_DynamicPaint = { - /* name */ "Dynamic Paint", + /* name */ N_("Dynamic Paint"), /* structName */ "DynamicPaintModifierData", /* structSize */ sizeof(DynamicPaintModifierData), /* srna */ &RNA_DynamicPaintModifier, diff --git a/source/blender/modifiers/intern/MOD_edgesplit.c b/source/blender/modifiers/intern/MOD_edgesplit.c index 49ddcb9a61d..b381ff32aa2 100644 --- a/source/blender/modifiers/intern/MOD_edgesplit.c +++ b/source/blender/modifiers/intern/MOD_edgesplit.c @@ -154,7 +154,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_EdgeSplit = { - /* name */ "EdgeSplit", + /* name */ N_("EdgeSplit"), /* structName */ "EdgeSplitModifierData", /* structSize */ sizeof(EdgeSplitModifierData), /* srna */ &RNA_EdgeSplitModifier, diff --git a/source/blender/modifiers/intern/MOD_explode.c b/source/blender/modifiers/intern/MOD_explode.c index f88e930e127..ff0616fd288 100644 --- a/source/blender/modifiers/intern/MOD_explode.c +++ b/source/blender/modifiers/intern/MOD_explode.c @@ -1224,7 +1224,7 @@ static void blendRead(BlendDataReader *UNUSED(reader), ModifierData *md) } ModifierTypeInfo modifierType_Explode = { - /* name */ "Explode", + /* name */ N_("Explode"), /* structName */ "ExplodeModifierData", /* structSize */ sizeof(ExplodeModifierData), /* srna */ &RNA_ExplodeModifier, diff --git a/source/blender/modifiers/intern/MOD_fluid.c b/source/blender/modifiers/intern/MOD_fluid.c index 562f0df510d..a3e9cd083d2 100644 --- a/source/blender/modifiers/intern/MOD_fluid.c +++ b/source/blender/modifiers/intern/MOD_fluid.c @@ -241,7 +241,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_Fluid = { - /* name */ "Fluid", + /* name */ N_("Fluid"), /* structName */ "FluidModifierData", /* structSize */ sizeof(FluidModifierData), /* srna */ &RNA_FluidModifier, diff --git a/source/blender/modifiers/intern/MOD_hook.c b/source/blender/modifiers/intern/MOD_hook.c index 3649ece12e1..3c4e6b0d90f 100644 --- a/source/blender/modifiers/intern/MOD_hook.c +++ b/source/blender/modifiers/intern/MOD_hook.c @@ -545,7 +545,7 @@ static void blendRead(BlendDataReader *reader, ModifierData *md) } ModifierTypeInfo modifierType_Hook = { - /* name */ "Hook", + /* name */ N_("Hook"), /* structName */ "HookModifierData", /* structSize */ sizeof(HookModifierData), /* srna */ &RNA_HookModifier, diff --git a/source/blender/modifiers/intern/MOD_laplaciandeform.c b/source/blender/modifiers/intern/MOD_laplaciandeform.c index a22f4b35e0d..e29098eb218 100644 --- a/source/blender/modifiers/intern/MOD_laplaciandeform.c +++ b/source/blender/modifiers/intern/MOD_laplaciandeform.c @@ -875,7 +875,7 @@ static void blendRead(BlendDataReader *reader, ModifierData *md) } ModifierTypeInfo modifierType_LaplacianDeform = { - /* name */ "LaplacianDeform", + /* name */ N_("LaplacianDeform"), /* structName */ "LaplacianDeformModifierData", /* structSize */ sizeof(LaplacianDeformModifierData), /* srna */ &RNA_LaplacianDeformModifier, diff --git a/source/blender/modifiers/intern/MOD_laplaciansmooth.c b/source/blender/modifiers/intern/MOD_laplaciansmooth.c index 95283b1cd20..2cce0c14e4c 100644 --- a/source/blender/modifiers/intern/MOD_laplaciansmooth.c +++ b/source/blender/modifiers/intern/MOD_laplaciansmooth.c @@ -608,7 +608,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_LaplacianSmooth = { - /* name */ "LaplacianSmooth", + /* name */ N_("LaplacianSmooth"), /* structName */ "LaplacianSmoothModifierData", /* structSize */ sizeof(LaplacianSmoothModifierData), /* srna */ &RNA_LaplacianSmoothModifier, diff --git a/source/blender/modifiers/intern/MOD_lattice.c b/source/blender/modifiers/intern/MOD_lattice.c index 832372304a0..0e1994eed36 100644 --- a/source/blender/modifiers/intern/MOD_lattice.c +++ b/source/blender/modifiers/intern/MOD_lattice.c @@ -160,7 +160,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_Lattice = { - /* name */ "Lattice", + /* name */ N_("Lattice"), /* structName */ "LatticeModifierData", /* structSize */ sizeof(LatticeModifierData), /* srna */ &RNA_LatticeModifier, diff --git a/source/blender/modifiers/intern/MOD_mask.cc b/source/blender/modifiers/intern/MOD_mask.cc index 0813901fc49..fac3ea36537 100644 --- a/source/blender/modifiers/intern/MOD_mask.cc +++ b/source/blender/modifiers/intern/MOD_mask.cc @@ -804,7 +804,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_Mask = { - /* name */ "Mask", + /* name */ N_("Mask"), /* structName */ "MaskModifierData", /* structSize */ sizeof(MaskModifierData), /* srna */ &RNA_MaskModifier, diff --git a/source/blender/modifiers/intern/MOD_mesh_to_volume.cc b/source/blender/modifiers/intern/MOD_mesh_to_volume.cc index 01c1875a760..7d4affa2dce 100644 --- a/source/blender/modifiers/intern/MOD_mesh_to_volume.cc +++ b/source/blender/modifiers/intern/MOD_mesh_to_volume.cc @@ -7,6 +7,7 @@ #include <vector> #include "BKE_geometry_set.hh" +#include "BKE_lib_id.h" #include "BKE_lib_query.h" #include "BKE_mesh_runtime.h" #include "BKE_mesh_wrapper.h" @@ -14,6 +15,8 @@ #include "BKE_object.h" #include "BKE_volume.h" +#include "BLT_translation.h" + #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_object_types.h" @@ -161,7 +164,13 @@ static Volume *mesh_to_volume(ModifierData *md, mesh_to_own_object_space_transform); /* Create a new volume. */ - Volume *volume = BKE_volume_new_for_eval(input_volume); + Volume *volume; + if (input_volume == nullptr) { + volume = static_cast<Volume *>(BKE_id_new_nomain(ID_VO, "Volume")); + } + else { + volume = BKE_volume_new_for_eval(input_volume); + } /* Convert mesh to grid and add to volume. */ geometry::volume_grid_add_from_mesh(volume, @@ -195,7 +204,7 @@ static void modifyGeometrySet(ModifierData *md, } ModifierTypeInfo modifierType_MeshToVolume = { - /* name */ "Mesh to Volume", + /* name */ N_("Mesh to Volume"), /* structName */ "MeshToVolumeModifierData", /* structSize */ sizeof(MeshToVolumeModifierData), /* srna */ &RNA_MeshToVolumeModifier, diff --git a/source/blender/modifiers/intern/MOD_meshcache.c b/source/blender/modifiers/intern/MOD_meshcache.c index 6f065797b43..8dfdd07ace9 100644 --- a/source/blender/modifiers/intern/MOD_meshcache.c +++ b/source/blender/modifiers/intern/MOD_meshcache.c @@ -416,7 +416,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_MeshCache = { - /* name */ "MeshCache", + /* name */ N_("MeshCache"), /* structName */ "MeshCacheModifierData", /* structSize */ sizeof(MeshCacheModifierData), /* srna */ &RNA_MeshCacheModifier, diff --git a/source/blender/modifiers/intern/MOD_meshdeform.c b/source/blender/modifiers/intern/MOD_meshdeform.c index 5dac31ac1df..40aa0f84f92 100644 --- a/source/blender/modifiers/intern/MOD_meshdeform.c +++ b/source/blender/modifiers/intern/MOD_meshdeform.c @@ -650,7 +650,7 @@ static void blendRead(BlendDataReader *reader, ModifierData *md) } ModifierTypeInfo modifierType_MeshDeform = { - /* name */ "MeshDeform", + /* name */ N_("MeshDeform"), /* structName */ "MeshDeformModifierData", /* structSize */ sizeof(MeshDeformModifierData), /* srna */ &RNA_MeshDeformModifier, diff --git a/source/blender/modifiers/intern/MOD_meshsequencecache.cc b/source/blender/modifiers/intern/MOD_meshsequencecache.cc index 273050eafd8..1c35160d3ef 100644 --- a/source/blender/modifiers/intern/MOD_meshsequencecache.cc +++ b/source/blender/modifiers/intern/MOD_meshsequencecache.cc @@ -392,7 +392,7 @@ static void blendRead(BlendDataReader *UNUSED(reader), ModifierData *md) } ModifierTypeInfo modifierType_MeshSequenceCache = { - /* name */ "MeshSequenceCache", + /* name */ N_("MeshSequenceCache"), /* structName */ "MeshSeqCacheModifierData", /* structSize */ sizeof(MeshSeqCacheModifierData), /* srna */ &RNA_MeshSequenceCacheModifier, diff --git a/source/blender/modifiers/intern/MOD_mirror.c b/source/blender/modifiers/intern/MOD_mirror.c index b6ba8c9e0f9..5f095a72dca 100644 --- a/source/blender/modifiers/intern/MOD_mirror.c +++ b/source/blender/modifiers/intern/MOD_mirror.c @@ -204,7 +204,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_Mirror = { - /* name */ "Mirror", + /* name */ N_("Mirror"), /* structName */ "MirrorModifierData", /* structSize */ sizeof(MirrorModifierData), /* srna */ &RNA_MirrorModifier, diff --git a/source/blender/modifiers/intern/MOD_multires.c b/source/blender/modifiers/intern/MOD_multires.c index a4c5ddac5c9..cdad834f9b4 100644 --- a/source/blender/modifiers/intern/MOD_multires.c +++ b/source/blender/modifiers/intern/MOD_multires.c @@ -487,7 +487,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_Multires = { - /* name */ "Multires", + /* name */ N_("Multires"), /* structName */ "MultiresModifierData", /* structSize */ sizeof(MultiresModifierData), /* srna */ &RNA_MultiresModifier, diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 885d2f901ec..01e4d5ff6b3 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -1825,7 +1825,7 @@ static void requiredDataMask(Object *UNUSED(ob), } ModifierTypeInfo modifierType_Nodes = { - /* name */ "GeometryNodes", + /* name */ N_("GeometryNodes"), /* structName */ "NodesModifierData", /* structSize */ sizeof(NodesModifierData), /* srna */ &RNA_NodesModifier, diff --git a/source/blender/modifiers/intern/MOD_normal_edit.c b/source/blender/modifiers/intern/MOD_normal_edit.c index c215ac601a1..09bc9546325 100644 --- a/source/blender/modifiers/intern/MOD_normal_edit.c +++ b/source/blender/modifiers/intern/MOD_normal_edit.c @@ -756,7 +756,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_NormalEdit = { - /* name */ "NormalEdit", + /* name */ N_("NormalEdit"), /* structName */ "NormalEditModifierData", /* structSize */ sizeof(NormalEditModifierData), /* srna */ &RNA_NormalEditModifier, diff --git a/source/blender/modifiers/intern/MOD_ocean.c b/source/blender/modifiers/intern/MOD_ocean.c index 75708851030..ea9049200cc 100644 --- a/source/blender/modifiers/intern/MOD_ocean.c +++ b/source/blender/modifiers/intern/MOD_ocean.c @@ -701,7 +701,7 @@ static void blendRead(BlendDataReader *UNUSED(reader), ModifierData *md) } ModifierTypeInfo modifierType_Ocean = { - /* name */ "Ocean", + /* name */ N_("Ocean"), /* structName */ "OceanModifierData", /* structSize */ sizeof(OceanModifierData), /* srna */ &RNA_OceanModifier, diff --git a/source/blender/modifiers/intern/MOD_particleinstance.c b/source/blender/modifiers/intern/MOD_particleinstance.c index 2a8d2454670..5018b2d1030 100644 --- a/source/blender/modifiers/intern/MOD_particleinstance.c +++ b/source/blender/modifiers/intern/MOD_particleinstance.c @@ -644,7 +644,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_ParticleInstance = { - /* name */ "ParticleInstance", + /* name */ N_("ParticleInstance"), /* structName */ "ParticleInstanceModifierData", /* structSize */ sizeof(ParticleInstanceModifierData), /* srna */ &RNA_ParticleInstanceModifier, diff --git a/source/blender/modifiers/intern/MOD_particlesystem.cc b/source/blender/modifiers/intern/MOD_particlesystem.cc index ccbc8f1d835..7f7465947f9 100644 --- a/source/blender/modifiers/intern/MOD_particlesystem.cc +++ b/source/blender/modifiers/intern/MOD_particlesystem.cc @@ -301,7 +301,7 @@ static void blendRead(BlendDataReader *reader, ModifierData *md) } ModifierTypeInfo modifierType_ParticleSystem = { - /* name */ "ParticleSystem", + /* name */ N_("ParticleSystem"), /* structName */ "ParticleSystemModifierData", /* structSize */ sizeof(ParticleSystemModifierData), /* srna */ &RNA_ParticleSystemModifier, diff --git a/source/blender/modifiers/intern/MOD_remesh.c b/source/blender/modifiers/intern/MOD_remesh.c index 288faee247f..f21d536fadf 100644 --- a/source/blender/modifiers/intern/MOD_remesh.c +++ b/source/blender/modifiers/intern/MOD_remesh.c @@ -268,7 +268,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_Remesh = { - /* name */ "Remesh", + /* name */ N_("Remesh"), /* structName */ "RemeshModifierData", /* structSize */ sizeof(RemeshModifierData), /* srna */ &RNA_RemeshModifier, diff --git a/source/blender/modifiers/intern/MOD_screw.c b/source/blender/modifiers/intern/MOD_screw.c index 0e22f59c2fb..9588b9acd3b 100644 --- a/source/blender/modifiers/intern/MOD_screw.c +++ b/source/blender/modifiers/intern/MOD_screw.c @@ -1235,7 +1235,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_Screw = { - /* name */ "Screw", + /* name */ N_("Screw"), /* structName */ "ScrewModifierData", /* structSize */ sizeof(ScrewModifierData), /* srna */ &RNA_ScrewModifier, diff --git a/source/blender/modifiers/intern/MOD_shapekey.c b/source/blender/modifiers/intern/MOD_shapekey.c index 6953e89dfc2..cc214c1147a 100644 --- a/source/blender/modifiers/intern/MOD_shapekey.c +++ b/source/blender/modifiers/intern/MOD_shapekey.c @@ -9,6 +9,8 @@ #include "BLI_math.h" +#include "BLT_translation.h" + #include "DNA_key_types.h" #include "DNA_mesh_types.h" #include "DNA_object_types.h" @@ -108,7 +110,7 @@ static void deformMatricesEM(ModifierData *UNUSED(md), } ModifierTypeInfo modifierType_ShapeKey = { - /* name */ "ShapeKey", + /* name */ N_("ShapeKey"), /* structName */ "ShapeKeyModifierData", /* structSize */ sizeof(ShapeKeyModifierData), /* srna */ &RNA_Modifier, diff --git a/source/blender/modifiers/intern/MOD_shrinkwrap.c b/source/blender/modifiers/intern/MOD_shrinkwrap.c index 488df3d6f4c..be12dc6639b 100644 --- a/source/blender/modifiers/intern/MOD_shrinkwrap.c +++ b/source/blender/modifiers/intern/MOD_shrinkwrap.c @@ -260,7 +260,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_Shrinkwrap = { - /* name */ "Shrinkwrap", + /* name */ N_("Shrinkwrap"), /* structName */ "ShrinkwrapModifierData", /* structSize */ sizeof(ShrinkwrapModifierData), /* srna */ &RNA_ShrinkwrapModifier, diff --git a/source/blender/modifiers/intern/MOD_simpledeform.c b/source/blender/modifiers/intern/MOD_simpledeform.c index e3c7f1c423b..9f1d0cd36c4 100644 --- a/source/blender/modifiers/intern/MOD_simpledeform.c +++ b/source/blender/modifiers/intern/MOD_simpledeform.c @@ -564,7 +564,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_SimpleDeform = { - /* name */ "SimpleDeform", + /* name */ N_("SimpleDeform"), /* structName */ "SimpleDeformModifierData", /* structSize */ sizeof(SimpleDeformModifierData), /* srna */ &RNA_SimpleDeformModifier, diff --git a/source/blender/modifiers/intern/MOD_skin.c b/source/blender/modifiers/intern/MOD_skin.c index 5f238209015..84795cdb2d9 100644 --- a/source/blender/modifiers/intern/MOD_skin.c +++ b/source/blender/modifiers/intern/MOD_skin.c @@ -2069,7 +2069,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_Skin = { - /* name */ "Skin", + /* name */ N_("Skin"), /* structName */ "SkinModifierData", /* structSize */ sizeof(SkinModifierData), /* srna */ &RNA_SkinModifier, diff --git a/source/blender/modifiers/intern/MOD_smooth.c b/source/blender/modifiers/intern/MOD_smooth.c index 5439083d9a7..c868c47cb90 100644 --- a/source/blender/modifiers/intern/MOD_smooth.c +++ b/source/blender/modifiers/intern/MOD_smooth.c @@ -253,7 +253,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_Smooth = { - /* name */ "Smooth", + /* name */ N_("Smooth"), /* structName */ "SmoothModifierData", /* structSize */ sizeof(SmoothModifierData), /* srna */ &RNA_SmoothModifier, diff --git a/source/blender/modifiers/intern/MOD_softbody.c b/source/blender/modifiers/intern/MOD_softbody.c index d8379cc870a..a49f2609641 100644 --- a/source/blender/modifiers/intern/MOD_softbody.c +++ b/source/blender/modifiers/intern/MOD_softbody.c @@ -86,7 +86,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_Softbody = { - /* name */ "Softbody", + /* name */ N_("Softbody"), /* structName */ "SoftbodyModifierData", /* structSize */ sizeof(SoftbodyModifierData), /* srna */ &RNA_SoftBodyModifier, diff --git a/source/blender/modifiers/intern/MOD_solidify.c b/source/blender/modifiers/intern/MOD_solidify.c index 1f0aee7d689..3e2d590c928 100644 --- a/source/blender/modifiers/intern/MOD_solidify.c +++ b/source/blender/modifiers/intern/MOD_solidify.c @@ -241,7 +241,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_Solidify = { - /* name */ "Solidify", + /* name */ N_("Solidify"), /* structName */ "SolidifyModifierData", /* structSize */ sizeof(SolidifyModifierData), /* srna */ &RNA_SolidifyModifier, diff --git a/source/blender/modifiers/intern/MOD_subsurf.c b/source/blender/modifiers/intern/MOD_subsurf.c index 23f447f2469..8faf2bdbea2 100644 --- a/source/blender/modifiers/intern/MOD_subsurf.c +++ b/source/blender/modifiers/intern/MOD_subsurf.c @@ -472,7 +472,7 @@ static void blendRead(BlendDataReader *UNUSED(reader), ModifierData *md) } ModifierTypeInfo modifierType_Subsurf = { - /* name */ "Subdivision", + /* name */ N_("Subdivision"), /* structName */ "SubsurfModifierData", /* structSize */ sizeof(SubsurfModifierData), /* srna */ &RNA_SubsurfModifier, diff --git a/source/blender/modifiers/intern/MOD_surface.c b/source/blender/modifiers/intern/MOD_surface.c index af1de77d8c9..8cfe3b35949 100644 --- a/source/blender/modifiers/intern/MOD_surface.c +++ b/source/blender/modifiers/intern/MOD_surface.c @@ -211,7 +211,7 @@ static void blendRead(BlendDataReader *UNUSED(reader), ModifierData *md) } ModifierTypeInfo modifierType_Surface = { - /* name */ "Surface", + /* name */ N_("Surface"), /* structName */ "SurfaceModifierData", /* structSize */ sizeof(SurfaceModifierData), /* srna */ &RNA_SurfaceModifier, diff --git a/source/blender/modifiers/intern/MOD_surfacedeform.c b/source/blender/modifiers/intern/MOD_surfacedeform.c index 20cc9b2392f..edc6819a26c 100644 --- a/source/blender/modifiers/intern/MOD_surfacedeform.c +++ b/source/blender/modifiers/intern/MOD_surfacedeform.c @@ -1521,11 +1521,11 @@ static void surfacedeformModifier_do(ModifierData *md, * added after the original ones. This covers typical case when target was at the subdivision * level 0 and then subdivision was increased (i.e. for the render purposes). */ - BKE_modifier_set_error(ob, - md, - "Target vertices changed from %u to %u, continuing anyway", - smd->target_verts_num, - target_verts_num); + BKE_modifier_set_warning(ob, + md, + "Target vertices changed from %u to %u, continuing anyway", + smd->target_verts_num, + target_verts_num); /* In theory we only need the `smd->verts_num` vertices in the `targetCos` for evaluation, but * it is not currently possible to request a subset of coordinates: the API expects that the @@ -1741,7 +1741,7 @@ static void blendRead(BlendDataReader *reader, ModifierData *md) } ModifierTypeInfo modifierType_SurfaceDeform = { - /* name */ "SurfaceDeform", + /* name */ N_("SurfaceDeform"), /* structName */ "SurfaceDeformModifierData", /* structSize */ sizeof(SurfaceDeformModifierData), /* srna */ &RNA_SurfaceDeformModifier, diff --git a/source/blender/modifiers/intern/MOD_triangulate.c b/source/blender/modifiers/intern/MOD_triangulate.c index f1e8ef5bf38..d4faf682cdc 100644 --- a/source/blender/modifiers/intern/MOD_triangulate.c +++ b/source/blender/modifiers/intern/MOD_triangulate.c @@ -139,7 +139,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_Triangulate = { - /* name */ "Triangulate", + /* name */ N_("Triangulate"), /* structName */ "TriangulateModifierData", /* structSize */ sizeof(TriangulateModifierData), /* srna */ &RNA_TriangulateModifier, diff --git a/source/blender/modifiers/intern/MOD_uvproject.c b/source/blender/modifiers/intern/MOD_uvproject.c index d4d7ecef283..0474d3e47e6 100644 --- a/source/blender/modifiers/intern/MOD_uvproject.c +++ b/source/blender/modifiers/intern/MOD_uvproject.c @@ -349,7 +349,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_UVProject = { - /* name */ "UVProject", + /* name */ N_("UVProject"), /* structName */ "UVProjectModifierData", /* structSize */ sizeof(UVProjectModifierData), /* srna */ &RNA_UVProjectModifier, diff --git a/source/blender/modifiers/intern/MOD_uvwarp.c b/source/blender/modifiers/intern/MOD_uvwarp.c index a15efdaa381..c33b25c38e3 100644 --- a/source/blender/modifiers/intern/MOD_uvwarp.c +++ b/source/blender/modifiers/intern/MOD_uvwarp.c @@ -308,7 +308,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_UVWarp = { - /* name */ "UVWarp", + /* name */ N_("UVWarp"), /* structName */ "UVWarpModifierData", /* structSize */ sizeof(UVWarpModifierData), /* srna */ &RNA_UVWarpModifier, diff --git a/source/blender/modifiers/intern/MOD_volume_displace.cc b/source/blender/modifiers/intern/MOD_volume_displace.cc index 059cfdbdd4e..d9b94d79348 100644 --- a/source/blender/modifiers/intern/MOD_volume_displace.cc +++ b/source/blender/modifiers/intern/MOD_volume_displace.cc @@ -12,6 +12,8 @@ #include "BKE_texture.h" #include "BKE_volume.h" +#include "BLT_translation.h" + #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_object_types.h" @@ -307,7 +309,7 @@ static void modifyGeometrySet(ModifierData *md, } ModifierTypeInfo modifierType_VolumeDisplace = { - /* name */ "Volume Displace", + /* name */ N_("Volume Displace"), /* structName */ "VolumeDisplaceModifierData", /* structSize */ sizeof(VolumeDisplaceModifierData), /* srna */ &RNA_VolumeDisplaceModifier, diff --git a/source/blender/modifiers/intern/MOD_volume_to_mesh.cc b/source/blender/modifiers/intern/MOD_volume_to_mesh.cc index d33687e4d92..3292f73137a 100644 --- a/source/blender/modifiers/intern/MOD_volume_to_mesh.cc +++ b/source/blender/modifiers/intern/MOD_volume_to_mesh.cc @@ -12,6 +12,8 @@ #include "BKE_volume.h" #include "BKE_volume_to_mesh.hh" +#include "BLT_translation.h" + #include "MOD_modifiertypes.h" #include "MOD_ui_common.h" @@ -193,7 +195,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * } ModifierTypeInfo modifierType_VolumeToMesh = { - /* name */ "Volume to Mesh", + /* name */ N_("Volume to Mesh"), /* structName */ "VolumeToMeshModifierData", /* structSize */ sizeof(VolumeToMeshModifierData), /* srna */ &RNA_VolumeToMeshModifier, diff --git a/source/blender/modifiers/intern/MOD_warp.c b/source/blender/modifiers/intern/MOD_warp.c index 9693cf0c0f2..afdc230a877 100644 --- a/source/blender/modifiers/intern/MOD_warp.c +++ b/source/blender/modifiers/intern/MOD_warp.c @@ -510,7 +510,7 @@ static void blendRead(BlendDataReader *reader, ModifierData *md) } ModifierTypeInfo modifierType_Warp = { - /* name */ "Warp", + /* name */ N_("Warp"), /* structName */ "WarpModifierData", /* structSize */ sizeof(WarpModifierData), /* srna */ &RNA_WarpModifier, diff --git a/source/blender/modifiers/intern/MOD_wave.c b/source/blender/modifiers/intern/MOD_wave.c index 4073f028db5..b92e3a0fa9d 100644 --- a/source/blender/modifiers/intern/MOD_wave.c +++ b/source/blender/modifiers/intern/MOD_wave.c @@ -372,7 +372,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) uiItemR(sub, ptr, "use_normal_z", UI_ITEM_R_TOGGLE, "Z", ICON_NONE); col = uiLayoutColumn(layout, false); - uiItemR(col, ptr, "falloff_radius", 0, "Falloff", ICON_NONE); + uiItemR(col, ptr, "falloff_radius", 0, IFACE_("Falloff"), ICON_NONE); uiItemR(col, ptr, "height", UI_ITEM_R_SLIDER, NULL, ICON_NONE); uiItemR(col, ptr, "width", UI_ITEM_R_SLIDER, NULL, ICON_NONE); uiItemR(col, ptr, "narrowness", UI_ITEM_R_SLIDER, NULL, ICON_NONE); @@ -394,7 +394,7 @@ static void position_panel_draw(const bContext *UNUSED(C), Panel *panel) uiItemR(layout, ptr, "start_position_object", 0, IFACE_("Object"), ICON_NONE); col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "start_position_x", 0, "Start Position X", ICON_NONE); + uiItemR(col, ptr, "start_position_x", 0, IFACE_("Start Position X"), ICON_NONE); uiItemR(col, ptr, "start_position_y", 0, "Y", ICON_NONE); } @@ -408,9 +408,9 @@ static void time_panel_draw(const bContext *UNUSED(C), Panel *panel) uiLayoutSetPropSep(layout, true); col = uiLayoutColumn(layout, false); - uiItemR(col, ptr, "time_offset", 0, "Offset", ICON_NONE); - uiItemR(col, ptr, "lifetime", 0, "Life", ICON_NONE); - uiItemR(col, ptr, "damping_time", 0, "Damping", ICON_NONE); + uiItemR(col, ptr, "time_offset", 0, IFACE_("Offset"), ICON_NONE); + uiItemR(col, ptr, "lifetime", 0, IFACE_("Life"), ICON_NONE); + uiItemR(col, ptr, "damping_time", 0, IFACE_("Damping"), ICON_NONE); uiItemR(col, ptr, "speed", UI_ITEM_R_SLIDER, NULL, ICON_NONE); } @@ -462,7 +462,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_Wave = { - /* name */ "Wave", + /* name */ N_("Wave"), /* structName */ "WaveModifierData", /* structSize */ sizeof(WaveModifierData), /* srna */ &RNA_WaveModifier, diff --git a/source/blender/modifiers/intern/MOD_weighted_normal.c b/source/blender/modifiers/intern/MOD_weighted_normal.c index d436acb8ad5..af992c00097 100644 --- a/source/blender/modifiers/intern/MOD_weighted_normal.c +++ b/source/blender/modifiers/intern/MOD_weighted_normal.c @@ -726,7 +726,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_WeightedNormal = { - /* name */ "WeightedNormal", + /* name */ N_("WeightedNormal"), /* structName */ "WeightedNormalModifierData", /* structSize */ sizeof(WeightedNormalModifierData), /* srna */ &RNA_WeightedNormalModifier, diff --git a/source/blender/modifiers/intern/MOD_weightvgedit.c b/source/blender/modifiers/intern/MOD_weightvgedit.c index e1b43157adb..f6e0cd9303d 100644 --- a/source/blender/modifiers/intern/MOD_weightvgedit.c +++ b/source/blender/modifiers/intern/MOD_weightvgedit.c @@ -397,7 +397,7 @@ static void blendRead(BlendDataReader *reader, ModifierData *md) } ModifierTypeInfo modifierType_WeightVGEdit = { - /* name */ "VertexWeightEdit", + /* name */ N_("VertexWeightEdit"), /* structName */ "WeightVGEditModifierData", /* structSize */ sizeof(WeightVGEditModifierData), /* srna */ &RNA_VertexWeightEditModifier, diff --git a/source/blender/modifiers/intern/MOD_weightvgmix.c b/source/blender/modifiers/intern/MOD_weightvgmix.c index b827d41e80a..49088d42a5e 100644 --- a/source/blender/modifiers/intern/MOD_weightvgmix.c +++ b/source/blender/modifiers/intern/MOD_weightvgmix.c @@ -496,7 +496,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_WeightVGMix = { - /* name */ "VertexWeightMix", + /* name */ N_("VertexWeightMix"), /* structName */ "WeightVGMixModifierData", /* structSize */ sizeof(WeightVGMixModifierData), /* srna */ &RNA_VertexWeightMixModifier, diff --git a/source/blender/modifiers/intern/MOD_weightvgproximity.c b/source/blender/modifiers/intern/MOD_weightvgproximity.c index 1bea5b93c97..d798bf88bbc 100644 --- a/source/blender/modifiers/intern/MOD_weightvgproximity.c +++ b/source/blender/modifiers/intern/MOD_weightvgproximity.c @@ -737,7 +737,7 @@ static void blendRead(BlendDataReader *reader, ModifierData *md) } ModifierTypeInfo modifierType_WeightVGProximity = { - /* name */ "VertexWeightProximity", + /* name */ N_("VertexWeightProximity"), /* structName */ "WeightVGProximityModifierData", /* structSize */ sizeof(WeightVGProximityModifierData), /* srna */ &RNA_VertexWeightProximityModifier, diff --git a/source/blender/modifiers/intern/MOD_weld.cc b/source/blender/modifiers/intern/MOD_weld.cc index 93d4b56176c..19b0bf62fea 100644 --- a/source/blender/modifiers/intern/MOD_weld.cc +++ b/source/blender/modifiers/intern/MOD_weld.cc @@ -185,7 +185,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_Weld = { - /* name */ "Weld", + /* name */ N_("Weld"), /* structName */ "WeldModifierData", /* structSize */ sizeof(WeldModifierData), /* srna */ &RNA_WeldModifier, diff --git a/source/blender/modifiers/intern/MOD_wireframe.c b/source/blender/modifiers/intern/MOD_wireframe.c index b657ea87244..5799da5d156 100644 --- a/source/blender/modifiers/intern/MOD_wireframe.c +++ b/source/blender/modifiers/intern/MOD_wireframe.c @@ -165,7 +165,7 @@ static void panelRegister(ARegionType *region_type) } ModifierTypeInfo modifierType_Wireframe = { - /* name */ "Wireframe", + /* name */ N_("Wireframe"), /* structName */ "WireframeModifierData", /* structSize */ sizeof(WireframeModifierData), /* srna */ &RNA_WireframeModifier, diff --git a/source/blender/nodes/NOD_composite.h b/source/blender/nodes/NOD_composite.h index 5d782674f16..58126c5722d 100644 --- a/source/blender/nodes/NOD_composite.h +++ b/source/blender/nodes/NOD_composite.h @@ -169,7 +169,7 @@ void ntreeCompositClearTags(struct bNodeTree *ntree); struct bNodeSocket *ntreeCompositOutputFileAddSocket(struct bNodeTree *ntree, struct bNode *node, const char *name, - struct ImageFormatData *im_format); + const struct ImageFormatData *im_format); int ntreeCompositOutputFileRemoveActiveSocket(struct bNodeTree *ntree, struct bNode *node); void ntreeCompositOutputFileSetPath(struct bNode *node, diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 7c7f114bb78..71d8f014399 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -64,7 +64,7 @@ DefNode(ShaderNode, SH_NODE_BSDF_DIFFUSE, 0, "BSD DefNode(ShaderNode, SH_NODE_BSDF_PRINCIPLED, def_principled, "BSDF_PRINCIPLED", BsdfPrincipled, "Principled BSDF", "Physically-based, easy-to-use shader for rendering surface materials, based on the Disney principled model also known as the \"PBR\" shader") DefNode(ShaderNode, SH_NODE_BSDF_GLOSSY, def_glossy, "BSDF_GLOSSY", BsdfGlossy, "Glossy BSDF", "Reflection with microfacet distribution, used for materials such as metal or mirrors") DefNode(ShaderNode, SH_NODE_BSDF_GLASS, def_glass, "BSDF_GLASS", BsdfGlass, "Glass BSDF", "Glass-like shader mixing refraction and reflection at grazing angles") -DefNode(ShaderNode, SH_NODE_BSDF_REFRACTION, def_refraction, "BSDF_REFRACTION", BsdfRefraction, "Refraction BSDF", "Glossy refraction with sharp or microfacet distribution,. Typically used for materials that transmit light") +DefNode(ShaderNode, SH_NODE_BSDF_REFRACTION, def_refraction, "BSDF_REFRACTION", BsdfRefraction, "Refraction BSDF", "Glossy refraction with sharp or microfacet distribution, typically used for materials that transmit light") DefNode(ShaderNode, SH_NODE_BSDF_TRANSLUCENT, 0, "BSDF_TRANSLUCENT", BsdfTranslucent, "Translucent BSDF", "Lambertian diffuse transmission") DefNode(ShaderNode, SH_NODE_BSDF_TRANSPARENT, 0, "BSDF_TRANSPARENT", BsdfTransparent, "Transparent BSDF", "Transparency without refraction, passing straight through the surface as if there were no geometry") DefNode(ShaderNode, SH_NODE_BSDF_VELVET, 0, "BSDF_VELVET", BsdfVelvet, "Velvet BSDF", "Reflection for materials such as cloth.\nTypically mixed with other shaders (such as a Diffuse Shader) and is not particularly useful on its own") diff --git a/source/blender/nodes/composite/nodes/node_composite_diff_matte.cc b/source/blender/nodes/composite/nodes/node_composite_diff_matte.cc index 20dd61a9725..b87bbe439db 100644 --- a/source/blender/nodes/composite/nodes/node_composite_diff_matte.cc +++ b/source/blender/nodes/composite/nodes/node_composite_diff_matte.cc @@ -19,7 +19,7 @@ static void cmp_node_diff_matte_declare(NodeDeclarationBuilder &b) b.add_input<decl::Color>(N_("Image 1")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); b.add_input<decl::Color>(N_("Image 2")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); b.add_output<decl::Color>(N_("Image")); - b.add_output<decl::Color>(N_("Matte")); + b.add_output<decl::Float>(N_("Matte")); } static void node_composit_init_diff_matte(bNodeTree *UNUSED(ntree), bNode *node) diff --git a/source/blender/nodes/composite/nodes/node_composite_output_file.cc b/source/blender/nodes/composite/nodes/node_composite_output_file.cc index f1621f83ac3..84235b085a4 100644 --- a/source/blender/nodes/composite/nodes/node_composite_output_file.cc +++ b/source/blender/nodes/composite/nodes/node_composite_output_file.cc @@ -114,7 +114,7 @@ void ntreeCompositOutputFileUniqueLayer(ListBase *list, bNodeSocket *ntreeCompositOutputFileAddSocket(bNodeTree *ntree, bNode *node, const char *name, - ImageFormatData *im_format) + const ImageFormatData *im_format) { NodeImageMultiFile *nimf = (NodeImageMultiFile *)node->storage; bNodeSocket *sock = nodeAddStaticSocket( diff --git a/source/blender/nodes/composite/nodes/node_composite_val_to_rgb.cc b/source/blender/nodes/composite/nodes/node_composite_val_to_rgb.cc index 0dfdeda24e6..df669d5beda 100644 --- a/source/blender/nodes/composite/nodes/node_composite_val_to_rgb.cc +++ b/source/blender/nodes/composite/nodes/node_composite_val_to_rgb.cc @@ -49,7 +49,7 @@ namespace blender::nodes::node_composite_val_to_rgb_cc { static void cmp_node_rgbtobw_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Color>(N_("Image")).default_value({0.8f, 0.8f, 0.8f, 1.0f}); - b.add_output<decl::Color>(N_("Val")); + b.add_output<decl::Float>(N_("Val")); } } // namespace blender::nodes::node_composite_val_to_rgb_cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc index 7e4904a7a6a..f6ea6073459 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc @@ -65,13 +65,12 @@ static void node_update(bNodeTree *ntree, bNode *node) static void node_geo_exec(GeoNodeExecParams params) { - GeometryComponentType component = (GeometryComponentType)params.node().custom1; - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + const GeometryComponentType component = (GeometryComponentType)params.node().custom1; + const GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); switch (component) { case GEO_COMPONENT_TYPE_MESH: { - if (geometry_set.has_mesh()) { - const MeshComponent *component = geometry_set.get_component_for_read<MeshComponent>(); + if (const MeshComponent *component = geometry_set.get_component_for_read<MeshComponent>()) { const AttributeAccessor attributes = *component->attributes(); params.set_output("Point Count", attributes.domain_size(ATTR_DOMAIN_POINT)); params.set_output("Edge Count", attributes.domain_size(ATTR_DOMAIN_EDGE)); @@ -84,8 +83,8 @@ static void node_geo_exec(GeoNodeExecParams params) break; } case GEO_COMPONENT_TYPE_CURVE: { - if (geometry_set.has_curves()) { - const CurveComponent *component = geometry_set.get_component_for_read<CurveComponent>(); + if (const CurveComponent *component = + geometry_set.get_component_for_read<CurveComponent>()) { const AttributeAccessor attributes = *component->attributes(); params.set_output("Point Count", attributes.domain_size(ATTR_DOMAIN_POINT)); params.set_output("Spline Count", attributes.domain_size(ATTR_DOMAIN_CURVE)); @@ -96,10 +95,10 @@ static void node_geo_exec(GeoNodeExecParams params) break; } case GEO_COMPONENT_TYPE_POINT_CLOUD: { - if (geometry_set.has_pointcloud()) { - const PointCloudComponent *component = - geometry_set.get_component_for_read<PointCloudComponent>(); - params.set_output("Point Count", component->attributes()->domain_size(ATTR_DOMAIN_POINT)); + if (const PointCloudComponent *component = + geometry_set.get_component_for_read<PointCloudComponent>()) { + const AttributeAccessor attributes = *component->attributes(); + params.set_output("Point Count", attributes.domain_size(ATTR_DOMAIN_POINT)); } else { params.set_default_remaining_outputs(); @@ -107,11 +106,10 @@ static void node_geo_exec(GeoNodeExecParams params) break; } case GEO_COMPONENT_TYPE_INSTANCES: { - if (geometry_set.has_instances()) { - const InstancesComponent *component = - geometry_set.get_component_for_read<InstancesComponent>(); - params.set_output("Instance Count", - component->attributes()->domain_size(ATTR_DOMAIN_INSTANCE)); + if (const InstancesComponent *component = + geometry_set.get_component_for_read<InstancesComponent>()) { + const AttributeAccessor attributes = *component->attributes(); + params.set_output("Instance Count", attributes.domain_size(ATTR_DOMAIN_INSTANCE)); } else { params.set_default_remaining_outputs(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc index 489b618b8be..7c26ffc2099 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc @@ -140,28 +140,35 @@ static Mesh *compute_hull(const GeometrySet &geometry_set) Span<float3> positions_span; - if (geometry_set.has_mesh()) { + if (const MeshComponent *component = geometry_set.get_component_for_read<MeshComponent>()) { count++; - const MeshComponent *component = geometry_set.get_component_for_read<MeshComponent>(); - const Mesh *mesh = component->get_for_read(); - total_num += mesh->totvert; + if (const VArray<float3> positions = component->attributes()->lookup<float3>( + "position", ATTR_DOMAIN_POINT)) { + if (positions.is_span()) { + span_count++; + positions_span = positions.get_internal_span(); + } + total_num += positions.size(); + } } - if (geometry_set.has_pointcloud()) { + if (const PointCloudComponent *component = + geometry_set.get_component_for_read<PointCloudComponent>()) { count++; - span_count++; - const PointCloudComponent *component = - geometry_set.get_component_for_read<PointCloudComponent>(); - const PointCloud *pointcloud = component->get_for_read(); - positions_span = {reinterpret_cast<const float3 *>(pointcloud->co), pointcloud->totpoint}; - total_num += pointcloud->totpoint; + if (const VArray<float3> positions = component->attributes()->lookup<float3>( + "position", ATTR_DOMAIN_POINT)) { + if (positions.is_span()) { + span_count++; + positions_span = positions.get_internal_span(); + } + total_num += positions.size(); + } } - if (geometry_set.has_curves()) { + if (const Curves *curves_id = geometry_set.get_curves_for_read()) { count++; span_count++; - const Curves &curves_id = *geometry_set.get_curves_for_read(); - const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); + const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry); positions_span = curves.evaluated_positions(); total_num += positions_span.size(); } @@ -179,26 +186,25 @@ static Mesh *compute_hull(const GeometrySet &geometry_set) Array<float3> positions(total_num); int offset = 0; - if (geometry_set.has_mesh()) { - const MeshComponent *component = geometry_set.get_component_for_read<MeshComponent>(); - const VArray<float3> varray = component->attributes()->lookup<float3>("position", - ATTR_DOMAIN_POINT); - varray.materialize(positions.as_mutable_span().slice(offset, varray.size())); - offset += varray.size(); + if (const MeshComponent *component = geometry_set.get_component_for_read<MeshComponent>()) { + if (const VArray<float3> varray = component->attributes()->lookup<float3>("position", + ATTR_DOMAIN_POINT)) { + varray.materialize(positions.as_mutable_span().slice(offset, varray.size())); + offset += varray.size(); + } } - if (geometry_set.has_pointcloud()) { - const PointCloudComponent *component = - geometry_set.get_component_for_read<PointCloudComponent>(); - const VArray<float3> varray = component->attributes()->lookup<float3>("position", - ATTR_DOMAIN_POINT); - varray.materialize(positions.as_mutable_span().slice(offset, varray.size())); - offset += varray.size(); + if (const PointCloudComponent *component = + geometry_set.get_component_for_read<PointCloudComponent>()) { + if (const VArray<float3> varray = component->attributes()->lookup<float3>("position", + ATTR_DOMAIN_POINT)) { + varray.materialize(positions.as_mutable_span().slice(offset, varray.size())); + offset += varray.size(); + } } - if (geometry_set.has_curves()) { - const Curves &curves_id = *geometry_set.get_curves_for_read(); - const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); + if (const Curves *curves_id = geometry_set.get_curves_for_read()) { + const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry); Span<float3> array = curves.evaluated_positions(); positions.as_mutable_span().slice(offset, array.size()).copy_from(array); offset += array.size(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc index e200350dc18..ab1f8269c39 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc @@ -1,16 +1,12 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#include "BLI_task.hh" - #include "UI_interface.h" #include "UI_resources.h" -#include "DNA_node_types.h" +#include "GEO_fillet_curves.hh" #include "node_geometry_util.hh" -#include "BKE_spline.hh" - namespace blender::nodes::node_geo_curve_fillet_cc { NODE_STORAGE_FUNCS(NodeGeometryCurveFillet) @@ -44,571 +40,18 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) static void node_init(bNodeTree *UNUSED(tree), bNode *node) { NodeGeometryCurveFillet *data = MEM_cnew<NodeGeometryCurveFillet>(__func__); - data->mode = GEO_NODE_CURVE_FILLET_BEZIER; node->storage = data; } -struct FilletParam { - GeometryNodeCurveFilletMode mode; - - /* Number of points to be added. */ - VArray<int> counts; - - /* Radii for fillet arc at all vertices. */ - VArray<float> radii; - - /* Whether or not fillets are allowed to overlap. */ - bool limit_radius; -}; - -/* A data structure used to store fillet data about all vertices to be filleted. */ -struct FilletData { - Span<float3> positions; - Array<float3> directions, axes; - Array<float> radii, angles; - Array<int> counts; -}; - static void node_update(bNodeTree *ntree, bNode *node) { const NodeGeometryCurveFillet &storage = node_storage(*node); const GeometryNodeCurveFilletMode mode = (GeometryNodeCurveFilletMode)storage.mode; - bNodeSocket *poly_socket = ((bNodeSocket *)node->inputs.first)->next; - nodeSetSocketAvailability(ntree, poly_socket, mode == GEO_NODE_CURVE_FILLET_POLY); } -/* Function to get the center of a fillet. */ -static float3 get_center(const float3 vec_pos2prev, - const float3 pos, - const float3 axis, - const float angle) -{ - float3 vec_pos2center; - rotate_normalized_v3_v3v3fl(vec_pos2center, vec_pos2prev, axis, M_PI_2 - angle / 2.0f); - vec_pos2center *= 1.0f / sinf(angle / 2.0f); - - return vec_pos2center + pos; -} - -/* Function to get the center of the fillet using fillet data */ -static float3 get_center(const float3 vec_pos2prev, const FilletData &fd, const int index) -{ - const float angle = fd.angles[index]; - const float3 axis = fd.axes[index]; - const float3 pos = fd.positions[index]; - - return get_center(vec_pos2prev, pos, axis, angle); -} - -/* Calculate the direction vectors from each vertex to their previous vertex. */ -static Array<float3> calculate_directions(const Span<float3> positions) -{ - const int num = positions.size(); - Array<float3> directions(num); - - for (const int i : IndexRange(num - 1)) { - directions[i] = math::normalize(positions[i + 1] - positions[i]); - } - directions[num - 1] = math::normalize(positions[0] - positions[num - 1]); - - return directions; -} - -/* Calculate the axes around which the fillet is built. */ -static Array<float3> calculate_axes(const Span<float3> directions) -{ - const int num = directions.size(); - Array<float3> axes(num); - - axes[0] = math::normalize(math::cross(-directions[num - 1], directions[0])); - for (const int i : IndexRange(1, num - 1)) { - axes[i] = math::normalize(math::cross(-directions[i - 1], directions[i])); - } - - return axes; -} - -/* Calculate the angle of the arc formed by the fillet. */ -static Array<float> calculate_angles(const Span<float3> directions) -{ - const int num = directions.size(); - Array<float> angles(num); - - angles[0] = M_PI - angle_v3v3(-directions[num - 1], directions[0]); - for (const int i : IndexRange(1, num - 1)) { - angles[i] = M_PI - angle_v3v3(-directions[i - 1], directions[i]); - } - - return angles; -} - -/* Calculate the segment count in each filleted arc. */ -static Array<int> calculate_counts(const FilletParam &fillet_param, - const int num, - const int spline_offset, - const bool cyclic) -{ - Array<int> counts(num, 1); - if (fillet_param.mode == GEO_NODE_CURVE_FILLET_POLY) { - for (const int i : IndexRange(num)) { - counts[i] = fillet_param.counts[spline_offset + i]; - } - } - if (!cyclic) { - counts[0] = counts[num - 1] = 0; - } - - return counts; -} - -/* Calculate the radii for the vertices to be filleted. */ -static Array<float> calculate_radii(const FilletParam &fillet_param, - const int num, - const int spline_offset) -{ - Array<float> radii(num, 0.0f); - if (fillet_param.limit_radius) { - for (const int i : IndexRange(num)) { - radii[i] = std::max(fillet_param.radii[spline_offset + i], 0.0f); - } - } - else { - for (const int i : IndexRange(num)) { - radii[i] = fillet_param.radii[spline_offset + i]; - } - } - - return radii; -} - -/* Calculate the number of vertices added per vertex on the source spline. */ -static int calculate_point_counts(MutableSpan<int> point_counts, - const Span<float> radii, - const Span<int> counts) -{ - int added_count = 0; - for (const int i : IndexRange(point_counts.size())) { - /* Calculate number of points to be added for the vertex. */ - if (radii[i] != 0.0f) { - added_count += counts[i]; - point_counts[i] = counts[i] + 1; - } - } - - return added_count; -} - -static FilletData calculate_fillet_data(const Spline &spline, - const FilletParam &fillet_param, - int &added_count, - MutableSpan<int> point_counts, - const int spline_offset) -{ - const int num = spline.size(); - - FilletData fd; - fd.directions = calculate_directions(spline.positions()); - fd.positions = spline.positions(); - fd.axes = calculate_axes(fd.directions); - fd.angles = calculate_angles(fd.directions); - fd.counts = calculate_counts(fillet_param, num, spline_offset, spline.is_cyclic()); - fd.radii = calculate_radii(fillet_param, num, spline_offset); - - added_count = calculate_point_counts(point_counts, fd.radii, fd.counts); - - return fd; -} - -/* Limit the radius based on angle and radii to prevent overlapping. */ -static void limit_radii(FilletData &fd, const bool cyclic) -{ - MutableSpan<float> radii(fd.radii); - Span<float> angles(fd.angles); - Span<float3> positions(fd.positions); - - const int num = radii.size(); - const int fillet_count = cyclic ? num : num - 2; - const int start = cyclic ? 0 : 1; - Array<float> max_radii(num, FLT_MAX); - - if (cyclic) { - /* Calculate lengths between adjacent control points. */ - const float len_prev = math::distance(positions[0], positions[num - 1]); - const float len_next = math::distance(positions[0], positions[1]); - - /* Calculate tangent lengths of fillets in control points. */ - const float tan_len = radii[0] * tan(angles[0] / 2.0f); - const float tan_len_prev = radii[num - 1] * tan(angles[num - 1] / 2.0f); - const float tan_len_next = radii[1] * tan(angles[1] / 2.0f); - - float factor_prev = 1.0f, factor_next = 1.0f; - if (tan_len + tan_len_prev > len_prev) { - factor_prev = len_prev / (tan_len + tan_len_prev); - } - if (tan_len + tan_len_next > len_next) { - factor_next = len_next / (tan_len + tan_len_next); - } - - /* Scale max radii by calculated factors. */ - max_radii[0] = radii[0] * std::min(factor_next, factor_prev); - max_radii[1] = radii[1] * factor_next; - max_radii[num - 1] = radii[num - 1] * factor_prev; - } - - /* Initialize max_radii to largest possible radii. */ - float prev_dist = math::distance(positions[1], positions[0]); - for (const int i : IndexRange(1, num - 2)) { - const float temp_dist = math::distance(positions[i], positions[i + 1]); - max_radii[i] = std::min(prev_dist, temp_dist) / tan(angles[i] / 2.0f); - prev_dist = temp_dist; - } - - /* Max radii calculations for each index. */ - for (const int i : IndexRange(start, fillet_count - 1)) { - const float len_next = math::distance(positions[i], positions[i + 1]); - const float tan_len = radii[i] * tan(angles[i] / 2.0f); - const float tan_len_next = radii[i + 1] * tan(angles[i + 1] / 2.0f); - - /* Scale down radii if too large for segment. */ - float factor = 1.0f; - if (tan_len + tan_len_next > len_next) { - factor = len_next / (tan_len + tan_len_next); - } - max_radii[i] = std::min(max_radii[i], radii[i] * factor); - max_radii[i + 1] = std::min(max_radii[i + 1], radii[i + 1] * factor); - } - - /* Assign the max_radii to the fillet data's radii. */ - for (const int i : IndexRange(num)) { - radii[i] = std::min(radii[i], max_radii[i]); - } -} - -/* - * Create a mapping from each vertex in the destination spline to that of the source spline. - * Used for copying the data from the source spline. - */ -static Array<int> create_dst_to_src_map(const Span<int> point_counts, const int total_points) -{ - Array<int> map(total_points); - MutableSpan<int> map_span{map}; - int index = 0; - - for (const int i : point_counts.index_range()) { - map_span.slice(index, point_counts[i]).fill(i); - index += point_counts[i]; - } - - BLI_assert(index == total_points); - - return map; -} - -template<typename T> -static void copy_attribute_by_mapping(const Span<T> src, - MutableSpan<T> dst, - const Span<int> mapping) -{ - for (const int i : dst.index_range()) { - dst[i] = src[mapping[i]]; - } -} - -/* Copy radii and tilts from source spline to destination. Positions are handled later in update - * positions methods. */ -static void copy_common_attributes_by_mapping(const Spline &src, - Spline &dst, - const Span<int> mapping) -{ - copy_attribute_by_mapping(src.radii(), dst.radii(), mapping); - copy_attribute_by_mapping(src.tilts(), dst.tilts(), mapping); - - src.attributes.foreach_attribute( - [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { - std::optional<GSpan> src_attribute = src.attributes.get_for_read(attribute_id); - if (dst.attributes.create(attribute_id, meta_data.data_type)) { - std::optional<GMutableSpan> dst_attribute = dst.attributes.get_for_write(attribute_id); - if (dst_attribute) { - attribute_math::convert_to_static_type(dst_attribute->type(), [&](auto dummy) { - using T = decltype(dummy); - copy_attribute_by_mapping( - src_attribute->typed<T>(), dst_attribute->typed<T>(), mapping); - }); - return true; - } - } - BLI_assert_unreachable(); - return false; - }, - ATTR_DOMAIN_POINT); -} - -/* Update the vertex positions and handle positions of a Bezier spline based on fillet data. */ -static void update_bezier_positions(const FilletData &fd, - BezierSpline &dst_spline, - const BezierSpline &src_spline, - const Span<int> point_counts) -{ - Span<float> radii(fd.radii); - Span<float> angles(fd.angles); - Span<float3> axes(fd.axes); - Span<float3> positions(fd.positions); - Span<float3> directions(fd.directions); - - const int num = radii.size(); - - int i_dst = 0; - for (const int i_src : IndexRange(num)) { - const int count = point_counts[i_src]; - - /* Skip if the point count for the vertex is 1. */ - if (count == 1) { - dst_spline.positions()[i_dst] = src_spline.positions()[i_src]; - dst_spline.handle_types_left()[i_dst] = src_spline.handle_types_left()[i_src]; - dst_spline.handle_types_right()[i_dst] = src_spline.handle_types_right()[i_src]; - dst_spline.handle_positions_left()[i_dst] = src_spline.handle_positions_left()[i_src]; - dst_spline.handle_positions_right()[i_dst] = src_spline.handle_positions_right()[i_src]; - i_dst++; - continue; - } - - /* Calculate the angle to be formed between any 2 adjacent vertices within the fillet. */ - const float segment_angle = angles[i_src] / (count - 1); - /* Calculate the handle length for each added vertex. Equation: L = 4R/3 * tan(A/4) */ - const float handle_length = 4.0f * radii[i_src] / 3.0f * tan(segment_angle / 4.0f); - /* Calculate the distance by which each vertex should be displaced from their initial position. - */ - const float displacement = radii[i_src] * tan(angles[i_src] / 2.0f); - - /* Position the end points of the arc and their handles. */ - const int end_i = i_dst + count - 1; - const float3 prev_dir = i_src == 0 ? -directions[num - 1] : -directions[i_src - 1]; - const float3 next_dir = directions[i_src]; - dst_spline.positions()[i_dst] = positions[i_src] + displacement * prev_dir; - dst_spline.positions()[end_i] = positions[i_src] + displacement * next_dir; - dst_spline.handle_positions_right()[i_dst] = dst_spline.positions()[i_dst] - - handle_length * prev_dir; - dst_spline.handle_positions_left()[end_i] = dst_spline.positions()[end_i] - - handle_length * next_dir; - dst_spline.handle_types_right()[i_dst] = dst_spline.handle_types_left()[end_i] = - BEZIER_HANDLE_ALIGN; - dst_spline.handle_types_left()[i_dst] = dst_spline.handle_types_right()[end_i] = - BEZIER_HANDLE_VECTOR; - dst_spline.mark_cache_invalid(); - - /* Calculate the center of the radius to be formed. */ - const float3 center = get_center(dst_spline.positions()[i_dst] - positions[i_src], fd, i_src); - /* Calculate the vector of the radius formed by the first vertex. */ - float3 radius_vec = dst_spline.positions()[i_dst] - center; - float radius; - radius_vec = math::normalize_and_get_length(radius_vec, radius); - - dst_spline.handle_types_right().slice(1, count - 2).fill(BEZIER_HANDLE_ALIGN); - dst_spline.handle_types_left().slice(1, count - 2).fill(BEZIER_HANDLE_ALIGN); - - /* For each of the vertices in between the end points. */ - for (const int j : IndexRange(1, count - 2)) { - int index = i_dst + j; - /* Rotate the radius by the segment angle and determine its tangent (used for getting handle - * directions). */ - float3 new_radius_vec, tangent_vec; - rotate_normalized_v3_v3v3fl(new_radius_vec, radius_vec, -axes[i_src], segment_angle); - rotate_normalized_v3_v3v3fl(tangent_vec, new_radius_vec, axes[i_src], M_PI_2); - radius_vec = new_radius_vec; - tangent_vec *= handle_length; - - /* Adjust the positions of the respective vertex and its handles. */ - dst_spline.positions()[index] = center + new_radius_vec * radius; - dst_spline.handle_positions_left()[index] = dst_spline.positions()[index] + tangent_vec; - dst_spline.handle_positions_right()[index] = dst_spline.positions()[index] - tangent_vec; - } - - i_dst += count; - } -} - -/* Update the vertex positions of a Poly spline based on fillet data. */ -static void update_poly_positions(const FilletData &fd, - Spline &dst_spline, - const Spline &src_spline, - const Span<int> point_counts) -{ - Span<float> radii(fd.radii); - Span<float> angles(fd.angles); - Span<float3> axes(fd.axes); - Span<float3> positions(fd.positions); - Span<float3> directions(fd.directions); - - const int num = radii.size(); - - int i_dst = 0; - for (const int i_src : IndexRange(num)) { - const int count = point_counts[i_src]; - - /* Skip if the point count for the vertex is 1. */ - if (count == 1) { - dst_spline.positions()[i_dst] = src_spline.positions()[i_src]; - i_dst++; - continue; - } - - const float segment_angle = angles[i_src] / (count - 1); - const float displacement = radii[i_src] * tan(angles[i_src] / 2.0f); - - /* Position the end points of the arc. */ - const int end_i = i_dst + count - 1; - const float3 prev_dir = i_src == 0 ? -directions[num - 1] : -directions[i_src - 1]; - const float3 next_dir = directions[i_src]; - dst_spline.positions()[i_dst] = positions[i_src] + displacement * prev_dir; - dst_spline.positions()[end_i] = positions[i_src] + displacement * next_dir; - - /* Calculate the center of the radius to be formed. */ - const float3 center = get_center(dst_spline.positions()[i_dst] - positions[i_src], fd, i_src); - /* Calculate the vector of the radius formed by the first vertex. */ - float3 radius_vec = dst_spline.positions()[i_dst] - center; - - for (const int j : IndexRange(1, count - 2)) { - /* Rotate the radius by the segment angle */ - float3 new_radius_vec; - rotate_normalized_v3_v3v3fl(new_radius_vec, radius_vec, -axes[i_src], segment_angle); - radius_vec = new_radius_vec; - - dst_spline.positions()[i_dst + j] = center + new_radius_vec; - } - - i_dst += count; - } -} - -static SplinePtr fillet_spline(const Spline &spline, - const FilletParam &fillet_param, - const int spline_offset) -{ - const int num = spline.size(); - const bool cyclic = spline.is_cyclic(); - - if (num < 3) { - return spline.copy(); - } - - /* Initialize the point_counts with 1s (at least one vertex on dst for each vertex on src). */ - Array<int> point_counts(num, 1); - - int added_count = 0; - /* Update point_counts array and added_count. */ - FilletData fd = calculate_fillet_data( - spline, fillet_param, added_count, point_counts, spline_offset); - if (fillet_param.limit_radius) { - limit_radii(fd, cyclic); - } - - const int total_points = added_count + num; - const Array<int> dst_to_src = create_dst_to_src_map(point_counts, total_points); - SplinePtr dst_spline_ptr = spline.copy_only_settings(); - (*dst_spline_ptr).resize(total_points); - copy_common_attributes_by_mapping(spline, *dst_spline_ptr, dst_to_src); - - switch (spline.type()) { - case CURVE_TYPE_BEZIER: { - const BezierSpline &src_spline = static_cast<const BezierSpline &>(spline); - BezierSpline &dst_spline = static_cast<BezierSpline &>(*dst_spline_ptr); - if (fillet_param.mode == GEO_NODE_CURVE_FILLET_POLY) { - dst_spline.handle_types_left().fill(BEZIER_HANDLE_VECTOR); - dst_spline.handle_types_right().fill(BEZIER_HANDLE_VECTOR); - update_poly_positions(fd, dst_spline, src_spline, point_counts); - } - else { - update_bezier_positions(fd, dst_spline, src_spline, point_counts); - } - break; - } - case CURVE_TYPE_POLY: { - update_poly_positions(fd, *dst_spline_ptr, spline, point_counts); - break; - } - case CURVE_TYPE_NURBS: { - const NURBSpline &src_spline = static_cast<const NURBSpline &>(spline); - NURBSpline &dst_spline = static_cast<NURBSpline &>(*dst_spline_ptr); - copy_attribute_by_mapping(src_spline.weights(), dst_spline.weights(), dst_to_src); - update_poly_positions(fd, dst_spline, src_spline, point_counts); - break; - } - case CURVE_TYPE_CATMULL_ROM: { - BLI_assert_unreachable(); - break; - } - } - - return dst_spline_ptr; -} - -static std::unique_ptr<CurveEval> fillet_curve(const CurveEval &input_curve, - const FilletParam &fillet_param) -{ - Span<SplinePtr> input_splines = input_curve.splines(); - - std::unique_ptr<CurveEval> output_curve = std::make_unique<CurveEval>(); - const int splines_num = input_splines.size(); - output_curve->resize(splines_num); - MutableSpan<SplinePtr> output_splines = output_curve->splines(); - Array<int> spline_offsets = input_curve.control_point_offsets(); - - threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) { - for (const int i : range) { - output_splines[i] = fillet_spline(*input_splines[i], fillet_param, spline_offsets[i]); - } - }); - output_curve->attributes = input_curve.attributes; - - return output_curve; -} - -static void calculate_curve_fillet(GeometrySet &geometry_set, - const GeometryNodeCurveFilletMode mode, - const Field<float> &radius_field, - const std::optional<Field<int>> &count_field, - const bool limit_radius) -{ - if (!geometry_set.has_curves()) { - return; - } - - FilletParam fillet_param; - fillet_param.mode = mode; - - CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>(); - GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); - fn::FieldEvaluator field_evaluator{field_context, domain_size}; - - field_evaluator.add(radius_field); - - if (mode == GEO_NODE_CURVE_FILLET_POLY) { - field_evaluator.add(*count_field); - } - - field_evaluator.evaluate(); - - fillet_param.radii = field_evaluator.get_evaluated<float>(0); - if (fillet_param.radii.is_single() && fillet_param.radii.get_internal_single() < 0.0f) { - return; - } - - if (mode == GEO_NODE_CURVE_FILLET_POLY) { - fillet_param.counts = field_evaluator.get_evaluated<int>(1); - } - - fillet_param.limit_radius = limit_radius; - - const std::unique_ptr<CurveEval> input_curve = curves_to_curve_eval(*component.get_for_read()); - std::unique_ptr<CurveEval> output_curve = fillet_curve(*input_curve, fillet_param); - - geometry_set.replace_curves(curve_eval_to_curves(*output_curve)); -} - static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); @@ -625,7 +68,42 @@ static void node_geo_exec(GeoNodeExecParams params) } geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - calculate_curve_fillet(geometry_set, mode, radius_field, count_field, limit_radius); + if (!geometry_set.has_curves()) { + return; + } + + const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>(); + const Curves &curves_id = *component.get_for_read(); + const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); + GeometryComponentFieldContext context{component, ATTR_DOMAIN_POINT}; + fn::FieldEvaluator evaluator{context, curves.points_num()}; + evaluator.add(radius_field); + + switch (mode) { + case GEO_NODE_CURVE_FILLET_BEZIER: { + evaluator.evaluate(); + bke::CurvesGeometry dst_curves = geometry::fillet_curves_bezier( + curves, curves.curves_range(), evaluator.get_evaluated<float>(0), limit_radius); + Curves *dst_curves_id = bke::curves_new_nomain(std::move(dst_curves)); + bke::curves_copy_parameters(curves_id, *dst_curves_id); + geometry_set.replace_curves(dst_curves_id); + break; + } + case GEO_NODE_CURVE_FILLET_POLY: { + evaluator.add(*count_field); + evaluator.evaluate(); + bke::CurvesGeometry dst_curves = geometry::fillet_curves_poly( + curves, + curves.curves_range(), + evaluator.get_evaluated<float>(0), + evaluator.get_evaluated<int>(1), + limit_radius); + Curves *dst_curves_id = bke::curves_new_nomain(std::move(dst_curves)); + bke::curves_copy_parameters(curves_id, *dst_curves_id); + geometry_set.replace_curves(dst_curves_id); + break; + } + } }); params.set_output("Curve", std::move(geometry_set)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc index 78a132064ed..2815dd5b2e8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc @@ -65,8 +65,10 @@ static void node_geo_exec(GeoNodeExecParams params) Field<int> count = params.extract_input<Field<int>>("Count"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry) { if (const CurveComponent *component = geometry.get_component_for_read<CurveComponent>()) { - if (!component->is_empty()) { - geometry.replace_curves(geometry::resample_to_count(*component, selection, count)); + if (const Curves *src_curves = component->get_for_read()) { + Curves *dst_curves = geometry::resample_to_count(*component, selection, count); + bke::curves_copy_parameters(*src_curves, *dst_curves); + geometry.replace_curves(dst_curves); } } }); @@ -76,8 +78,10 @@ static void node_geo_exec(GeoNodeExecParams params) Field<float> length = params.extract_input<Field<float>>("Length"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry) { if (const CurveComponent *component = geometry.get_component_for_read<CurveComponent>()) { - if (!component->is_empty()) { - geometry.replace_curves(geometry::resample_to_length(*component, selection, length)); + if (const Curves *src_curves = component->get_for_read()) { + Curves *dst_curves = geometry::resample_to_length(*component, selection, length); + bke::curves_copy_parameters(*src_curves, *dst_curves); + geometry.replace_curves(dst_curves); } } }); @@ -86,8 +90,10 @@ static void node_geo_exec(GeoNodeExecParams params) case GEO_NODE_CURVE_RESAMPLE_EVALUATED: geometry_set.modify_geometry_sets([&](GeometrySet &geometry) { if (const CurveComponent *component = geometry.get_component_for_read<CurveComponent>()) { - if (!component->is_empty()) { - geometry.replace_curves(geometry::resample_to_evaluated(*component, selection)); + if (const Curves *src_curves = component->get_for_read()) { + Curves *dst_curves = geometry::resample_to_evaluated(*component, selection); + bke::curves_copy_parameters(*src_curves, *dst_curves); + geometry.replace_curves(dst_curves); } } }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc index 183c98e9c9f..a92479bc5f1 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc @@ -61,15 +61,18 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - if (geometry::try_curves_conversion_in_place(selection, dst_type, [&]() -> Curves & { - return *geometry_set.get_curves_for_write(); - })) { + if (geometry::try_curves_conversion_in_place( + selection, dst_type, [&]() -> bke::CurvesGeometry & { + return bke::CurvesGeometry::wrap(geometry_set.get_curves_for_write()->geometry); + })) { return; } - Curves *dst_curves = geometry::convert_curves(src_component, src_curves, selection, dst_type); + bke::CurvesGeometry dst_curves = geometry::convert_curves(src_curves, selection, dst_type); - geometry_set.replace_curves(dst_curves); + Curves *dst_curves_id = bke::curves_new_nomain(std::move(dst_curves)); + bke::curves_copy_parameters(src_curves_id, *dst_curves_id); + geometry_set.replace_curves(dst_curves_id); }); params.set_output("Curve", std::move(geometry_set)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc index 864e6289135..bd44adb35a2 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc @@ -35,11 +35,11 @@ static void node_geo_exec(GeoNodeExecParams params) } const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>(); - const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap( - component.get_for_read()->geometry); + const Curves &src_curves_id = *component.get_for_read(); + const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry); GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; - fn::FieldEvaluator evaluator{field_context, curves.points_num()}; + fn::FieldEvaluator evaluator{field_context, src_curves.points_num()}; evaluator.add(cuts_field); evaluator.evaluate(); const VArray<int> cuts = evaluator.get_evaluated<int>(0); @@ -47,9 +47,12 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - Curves *result = geometry::subdivide_curves(component, curves, curves.curves_range(), cuts); + bke::CurvesGeometry dst_curves = geometry::subdivide_curves( + src_curves, src_curves.curves_range(), cuts); - geometry_set.replace_curves(result); + Curves *dst_curves_id = bke::curves_new_nomain(std::move(dst_curves)); + bke::curves_copy_parameters(src_curves_id, *dst_curves_id); + geometry_set.replace_curves(dst_curves_id); }); params.set_output("Curve", geometry_set); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc index 00a79168087..51994cb8a41 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BKE_curves.hh" #include "BKE_spline.hh" #include "BLI_task.hh" @@ -515,7 +516,8 @@ static void geometry_set_curve_trim(GeometrySet &geometry_set, const VArray<float> starts = evaluator.get_evaluated<float>(0); const VArray<float> ends = evaluator.get_evaluated<float>(1); - std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*geometry_set.get_curves_for_read()); + const Curves &src_curves_id = *geometry_set.get_curves_for_read(); + std::unique_ptr<CurveEval> curve = curves_to_curve_eval(src_curves_id); MutableSpan<SplinePtr> splines = curve->splines(); threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { @@ -566,7 +568,9 @@ static void geometry_set_curve_trim(GeometrySet &geometry_set, } }); - geometry_set.replace_curves(curve_eval_to_curves(*curve)); + Curves *dst_curves_id = curve_eval_to_curves(*curve); + bke::curves_copy_parameters(src_curves_id, *dst_curves_id); + geometry_set.replace_curves(dst_curves_id); } static void node_geo_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc index ab7ddfa71f1..7f03d025db7 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc @@ -136,7 +136,7 @@ static void deform_curves(CurvesGeometry &curves, * * TODO: Figure out if this can be smoothly interpolated across the surface as well. * Currently, this is a source of discontinuity in the deformation, because the vector - * changes intantly from one triangle to the next. */ + * changes instantly from one triangle to the next. */ const float3 tangent_reference_dir_old = rest_pos_1 - rest_pos_0; const float3 tangent_reference_dir_new = pos_1_new - pos_0_new; @@ -218,6 +218,12 @@ static void node_geo_exec(GeoNodeExecParams params) return; } const Curves *self_curves_eval = static_cast<const Curves *>(self_ob_eval->data); + if (self_curves_eval->surface_uv_map == nullptr || self_curves_eval->surface_uv_map[0] == '\0') { + pass_through_input(); + const char *message = TIP_("Surface UV map not defined"); + params.error_message_add(NodeWarningType::Error, message); + return; + } /* Take surface information from self-object. */ Object *surface_ob_eval = self_curves_eval->surface; const StringRefNull uv_map_name = self_curves_eval->surface_uv_map; @@ -229,7 +235,7 @@ static void node_geo_exec(GeoNodeExecParams params) } if (surface_ob_eval == nullptr || surface_ob_eval->type != OB_MESH) { pass_through_input(); - params.error_message_add(NodeWarningType::Error, "Curves not attached to a surface."); + params.error_message_add(NodeWarningType::Error, "Curves not attached to a surface"); return; } Object *surface_ob_orig = DEG_get_original_object(surface_ob_eval); @@ -246,7 +252,7 @@ static void node_geo_exec(GeoNodeExecParams params) false); if (surface_mesh_eval == nullptr) { pass_through_input(); - params.error_message_add(NodeWarningType::Error, "Surface has no mesh."); + params.error_message_add(NodeWarningType::Error, "Surface has no mesh"); return; } @@ -258,15 +264,9 @@ static void node_geo_exec(GeoNodeExecParams params) Curves &curves_id = *curves_geometry.get_curves_for_write(); CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); - if (uv_map_name.is_empty()) { - pass_through_input(); - const char *message = TIP_("Surface UV map not defined."); - params.error_message_add(NodeWarningType::Error, message); - return; - } if (!mesh_attributes_eval.contains(uv_map_name)) { pass_through_input(); - char *message = BLI_sprintfN(TIP_("Evaluated surface missing UV map: %s."), + char *message = BLI_sprintfN(TIP_("Evaluated surface missing UV map: %s"), uv_map_name.c_str()); params.error_message_add(NodeWarningType::Error, message); MEM_freeN(message); @@ -274,8 +274,7 @@ static void node_geo_exec(GeoNodeExecParams params) } if (!mesh_attributes_orig.contains(uv_map_name)) { pass_through_input(); - char *message = BLI_sprintfN(TIP_("Original surface missing UV map: %s."), - uv_map_name.c_str()); + char *message = BLI_sprintfN(TIP_("Original surface missing UV map: %s"), uv_map_name.c_str()); params.error_message_add(NodeWarningType::Error, message); MEM_freeN(message); return; @@ -283,13 +282,13 @@ static void node_geo_exec(GeoNodeExecParams params) if (!mesh_attributes_eval.contains(rest_position_name)) { pass_through_input(); params.error_message_add(NodeWarningType::Error, - TIP_("Evaluated surface missing attribute: rest_position.")); + TIP_("Evaluated surface missing attribute: rest_position")); return; } - if (curves.surface_uv_coords().is_empty()) { + if (curves.surface_uv_coords().is_empty() && curves.curves_num() > 0) { pass_through_input(); params.error_message_add(NodeWarningType::Error, - TIP_("Curves are not attached to any UV map.")); + TIP_("Curves are not attached to any UV map")); return; } const VArraySpan<float2> uv_map_orig = mesh_attributes_orig.lookup<float2>(uv_map_name, @@ -337,7 +336,7 @@ static void node_geo_exec(GeoNodeExecParams params) curves.tag_positions_changed(); if (invalid_uv_count) { - char *message = BLI_sprintfN(TIP_("Invalid surface UVs on %d curves."), + char *message = BLI_sprintfN(TIP_("Invalid surface UVs on %d curves"), invalid_uv_count.load()); params.error_message_add(NodeWarningType::Warning, message); MEM_freeN(message); diff --git a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc index faf5b7f65fa..44793926bbd 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc @@ -497,8 +497,17 @@ static void point_distribution_calculate(GeometrySet &geometry_set, } PointCloud *pointcloud = BKE_pointcloud_new_nomain(positions.size()); - memcpy(pointcloud->co, positions.data(), sizeof(float3) * positions.size()); - uninitialized_fill_n(pointcloud->radius, pointcloud->totpoint, 0.05f); + bke::MutableAttributeAccessor point_attributes = bke::pointcloud_attributes_for_write( + *pointcloud); + bke::SpanAttributeWriter<float3> point_positions = + point_attributes.lookup_or_add_for_write_only_span<float3>("position", ATTR_DOMAIN_POINT); + bke::SpanAttributeWriter<float> point_radii = + point_attributes.lookup_or_add_for_write_only_span<float>("radius", ATTR_DOMAIN_POINT); + point_positions.span.copy_from(positions); + point_radii.span.fill(0.05f); + point_positions.finish(); + point_radii.finish(); + geometry_set.replace_pointcloud(pointcloud); PointCloudComponent &point_component = diff --git a/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc index 41136060ab7..8160bc8d589 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc @@ -369,6 +369,7 @@ static void duplicate_curves(GeometrySet &geometry_set, point_offsets.last() = dst_points_num; Curves *new_curves_id = bke::curves_new_nomain(dst_points_num, dst_curves_num); + bke::curves_copy_parameters(curves_id, *new_curves_id); bke::CurvesGeometry &new_curves = bke::CurvesGeometry::wrap(new_curves_id->geometry); MutableSpan<int> all_dst_offsets = new_curves.offsets_for_write(); @@ -830,6 +831,7 @@ static void duplicate_points_curve(GeometrySet &geometry_set, }); Curves *new_curves_id = bke::curves_new_nomain(dst_num, dst_num); + bke::curves_copy_parameters(src_curves_id, *new_curves_id); bke::CurvesGeometry &new_curves = bke::CurvesGeometry::wrap(new_curves_id->geometry); MutableSpan<int> new_curve_offsets = new_curves.offsets_for_write(); for (const int i : new_curves.curves_range()) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_field_on_domain.cc b/source/blender/nodes/geometry/nodes/node_geo_field_on_domain.cc index 5939ed5334d..59e243db4a2 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_field_on_domain.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_field_on_domain.cc @@ -85,12 +85,13 @@ class FieldOnDomain final : public GeometryFieldInput { IndexMask /* mask */) const final { const GeometryComponentFieldContext context{component, src_domain_}; - FieldEvaluator value_evaluator{context, component.attribute_domain_size(src_domain_)}; - value_evaluator.add(src_field_); + const int64_t src_domain_size = component.attribute_domain_size(src_domain_); + GArray values(src_field_.cpp_type(), src_domain_size); + FieldEvaluator value_evaluator{context, src_domain_size}; + value_evaluator.add_with_destination(src_field_, values.as_mutable_span()); value_evaluator.evaluate(); - const GVArray &values = value_evaluator.get_evaluated(0); - - return component.attributes()->adapt_domain(values, src_domain_, domain); + return component.attributes()->adapt_domain( + GVArray::ForGArray(std::move(values)), src_domain_, domain); } }; diff --git a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc index edbe6e1593f..f14329c96da 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc @@ -22,16 +22,6 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Points")); } -template<typename T> -static void copy_attribute_to_points(const VArray<T> &src, - const IndexMask mask, - MutableSpan<T> dst) -{ - for (const int i : mask.index_range()) { - dst[i] = src[mask[i]]; - } -} - static void convert_instances_to_points(GeometrySet &geometry_set, Field<float3> position_field, Field<float> radius_field, @@ -51,16 +41,24 @@ static void convert_instances_to_points(GeometrySet &geometry_set, if (selection.is_empty()) { return; } + const VArray<float3> &positions = evaluator.get_evaluated<float3>(0); + const VArray<float> radii = evaluator.get_evaluated<float>(1); PointCloud *pointcloud = BKE_pointcloud_new_nomain(selection.size()); geometry_set.replace_pointcloud(pointcloud); - PointCloudComponent &points = geometry_set.get_component_for_write<PointCloudComponent>(); + bke::MutableAttributeAccessor point_attributes = bke::pointcloud_attributes_for_write( + *pointcloud); - const VArray<float3> &positions = evaluator.get_evaluated<float3>(0); - copy_attribute_to_points(positions, selection, {(float3 *)pointcloud->co, pointcloud->totpoint}); - const VArray<float> radii = evaluator.get_evaluated<float>(1); - copy_attribute_to_points(radii, selection, {pointcloud->radius, pointcloud->totpoint}); + bke::SpanAttributeWriter<float3> point_positions = + point_attributes.lookup_or_add_for_write_only_span<float3>("position", ATTR_DOMAIN_POINT); + bke::SpanAttributeWriter<float> point_radii = + point_attributes.lookup_or_add_for_write_only_span<float>("radius", ATTR_DOMAIN_POINT); + + positions.materialize_compressed_to_uninitialized(selection, point_positions.span); + radii.materialize_compressed_to_uninitialized(selection, point_radii.span); + point_positions.finish(); + point_radii.finish(); Map<AttributeIDRef, AttributeKind> attributes_to_propagate; geometry_set.gather_attributes_for_propagation({GEO_COMPONENT_TYPE_INSTANCES}, @@ -78,14 +76,11 @@ static void convert_instances_to_points(GeometrySet &geometry_set, const GVArray src = instances.attributes()->lookup_or_default( attribute_id, ATTR_DOMAIN_INSTANCE, attribute_kind.data_type); BLI_assert(src); - GSpanAttributeWriter dst = points.attributes_for_write()->lookup_or_add_for_write_only_span( + GSpanAttributeWriter dst = point_attributes.lookup_or_add_for_write_only_span( attribute_id, ATTR_DOMAIN_POINT, attribute_kind.data_type); BLI_assert(dst); - attribute_math::convert_to_static_type(attribute_kind.data_type, [&](auto dummy) { - using T = decltype(dummy); - copy_attribute_to_points(src.typed<T>(), selection, dst.span.typed<T>()); - }); + src.materialize_compressed_to_uninitialized(selection, dst.span.data()); dst.finish(); } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc index f6ee3d00dee..4a79ec159f4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc @@ -18,7 +18,8 @@ static void node_geo_exec(GeoNodeExecParams params) GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (!geometry_set.has_mesh()) { + const Mesh *mesh = geometry_set.get_mesh_for_read(); + if (mesh == nullptr) { geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); return; } @@ -34,7 +35,8 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - geometry_set.replace_curves(geometry::mesh_to_curve_convert(component, selection)); + bke::CurvesGeometry curves = geometry::mesh_to_curve_convert(*mesh, selection); + geometry_set.replace_curves(bke::curves_new_nomain(std::move(curves))); geometry_set.keep_only({GEO_COMPONENT_TYPE_CURVE, GEO_COMPONENT_TYPE_INSTANCES}); }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc index 9cc64d4bc44..74fff8efeee 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc @@ -18,14 +18,6 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Mesh")); } -template<typename T> -static void copy_attribute_to_vertices(const Span<T> src, const IndexMask mask, MutableSpan<T> dst) -{ - for (const int i : mask.index_range()) { - dst[i] = src[mask[i]]; - } -} - /* One improvement would be to move the attribute arrays directly to the mesh when possible. */ static void geometry_set_points_to_vertices(GeometrySet &geometry_set, Field<bool> &selection_field) @@ -66,12 +58,7 @@ static void geometry_set_points_to_vertices(GeometrySet &geometry_set, mesh_component.attributes_for_write()->lookup_or_add_for_write_only_span( attribute_id, ATTR_DOMAIN_POINT, data_type); if (dst && src) { - attribute_math::convert_to_static_type(data_type, [&](auto dummy) { - using T = decltype(dummy); - VArray<T> src_typed = src.typed<T>(); - VArraySpan<T> src_typed_span{src_typed}; - copy_attribute_to_vertices(src_typed_span, selection, dst.span.typed<T>()); - }); + src.materialize_compressed_to_uninitialized(selection, dst.span.data()); dst.finish(); } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_transform.cc b/source/blender/nodes/geometry/nodes/node_geo_transform.cc index e95db205920..8e65e73d1e2 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_transform.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_transform.cc @@ -47,21 +47,24 @@ static void transform_mesh(Mesh &mesh, const float4x4 &transform) static void translate_pointcloud(PointCloud &pointcloud, const float3 translation) { - CustomData_duplicate_referenced_layer(&pointcloud.pdata, CD_PROP_FLOAT3, pointcloud.totpoint); - BKE_pointcloud_update_customdata_pointers(&pointcloud); - for (const int i : IndexRange(pointcloud.totpoint)) { - add_v3_v3(pointcloud.co[i], translation); + MutableAttributeAccessor attributes = bke::pointcloud_attributes_for_write(pointcloud); + SpanAttributeWriter position = attributes.lookup_or_add_for_write_span<float3>( + "position", ATTR_DOMAIN_POINT); + for (const int i : position.span.index_range()) { + position.span[i] += translation; } + position.finish(); } static void transform_pointcloud(PointCloud &pointcloud, const float4x4 &transform) { - CustomData_duplicate_referenced_layer(&pointcloud.pdata, CD_PROP_FLOAT3, pointcloud.totpoint); - BKE_pointcloud_update_customdata_pointers(&pointcloud); - for (const int i : IndexRange(pointcloud.totpoint)) { - float3 &co = *(float3 *)pointcloud.co[i]; - co = transform * co; + MutableAttributeAccessor attributes = bke::pointcloud_attributes_for_write(pointcloud); + SpanAttributeWriter position = attributes.lookup_or_add_for_write_span<float3>( + "position", ATTR_DOMAIN_POINT); + for (const int i : position.span.index_range()) { + position.span[i] = transform * position.span[i]; } + position.finish(); } static void translate_instances(InstancesComponent &instances, const float3 translation) diff --git a/source/blender/nodes/geometry/nodes/node_geo_uv_unwrap.cc b/source/blender/nodes/geometry/nodes/node_geo_uv_unwrap.cc index 2ec14ad2d29..03657f3e016 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_uv_unwrap.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_uv_unwrap.cc @@ -121,7 +121,7 @@ static VArray<float3> construct_uv_gvarray(const MeshComponent &component, GEO_uv_parametrizer_lscm_begin(handle, false, method == GEO_NODE_UV_UNWRAP_METHOD_ANGLE_BASED); GEO_uv_parametrizer_lscm_solve(handle, nullptr, nullptr); GEO_uv_parametrizer_lscm_end(handle); - GEO_uv_parametrizer_average(handle, true); + GEO_uv_parametrizer_average(handle, true, false, false); GEO_uv_parametrizer_pack(handle, margin, true, true); GEO_uv_parametrizer_flush(handle); GEO_uv_parametrizer_delete(handle); diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_hair_principled.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_hair_principled.cc index 6495dcfffba..a0579372a15 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bsdf_hair_principled.cc +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_hair_principled.cc @@ -42,8 +42,7 @@ static void node_declare(NodeDeclarationBuilder &b) .min(0.0f) .max(1.0f) .subtype(PROP_FACTOR); - b.add_input<decl::Float>(N_("IOR")).default_value(1.55f).min(0.0f).max(1000.0f).subtype( - PROP_FACTOR); + b.add_input<decl::Float>(N_("IOR")).default_value(1.55f).min(0.0f).max(1000.0f); b.add_input<decl::Float>(N_("Offset")) .default_value(2.0f * ((float)M_PI) / 180.0f) .min(-M_PI_2) diff --git a/source/blender/python/generic/bgl.c b/source/blender/python/generic/bgl.c index 92ed0c51b1f..f5c1f060e80 100644 --- a/source/blender/python/generic/bgl.c +++ b/source/blender/python/generic/bgl.c @@ -1082,18 +1082,34 @@ static PyObject *Buffer_repr(Buffer *self) /** \name OpenGL API Wrapping * \{ */ -#define BGL_Wrap(funcname, ret, arg_list) \ - static PyObject *Method_##funcname(PyObject *UNUSED(self), PyObject *args) \ - { \ - arg_def arg_list; \ - ret_def_##ret; \ - if (!PyArg_ParseTuple(args, arg_str arg_list, arg_ref arg_list)) { \ +#ifdef WITH_OPENGL +# define BGL_Wrap(funcname, ret, arg_list) \ + static PyObject *Method_##funcname(PyObject *UNUSED(self), PyObject *args) \ + { \ + arg_def arg_list; \ + ret_def_##ret; \ + if (!PyArg_ParseTuple(args, arg_str arg_list, arg_ref arg_list)) { \ + return NULL; \ + } \ + GPU_bgl_start(); \ + ret_set_##ret gl##funcname(arg_var arg_list); \ + ret_ret_##ret; \ + } +#else + +static void bgl_no_opengl_error(void) +{ + PyErr_SetString(PyExc_RuntimeError, "Built without OpenGL support"); +} + +# define BGL_Wrap(funcname, ret, arg_list) \ + static PyObject *Method_##funcname(PyObject *UNUSED(self), PyObject *args) \ + { \ + (void)args; \ + bgl_no_opengl_error(); \ return NULL; \ - } \ - GPU_bgl_start(); \ - ret_set_##ret gl##funcname(arg_var arg_list); \ - ret_ret_##ret; \ - } + } +#endif /* GL_VERSION_1_0 */ BGL_Wrap(BlendFunc, void, (GLenum, GLenum)); @@ -1421,12 +1437,22 @@ static void py_module_dict_add_method(PyObject *submodule, #ifdef __GNUC__ # pragma GCC diagnostic ignored "-Waddress" #endif -#define PY_MOD_ADD_METHOD(func) \ - { \ - static PyMethodDef method_def = {"gl" #func, Method_##func, METH_VARARGS}; \ - py_module_dict_add_method(submodule, dict, &method_def, (gl##func != NULL)); \ - } \ - ((void)0) + +#ifdef WITH_OPENGL +# define PY_MOD_ADD_METHOD(func) \ + { \ + static PyMethodDef method_def = {"gl" #func, Method_##func, METH_VARARGS}; \ + py_module_dict_add_method(submodule, dict, &method_def, (gl##func != NULL)); \ + } \ + ((void)0) +#else +# define PY_MOD_ADD_METHOD(func) \ + { \ + static PyMethodDef method_def = {"gl" #func, Method_##func, METH_VARARGS}; \ + py_module_dict_add_method(submodule, dict, &method_def, false); \ + } \ + ((void)0) +#endif static void init_bgl_version_1_0_methods(PyObject *submodule, PyObject *dict) { @@ -2620,9 +2646,13 @@ static PyObject *Method_ShaderSource(PyObject *UNUSED(self), PyObject *args) return NULL; } +#ifdef WITH_OPENGL glShaderSource(shader, 1, (const char **)&source, NULL); - Py_RETURN_NONE; +#else + bgl_no_opengl_error(); + return NULL; +#endif } /** \} */ diff --git a/source/blender/python/gpu/gpu_py_vertex_buffer.c b/source/blender/python/gpu/gpu_py_vertex_buffer.c index a295bedeae2..ac050128a1d 100644 --- a/source/blender/python/gpu/gpu_py_vertex_buffer.c +++ b/source/blender/python/gpu/gpu_py_vertex_buffer.c @@ -323,14 +323,14 @@ static void pygpu_vertbuf__tp_dealloc(BPyGPUVertBuf *self) } PyDoc_STRVAR(pygpu_vertbuf__tp_doc, - ".. class:: GPUVertBuf(len, format)\n" + ".. class:: GPUVertBuf(format, len)\n" "\n" " Contains a VBO.\n" "\n" - " :param len: Amount of vertices that will fit into this buffer.\n" - " :type type: `int`\n" " :param format: Vertex format.\n" - " :type buf: :class:`gpu.types.GPUVertFormat`\n"); + " :type buf: :class:`gpu.types.GPUVertFormat`\n" + " :param len: Amount of vertices that will fit into this buffer.\n" + " :type type: `int`\n"); PyTypeObject BPyGPUVertBuf_Type = { PyVarObject_HEAD_INIT(NULL, 0).tp_name = "GPUVertBuf", .tp_basicsize = sizeof(BPyGPUVertBuf), diff --git a/source/blender/python/intern/bpy.c b/source/blender/python/intern/bpy.c index 2e97ae0fc1d..7fe0b9455e6 100644 --- a/source/blender/python/intern/bpy.c +++ b/source/blender/python/intern/bpy.c @@ -32,6 +32,7 @@ #include "bpy.h" #include "bpy_app.h" #include "bpy_capi_utils.h" +#include "bpy_driver.h" #include "bpy_library.h" #include "bpy_operator.h" #include "bpy_props.h" @@ -326,6 +327,49 @@ static PyObject *bpy_resource_path(PyObject *UNUSED(self), PyObject *args, PyObj return PyC_UnicodeFromByte(path ? path : ""); } +/* This is only exposed for tests, see: `tests/python/bl_pyapi_bpy_driver_secure_eval.py`. */ +PyDoc_STRVAR(bpy_driver_secure_code_test_doc, + ".. function:: _driver_secure_code_test(code)\n" + "\n" + " Test if the script should be considered trusted.\n" + "\n" + " :arg code: The code to test.\n" + " :type code: code\n" + " :arg namespace: The namespace of values which are allowed.\n" + " :type namespace: dict\n" + " :arg verbose: Print the reason for considering insecure to the ``stderr``.\n" + " :type verbose: bool\n" + " :return: True when the script is considered trusted.\n" + " :rtype: bool\n"); +static PyObject *bpy_driver_secure_code_test(PyObject *UNUSED(self), PyObject *args, PyObject *kw) +{ + PyObject *py_code; + PyObject *py_namespace = NULL; + const bool verbose = false; + static const char *_keywords[] = {"code", "namespace", "verbose", NULL}; + static _PyArg_Parser _parser = { + "O!" /* `expression` */ + "|$" /* Optional keyword only arguments. */ + "O!" /* `namespace` */ + "O&" /* `verbose` */ + ":driver_secure_code_test", + _keywords, + 0, + }; + if (!_PyArg_ParseTupleAndKeywordsFast(args, + kw, + &_parser, + &PyCode_Type, + &py_code, + &PyDict_Type, + &py_namespace, + PyC_ParseBool, + &verbose)) { + return NULL; + } + return PyBool_FromLong(BPY_driver_secure_bytecode_test(py_code, py_namespace, verbose)); +} + PyDoc_STRVAR(bpy_escape_identifier_doc, ".. function:: escape_identifier(string)\n" "\n" @@ -492,65 +536,37 @@ static PyObject *bpy_rna_enum_items_static(PyObject *UNUSED(self)) return result; } -static PyMethodDef meth_bpy_script_paths = { - "script_paths", - (PyCFunction)bpy_script_paths, - METH_NOARGS, - bpy_script_paths_doc, -}; -static PyMethodDef meth_bpy_blend_paths = { - "blend_paths", - (PyCFunction)bpy_blend_paths, - METH_VARARGS | METH_KEYWORDS, - bpy_blend_paths_doc, -}; -static PyMethodDef meth_bpy_flip_name = { - "flip_name", - (PyCFunction)bpy_flip_name, - METH_VARARGS | METH_KEYWORDS, - bpy_flip_name_doc, -}; -static PyMethodDef meth_bpy_user_resource = { - "user_resource", - (PyCFunction)bpy_user_resource, - METH_VARARGS | METH_KEYWORDS, - NULL, -}; -static PyMethodDef meth_bpy_system_resource = { - "system_resource", - (PyCFunction)bpy_system_resource, - METH_VARARGS | METH_KEYWORDS, - bpy_system_resource_doc, -}; -static PyMethodDef meth_bpy_resource_path = { - "resource_path", - (PyCFunction)bpy_resource_path, - METH_VARARGS | METH_KEYWORDS, - bpy_resource_path_doc, -}; -static PyMethodDef meth_bpy_escape_identifier = { - "escape_identifier", - (PyCFunction)bpy_escape_identifier, - METH_O, - bpy_escape_identifier_doc, -}; -static PyMethodDef meth_bpy_unescape_identifier = { - "unescape_identifier", - (PyCFunction)bpy_unescape_identifier, - METH_O, - bpy_unescape_identifier_doc, -}; -static PyMethodDef meth_bpy_context_members = { - "context_members", - (PyCFunction)bpy_context_members, - METH_NOARGS, - bpy_context_members_doc, -}; -static PyMethodDef meth_bpy_rna_enum_items_static = { - "rna_enum_items_static", - (PyCFunction)bpy_rna_enum_items_static, - METH_NOARGS, - bpy_rna_enum_items_static_doc, +static PyMethodDef bpy_methods[] = { + {"script_paths", (PyCFunction)bpy_script_paths, METH_NOARGS, bpy_script_paths_doc}, + {"blend_paths", + (PyCFunction)bpy_blend_paths, + METH_VARARGS | METH_KEYWORDS, + bpy_blend_paths_doc}, + {"flip_name", (PyCFunction)bpy_flip_name, METH_VARARGS | METH_KEYWORDS, bpy_flip_name_doc}, + {"user_resource", (PyCFunction)bpy_user_resource, METH_VARARGS | METH_KEYWORDS, NULL}, + {"system_resource", + (PyCFunction)bpy_system_resource, + METH_VARARGS | METH_KEYWORDS, + bpy_system_resource_doc}, + {"resource_path", + (PyCFunction)bpy_resource_path, + METH_VARARGS | METH_KEYWORDS, + bpy_resource_path_doc}, + {"_driver_secure_code_test", + (PyCFunction)bpy_driver_secure_code_test, + METH_VARARGS | METH_KEYWORDS, + bpy_driver_secure_code_test_doc}, + {"escape_identifier", (PyCFunction)bpy_escape_identifier, METH_O, bpy_escape_identifier_doc}, + {"unescape_identifier", + (PyCFunction)bpy_unescape_identifier, + METH_O, + bpy_unescape_identifier_doc}, + {"context_members", (PyCFunction)bpy_context_members, METH_NOARGS, bpy_context_members_doc}, + {"rna_enum_items_static", + (PyCFunction)bpy_rna_enum_items_static, + METH_NOARGS, + bpy_rna_enum_items_static_doc}, + {NULL, NULL, 0, NULL}, }; static PyObject *bpy_import_test(const char *modname) @@ -632,35 +648,12 @@ void BPy_init_modules(struct bContext *C) /* Register methods and property get/set for RNA types. */ BPY_rna_types_extend_capi(); - /* utility func's that have nowhere else to go */ - PyModule_AddObject(mod, - meth_bpy_script_paths.ml_name, - (PyObject *)PyCFunction_New(&meth_bpy_script_paths, NULL)); - PyModule_AddObject( - mod, meth_bpy_blend_paths.ml_name, (PyObject *)PyCFunction_New(&meth_bpy_blend_paths, NULL)); - PyModule_AddObject(mod, - meth_bpy_user_resource.ml_name, - (PyObject *)PyCFunction_New(&meth_bpy_user_resource, NULL)); - PyModule_AddObject(mod, - meth_bpy_system_resource.ml_name, - (PyObject *)PyCFunction_New(&meth_bpy_system_resource, NULL)); - PyModule_AddObject(mod, - meth_bpy_resource_path.ml_name, - (PyObject *)PyCFunction_New(&meth_bpy_resource_path, NULL)); - PyModule_AddObject(mod, - meth_bpy_escape_identifier.ml_name, - (PyObject *)PyCFunction_New(&meth_bpy_escape_identifier, NULL)); - PyModule_AddObject(mod, - meth_bpy_unescape_identifier.ml_name, - (PyObject *)PyCFunction_New(&meth_bpy_unescape_identifier, NULL)); - PyModule_AddObject( - mod, meth_bpy_flip_name.ml_name, (PyObject *)PyCFunction_New(&meth_bpy_flip_name, NULL)); - PyModule_AddObject(mod, - meth_bpy_context_members.ml_name, - (PyObject *)PyCFunction_New(&meth_bpy_context_members, NULL)); - PyModule_AddObject(mod, - meth_bpy_rna_enum_items_static.ml_name, - (PyObject *)PyCFunction_New(&meth_bpy_rna_enum_items_static, NULL)); + for (int i = 0; bpy_methods[i].ml_name; i++) { + PyMethodDef *m = &bpy_methods[i]; + /* Currently there is no need to support these. */ + BLI_assert((m->ml_flags & (METH_CLASS | METH_STATIC)) == 0); + PyModule_AddObject(mod, m->ml_name, (PyObject *)PyCFunction_New(m, NULL)); + } /* register funcs (bpy_rna.c) */ PyModule_AddObject(mod, diff --git a/source/blender/python/intern/bpy_driver.c b/source/blender/python/intern/bpy_driver.c index cc64af2a489..04aa203d198 100644 --- a/source/blender/python/intern/bpy_driver.c +++ b/source/blender/python/intern/bpy_driver.c @@ -318,6 +318,7 @@ static const bool secure_opcodes[255] = { OK_OP(UNARY_INVERT), OK_OP(BINARY_SUBSCR), OK_OP(GET_LEN), + OK_OP(LIST_TO_TUPLE), OK_OP(RETURN_VALUE), OK_OP(SWAP), OK_OP(BUILD_TUPLE), @@ -332,6 +333,7 @@ static const bool secure_opcodes[255] = { OK_OP(POP_JUMP_FORWARD_IF_TRUE), OK_OP(LOAD_GLOBAL), OK_OP(IS_OP), + OK_OP(CONTAINS_OP), OK_OP(BINARY_OP), OK_OP(LOAD_FAST), OK_OP(STORE_FAST), @@ -342,6 +344,14 @@ static const bool secure_opcodes[255] = { OK_OP(LOAD_DEREF), OK_OP(STORE_DEREF), OK_OP(RESUME), + OK_OP(LIST_EXTEND), + OK_OP(SET_UPDATE), +/* NOTE(@campbellbarton): Don't enable dict manipulation, unless we can prove there is not way it + * can be used to manipulate the name-space (potentially allowing malicious code). */ +# if 0 + OK_OP(DICT_MERGE), + OK_OP(DICT_UPDATE), +# endif OK_OP(POP_JUMP_BACKWARD_IF_NOT_NONE), OK_OP(POP_JUMP_BACKWARD_IF_NONE), OK_OP(POP_JUMP_BACKWARD_IF_FALSE), @@ -395,6 +405,7 @@ static const bool secure_opcodes[255] = { OK_OP(INPLACE_AND), OK_OP(INPLACE_XOR), OK_OP(INPLACE_OR), + OK_OP(LIST_TO_TUPLE), OK_OP(RETURN_VALUE), OK_OP(ROT_N), OK_OP(BUILD_TUPLE), @@ -410,12 +421,21 @@ static const bool secure_opcodes[255] = { OK_OP(POP_JUMP_IF_TRUE), OK_OP(LOAD_GLOBAL), OK_OP(IS_OP), + OK_OP(CONTAINS_OP), OK_OP(LOAD_FAST), OK_OP(STORE_FAST), OK_OP(DELETE_FAST), OK_OP(BUILD_SLICE), OK_OP(LOAD_DEREF), OK_OP(STORE_DEREF), + OK_OP(LIST_EXTEND), + OK_OP(SET_UPDATE), +/* NOTE(@campbellbarton): Don't enable dict manipulation, unless we can prove there is not way it + * can be used to manipulate the name-space (potentially allowing malicious code). */ +# if 0 + OK_OP(DICT_MERGE), + OK_OP(DICT_UPDATE), +# endif /* Special cases. */ OK_OP(LOAD_CONST), /* Ok because constants are accepted. */ @@ -429,9 +449,10 @@ static const bool secure_opcodes[255] = { # undef OK_OP -static bool bpy_driver_secure_bytecode_validate(PyObject *expr_code, - PyObject *dict_arr[], - const char *error_prefix) +bool BPY_driver_secure_bytecode_test_ex(PyObject *expr_code, + PyObject *namespace_array[], + const bool verbose, + const char *error_prefix) { PyCodeObject *py_code = (PyCodeObject *)expr_code; @@ -439,21 +460,23 @@ static bool bpy_driver_secure_bytecode_validate(PyObject *expr_code, { for (int i = 0; i < PyTuple_GET_SIZE(py_code->co_names); i++) { PyObject *name = PyTuple_GET_ITEM(py_code->co_names, i); - + const char *name_str = PyUnicode_AsUTF8(name); bool contains_name = false; - for (int j = 0; dict_arr[j]; j++) { - if (PyDict_Contains(dict_arr[j], name)) { + for (int j = 0; namespace_array[j]; j++) { + if (PyDict_Contains(namespace_array[j], name)) { contains_name = true; break; } } - if (contains_name == false) { - fprintf(stderr, - "\t%s: restricted access disallows name '%s', " - "enable auto-execution to support\n", - error_prefix, - PyUnicode_AsUTF8(name)); + if ((contains_name == false) || (name_str[0] == '_')) { + if (verbose) { + fprintf(stderr, + "\t%s: restricted access disallows name '%s', " + "enable auto-execution to support\n", + error_prefix, + name_str); + } return false; } } @@ -485,11 +508,13 @@ static bool bpy_driver_secure_bytecode_validate(PyObject *expr_code, for (Py_ssize_t i = 0; i < code_len; i++) { const int opcode = _Py_OPCODE(codestr[i]); if (secure_opcodes[opcode] == false) { - fprintf(stderr, - "\t%s: restricted access disallows opcode '%d', " - "enable auto-execution to support\n", - error_prefix, - opcode); + if (verbose) { + fprintf(stderr, + "\t%s: restricted access disallows opcode '%d', " + "enable auto-execution to support\n", + error_prefix, + opcode); + } ok = false; break; } @@ -506,6 +531,26 @@ static bool bpy_driver_secure_bytecode_validate(PyObject *expr_code, return true; } +bool BPY_driver_secure_bytecode_test(PyObject *expr_code, PyObject *namespace, const bool verbose) +{ + + if (!bpy_pydriver_Dict) { + if (bpy_pydriver_create_dict() != 0) { + fprintf(stderr, "%s: couldn't create Python dictionary\n", __func__); + return false; + } + } + return BPY_driver_secure_bytecode_test_ex(expr_code, + (PyObject *[]){ + bpy_pydriver_Dict, + bpy_pydriver_Dict__whitelist, + namespace, + NULL, + }, + verbose, + __func__); +} + #endif /* USE_BYTECODE_WHITELIST */ float BPY_driver_exec(struct PathResolvedRNA *anim_rna, ChannelDriver *driver, @@ -697,14 +742,17 @@ float BPY_driver_exec(struct PathResolvedRNA *anim_rna, #ifdef USE_BYTECODE_WHITELIST if (is_recompile && expr_code) { if (!(G.f & G_FLAG_SCRIPT_AUTOEXEC)) { - if (!bpy_driver_secure_bytecode_validate(expr_code, - (PyObject *[]){ - bpy_pydriver_Dict, - bpy_pydriver_Dict__whitelist, - driver_vars, - NULL, - }, - __func__)) { + if (!BPY_driver_secure_bytecode_test_ex( + expr_code, + (PyObject *[]){ + bpy_pydriver_Dict, + bpy_pydriver_Dict__whitelist, + driver_vars, + NULL, + }, + /* Always be verbose since this can give hints to why evaluation fails. */ + true, + __func__)) { if (!(G.f & G_FLAG_SCRIPT_AUTOEXEC_FAIL_QUIET)) { G.f |= G_FLAG_SCRIPT_AUTOEXEC_FAIL; BLI_snprintf(G.autoexec_fail, sizeof(G.autoexec_fail), "Driver '%s'", expr); diff --git a/source/blender/python/intern/bpy_driver.h b/source/blender/python/intern/bpy_driver.h index 301c6b3662e..f0d9717dbbd 100644 --- a/source/blender/python/intern/bpy_driver.h +++ b/source/blender/python/intern/bpy_driver.h @@ -10,6 +10,8 @@ extern "C" { #endif +#include <stdbool.h> + /** * For faster execution we keep a special dictionary for py-drivers, with * the needed modules and aliases. @@ -21,6 +23,14 @@ int bpy_pydriver_create_dict(void); */ extern PyObject *bpy_pydriver_Dict; +extern bool BPY_driver_secure_bytecode_test_ex(PyObject *expr_code, + PyObject *namespace_array[], + const bool verbose, + const char *error_prefix); +extern bool BPY_driver_secure_bytecode_test(PyObject *expr_code, + PyObject *namespace, + const bool verbose); + #ifdef __cplusplus } #endif diff --git a/source/blender/python/intern/bpy_rna_anim.c b/source/blender/python/intern/bpy_rna_anim.c index fb744432d4b..f25e9d0bbbc 100644 --- a/source/blender/python/intern/bpy_rna_anim.c +++ b/source/blender/python/intern/bpy_rna_anim.c @@ -487,7 +487,8 @@ PyObject *pyrna_struct_keyframe_delete(BPy_StructRNA *self, PyObject *args, PyOb i = BKE_fcurve_bezt_binarysearch_index(fcu->bezt, cfra, fcu->totvert, &found); if (found) { /* delete the key at the index (will sanity check + do recalc afterwards) */ - delete_fcurve_key(fcu, i, 1); + BKE_fcurve_delete_key(fcu, i); + BKE_fcurve_handles_recalc(fcu); result = true; } } diff --git a/source/blender/python/intern/bpy_rna_gizmo.c b/source/blender/python/intern/bpy_rna_gizmo.c index 61f439e5152..32ef7865e84 100644 --- a/source/blender/python/intern/bpy_rna_gizmo.c +++ b/source/blender/python/intern/bpy_rna_gizmo.c @@ -314,6 +314,8 @@ PyDoc_STRVAR( "\n" " Assigns callbacks to a gizmos property.\n" "\n" + " :arg target: Target property name.\n" + " :type target: string\n" " :arg get: Function that returns the value for this property (single value or sequence).\n" " :type get: callable\n" " :arg set: Function that takes a single value argument and applies it.\n" diff --git a/source/blender/python/mathutils/mathutils_Matrix.c b/source/blender/python/mathutils/mathutils_Matrix.c index 8cd7a5c7d87..1e85ece124d 100644 --- a/source/blender/python/mathutils/mathutils_Matrix.c +++ b/source/blender/python/mathutils/mathutils_Matrix.c @@ -290,6 +290,18 @@ static PyObject *matrix__apply_to_copy(PyObject *(*matrix_func)(MatrixObject *), return NULL; } +static bool matrix_is_identity(MatrixObject *self) +{ + for (int row = 0; row < self->row_num; row++) { + for (int col = 0; col < self->col_num; col++) { + if (MATRIX_ITEM(self, row, col) != ((row != col) ? 0.0f : 1.0f)) { + return false; + } + } + } + return true; +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -3104,6 +3116,16 @@ static PyObject *Matrix_median_scale_get(MatrixObject *self, void *UNUSED(closur return PyFloat_FromDouble(mat3_to_scale(mat)); } +PyDoc_STRVAR(Matrix_is_identity_doc, + "True if this is an identity matrix (read-only).\n\n:type: bool"); +static PyObject *Matrix_is_identity_get(MatrixObject *self, void *UNUSED(closure)) +{ + if (BaseMath_ReadCallback(self) == -1) { + return NULL; + } + return PyBool_FromLong(matrix_is_identity(self)); +} + PyDoc_STRVAR(Matrix_is_negative_doc, "True if this matrix results in a negative scale, 3x3 and 4x4 only, " "(read-only).\n\n:type: bool"); @@ -3187,6 +3209,7 @@ static PyGetSetDef Matrix_getseters[] = { NULL}, {"row", (getter)Matrix_row_get, (setter)NULL, Matrix_row_doc, NULL}, {"col", (getter)Matrix_col_get, (setter)NULL, Matrix_col_doc, NULL}, + {"is_identity", (getter)Matrix_is_identity_get, (setter)NULL, Matrix_is_identity_doc, NULL}, {"is_negative", (getter)Matrix_is_negative_get, (setter)NULL, Matrix_is_negative_doc, NULL}, {"is_orthogonal", (getter)Matrix_is_orthogonal_get, diff --git a/source/blender/render/intern/engine.c b/source/blender/render/intern/engine.c index 8a4b4c2a70d..266e66092b8 100644 --- a/source/blender/render/intern/engine.c +++ b/source/blender/render/intern/engine.c @@ -46,6 +46,8 @@ #include "DRW_engine.h" +#include "GPU_context.h" + #include "pipeline.h" #include "render_result.h" #include "render_types.h" @@ -950,6 +952,16 @@ bool RE_engine_render(Render *re, bool do_all) re->draw_lock(re->dlh, true); } + if ((type->flag & RE_USE_GPU_CONTEXT) && !GPU_backend_supported()) { + /* Clear UI drawing locks. */ + if (re->draw_lock) { + re->draw_lock(re->dlh, false); + } + BKE_report(re->reports, RPT_ERROR, "Can not initialize the GPU"); + G.is_break = true; + return true; + } + /* update animation here so any render layer animation is applied before * creating the render result */ if ((re->r.scemode & (R_NO_FRAME_UPDATE | R_BUTS_PREVIEW)) == 0) { diff --git a/source/blender/render/intern/pipeline.c b/source/blender/render/intern/pipeline.c index 154b689b4a5..1c42467bc3d 100644 --- a/source/blender/render/intern/pipeline.c +++ b/source/blender/render/intern/pipeline.c @@ -1036,8 +1036,7 @@ static void do_render_compositor_scene(Render *re, Scene *sce, int cfra) /* exception: scene uses own size (unfinished code) */ if (0) { - winx = (sce->r.size * sce->r.xsch) / 100; - winy = (sce->r.size * sce->r.ysch) / 100; + BKE_render_resolution(&sce->r, false, &winx, &winy); } /* initial setup */ @@ -1675,8 +1674,7 @@ static int render_init_from_main(Render *re, * r.border is the clipping rect */ /* calculate actual render result and display size */ - winx = (rd->size * rd->xsch) / 100; - winy = (rd->size * rd->ysch) / 100; + BKE_render_resolution(rd, false, &winx, &winy); /* We always render smaller part, inserting it in larger image is compositor business, * it uses 'disprect' for it. */ @@ -2409,8 +2407,7 @@ void RE_PreviewRender(Render *re, Main *bmain, Scene *sce) Object *camera; int winx, winy; - winx = (sce->r.size * sce->r.xsch) / 100; - winy = (sce->r.size * sce->r.ysch) / 100; + BKE_render_resolution(&sce->r, false, &winx, &winy); RE_InitState(re, NULL, &sce->r, &sce->view_layers, NULL, winx, winy, NULL); @@ -2439,8 +2436,7 @@ bool RE_ReadRenderResult(Scene *scene, Scene *scenode) rcti disprect; /* calculate actual render result and display size */ - winx = (scene->r.size * scene->r.xsch) / 100; - winy = (scene->r.size * scene->r.ysch) / 100; + BKE_render_resolution(&scene->r, false, &winx, &winy); /* only in movie case we render smaller part */ if (scene->r.mode & R_BORDER) { diff --git a/source/blender/sequencer/intern/proxy.c b/source/blender/sequencer/intern/proxy.c index 522c64f7791..374e18dd36a 100644 --- a/source/blender/sequencer/intern/proxy.c +++ b/source/blender/sequencer/intern/proxy.c @@ -511,15 +511,11 @@ void SEQ_proxy_rebuild(SeqIndexBuildContext *context, } /* fail safe code */ + int width, height; + BKE_render_resolution(&scene->r, false, &width, &height); - SEQ_render_new_render_data(bmain, - context->depsgraph, - context->scene, - roundf((scene->r.size * (float)scene->r.xsch) / 100.0f), - roundf((scene->r.size * (float)scene->r.ysch) / 100.0f), - 100, - false, - &render_context); + SEQ_render_new_render_data( + bmain, context->depsgraph, context->scene, width, height, 100, false, &render_context); render_context.skip_cache = true; render_context.is_proxy_render = true; diff --git a/source/blender/sequencer/intern/render.c b/source/blender/sequencer/intern/render.c index 15487a597d1..b7dc0e7035d 100644 --- a/source/blender/sequencer/intern/render.c +++ b/source/blender/sequencer/intern/render.c @@ -1447,8 +1447,8 @@ static ImBuf *seq_render_scene_strip(const SeqRenderData *context, if ((sequencer_view3d_fn && do_seq_gl && camera) && is_thread_main) { char err_out[256] = "unknown"; - const int width = (scene->r.xsch * scene->r.size) / 100; - const int height = (scene->r.ysch * scene->r.size) / 100; + int width, height; + BKE_render_resolution(&scene->r, false, &width, &height); const char *viewname = BKE_scene_multiview_render_view_name_get(&scene->r, context->view_id); unsigned int draw_flags = V3D_OFSDRAW_NONE; diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index d852b85a3d0..44c5b86857d 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -1214,10 +1214,24 @@ int WM_operator_flag_only_pass_through_on_press(int retval, const struct wmEvent /* Drag and drop. */ /** - * Note that the pointer should be valid allocated and not on stack. + * Start dragging immediately with the given data. + * Note that \a poin should be valid allocated and not on stack. */ -struct wmDrag *WM_event_start_drag( +void WM_event_start_drag( struct bContext *C, int icon, int type, void *poin, double value, unsigned int flags); +/** + * Create and fill the dragging data, but don't start dragging just yet (unlike + * #WM_event_start_drag()). Must be followed up by #WM_event_start_prepared_drag(), otherwise the + * returned pointer will leak memory. + * + * Note that \a poin should be valid allocated and not on stack. + */ +wmDrag *WM_drag_data_create( + struct bContext *C, int icon, int type, void *poin, double value, unsigned int flags); +/** + * Invoke dragging using the given \a drag data. + */ +void WM_event_start_prepared_drag(struct bContext *C, wmDrag *drag); void WM_event_drag_image(struct wmDrag *, struct ImBuf *, float scale); void WM_drag_free(struct wmDrag *drag); void WM_drag_data_free(int dragtype, void *poin); @@ -1530,6 +1544,7 @@ bool WM_event_is_modal_drag_exit(const struct wmEvent *event, bool WM_event_is_mouse_drag(const struct wmEvent *event); bool WM_event_is_mouse_drag_or_press(const wmEvent *event); int WM_event_drag_direction(const wmEvent *event); +char WM_event_utf8_to_ascii(const struct wmEvent *event) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT; /** * Detect motion between selection (callers should only use this for selection picking), diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index 32bc2f96365..e7cbe936607 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -683,13 +683,11 @@ typedef struct wmEvent { /** Region relative mouse position (name convention before Blender 2.5). */ int mval[2]; /** - * From, ghost if utf8 is enabled for the platform, - * #BLI_str_utf8_size() must _always_ be valid, check - * when assigning s we don't need to check on every access after. + * A single UTF8 encoded character. + * #BLI_str_utf8_size() must _always_ return a valid value, + * check when assigning so we don't need to check on every access after. */ char utf8_buf[6]; - /** From ghost, fallback if utf8 isn't set. */ - char ascii; /** Modifier states: #KM_SHIFT, #KM_CTRL, #KM_ALT & #KM_OSKEY. */ uint8_t modifier; diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c index 32cec8f779c..0d74bc259f4 100644 --- a/source/blender/windowmanager/intern/wm.c +++ b/source/blender/windowmanager/intern/wm.c @@ -257,7 +257,7 @@ static void window_manager_blend_read_lib(BlendLibReader *reader, ID *id) IDTypeInfo IDType_ID_WM = { .id_code = ID_WM, - .id_filter = 0, + .id_filter = FILTER_ID_WM, .main_listbase_index = INDEX_ID_WM, .struct_size = sizeof(wmWindowManager), .name = "WindowManager", diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c index 546ba795892..36bd69a9b25 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.c +++ b/source/blender/windowmanager/intern/wm_dragdrop.c @@ -175,16 +175,14 @@ static void wm_dropbox_invoke(bContext *C, wmDrag *drag) } } -wmDrag *WM_event_start_drag( +wmDrag *WM_drag_data_create( struct bContext *C, int icon, int type, void *poin, double value, unsigned int flags) { - wmWindowManager *wm = CTX_wm_manager(C); wmDrag *drag = MEM_callocN(sizeof(struct wmDrag), "new drag"); /* Keep track of future multi-touch drag too, add a mouse-pointer id or so. */ /* if multiple drags are added, they're drawn as list */ - BLI_addtail(&wm->drags, drag); drag->flags = flags; drag->icon = icon; drag->type = type; @@ -226,9 +224,22 @@ wmDrag *WM_event_start_drag( } drag->value = value; + return drag; +} + +void WM_event_start_prepared_drag(bContext *C, wmDrag *drag) +{ + wmWindowManager *wm = CTX_wm_manager(C); + + BLI_addtail(&wm->drags, drag); wm_dropbox_invoke(C, drag); +} - return drag; +void WM_event_start_drag( + struct bContext *C, int icon, int type, void *poin, double value, unsigned int flags) +{ + wmDrag *drag = WM_drag_data_create(C, icon, type, poin, value, flags); + WM_event_start_prepared_drag(C, drag); } void wm_drags_exit(wmWindowManager *wm, wmWindow *win) diff --git a/source/blender/windowmanager/intern/wm_event_query.c b/source/blender/windowmanager/intern/wm_event_query.c index 221073d288a..81044197ae7 100644 --- a/source/blender/windowmanager/intern/wm_event_query.c +++ b/source/blender/windowmanager/intern/wm_event_query.c @@ -112,7 +112,7 @@ void WM_event_print(const wmEvent *event) "wmEvent type:%d/%s, val:%d/%s, " "prev_type:%d/%s, prev_val:%d/%s, " "modifier=%s, keymodifier:%d, flag:%s, " - "mouse:(%d,%d), ascii:'%c', utf8:'%.*s', pointer:%p", + "mouse:(%d,%d), utf8:'%.*s', pointer:%p", event->type, type_id, event->val, @@ -126,7 +126,6 @@ void WM_event_print(const wmEvent *event) flag_id, event->xy[0], event->xy[1], - event->ascii, BLI_str_utf8_size(event->utf8_buf), event->utf8_buf, (const void *)event); @@ -401,6 +400,20 @@ void WM_event_drag_start_xy(const wmEvent *event, int r_xy[2]) /** \} */ /* -------------------------------------------------------------------- */ +/** \name Event Text Queries + * \{ */ + +char WM_event_utf8_to_ascii(const struct wmEvent *event) +{ + if (BLI_str_utf8_size(event->utf8_buf) == 1) { + return event->utf8_buf[0]; + } + return '\0'; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Event Preference Mapping * \{ */ diff --git a/source/blender/windowmanager/intern/wm_event_system.cc b/source/blender/windowmanager/intern/wm_event_system.cc index 0395e8bda7a..79b303364d8 100644 --- a/source/blender/windowmanager/intern/wm_event_system.cc +++ b/source/blender/windowmanager/intern/wm_event_system.cc @@ -2086,9 +2086,7 @@ static bool wm_eventmatch(const wmEvent *winevent, const wmKeyMapItem *kmi) /* The matching rules. */ if (kmitype == KM_TEXTINPUT) { if (winevent->val == KM_PRESS) { /* Prevent double clicks. */ - /* Not using #ISTEXTINPUT anymore because (at least on Windows) some key codes above 255 - * could have printable ascii keys, See T30479. */ - if (ISKEYBOARD(winevent->type) && (winevent->ascii || winevent->utf8_buf[0])) { + if (ISKEYBOARD(winevent->type) && winevent->utf8_buf[0]) { return true; } } @@ -5042,7 +5040,6 @@ static wmEvent *wm_event_add_mousemove_to_head(wmWindow *win) tevent = *event_last; tevent.flag = (eWM_EventFlag)0; - tevent.ascii = '\0'; tevent.utf8_buf[0] = '\0'; wm_event_custom_clear(&tevent); @@ -5136,6 +5133,53 @@ static void wm_event_state_update_and_click_set(wmEvent *event, wm_event_state_update_and_click_set_ex(event, event_state, is_keyboard, check_double_click); } +/* Returns true when the two events corresponds to a press of the same key with the same modifiers. + */ +static bool wm_event_is_same_key_press(const wmEvent &event_a, const wmEvent &event_b) +{ + if (event_a.val != KM_PRESS || event_b.val != KM_PRESS) { + return false; + } + + if (event_a.modifier != event_b.modifier || event_a.type != event_b.type) { + return false; + } + + return true; +} + +/** + * Returns true if the event is a key press event which is to be ignored and not added to the event + * queue. + * + * A key press event will be ignored if there is already matched key press in the queue. + * This avoids the event queue "clogging" in the situations when there is an operator bound to a + * key press event and the execution time of the operator is longer than the key repeat. + */ +static bool wm_event_is_ignorable_key_press(const wmWindow *win, const wmEvent &event) +{ + if (BLI_listbase_is_empty(&win->event_queue)) { + /* If the queue is empty never ignore the event. + * Empty queue at this point means that the events are handled fast enough, and there is no + * reason to ignore anything. */ + return false; + } + + if ((event.flag & WM_EVENT_IS_REPEAT) == 0) { + /* Only ignore repeat events from the keyboard, and allow accumulation of non-repeat events. + * + * The goal of this check is to allow events coming from a keyboard macro software, which can + * generate events quicker than the main loop handles them. In this case we want all events to + * be handled (unless the keyboard macro software tags them as repeat) because otherwise it + * will become impossible to get reliable results of automated events testing. */ + return false; + } + + const wmEvent &last_event = *reinterpret_cast<const wmEvent *>(win->event_queue.last); + + return wm_event_is_same_key_press(last_event, event); +} + void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void *customdata) { if (UNLIKELY(G.f & G_FLAG_EVENT_SIMULATE)) { @@ -5329,8 +5373,7 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void break; } - event.ascii = kd->ascii; - /* Might be not nullptr terminated. */ + /* Might be not null terminated. */ memcpy(event.utf8_buf, kd->utf8_buf, sizeof(event.utf8_buf)); if (kd->is_repeat) { event.flag |= WM_EVENT_IS_REPEAT; @@ -5341,8 +5384,6 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void /* Exclude arrow keys, escape, etc from text input. */ if (type == GHOST_kEventKeyUp) { - event.ascii = '\0'; - /* Ghost should do this already for key up. */ if (event.utf8_buf[0]) { CLOG_ERROR(WM_LOG_EVENTS, @@ -5351,15 +5392,28 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void event.utf8_buf[0] = '\0'; } else { - if (event.ascii < 32 && event.ascii > 0) { - event.ascii = '\0'; - } if (event.utf8_buf[0] < 32 && event.utf8_buf[0] > 0) { event.utf8_buf[0] = '\0'; } } if (event.utf8_buf[0]) { + /* NOTE(@campbellbarton): Detect non-ASCII characters stored in `utf8_buf`, + * ideally this would never happen but it can't be ruled out for X11 which has + * special handling of Latin1 when building without UTF8 support. + * Avoid regressions by adding this conversions, it should eventually be removed. */ + if ((event.utf8_buf[0] >= 0x80) && (event.utf8_buf[1] == '\0')) { + const uint c = (uint)event.utf8_buf[0]; + int utf8_buf_len = BLI_str_utf8_from_unicode(c, event.utf8_buf, sizeof(event.utf8_buf)); + CLOG_ERROR(WM_LOG_EVENTS, + "ghost detected non-ASCII single byte character '%u', converting to utf8 " + "('%.*s', length=%d)", + c, + utf8_buf_len, + event.utf8_buf, + utf8_buf_len); + } + if (BLI_str_utf8_size(event.utf8_buf) == -1) { CLOG_ERROR(WM_LOG_EVENTS, "ghost detected an invalid unicode character '%d'", @@ -5432,7 +5486,9 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void G.is_break = true; } - wm_event_add(win, &event); + if (!wm_event_is_ignorable_key_press(win, event)) { + wm_event_add(win, &event); + } break; } diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index 252cfc6e143..7f5ec77e16d 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -169,6 +169,10 @@ void WM_init_opengl(void) wm_ghost_init(NULL); } + if (!GPU_backend_supported()) { + return; + } + /* Needs to be first to have an OpenGL context bound. */ DRW_opengl_context_create(); @@ -572,14 +576,6 @@ void WM_exit_ex(bContext *C, const bool do_python) BLF_exit(); - if (opengl_is_init) { - DRW_opengl_context_enable_ex(false); - GPU_pass_cache_free(); - GPU_exit(); - DRW_opengl_context_disable_ex(false); - DRW_opengl_context_destroy(); - } - BLT_lang_free(); ANIM_keyingset_infos_exit(); @@ -604,13 +600,24 @@ void WM_exit_ex(bContext *C, const bool do_python) ED_file_exit(); /* for fsmenu */ - UI_exit(); + /* Delete GPU resources and context. The UI also uses GPU resources and so + * is also deleted with the context active. */ + if (opengl_is_init) { + DRW_opengl_context_enable_ex(false); + UI_exit(); + GPU_pass_cache_free(); + GPU_exit(); + DRW_opengl_context_disable_ex(false); + DRW_opengl_context_destroy(); + } + else { + UI_exit(); + } + BKE_blender_userdef_data_free(&U, false); RNA_exit(); /* should be after BPY_python_end so struct python slots are cleared */ - GPU_backend_exit(); - wm_ghost_exit(); CTX_free(C); diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index 36f91f8414a..d2182f759e5 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -1148,7 +1148,6 @@ static bool ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_pt */ GHOST_TEventKeyData kdata = { .key = GHOST_kKeyUnknown, - .ascii = '\0', .utf8_buf = {'\0'}, .is_repeat = false, }; @@ -1363,6 +1362,11 @@ static bool ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_pt event.type = MOUSEMOVE; event.val = KM_NOTHING; copy_v2_v2_int(event.prev_xy, event.xy); + + wm_cursor_position_from_ghost_screen_coords(win, &ddd->x, &ddd->y); + event.xy[0] = ddd->x; + event.xy[1] = ddd->y; + event.flag = 0; /* No context change! C->wm->windrawable is drawable, or for area queues. */ diff --git a/source/blender/windowmanager/wm_event_types.h b/source/blender/windowmanager/wm_event_types.h index 2d3624704d0..edac3ada73b 100644 --- a/source/blender/windowmanager/wm_event_types.h +++ b/source/blender/windowmanager/wm_event_types.h @@ -358,12 +358,6 @@ enum { /** Test whether the event is timer event. */ #define ISTIMER(event_type) ((event_type) >= TIMER && (event_type) <= TIMERF) -/* for event checks */ -/* only used for KM_TEXTINPUT, so assume that we want all user-inputtable ascii codes included */ -/* Unused, see #wm_eventmatch, see: T30479. */ -// #define ISTEXTINPUT(event_type) ((event_type) >= ' ' && (event_type) <= 255) -/* NOTE: an alternative could be to check `event->utf8_buf`. */ - /** Test whether the event is a key on the keyboard (including modifier keys). */ #define ISKEYBOARD(event_type) \ (((event_type) >= _EVT_KEYBOARD_MIN && (event_type) <= _EVT_KEYBOARD_MAX) || \ diff --git a/source/tools b/source/tools -Subproject 01b4c0e4a172819414229445c314be34527bf41 +Subproject da8bdd7244c7b6c2eadf4c949ff391d0cc43027 diff --git a/tests/python/CMakeLists.txt b/tests/python/CMakeLists.txt index 38c3fc4389a..d95f2cd2644 100644 --- a/tests/python/CMakeLists.txt +++ b/tests/python/CMakeLists.txt @@ -102,6 +102,11 @@ add_blender_test( ) add_blender_test( + script_pyapi_bpy_driver_secure_eval + --python ${CMAKE_CURRENT_LIST_DIR}/bl_pyapi_bpy_driver_secure_eval.py +) + +add_blender_test( script_pyapi_idprop --python ${CMAKE_CURRENT_LIST_DIR}/bl_pyapi_idprop.py ) diff --git a/tests/python/bl_pyapi_bpy_driver_secure_eval.py b/tests/python/bl_pyapi_bpy_driver_secure_eval.py new file mode 100644 index 00000000000..953dbcd5381 --- /dev/null +++ b/tests/python/bl_pyapi_bpy_driver_secure_eval.py @@ -0,0 +1,220 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +# ./blender.bin --background -noaudio --python tests/python/bl_pyapi_bpy_driver_secure_eval.py -- --verbose +import bpy +import unittest +import builtins +from types import ModuleType + + +# ----------------------------------------------------------------------------- +# Mock Environment + + +expect_unreachable_msg = "This function should _NEVER_ run!" +# Internal check, to ensure this actually runs as expected. +expect_unreachable_count = 0 + + +def expect_os_unreachable(): + global expect_unreachable_count + expect_unreachable_count += 1 + raise Exception(expect_unreachable_msg) + + +__import__("os").expect_os_unreachable = expect_os_unreachable + + +expect_open_unreachable_count = 0 + + +def open_expect_unreachable(*args, **kwargs): + global expect_open_unreachable_count + expect_open_unreachable_count += 1 + raise Exception(expect_unreachable_msg) + + +mock_builtins = {**builtins.__dict__, **{"open": open_expect_unreachable}} + + +# ----------------------------------------------------------------------------- +# Utility Functions + + +def is_expression_secure(expr_str, verbose): + """ + Return (ok, code) where ok is true if expr_str is considered secure. + """ + # Internal function only for testing (not part of the public API). + from _bpy import _driver_secure_code_test + expr_code = compile(expr_str, "<is_expression_secure>", 'eval') + ok = _driver_secure_code_test(expr_code, verbose=verbose) + return ok, expr_code + + +# ----------------------------------------------------------------------------- +# Tests (Accept) + + +class _TestExprMixIn: + """ + Sub-classes must define: + - expressions_expect_secure: boolean, the expected secure state. + - expressions: A sequence of expressions that must evaluate in the driver name-space. + + Optionally: + - expressions_expect_unreachable: + A boolean, when true, it's expected each expression should call + ``expect_os_unreachable`` or ``expect_open_unreachable``. + """ + + # Sub-class may override. + expressions_expect_unreachable = False + + def assertSecure(self, expect_secure, expr_str): + is_secure, expr_code = is_expression_secure( + expr_str, + # Only verbose when secure as this is will result in an failure, + # in that case it's useful to know which op-codes caused the test to unexpectedly fail. + verbose=expect_secure, + ) + if is_secure != expect_secure: + raise self.failureException( + "Expression \"%s\" was expected to be %s" % + (expr_str, "secure" if expect_secure else "insecure")) + # NOTE: executing is not essential, it's just better to ensure the expressions make sense. + try: + exec( + expr_code, + {"__builtins__": mock_builtins}, + {**bpy.app.driver_namespace, **{"__builtins__": mock_builtins}}, + ) + # exec(expr_code, {}, bpy.app.driver_namespace) + ex = None + except BaseException as ex_test: + ex = ex_test + + if self.expressions_expect_unreachable: + if ex and ex.args == (expect_unreachable_msg,): + ex = None + elif not ex: + raise self.failureException("Expression \"%s\" failed to run `os.expect_os_unreachable`" % (expr_str,)) + else: + # An unknown exception was raised, use the exception below. + pass + + if ex: + raise self.failureException("Expression \"%s\" failed to evaluate with error: %r" % (expr_str, ex)) + + def test_expr(self): + expect_secure = self.expressions_expect_secure + for expr_str in self.expressions: + self.assertSecure(expect_secure, expr_str) + + +class TestExprMixIn_Accept(_TestExprMixIn): + expressions_expect_secure = True + + +class TestExprMixIn_Reject(_TestExprMixIn): + expressions_expect_secure = False + + +class TestAcceptLiteralNumbers(unittest.TestCase, TestExprMixIn_Accept): + expressions = ("1", "1_1", "1.1", "1j", "0x1", "0o1", "0b1") + + +class TestAcceptLiteralStrings(unittest.TestCase, TestExprMixIn_Accept): + expressions = ("''", "'_'", "r''", "r'_'", "'''_'''") + + +class TestAcceptSequencesEmpty(unittest.TestCase, TestExprMixIn_Accept): + expressions = ("()", "[]", "{}", "[[]]", "(())") + + +class TestAcceptSequencesSimple(unittest.TestCase, TestExprMixIn_Accept): + expressions = ("('', '')", "['', '_']", "{'', '_'}", "{'': '_'}") + + +class TestAcceptSequencesExpand(unittest.TestCase, TestExprMixIn_Accept): + expressions = ("(*('', '_'),)", "[*(), *[]]", "{*{1, 2}}") + + +class TestAcceptSequencesComplex(unittest.TestCase, TestExprMixIn_Accept): + expressions = ("[1, 2, 3][-1:0:-1][0]", "1 in (1, 2)", "False if 1 in {1, 2} else True") + + +class TestAcceptMathOperators(unittest.TestCase, TestExprMixIn_Accept): + expressions = ("4 / 4", "4 * 4", "4 // 4", "2 ** 2", "4 ^ -1", "4 & 1", "4 % 1") + + +class TestAcceptMathFunctionsSimple(unittest.TestCase, TestExprMixIn_Accept): + expressions = ("sin(pi)", "degrees(pi / 2)", "clamp(4, 0, 1)") + + +class TestAcceptMathFunctionsComplex(unittest.TestCase, TestExprMixIn_Accept): + expressions = ("-(sin(pi) ** 2) / 2", "floor(22 / 7)", "ceil(pi + 1)") + + +# ----------------------------------------------------------------------------- +# Tests (Reject) + +class TestRejectLiteralFStrings(unittest.TestCase, TestExprMixIn_Reject): + # F-String's are not supported as `BUILD_STRING` op-code is disabled, + # while it may be safe to enable that needs to be double-checked. + # Further it doesn't seem useful for typical math expressions used in drivers. + expressions = ("f''", "f'{1}'", "f'{\"_\"}'") + + +class TestRejectModuleAccess(unittest.TestCase, TestExprMixIn_Reject): + # Each of these commands _must_ run `expect_os_unreachable`, + # and must also be rejected as insecure - otherwise we have problems. + expressions_expect_unreachable = True + expressions = ( + "__import__('os').expect_os_unreachable()", + "exec(\"__import__('os').expect_os_unreachable()\")", + "(globals().update(__import__('os').__dict__), expect_os_unreachable())", + ) + + # Ensure the functions are actually called. + def setUp(self): + self._count = expect_unreachable_count + + def tearDown(self): + count_actual = expect_unreachable_count - self._count + count_expect = len(self.expressions) + if count_actual != count_expect: + raise Exception( + "Expected 'os.expect_os_unreachable' to be called %d times but was called %d times" % + (count_expect, count_actual), + ) + + +class TestRejectOpenAccess(unittest.TestCase, TestExprMixIn_Reject): + # Each of these commands _must_ run `expect_open_unreachable`, + # and must also be rejected as insecure - otherwise we have problems. + expressions_expect_unreachable = True + expressions = ( + "open('file.txt', 'r')", + "exec(\"open('file.txt', 'r')\")", + "(globals().update({'fake_open': __builtins__['open']}), fake_open())", + ) + + # Ensure the functions are actually called. + def setUp(self): + self._count = expect_open_unreachable_count + + def tearDown(self): + count_actual = expect_open_unreachable_count - self._count + count_expect = len(self.expressions) + if count_actual != count_expect: + raise Exception( + "Expected 'open' to be called %d times but was called %d times" % + (count_expect, count_actual), + ) + + +if __name__ == '__main__': + import sys + sys.argv = [__file__] + (sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else []) + unittest.main() |