diff options
author | Falk David <falkdavid@gmx.de> | 2022-05-17 12:19:52 +0300 |
---|---|---|
committer | Falk David <falkdavid@gmx.de> | 2022-05-17 12:19:52 +0300 |
commit | 91cf6fdd8e49144853d3dfa3a54f02575eb73963 (patch) | |
tree | a5ea9af05ab94c59a5675b24d2c085f352534623 | |
parent | 2f8087b2c881b2ec728048937a73d987006bddca (diff) | |
parent | 210d0f1b801fb23c7edabb35b67224eab671e363 (diff) |
Merge branch 'blender-v3.2-release' into spa-studios-v2.0.0-release
146 files changed, 1602 insertions, 955 deletions
diff --git a/build_files/build_environment/CMakeLists.txt b/build_files/build_environment/CMakeLists.txt index 8f4738d1f1c..b63e86a3ac2 100644 --- a/build_files/build_environment/CMakeLists.txt +++ b/build_files/build_environment/CMakeLists.txt @@ -54,9 +54,6 @@ include(cmake/freetype.cmake) include(cmake/freeglut.cmake) include(cmake/glew.cmake) include(cmake/alembic.cmake) -include(cmake/glfw.cmake) -include(cmake/clew.cmake) -include(cmake/cuew.cmake) include(cmake/opensubdiv.cmake) include(cmake/sdl.cmake) include(cmake/opencollada.cmake) diff --git a/build_files/build_environment/cmake/clew.cmake b/build_files/build_environment/cmake/clew.cmake deleted file mode 100644 index 8c965d52d2c..00000000000 --- a/build_files/build_environment/cmake/clew.cmake +++ /dev/null @@ -1,12 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-or-later - -set(CLEW_EXTRA_ARGS) - -ExternalProject_Add(external_clew - URL file://${PACKAGE_DIR}/${CLEW_FILE} - DOWNLOAD_DIR ${DOWNLOAD_DIR} - URL_HASH ${CLEW_HASH_TYPE}=${CLEW_HASH} - PREFIX ${BUILD_DIR}/clew - CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${LIBDIR}/clew -Wno-dev ${DEFAULT_CMAKE_FLAGS} ${CLEW_EXTRA_ARGS} - INSTALL_DIR ${LIBDIR}/clew -) diff --git a/build_files/build_environment/cmake/cuew.cmake b/build_files/build_environment/cmake/cuew.cmake deleted file mode 100644 index 8beb8b4fade..00000000000 --- a/build_files/build_environment/cmake/cuew.cmake +++ /dev/null @@ -1,13 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-or-later - -set(CUEW_EXTRA_ARGS) - -ExternalProject_Add(external_cuew - URL file://${PACKAGE_DIR}/${CUEW_FILE} - DOWNLOAD_DIR ${DOWNLOAD_DIR} - URL_HASH ${CUEW_HASH_TYPE}=${CUEW_HASH} - PREFIX ${BUILD_DIR}/cuew - PATCH_COMMAND ${PATCH_CMD} --verbose -p 0 -N -d ${BUILD_DIR}/cuew/src/external_cuew < ${PATCH_DIR}/cuew.diff - CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${LIBDIR}/cuew -Wno-dev ${DEFAULT_CMAKE_FLAGS} ${CUEW_EXTRA_ARGS} - INSTALL_DIR ${LIBDIR}/cuew -) diff --git a/build_files/build_environment/cmake/download.cmake b/build_files/build_environment/cmake/download.cmake index 92dea453133..e305b05ee70 100644 --- a/build_files/build_environment/cmake/download.cmake +++ b/build_files/build_environment/cmake/download.cmake @@ -39,10 +39,6 @@ download_source(FREETYPE) download_source(GLEW) download_source(FREEGLUT) download_source(ALEMBIC) -download_source(GLFW) -download_source(CLEW) -download_source(GLFW) -download_source(CUEW) download_source(OPENSUBDIV) download_source(SDL) download_source(OPENCOLLADA) diff --git a/build_files/build_environment/cmake/glfw.cmake b/build_files/build_environment/cmake/glfw.cmake deleted file mode 100644 index 29fce7609e5..00000000000 --- a/build_files/build_environment/cmake/glfw.cmake +++ /dev/null @@ -1,12 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-or-later - -set(GLFW_EXTRA_ARGS) - -ExternalProject_Add(external_glfw - URL file://${PACKAGE_DIR}/${GLFW_FILE} - DOWNLOAD_DIR ${DOWNLOAD_DIR} - URL_HASH ${GLFW_HASH_TYPE}=${GLFW_HASH} - PREFIX ${BUILD_DIR}/glfw - CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${LIBDIR}/glfw -Wno-dev ${DEFAULT_CMAKE_FLAGS} ${GLFW_EXTRA_ARGS} - INSTALL_DIR ${LIBDIR}/glfw -) diff --git a/build_files/build_environment/cmake/opencolorio.cmake b/build_files/build_environment/cmake/opencolorio.cmake index 2246031be83..4e2d1503406 100644 --- a/build_files/build_environment/cmake/opencolorio.cmake +++ b/build_files/build_environment/cmake/opencolorio.cmake @@ -53,7 +53,8 @@ add_dependencies( external_opencolorio external_yamlcpp external_expat - external_openexr + external_imath + external_pystring ) if(WIN32) diff --git a/build_files/build_environment/cmake/opensubdiv.cmake b/build_files/build_environment/cmake/opensubdiv.cmake index 6d6e3568406..a142160b9cc 100644 --- a/build_files/build_environment/cmake/opensubdiv.cmake +++ b/build_files/build_environment/cmake/opensubdiv.cmake @@ -10,20 +10,16 @@ set(OPENSUBDIV_EXTRA_ARGS -DNO_OMP=ON -DNO_TBB=OFF -DNO_CUDA=ON - -DNO_OPENCL=OFF - -DNO_CLEW=OFF + -DNO_OPENCL=ON + -DNO_CLEW=ON -DNO_OPENGL=OFF -DNO_METAL=OFF -DNO_DX=ON -DNO_TESTS=ON -DNO_GLTESTS=ON - -DNO_GLEW=OFF - -DNO_GLFW=OFF + -DNO_GLEW=ON + -DNO_GLFW=ON -DNO_GLFW_X11=ON - -DGLEW_INCLUDE_DIR=${LIBDIR}/glew/include - -DGLEW_LIBRARY=${LIBDIR}/glew/lib/libGLEW${LIBEXT} - -DGLFW_INCLUDE_DIR=${LIBDIR}/glfw/include - -DGLFW_LIBRARIES=${LIBDIR}/glfw/lib/glfw3${LIBEXT} ) if(WIN32) @@ -31,19 +27,12 @@ if(WIN32) ${OPENSUBDIV_EXTRA_ARGS} -DTBB_INCLUDE_DIR=${LIBDIR}/tbb/include -DTBB_LIBRARIES=${LIBDIR}/tbb/lib/tbb.lib - -DCLEW_INCLUDE_DIR=${LIBDIR}/clew/include/CL - -DCLEW_LIBRARY=${LIBDIR}/clew/lib/clew${LIBEXT} - -DCUEW_INCLUDE_DIR=${LIBDIR}/cuew/include - -DCUEW_LIBRARY=${LIBDIR}/cuew/lib/cuew${LIBEXT} ) else() set(OPENSUBDIV_EXTRA_ARGS ${OPENSUBDIV_EXTRA_ARGS} -DTBB_INCLUDE_DIR=${LIBDIR}/tbb/include -DTBB_tbb_LIBRARY=${LIBDIR}/tbb/lib/${LIBPREFIX}tbb_static${LIBEXT} - -DCUEW_INCLUDE_DIR=${LIBDIR}/cuew/include - -DCLEW_INCLUDE_DIR=${LIBDIR}/clew/include/CL - -DCLEW_LIBRARY=${LIBDIR}/clew/lib/static/${LIBPREFIX}clew${LIBEXT} ) endif() @@ -75,9 +64,5 @@ endif() add_dependencies( external_opensubdiv - external_glew - external_glfw - external_clew - external_cuew external_tbb ) diff --git a/build_files/build_environment/cmake/versions.cmake b/build_files/build_environment/cmake/versions.cmake index 25267273db0..550be86b6b6 100644 --- a/build_files/build_environment/cmake/versions.cmake +++ b/build_files/build_environment/cmake/versions.cmake @@ -98,27 +98,6 @@ set(ALEMBIC_HASH 2cd8d6e5a3ac4a014e24a4b04f4fadf9) set(ALEMBIC_HASH_TYPE MD5) set(ALEMBIC_FILE alembic-${ALEMBIC_VERSION}.tar.gz) -# hash is for 3.1.2 -set(GLFW_GIT_UID 30306e54705c3adae9fe082c816a3be71963485c) -set(GLFW_URI https://github.com/glfw/glfw/archive/${GLFW_GIT_UID}.zip) -set(GLFW_HASH 20cacb1613da7eeb092f3ac4f6b2b3d0) -set(GLFW_HASH_TYPE MD5) -set(GLFW_FILE glfw-${GLFW_GIT_UID}.zip) - -# latest uid in git as of 2016-04-01 -set(CLEW_GIT_UID 277db43f6cafe8b27c6f1055f69dc67da4aeb299) -set(CLEW_URI https://github.com/OpenCLWrangler/clew/archive/${CLEW_GIT_UID}.zip) -set(CLEW_HASH 2c699d10ed78362e71f56fae2a4c5f98) -set(CLEW_HASH_TYPE MD5) -set(CLEW_FILE clew-${CLEW_GIT_UID}.zip) - -# latest uid in git as of 2016-04-01 -set(CUEW_GIT_UID 1744972026de9cf27c8a7dc39cf39cd83d5f922f) -set(CUEW_URI https://github.com/CudaWrangler/cuew/archive/${CUEW_GIT_UID}.zip) -set(CUEW_HASH 86760d62978ebfd96cd93f5aa1abaf4a) -set(CUEW_HASH_TYPE MD5) -set(CUEW_FILE cuew-${CUEW_GIT_UID}.zip) - set(OPENSUBDIV_VERSION v3_4_4) set(OPENSUBDIV_URI https://github.com/PixarAnimationStudios/OpenSubdiv/archive/${OPENSUBDIV_VERSION}.tar.gz) set(OPENSUBDIV_HASH 39ecc5caf0abebc943d1ce131855e76e) diff --git a/build_files/build_environment/dependencies.dot b/build_files/build_environment/dependencies.dot index 62949f3de62..7e8637fbced 100644 --- a/build_files/build_environment/dependencies.dot +++ b/build_files/build_environment/dependencies.dot @@ -37,10 +37,6 @@ graph[autosize = false, size = "25.7,8.3!", resolution = 300, overlap = false, s external_openimageio -- external_webp; external_openimageio -- external_opencolorio_extra; external_openmp -- external_clang; - external_opensubdiv -- external_glew; - external_opensubdiv -- external_glfw; - external_opensubdiv -- external_clew; - external_opensubdiv -- external_cuew; external_opensubdiv -- external_tbb; openvdb -- external_tbb; openvdb -- external_boost; diff --git a/build_files/build_environment/patches/cuew.diff b/build_files/build_environment/patches/cuew.diff deleted file mode 100644 index 0363034cd93..00000000000 --- a/build_files/build_environment/patches/cuew.diff +++ /dev/null @@ -1,26 +0,0 @@ ---- CmakeLists.txt.orig 2015-12-31 03:46:41 -0700 -+++ CMakeLists.txt 2016-04-01 13:28:33 -0600 -@@ -22,3 +22,10 @@ - - add_executable(testcuew cuewTest/cuewTest.c include/cuew.h) - target_link_libraries(testcuew cuew ${CMAKE_DL_LIBS}) -+ -+install(TARGETS cuew -+ LIBRARY DESTINATION lib COMPONENT libraries -+ ARCHIVE DESTINATION lib/static COMPONENT libraries) -+ -+INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/include/cuew.h -+ DESTINATION include/) -\ No newline at end of file ---- src/cuew.c 2016-04-01 13:41:43 -0600 -+++ src/cuew.c 2016-04-01 13:41:11 -0600 -@@ -15,7 +15,9 @@ - */ - - #ifdef _MSC_VER -+#if _MSC_VER < 1900 - # define snprintf _snprintf -+#endif - # define popen _popen - # define pclose _pclose - # define _CRT_SECURE_NO_WARNINGS diff --git a/build_files/build_environment/patches/usd.diff b/build_files/build_environment/patches/usd.diff index eb5905109a7..3d0aa4498b7 100644 --- a/build_files/build_environment/patches/usd.diff +++ b/build_files/build_environment/patches/usd.diff @@ -51,3 +51,52 @@ diff --git a/pxr/usd/sdr/shaderMetadataHelpers.h b/pxr/usd/sdr/shaderMetadataHel /// \namespace ShaderMetadataHelpers +diff --git a/pxr/base/arch/timing.h b/pxr/base/arch/timing.h +index 517561f..fda5a1f 100644 +--- a/pxr/base/arch/timing.h ++++ b/pxr/base/arch/timing.h +@@ -91,6 +91,10 @@ ArchGetTickTime() + inline uint64_t + ArchGetStartTickTime() + { ++ // BLENDER: avoid using rdtsc instruction that is not supported on older CPUs. ++ return ArchGetTickTime(); ++ ++#if 0 + uint64_t t; + #if defined (ARCH_OS_DARWIN) + return ArchGetTickTime(); +@@ -123,6 +127,7 @@ ArchGetStartTickTime() + #error "Unsupported architecture." + #endif + return t; ++#endif + } + + /// Get a "stop" tick time for measuring an interval of time. See +@@ -132,6 +137,10 @@ ArchGetStartTickTime() + inline uint64_t + ArchGetStopTickTime() + { ++ // BLENDER: avoid using rdtsc instruction that is not supported on older CPUs. ++ return ArchGetTickTime(); ++ ++#if 0 + uint64_t t; + #if defined (ARCH_OS_DARWIN) + return ArchGetTickTime(); +@@ -162,11 +171,11 @@ ArchGetStopTickTime() + #error "Unsupported architecture." + #endif + return t; ++#endif + } + +-#if defined (doxygen) || \ +- (!defined(ARCH_OS_DARWIN) && defined(ARCH_CPU_INTEL) && \ +- (defined(ARCH_COMPILER_CLANG) || defined(ARCH_COMPILER_GCC))) ++// BLENDER: avoid using rdtsc instruction that is not supported on older CPUs. ++#if 0 + + /// A simple timer class for measuring an interval of time using the + /// ArchTickTimer facilities. diff --git a/intern/cycles/blender/mesh.cpp b/intern/cycles/blender/mesh.cpp index 624a07762f2..de67e27923d 100644 --- a/intern/cycles/blender/mesh.cpp +++ b/intern/cycles/blender/mesh.cpp @@ -301,11 +301,11 @@ static void attr_create_sculpt_vertex_color(Scene *scene, template<typename TypeInCycles, typename GetValueAtIndex> static void fill_generic_attribute(BL::Mesh &b_mesh, TypeInCycles *data, - const AttributeElement element, + const BL::Attribute::domain_enum b_domain, const GetValueAtIndex &get_value_at_index) { - switch (element) { - case ATTR_ELEMENT_CORNER: { + switch (b_domain) { + case BL::Attribute::domain_CORNER: { for (BL::MeshLoopTriangle &t : b_mesh.loop_triangles) { const int index = t.index() * 3; BL::Array<int, 3> loops = t.loops(); @@ -315,14 +315,37 @@ static void fill_generic_attribute(BL::Mesh &b_mesh, } break; } - case ATTR_ELEMENT_VERTEX: { + case BL::Attribute::domain_EDGE: { + /* Averge edge attributes at vertices. */ + const size_t num_verts = b_mesh.vertices.length(); + vector<int> count(num_verts, 0); + + for (BL::MeshEdge &e : b_mesh.edges) { + BL::Array<int, 2> vertices = e.vertices(); + TypeInCycles value = get_value_at_index(e.index()); + + data[vertices[0]] += value; + data[vertices[1]] += value; + count[vertices[0]]++; + count[vertices[1]]++; + } + + for (size_t i = 0; i < num_verts; i++) { + if (count[i] > 1) { + data[i] /= (float)count[i]; + } + } + + break; + } + case BL::Attribute::domain_POINT: { const int num_verts = b_mesh.vertices.length(); for (int i = 0; i < num_verts; i++) { data[i] = get_value_at_index(i); } break; } - case ATTR_ELEMENT_FACE: { + case BL::Attribute::domain_FACE: { for (BL::MeshLoopTriangle &t : b_mesh.loop_triangles) { data[t.index()] = get_value_at_index(t.polygon_index()); } @@ -404,6 +427,9 @@ static void attr_create_generic(Scene *scene, case BL::Attribute::domain_POINT: element = ATTR_ELEMENT_VERTEX; break; + case BL::Attribute::domain_EDGE: + element = ATTR_ELEMENT_VERTEX; + break; case BL::Attribute::domain_FACE: element = ATTR_ELEMENT_FACE; break; @@ -420,15 +446,16 @@ static void attr_create_generic(Scene *scene, Attribute *attr = attributes.add(name, TypeFloat, element); float *data = attr->data_float(); fill_generic_attribute( - b_mesh, data, element, [&](int i) { return b_float_attribute.data[i].value(); }); + b_mesh, data, b_domain, [&](int i) { return b_float_attribute.data[i].value(); }); break; } case BL::Attribute::data_type_BOOLEAN: { BL::BoolAttribute b_bool_attribute{b_attribute}; Attribute *attr = attributes.add(name, TypeFloat, element); float *data = attr->data_float(); - fill_generic_attribute( - b_mesh, data, element, [&](int i) { return (float)b_bool_attribute.data[i].value(); }); + fill_generic_attribute(b_mesh, data, b_domain, [&](int i) { + return (float)b_bool_attribute.data[i].value(); + }); break; } case BL::Attribute::data_type_INT: { @@ -436,14 +463,14 @@ static void attr_create_generic(Scene *scene, Attribute *attr = attributes.add(name, TypeFloat, element); float *data = attr->data_float(); fill_generic_attribute( - b_mesh, data, element, [&](int i) { return (float)b_int_attribute.data[i].value(); }); + b_mesh, data, b_domain, [&](int i) { return (float)b_int_attribute.data[i].value(); }); break; } case BL::Attribute::data_type_FLOAT_VECTOR: { BL::FloatVectorAttribute b_vector_attribute{b_attribute}; Attribute *attr = attributes.add(name, TypeVector, element); float3 *data = attr->data_float3(); - fill_generic_attribute(b_mesh, data, element, [&](int i) { + fill_generic_attribute(b_mesh, data, b_domain, [&](int i) { BL::Array<float, 3> v = b_vector_attribute.data[i].vector(); return make_float3(v[0], v[1], v[2]); }); @@ -453,7 +480,7 @@ static void attr_create_generic(Scene *scene, BL::FloatColorAttribute b_color_attribute{b_attribute}; Attribute *attr = attributes.add(name, TypeRGBA, element); float4 *data = attr->data_float4(); - fill_generic_attribute(b_mesh, data, element, [&](int i) { + fill_generic_attribute(b_mesh, data, b_domain, [&](int i) { BL::Array<float, 4> v = b_color_attribute.data[i].color(); return make_float4(v[0], v[1], v[2], v[3]); }); @@ -463,7 +490,7 @@ static void attr_create_generic(Scene *scene, BL::Float2Attribute b_float2_attribute{b_attribute}; Attribute *attr = attributes.add(name, TypeFloat2, element); float2 *data = attr->data_float2(); - fill_generic_attribute(b_mesh, data, element, [&](int i) { + fill_generic_attribute(b_mesh, data, b_domain, [&](int i) { BL::Array<float, 2> v = b_float2_attribute.data[i].vector(); return make_float2(v[0], v[1]); }); diff --git a/intern/cycles/blender/util.h b/intern/cycles/blender/util.h index eead6cec08c..49cecb6d0f3 100644 --- a/intern/cycles/blender/util.h +++ b/intern/cycles/blender/util.h @@ -262,8 +262,11 @@ static inline bool BKE_object_is_modified(BL::Object &self, BL::Scene &scene, bo static inline bool BKE_object_is_deform_modified(BObjectInfo &self, BL::Scene &scene, bool preview) { if (!self.is_real_object_data()) { - return false; + /* Comes from geometry nodes, can't use heuristic to guess if it's animated. */ + return true; } + + /* Use heuristic to quickly check if object is potentially animated. */ return self.real_object.is_deform_modified(scene, (preview) ? (1 << 0) : (1 << 1)) ? true : false; } diff --git a/intern/cycles/integrator/denoiser_oidn.cpp b/intern/cycles/integrator/denoiser_oidn.cpp index 41e5165623c..b074408e229 100644 --- a/intern/cycles/integrator/denoiser_oidn.cpp +++ b/intern/cycles/integrator/denoiser_oidn.cpp @@ -626,6 +626,7 @@ uint OIDNDenoiser::get_device_type_mask() const Device *OIDNDenoiser::ensure_denoiser_device(Progress *progress) { #ifndef WITH_OPENIMAGEDENOISE + (void)progress; path_trace_device_->set_error("Build without OpenImageDenoiser"); return nullptr; #else diff --git a/intern/cycles/kernel/integrator/mnee.h b/intern/cycles/kernel/integrator/mnee.h index 455d15b28c2..7b86c660380 100644 --- a/intern/cycles/kernel/integrator/mnee.h +++ b/intern/cycles/kernel/integrator/mnee.h @@ -36,7 +36,7 @@ * https://cg.ivd.kit.edu/english/HSLT.php */ -# define MNEE_MAX_ITERATIONS 32 +# define MNEE_MAX_ITERATIONS 64 # define MNEE_MAX_INTERSECTION_COUNT 10 # define MNEE_SOLVER_THRESHOLD 0.001f # define MNEE_MINIMUM_STEP_SIZE 0.0001f @@ -140,26 +140,8 @@ ccl_device_forceinline void mnee_update_light_sample(KernelGlobals kg, ls->D = normalize_len(ls->P - P, &ls->t); ls->pdf = fabsf(klight->area.invarea); } -} -/* Compute orthonormal basis - * https://graphics.pixar.com/library/OrthonormalB/paper.pdf */ -ccl_device_forceinline void mnee_make_orthonormals(const float3 n, - ccl_private float3 *dp_du, - ccl_private float3 *dp_dv) -{ - if (n.z < 0.0f) { - const float a = 1.0f / (1.0f - n.z); - const float b = n.x * n.y * a; - *dp_du = make_float3(1.0f - n.x * n.x * a, -b, n.x); - *dp_dv = make_float3(b, n.y * n.y * a - 1.0f, -n.y); - } - else { - const float a = 1.0f / (1.0f + n.z); - const float b = -n.x * n.y * a; - *dp_du = make_float3(1.0f - n.x * n.x * a, b, -n.x); - *dp_dv = make_float3(b, 1.0f - n.y * n.y * a, -n.y); - } + ls->pdf *= kernel_data.integrator.pdf_lights; } /* Manifold vertex setup from ray and intersection data */ @@ -170,8 +152,7 @@ ccl_device_forceinline void mnee_setup_manifold_vertex(KernelGlobals kg, const float2 n_offset, ccl_private const Ray *ray, ccl_private const Intersection *isect, - ccl_private ShaderData *sd_vtx, - bool seed) + ccl_private ShaderData *sd_vtx) { sd_vtx->object = (isect->object == OBJECT_NONE) ? kernel_tex_fetch(__prim_object, isect->prim) : isect->object; @@ -263,30 +244,13 @@ ccl_device_forceinline void mnee_setup_manifold_vertex(KernelGlobals kg, dp_dv *= inv_len_dp_dv; dn_dv *= inv_len_dp_dv; - /* Final local differential geometry. */ - if (seed) { - vtx->dp_du = dp_du; - vtx->dp_dv = dp_dv; - vtx->dn_du = dn_du; - vtx->dn_dv = dn_dv; - } - else { - /* Find angle subtended by reference direction (travel direction). */ - const float3 reference_direction = normalize(sd_vtx->P - vtx->p); - const float reference_theta = atan2(dot(reference_direction, vtx->dp_dv), - dot(reference_direction, vtx->dp_du)); - const float current_theta = atan2(dot(reference_direction, dp_dv), - dot(reference_direction, dp_du)); - const float theta = reference_theta - current_theta; - - /* Rotate (dp_du,dp_dv) to be consistent with previous tangent frame. */ - float cos_theta, sin_theta; - fast_sincosf(theta, &sin_theta, &cos_theta); - vtx->dp_du = cos_theta * dp_du - sin_theta * dp_dv; - vtx->dp_dv = sin_theta * dp_du + cos_theta * dp_dv; - vtx->dn_du = cos_theta * dn_du - sin_theta * dn_dv; - vtx->dn_dv = sin_theta * dn_du + cos_theta * dn_dv; - } + /* Find consistent tangent frame for every point on the surface. */ + make_orthonormals(vtx->ng, &vtx->dp_du, &vtx->dp_dv); + /* Apply the equivalent rotation to the normal derivatives. */ + const float cos_theta = dot(dp_du, vtx->dp_du); + const float sin_theta = -dot(dp_dv, vtx->dp_du); + vtx->dn_du = cos_theta * dn_du - sin_theta * dn_dv; + vtx->dn_dv = sin_theta * dn_du + cos_theta * dn_dv; /* Manifold vertex position. */ vtx->p = sd_vtx->P; @@ -577,8 +541,7 @@ ccl_device_forceinline bool mnee_newton_solver(KernelGlobals kg, mv.n_offset, &projection_ray, &projection_isect, - sd_vtx, - false); + sd_vtx); /* Fail newton solve if we are not making progress, probably stuck trying to move off the * edge of the mesh. */ @@ -749,7 +712,7 @@ ccl_device_forceinline bool mnee_compute_transfer_matrix(ccl_private const Shade /* Local differential geometry. */ float3 dp_du, dp_dv; - mnee_make_orthonormals(ls->Ng, &dp_du, &dp_dv); + make_orthonormals(ls->Ng, &dp_du, &dp_dv); /* Direction toward surface sample. */ float3 wi = vertex_count == 1 ? sd->P - m.p : vertices[mi - 1].p - m.p; @@ -1026,7 +989,9 @@ ccl_device_forceinline int kernel_path_mnee_sample(KernelGlobals kg, if (bsdf->type == CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID || bsdf->type == CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID || bsdf->type == CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID || - bsdf->type == CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_FRESNEL_ID) { + bsdf->type == CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_FRESNEL_ID || + bsdf->type == CLOSURE_BSDF_REFRACTION_ID || + bsdf->type == CLOSURE_BSDF_SHARP_GLASS_ID) { /* Note that CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID and * CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_FRESNEL_ID are treated as * CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID further below. */ @@ -1038,15 +1003,18 @@ ccl_device_forceinline int kernel_path_mnee_sample(KernelGlobals kg, const float eta = (sd_mnee->flag & SD_BACKFACING) ? 1.0f / microfacet_bsdf->ior : microfacet_bsdf->ior; - /* Sample transmissive microfacet bsdf. */ - float bsdf_u, bsdf_v; - path_state_rng_2D(kg, rng_state, PRNG_BSDF_U, &bsdf_u, &bsdf_v); - float2 h = mnee_sample_bsdf_dh( - bsdf->type, microfacet_bsdf->alpha_x, microfacet_bsdf->alpha_y, bsdf_u, bsdf_v); + float2 h = zero_float2(); + if (microfacet_bsdf->alpha_x > 0.f && microfacet_bsdf->alpha_y > 0.f) { + /* Sample transmissive microfacet bsdf. */ + float bsdf_u, bsdf_v; + path_state_rng_2D(kg, rng_state, PRNG_BSDF_U, &bsdf_u, &bsdf_v); + h = mnee_sample_bsdf_dh( + bsdf->type, microfacet_bsdf->alpha_x, microfacet_bsdf->alpha_y, bsdf_u, bsdf_v); + } /* Setup differential geometry on vertex. */ mnee_setup_manifold_vertex( - kg, &mv, bsdf, eta, h, &probe_ray, &probe_isect, sd_mnee, true); + kg, &mv, bsdf, eta, h, &probe_ray, &probe_isect, sd_mnee); break; } } diff --git a/intern/cycles/kernel/light/sample.h b/intern/cycles/kernel/light/sample.h index 5acfc92cca1..9bbbd5b0d10 100644 --- a/intern/cycles/kernel/light/sample.h +++ b/intern/cycles/kernel/light/sample.h @@ -143,7 +143,7 @@ ccl_device_inline float3 shadow_ray_smooth_surface_offset( float3 n = N[0] * u + N[1] * v + N[2] * w; /* We get away without normalization */ if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { - object_normal_transform(kg, sd, &n); /* Normal x scale, world space */ + object_dir_transform(kg, sd, &n); /* Normal x scale, to world space */ } /* Parabolic approximation */ diff --git a/release/datafiles/locale b/release/datafiles/locale -Subproject 44cf4b4bf74f22fff55941e39cebeacec68a5a8 +Subproject fb1eac2ec80c0adee69990a5386b74a5bd4ca00 diff --git a/release/scripts/addons b/release/scripts/addons -Subproject b87642c5dcbfe5d2779e070984ce0914b8f64f7 +Subproject d3eda2e271c00c7204a57e8106164e923c2f408 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 87d54213d1b..604a577eec9 100644 --- a/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py +++ b/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py @@ -341,7 +341,8 @@ def dump_rna_messages(msgs, reports, settings, verbose=False): msgsrc = "bpy.types." + bl_rna.identifier msgctxt = bl_rna.translation_context or default_context - if bl_rna.name and (bl_rna.name != bl_rna.identifier or msgctxt != default_context): + if bl_rna.name and (bl_rna.name != bl_rna.identifier or + (msgctxt != default_context and not hasattr(bl_rna, 'bl_label'))): process_msg(msgs, msgctxt, bl_rna.name, msgsrc, reports, check_ctxt_rna, settings) if bl_rna.description: diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 1d322c68cd7..5603668222b 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -2038,37 +2038,20 @@ def km_node_editor(params): {"items": items}, ) - def node_select_ops(select_mouse): - return [ - ("node.select", {"type": select_mouse, "value": 'PRESS'}, - {"properties": [("deselect_all", True)]}), - ("node.select", {"type": select_mouse, "value": 'PRESS', "ctrl": True}, None), - ("node.select", {"type": select_mouse, "value": 'PRESS', "alt": True}, None), - ("node.select", {"type": select_mouse, "value": 'PRESS', "ctrl": True, "alt": True}, None), - ("node.select", {"type": select_mouse, "value": 'PRESS', "shift": True}, - {"properties": [("extend", True)]}), - ("node.select", {"type": select_mouse, "value": 'PRESS', "shift": True, "ctrl": True}, - {"properties": [("extend", True)]}), - ("node.select", {"type": select_mouse, "value": 'PRESS', "shift": True, "alt": True}, - {"properties": [("extend", True)]}), - ("node.select", {"type": select_mouse, "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, - {"properties": [("extend", True)]}), - ] - - # Allow node selection with both for RMB select if not params.legacy: + items.extend(_template_node_select(type=params.select_mouse, + value=params.select_mouse_value, select_passthrough=True)) + # Allow node selection with both for RMB select. if params.select_mouse == 'RIGHTMOUSE': - items.extend(node_select_ops('LEFTMOUSE')) - items.extend(node_select_ops('RIGHTMOUSE')) - else: - items.extend(node_select_ops('LEFTMOUSE')) + items.extend(_template_node_select(type='LEFTMOUSE', value='PRESS', select_passthrough=True)) else: - items.extend(node_select_ops('RIGHTMOUSE')) + items.extend(_template_node_select( + type='RIGHTMOUSE', value=params.select_mouse_value, select_passthrough=False)) items.extend([ ("node.select", {"type": 'LEFTMOUSE', "value": 'PRESS'}, {"properties": [("deselect_all", False)]}), ("node.select", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True}, - {"properties": [("extend", True)]}), + {"properties": [("toggle", True)]}), ]) items.extend([ @@ -4801,6 +4784,35 @@ def _template_view3d_gpencil_select(*, type, value, legacy, use_select_mouse=Tru ] +def _template_node_select(*, type, value, select_passthrough): + items = [ + ("node.select", {"type": type, "value": value}, + {"properties": [("deselect_all", True), ("select_passthrough", True)]}), + ("node.select", {"type": type, "value": value, "ctrl": True}, None), + ("node.select", {"type": type, "value": value, "alt": True}, None), + ("node.select", {"type": type, "value": value, "ctrl": True, "alt": True}, None), + ("node.select", {"type": type, "value": value, "shift": True}, + {"properties": [("toggle", True)]}), + ("node.select", {"type": type, "value": value, "shift": True, "ctrl": True}, + {"properties": [("toggle", True)]}), + ("node.select", {"type": type, "value": value, "shift": True, "alt": True}, + {"properties": [("toggle", True)]}), + ("node.select", {"type": type, "value": value, "shift": True, "ctrl": True, "alt": True}, + {"properties": [("toggle", True)]}), + ] + + if select_passthrough and (value == 'PRESS'): + # Add an additional click item to de-select all other items, + # needed so pass-through is able to de-select other items. + items.append(( + "node.select", + {"type": type, "value": 'CLICK'}, + {"properties": [("deselect_all", True)]}, + )) + + return items + + def _template_uv_select(*, type, value, select_passthrough, legacy): # See: `use_tweak_select_passthrough` doc-string. @@ -6562,10 +6574,8 @@ def km_node_editor_tool_select(params, *, fallback): _fallback_id("Node Tool: Tweak", fallback), {"space_type": 'NODE_EDITOR', "region_type": 'WINDOW'}, {"items": [ - *([] if (fallback and (params.select_mouse == 'RIGHTMOUSE')) else [ - ("node.select", {"type": params.select_mouse, "value": 'PRESS'}, - {"properties": [("deselect_all", not params.legacy)]}), - ]), + *([] if (fallback and (params.select_mouse == 'RIGHTMOUSE')) else + _template_node_select(type=params.select_mouse, value='PRESS', select_passthrough=True)), ]}, ) @@ -6582,6 +6592,8 @@ def km_node_editor_tool_select_box(params, *, fallback): params.tool_tweak_event), properties=[("tweak", True)], )), + *([] if (params.select_mouse == 'RIGHTMOUSE') else + _template_node_select(type='LEFTMOUSE', value='PRESS', select_passthrough=True)), ]}, ) diff --git a/release/scripts/startup/bl_ui/space_node.py b/release/scripts/startup/bl_ui/space_node.py index e105b07ec53..813a799db24 100644 --- a/release/scripts/startup/bl_ui/space_node.py +++ b/release/scripts/startup/bl_ui/space_node.py @@ -727,7 +727,20 @@ class NODE_UL_interface_sockets(bpy.types.UIList): layout.template_node_socket(color=color) -class NodeTreeInterfacePanel: +class NodeTreeInterfacePanel(Panel): + + @classmethod + def poll(cls, context): + snode = context.space_data + if snode is None: + return False + tree = snode.edit_tree + if tree is None: + return False + if tree.is_embedded_data: + return False + return True + def draw_socket_list(self, context, in_out, sockets_propname, active_socket_propname): layout = self.layout @@ -804,32 +817,22 @@ class NodeTreeInterfacePanel: active_socket.draw(context, layout) -class NODE_PT_node_tree_interface_inputs(NodeTreeInterfacePanel, Panel): +class NODE_PT_node_tree_interface_inputs(NodeTreeInterfacePanel): bl_space_type = 'NODE_EDITOR' bl_region_type = 'UI' bl_category = "Group" bl_label = "Inputs" - @classmethod - def poll(cls, context): - snode = context.space_data - return snode.edit_tree is not None - def draw(self, context): self.draw_socket_list(context, "IN", "inputs", "active_input") -class NODE_PT_node_tree_interface_outputs(NodeTreeInterfacePanel, Panel): +class NODE_PT_node_tree_interface_outputs(NodeTreeInterfacePanel): bl_space_type = 'NODE_EDITOR' bl_region_type = 'UI' bl_category = "Group" bl_label = "Outputs" - @classmethod - def poll(cls, context): - snode = context.space_data - return snode.edit_tree is not None - def draw(self, context): self.draw_socket_list(context, "OUT", "outputs", "active_output") diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index 9cc21228090..089dcd1c1a7 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -2277,6 +2277,7 @@ class USERPREF_PT_experimental_prototypes(ExperimentalPanel, Panel): ({"property": "use_new_point_cloud_type"}, "T75717"), ({"property": "use_full_frame_compositor"}, "T88150"), ({"property": "enable_eevee_next"}, "T93220"), + ({"property": "use_draw_manager_acquire_lock"}, "T98016"), ), ) diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index 94a4988f791..b9f2ad6259a 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -1235,24 +1235,23 @@ class VIEW3D_MT_view_viewpoint(Menu): def draw(self, _context): layout = self.layout - i18n_text_ctxt = bpy.app.translations.contexts_C_to_py['BLT_I18NCONTEXT_EDITOR_VIEW3D'] - layout.operator("view3d.view_camera", text="Camera", text_ctxt=i18n_text_ctxt) + layout.operator("view3d.view_camera", text="Camera", text_ctxt=i18n_contexts.editor_view3d) layout.separator() - layout.operator("view3d.view_axis", text="Top", text_ctxt=i18n_text_ctxt).type = 'TOP' - layout.operator("view3d.view_axis", text="Bottom", text_ctxt=i18n_text_ctxt).type = 'BOTTOM' + layout.operator("view3d.view_axis", text="Top", text_ctxt=i18n_contexts.editor_view3d).type = 'TOP' + layout.operator("view3d.view_axis", text="Bottom", text_ctxt=i18n_contexts.editor_view3d).type = 'BOTTOM' layout.separator() - layout.operator("view3d.view_axis", text="Front", text_ctxt=i18n_text_ctxt).type = 'FRONT' - layout.operator("view3d.view_axis", text="Back", text_ctxt=i18n_text_ctxt).type = 'BACK' + layout.operator("view3d.view_axis", text="Front", text_ctxt=i18n_contexts.editor_view3d).type = 'FRONT' + layout.operator("view3d.view_axis", text="Back", text_ctxt=i18n_contexts.editor_view3d).type = 'BACK' layout.separator() - layout.operator("view3d.view_axis", text="Right", text_ctxt=i18n_text_ctxt).type = 'RIGHT' - layout.operator("view3d.view_axis", text="Left", text_ctxt=i18n_text_ctxt).type = 'LEFT' + layout.operator("view3d.view_axis", text="Right", text_ctxt=i18n_contexts.editor_view3d).type = 'RIGHT' + layout.operator("view3d.view_axis", text="Left", text_ctxt=i18n_contexts.editor_view3d).type = 'LEFT' class VIEW3D_MT_view_navigation(Menu): @@ -1319,33 +1318,32 @@ class VIEW3D_MT_view_align_selected(Menu): def draw(self, _context): layout = self.layout - i18n_text_ctxt = bpy.app.translations.contexts_C_to_py['BLT_I18NCONTEXT_EDITOR_VIEW3D'] - props = layout.operator("view3d.view_axis", text="Top", text_ctxt=i18n_text_ctxt) + props = layout.operator("view3d.view_axis", text="Top", text_ctxt=i18n_contexts.editor_view3d) props.align_active = True props.type = 'TOP' - props = layout.operator("view3d.view_axis", text="Bottom", text_ctxt=i18n_text_ctxt) + props = layout.operator("view3d.view_axis", text="Bottom", text_ctxt=i18n_contexts.editor_view3d) props.align_active = True props.type = 'BOTTOM' layout.separator() - props = layout.operator("view3d.view_axis", text="Front", text_ctxt=i18n_text_ctxt) + props = layout.operator("view3d.view_axis", text="Front", text_ctxt=i18n_contexts.editor_view3d) props.align_active = True props.type = 'FRONT' - props = layout.operator("view3d.view_axis", text="Back", text_ctxt=i18n_text_ctxt) + props = layout.operator("view3d.view_axis", text="Back", text_ctxt=i18n_contexts.editor_view3d) props.align_active = True props.type = 'BACK' layout.separator() - props = layout.operator("view3d.view_axis", text="Right", text_ctxt=i18n_text_ctxt) + props = layout.operator("view3d.view_axis", text="Right", text_ctxt=i18n_contexts.editor_view3d) props.align_active = True props.type = 'RIGHT' - props = layout.operator("view3d.view_axis", text="Left", text_ctxt=i18n_text_ctxt) + props = layout.operator("view3d.view_axis", text="Left", text_ctxt=i18n_contexts.editor_view3d) props.align_active = True props.type = 'LEFT' diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 4f94184f006..f9bd4342b6e 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -145,10 +145,10 @@ def geometry_node_items(context): yield NodeItem("GeometryNodeConvexHull") yield NodeItem("GeometryNodeDeleteGeometry") yield NodeItem("GeometryNodeDuplicateElements") - yield NodeItem("GeometryNodeGeometryToInstance") - yield NodeItem("GeometryNodeMergeByDistance") yield NodeItem("GeometryNodeProximity") + yield NodeItem("GeometryNodeGeometryToInstance") yield NodeItem("GeometryNodeJoinGeometry") + yield NodeItem("GeometryNodeMergeByDistance") yield NodeItem("GeometryNodeRaycast") yield NodeItem("GeometryNodeSeparateComponents") yield NodeItem("GeometryNodeSeparateGeometry") diff --git a/source/blender/blenkernel/BKE_unit.h b/source/blender/blenkernel/BKE_unit.h index d6de95a19b7..823d32f83ba 100644 --- a/source/blender/blenkernel/BKE_unit.h +++ b/source/blender/blenkernel/BKE_unit.h @@ -91,7 +91,7 @@ const char *BKE_unit_identifier_get(const void *usys_pt, int index); double BKE_unit_scalar_get(const void *usys_pt, int index); bool BKE_unit_is_suppressed(const void *usys_pt, int index); -/** Aligned with #PropertyUnit. */ +/** Aligned with #PropertyUnit and `bpyunits_ucategories_items` in `bpy_utils_units.c`. */ enum { B_UNIT_NONE = 0, B_UNIT_LENGTH = 1, diff --git a/source/blender/blenkernel/intern/blendfile_link_append.c b/source/blender/blenkernel/intern/blendfile_link_append.c index 555c4690308..f9eea52360e 100644 --- a/source/blender/blenkernel/intern/blendfile_link_append.c +++ b/source/blender/blenkernel/intern/blendfile_link_append.c @@ -400,7 +400,9 @@ typedef struct LooseDataInstantiateContext { static bool object_in_any_scene(Main *bmain, Object *ob) { LISTBASE_FOREACH (Scene *, sce, &bmain->scenes) { - if (BKE_scene_object_find(sce, ob)) { + /* #BKE_scene_has_object checks bases cache of the scenes' viewlayer, not actual content of + * their collections. */ + if (BKE_collection_has_object_recursive(sce->master_collection, ob)) { return true; } } diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index 0593db34e20..3f243d04965 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -2029,7 +2029,7 @@ float BKE_brush_sample_tex_3d(const Scene *scene, /* leave the coordinates relative to the screen */ /* use unadjusted size for tiled mode */ - invradius = 1.0f / BKE_brush_size_get(scene, br); + invradius = 1.0f / ups->start_pixel_radius; x = point_2d[0]; y = point_2d[1]; @@ -2142,7 +2142,7 @@ float BKE_brush_sample_masktex( /* leave the coordinates relative to the screen */ /* use unadjusted size for tiled mode */ - invradius = 1.0f / BKE_brush_size_get(scene, br); + invradius = 1.0f / ups->start_pixel_radius; x = point_2d[0]; y = point_2d[1]; diff --git a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc index 88e0bae0c13..ef921797698 100644 --- a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc +++ b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc @@ -668,12 +668,12 @@ Mesh *curve_to_mesh_sweep(const CurvesGeometry &main, Vector<std::byte> eval_buffer; - Curves main_id = {nullptr}; + Curves main_id = {{nullptr}}; main_id.geometry = reinterpret_cast<const ::CurvesGeometry &>(main); CurveComponent main_component; main_component.replace(&main_id, GeometryOwnershipType::Editable); - Curves profile_id = {nullptr}; + Curves profile_id = {{nullptr}}; profile_id.geometry = reinterpret_cast<const ::CurvesGeometry &>(profile); CurveComponent profile_component; profile_component.replace(&profile_id, GeometryOwnershipType::Editable); diff --git a/source/blender/blenkernel/intern/image_save.cc b/source/blender/blenkernel/intern/image_save.cc index 0d7d238f3b2..5361f234a63 100644 --- a/source/blender/blenkernel/intern/image_save.cc +++ b/source/blender/blenkernel/intern/image_save.cc @@ -600,10 +600,11 @@ bool BKE_image_render_write_exr(ReportList *reports, const bool pass_half_float = half_float && pass_RGBA; /* Colorspace conversion only happens on RGBA passes. */ - float *output_rect = (save_as_render && pass_RGBA) ? - image_exr_from_scene_linear_to_output( - rp->rect, rr->rectx, rr->recty, 4, imf, tmp_output_rects) : - rp->rect; + float *output_rect = + (save_as_render && pass_RGBA) ? + image_exr_from_scene_linear_to_output( + rp->rect, rr->rectx, rr->recty, rp->channels, imf, tmp_output_rects) : + rp->rect; for (int a = 0; a < rp->channels; a++) { /* Save Combined as RGBA if single layer save. */ diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index a2338eb9b39..67df6b5527e 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -1105,8 +1105,8 @@ static void lib_override_library_create_post_process(Main *bmain, if (ID_REAL_USERS(ob_new) != 0) { continue; } - default_instantiating_collection = BKE_collection_add( - bmain, (Collection *)id_root, "OVERRIDE_HIDDEN"); + default_instantiating_collection = BKE_id_new(bmain, ID_GR, "OVERRIDE_HIDDEN"); + id_us_min(&default_instantiating_collection->id); /* Hide the collection from viewport and render. */ default_instantiating_collection->flag |= COLLECTION_HIDE_VIEWPORT | COLLECTION_HIDE_RENDER; @@ -1140,6 +1140,20 @@ static void lib_override_library_create_post_process(Main *bmain, } } + if (id_root != NULL && !ELEM(default_instantiating_collection, NULL, scene->master_collection)) { + ID *id_ref = id_root->newid != NULL ? id_root->newid : id_root; + switch (GS(id_ref->name)) { + case ID_GR: + BKE_collection_add_from_collection( + bmain, scene, (Collection *)id_ref, default_instantiating_collection); + break; + default: + /* Add to master collection. */ + BKE_collection_add_from_collection(bmain, scene, NULL, default_instantiating_collection); + break; + } + } + BLI_gset_free(all_objects_in_scene, NULL); } @@ -1384,7 +1398,21 @@ void BKE_lib_override_library_main_hierarchy_root_ensure(Main *bmain) continue; } if (id->override_library->hierarchy_root != NULL) { - continue; + if (!ID_IS_OVERRIDE_LIBRARY_REAL(id->override_library->hierarchy_root) || + id->override_library->hierarchy_root->lib != id->lib) { + CLOG_ERROR( + &LOG, + "Existing override hierarchy root ('%s') for ID '%s' is invalid, will try to find a " + "new valid one", + id->override_library->hierarchy_root != NULL ? + id->override_library->hierarchy_root->name : + "<NONE>", + id->name); + id->override_library->hierarchy_root = NULL; + } + else { + continue; + } } BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false); @@ -1402,7 +1430,7 @@ void BKE_lib_override_library_main_hierarchy_root_ensure(Main *bmain) continue; } - lib_override_root_hierarchy_set(bmain, id_root, id_root, NULL); + lib_override_root_hierarchy_set(bmain, id_root, id, NULL); BLI_assert(id->override_library->hierarchy_root != NULL); } @@ -1451,6 +1479,7 @@ static void lib_override_library_remap(Main *bmain, remapper, ID_REMAP_FORCE_USER_REFCOUNT | ID_REMAP_FORCE_NEVER_NULL_USAGE); BKE_id_remapper_free(remapper); + BLI_linklist_free(nomain_ids, NULL); } static bool lib_override_library_resync(Main *bmain, @@ -2058,6 +2087,10 @@ static bool lib_override_resync_tagging_finalize_recurse( CLOG_INFO(&LOG, 4, "Found root ID '%s' for resync root ID '%s'", id_root->name, id->name); + if (id_root->override_library == NULL) { + BLI_assert(0); + } + LinkNodePair **id_resync_roots_p; if (!BLI_ghash_ensure_p(id_roots, id_root, (void ***)&id_resync_roots_p)) { *id_resync_roots_p = MEM_callocN(sizeof(**id_resync_roots_p), __func__); diff --git a/source/blender/blenkernel/intern/subdiv_modifier.c b/source/blender/blenkernel/intern/subdiv_modifier.c index 83772f153d9..3692a3cc4f7 100644 --- a/source/blender/blenkernel/intern/subdiv_modifier.c +++ b/source/blender/blenkernel/intern/subdiv_modifier.c @@ -77,7 +77,7 @@ static bool is_subdivision_evaluation_possible_on_gpu(void) return false; } - if (GPU_max_shader_storage_buffer_bindings() < MAX_GPU_SUBDIV_SSBOS) { + if (GPU_max_compute_shader_storage_blocks() < MAX_GPU_SUBDIV_SSBOS) { return false; } diff --git a/source/blender/blenlib/BLI_string.h b/source/blender/blenlib/BLI_string.h index 45abac33795..0344622e81d 100644 --- a/source/blender/blenlib/BLI_string.h +++ b/source/blender/blenlib/BLI_string.h @@ -343,8 +343,16 @@ int BLI_strcmp_ignore_pad(const char *str1, const char *str2, char pad) ATTR_WAR */ size_t BLI_strnlen(const char *str, size_t maxlen) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +/** + * String case conversion, not affected by locale. + */ + void BLI_str_tolower_ascii(char *str, size_t len) ATTR_NONNULL(); void BLI_str_toupper_ascii(char *str, size_t len) ATTR_NONNULL(); + +char BLI_tolower_ascii(const char c); +char BLI_toupper_ascii(const char c); + /** * Strip white-space from end of the string. */ diff --git a/source/blender/blenlib/intern/string.c b/source/blender/blenlib/intern/string.c index 74559751d91..8387eb5f4f9 100644 --- a/source/blender/blenlib/intern/string.c +++ b/source/blender/blenlib/intern/string.c @@ -914,14 +914,22 @@ size_t BLI_strnlen(const char *s, const size_t maxlen) /** \name String Case Conversion * \{ */ +char BLI_tolower_ascii(const char c) +{ + return (c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c; +} + +char BLI_toupper_ascii(const char c) +{ + return (c >= 'a' && c <= 'z') ? c - ('a' - 'A') : c; +} + void BLI_str_tolower_ascii(char *str, const size_t len) { size_t i; for (i = 0; (i < len) && str[i]; i++) { - if (str[i] >= 'A' && str[i] <= 'Z') { - str[i] += 'a' - 'A'; - } + str[i] = BLI_tolower_ascii(str[i]); } } @@ -930,9 +938,7 @@ void BLI_str_toupper_ascii(char *str, const size_t len) size_t i; for (i = 0; (i < len) && str[i]; i++) { - if (str[i] >= 'a' && str[i] <= 'z') { - str[i] -= 'a' - 'A'; - } + str[i] = BLI_toupper_ascii(str[i]); } } diff --git a/source/blender/bmesh/intern/bmesh_log.c b/source/blender/bmesh/intern/bmesh_log.c index a398f14b312..a55eb74285d 100644 --- a/source/blender/bmesh/intern/bmesh_log.c +++ b/source/blender/bmesh/intern/bmesh_log.c @@ -288,6 +288,8 @@ static void bm_log_verts_restore(BMesh *bm, BMLog *log, GHash *verts) static void bm_log_faces_restore(BMesh *bm, BMLog *log, GHash *faces) { GHashIterator gh_iter; + const int cd_face_sets = CustomData_get_offset(&bm->pdata, CD_SCULPT_FACE_SETS); + GHASH_ITER (gh_iter, faces) { void *key = BLI_ghashIterator_getKey(&gh_iter); BMLogFace *lf = BLI_ghashIterator_getValue(&gh_iter); @@ -301,6 +303,11 @@ static void bm_log_faces_restore(BMesh *bm, BMLog *log, GHash *faces) f = BM_face_create_verts(bm, v, 3, NULL, BM_CREATE_NOP, true); f->head.hflag = lf->hflag; bm_log_face_id_set(log, f, POINTER_AS_UINT(key)); + + /* Ensure face sets have valid values. Fixes T80174. */ + if (cd_face_sets != -1) { + BM_ELEM_CD_SET_INT(f, cd_face_sets, 1); + } } } diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index 23147b63e27..5eccb5a4eb2 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -1693,6 +1693,22 @@ void DepsgraphRelationBuilder::build_driver_variables(ID *id, FCurve *fcu) continue; } add_relation(variable_exit_key, driver_key, "RNA Target -> Driver"); + + /* The RNA getter for `object.data` can write to the mesh datablock due + * to the call to `BKE_mesh_wrapper_ensure_subdivision()`. This relation + * ensures it is safe to call when the driver is evaluated. + * + * For the sake of making the code more generic/defensive, the relation + * is added for any geometry type. + * + * See T96289 for more info. */ + if (object != nullptr && OB_TYPE_IS_GEOMETRY(object->type)) { + StringRef rna_path(dtar->rna_path); + if (rna_path == "data" || rna_path.startswith("data.")) { + ComponentKey ob_key(target_id, NodeType::GEOMETRY); + add_relation(ob_key, driver_key, "ID -> Driver"); + } + } } else { /* If rna_path is nullptr, and DTAR_FLAG_STRUCT_REF isn't set, this diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index ed90e77572e..1ff7585165b 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -333,6 +333,7 @@ set(GLSL_SRC engines/eevee/shaders/renderpass_lib.glsl engines/eevee/shaders/renderpass_postprocess_frag.glsl engines/eevee/shaders/cryptomatte_frag.glsl + engines/eevee/shaders/cryptomatte_vert.glsl engines/eevee/shaders/ltc_lib.glsl engines/eevee/shaders/ssr_lib.glsl engines/eevee/shaders/surface_frag.glsl diff --git a/source/blender/draw/engines/eevee/eevee_cryptomatte.c b/source/blender/draw/engines/eevee/eevee_cryptomatte.c index 50302eaa87a..b17efe4b68d 100644 --- a/source/blender/draw/engines/eevee/eevee_cryptomatte.c +++ b/source/blender/draw/engines/eevee/eevee_cryptomatte.c @@ -189,11 +189,8 @@ void EEVEE_cryptomatte_cache_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Dat } } -static DRWShadingGroup *eevee_cryptomatte_shading_group_create(EEVEE_Data *vedata, - EEVEE_ViewLayerData *UNUSED(sldata), - Object *ob, - Material *material, - bool is_hair) +static DRWShadingGroup *eevee_cryptomatte_shading_group_create( + EEVEE_Data *vedata, EEVEE_ViewLayerData *sldata, Object *ob, Material *material, bool is_hair) { const DRWContextState *draw_ctx = DRW_context_state_get(); const ViewLayer *view_layer = draw_ctx->view_layer; @@ -229,6 +226,7 @@ static DRWShadingGroup *eevee_cryptomatte_shading_group_create(EEVEE_Data *vedat DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_cryptomatte_sh_get(is_hair), psl->cryptomatte_ps); DRW_shgroup_uniform_vec4_copy(grp, "cryptohash", cryptohash); + DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo); return grp; } diff --git a/source/blender/draw/engines/eevee/eevee_data.c b/source/blender/draw/engines/eevee/eevee_data.c index 0c56b67ca99..1a1e3b80bad 100644 --- a/source/blender/draw/engines/eevee/eevee_data.c +++ b/source/blender/draw/engines/eevee/eevee_data.c @@ -114,7 +114,9 @@ void EEVEE_motion_blur_data_free(EEVEE_MotionBlurData *mb) } } -EEVEE_ObjectMotionData *EEVEE_motion_blur_object_data_get(EEVEE_MotionBlurData *mb, Object *ob) +EEVEE_ObjectMotionData *EEVEE_motion_blur_object_data_get(EEVEE_MotionBlurData *mb, + Object *ob, + bool is_psys) { if (mb->object == NULL) { return NULL; @@ -123,14 +125,16 @@ EEVEE_ObjectMotionData *EEVEE_motion_blur_object_data_get(EEVEE_MotionBlurData * EEVEE_ObjectKey key, *key_p; /* Assumes that all instances have the same object pointer. This is currently the case because * instance objects are temporary objects on the stack. */ - key.ob = ob; + /* WORKAROUND: Duplicate object key for particle system (hairs) to be able to store dupli offset + * matrix along with the emitter obmat. (see T97380) */ + key.ob = (void *)((char *)ob + is_psys); DupliObject *dup = DRW_object_get_dupli(ob); if (dup) { key.parent = DRW_object_get_dupli_parent(ob); memcpy(key.id, dup->persistent_id, sizeof(key.id)); } else { - key.parent = key.ob; + key.parent = ob; memset(key.id, 0, sizeof(key.id)); } diff --git a/source/blender/draw/engines/eevee/eevee_motion_blur.c b/source/blender/draw/engines/eevee/eevee_motion_blur.c index 2e0937dbe49..a3ca19c88e1 100644 --- a/source/blender/draw/engines/eevee/eevee_motion_blur.c +++ b/source/blender/draw/engines/eevee/eevee_motion_blur.c @@ -226,7 +226,8 @@ void EEVEE_motion_blur_hair_cache_populate(EEVEE_ViewLayerData *UNUSED(sldata), } /* For now we assume hair objects are always moving. */ - EEVEE_ObjectMotionData *mb_data = EEVEE_motion_blur_object_data_get(&effects->motion_blur, ob); + EEVEE_ObjectMotionData *mb_data = EEVEE_motion_blur_object_data_get( + &effects->motion_blur, ob, true); if (mb_data) { int mb_step = effects->motion_blur_step; @@ -283,7 +284,8 @@ void EEVEE_motion_blur_curves_cache_populate(EEVEE_ViewLayerData *UNUSED(sldata) } /* For now we assume curves objects are always moving. */ - EEVEE_ObjectMotionData *mb_data = EEVEE_motion_blur_object_data_get(&effects->motion_blur, ob); + EEVEE_ObjectMotionData *mb_data = EEVEE_motion_blur_object_data_get( + &effects->motion_blur, ob, false); if (mb_data == NULL) { return; } @@ -354,7 +356,8 @@ void EEVEE_motion_blur_cache_populate(EEVEE_ViewLayerData *UNUSED(sldata), return; } - EEVEE_ObjectMotionData *mb_data = EEVEE_motion_blur_object_data_get(&effects->motion_blur, ob); + EEVEE_ObjectMotionData *mb_data = EEVEE_motion_blur_object_data_get( + &effects->motion_blur, ob, false); if (mb_data) { int mb_step = effects->motion_blur_step; diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h index 6128556d364..b03a4fa70b4 100644 --- a/source/blender/draw/engines/eevee/eevee_private.h +++ b/source/blender/draw/engines/eevee/eevee_private.h @@ -644,8 +644,10 @@ typedef struct EEVEE_MotionBlurData { } EEVEE_MotionBlurData; typedef struct EEVEE_ObjectKey { - /** Object or source object for duplis */ - struct Object *ob; + /** Object or source object for duplis. */ + /** WORKAROUND: The pointer is different for particle systems and do not point to the real + * object. (See T97380) */ + void *ob; /** Parent object for duplis */ struct Object *parent; /** Dupli objects recursive unique identifier */ @@ -1093,7 +1095,9 @@ EEVEE_ViewLayerData *EEVEE_view_layer_data_ensure_ex(struct ViewLayer *view_laye EEVEE_ViewLayerData *EEVEE_view_layer_data_ensure(void); EEVEE_ObjectEngineData *EEVEE_object_data_get(Object *ob); EEVEE_ObjectEngineData *EEVEE_object_data_ensure(Object *ob); -EEVEE_ObjectMotionData *EEVEE_motion_blur_object_data_get(EEVEE_MotionBlurData *mb, Object *ob); +EEVEE_ObjectMotionData *EEVEE_motion_blur_object_data_get(EEVEE_MotionBlurData *mb, + Object *ob, + bool is_psys); EEVEE_GeometryMotionData *EEVEE_motion_blur_geometry_data_get(EEVEE_ObjectMotionData *mb_data); EEVEE_HairMotionData *EEVEE_motion_blur_hair_data_get(EEVEE_ObjectMotionData *mb_data, Object *ob); EEVEE_HairMotionData *EEVEE_motion_blur_curves_data_get(EEVEE_ObjectMotionData *mb_data); diff --git a/source/blender/draw/engines/eevee/eevee_shaders.c b/source/blender/draw/engines/eevee/eevee_shaders.c index 85cc7f65126..105600d2333 100644 --- a/source/blender/draw/engines/eevee/eevee_shaders.c +++ b/source/blender/draw/engines/eevee/eevee_shaders.c @@ -183,6 +183,7 @@ extern char datatoc_closure_eval_volume_lib_glsl[]; extern char datatoc_common_uniforms_lib_glsl[]; extern char datatoc_common_utiltex_lib_glsl[]; extern char datatoc_cryptomatte_frag_glsl[]; +extern char datatoc_cryptomatte_vert_glsl[]; extern char datatoc_cubemap_lib_glsl[]; extern char datatoc_default_frag_glsl[]; extern char datatoc_lookdev_world_frag_glsl[]; @@ -305,6 +306,7 @@ static void eevee_shader_library_ensure(void) DRW_SHADER_LIB_ADD(e_data.lib, closure_eval_refraction_lib); DRW_SHADER_LIB_ADD(e_data.lib, closure_eval_surface_lib); DRW_SHADER_LIB_ADD(e_data.lib, closure_eval_volume_lib); + DRW_SHADER_LIB_ADD(e_data.lib, surface_vert); e_data.surface_lit_frag = DRW_shader_library_create_shader_string(e_data.lib, datatoc_surface_frag_glsl); @@ -718,7 +720,7 @@ GPUShader *EEVEE_shaders_cryptomatte_sh_get(bool is_hair) if (e_data.cryptomatte_sh[index] == NULL) { DynStr *ds = BLI_dynstr_new(); BLI_dynstr_append(ds, SHADER_DEFINES); - + BLI_dynstr_append(ds, "#define attrib_load(a) \n"); if (is_hair) { BLI_dynstr_append(ds, "#define HAIR_SHADER\n"); } @@ -727,7 +729,7 @@ GPUShader *EEVEE_shaders_cryptomatte_sh_get(bool is_hair) } char *defines = BLI_dynstr_get_cstring(ds); e_data.cryptomatte_sh[index] = DRW_shader_create_with_shaderlib( - datatoc_surface_vert_glsl, NULL, datatoc_cryptomatte_frag_glsl, e_data.lib, defines); + datatoc_cryptomatte_vert_glsl, NULL, datatoc_cryptomatte_frag_glsl, e_data.lib, defines); BLI_dynstr_free(ds); MEM_freeN(defines); } diff --git a/source/blender/draw/engines/eevee/shaders/closure_eval_surface_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_eval_surface_lib.glsl index f85ef4a89a4..0f5290a7c07 100644 --- a/source/blender/draw/engines/eevee/shaders/closure_eval_surface_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/closure_eval_surface_lib.glsl @@ -309,16 +309,19 @@ vec4 closure_to_rgba(Closure closure) return vec4(closure.radiance, 1.0 - saturate(avg(closure.transmittance))); } -Closure closure_add(Closure cl1, Closure cl2) +Closure closure_add(inout Closure cl1, inout Closure cl2) { Closure cl; cl.radiance = cl1.radiance + cl2.radiance; cl.transmittance = cl1.transmittance + cl2.transmittance; cl.holdout = cl1.holdout + cl2.holdout; + /* Make sure each closure is only added once to the result. */ + cl1 = CLOSURE_DEFAULT; + cl2 = CLOSURE_DEFAULT; return cl; } -Closure closure_mix(Closure cl1, Closure cl2, float fac) +Closure closure_mix(inout Closure cl1, inout Closure cl2, float fac) { /* Weights have already been applied. */ return closure_add(cl1, cl2); diff --git a/source/blender/draw/engines/eevee/shaders/closure_eval_volume_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_eval_volume_lib.glsl index e450b8ad3c8..5e34d654cfd 100644 --- a/source/blender/draw/engines/eevee/shaders/closure_eval_volume_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/closure_eval_volume_lib.glsl @@ -92,7 +92,7 @@ vec4 closure_to_rgba(Closure closure) return vec4(0.0); } -Closure closure_mix(Closure cl1, Closure cl2, float fac) +Closure closure_mix(inout Closure cl1, inout Closure cl2, float fac) { Closure cl; cl.absorption = mix(cl1.absorption, cl2.absorption, fac); @@ -102,7 +102,7 @@ Closure closure_mix(Closure cl1, Closure cl2, float fac) return cl; } -Closure closure_add(Closure cl1, Closure cl2) +Closure closure_add(inout Closure cl1, inout Closure cl2) { Closure cl; cl.absorption = cl1.absorption + cl2.absorption; diff --git a/source/blender/draw/engines/eevee/shaders/closure_type_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_type_lib.glsl index 9698b5ea6f5..21d347942ca 100644 --- a/source/blender/draw/engines/eevee/shaders/closure_type_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/closure_type_lib.glsl @@ -87,8 +87,8 @@ Closure closure_eval(ClosureDiffuse diffuse, ClosureReflection clearcoat, ClosureRefraction refraction); -Closure closure_add(Closure cl1, Closure cl2); -Closure closure_mix(Closure cl1, Closure cl2, float fac); +Closure closure_add(inout Closure cl1, inout Closure cl2); +Closure closure_mix(inout Closure cl1, inout Closure cl2, float fac); float ambient_occlusion_eval(vec3 normal, float distance, diff --git a/source/blender/draw/engines/eevee/shaders/cryptomatte_vert.glsl b/source/blender/draw/engines/eevee/shaders/cryptomatte_vert.glsl new file mode 100644 index 00000000000..f8dbc4772e9 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/cryptomatte_vert.glsl @@ -0,0 +1,6 @@ + +#pragma BLENDER_REQUIRE(closure_type_lib.glsl) +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(common_attribute_lib.glsl) +#pragma BLENDER_REQUIRE(surface_vert.glsl) diff --git a/source/blender/draw/engines/eevee/shaders/surface_lib.glsl b/source/blender/draw/engines/eevee/shaders/surface_lib.glsl index 1f2f7cb65cc..696e5d4c97b 100644 --- a/source/blender/draw/engines/eevee/shaders/surface_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/surface_lib.glsl @@ -112,6 +112,7 @@ GlobalData init_globals(void) surf.N = -surf.N; } # ifdef HAIR_SHADER + vec3 V = cameraVec(surf.P); /* Shade as a cylinder. */ vec3 B = normalize(cross(worldNormal, hairTangent)); float cos_theta; @@ -125,7 +126,10 @@ GlobalData init_globals(void) } float sin_theta = sqrt(max(0.0, 1.0 - cos_theta * cos_theta)); surf.N = safe_normalize(worldNormal * sin_theta + B * cos_theta); - surf.T = hairTangent; + surf.curve_T = -hairTangent; + /* Costly, but follows cycles per pixel tangent space (not following curve shape). */ + surf.curve_B = cross(V, surf.curve_T); + surf.curve_N = safe_normalize(cross(surf.curve_T, surf.curve_B)); surf.is_strand = true; surf.hair_time = hairTime; surf.hair_thickness = hairThickness; @@ -134,7 +138,7 @@ GlobalData init_globals(void) surf.barycentric_coords = hair_resolve_barycentric(hairBary); # endif # else - surf.T = vec3(0.0); + surf.curve_T = surf.curve_B = surf.curve_N = vec3(0.0); surf.is_strand = false; surf.hair_time = 0.0; surf.hair_thickness = 0.0; diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_attributes_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_attributes_lib.glsl index abecb373995..3c5acf62e30 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_attributes_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_attributes_lib.glsl @@ -164,7 +164,7 @@ float attr_load_float(samplerBuffer cd_buf) * \{ */ # ifndef OBINFO_LIB -# error "draw_object_infos is mandatory for volume objects" +# error draw_object_infos is mandatory for volume objects # endif vec3 g_orco; diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_geom_curves_vert.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_geom_curves_vert.glsl index 11f93ad0d14..708bd153e84 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_geom_curves_vert.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_geom_curves_vert.glsl @@ -18,7 +18,7 @@ void main() ViewMatrixInverse[3].xyz, ViewMatrixInverse[2].xyz, interp.P, - T, + interp.curves_tangent, interp.curves_binormal, interp.curves_time, interp.curves_thickness, diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl index 06191a5c007..277b2e35d8b 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl @@ -9,6 +9,7 @@ float g_holdout; /* The Closure type is never used. Use float as dummy type. */ #define Closure float +#define CLOSURE_DEFAULT 0.0 /* Sampled closure parameters. */ ClosureDiffuse g_diffuse_data; diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_lib.glsl index 0d8644c9901..30b48edaa78 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_lib.glsl @@ -42,6 +42,12 @@ void init_globals_curves() float sin_theta = sqrt(max(0.0, 1.0 - cos_theta * cos_theta)); g_data.N = normalize(interp.N * sin_theta + interp.curves_binormal * cos_theta); + /* Costly, but follows cycles per pixel tangent space (not following curve shape). */ + vec3 V = cameraVec(g_data.P); + g_data.curve_T = -interp.curves_tangent; + g_data.curve_B = cross(V, g_data.curve_T); + g_data.curve_N = safe_normalize(cross(g_data.curve_T, g_data.curve_B)); + g_data.is_strand = true; g_data.hair_time = interp.curves_time; g_data.hair_thickness = interp.curves_thickness; @@ -94,6 +100,7 @@ void init_interface() interp.P = vec3(0.0); interp.N = vec3(0.0); interp.barycentric_coords = vec2(0.0); + interp.curves_tangent = vec3(0.0); interp.curves_binormal = vec3(0.0); interp.curves_time = 0.0; interp.curves_time_width = 0.0; diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh index e829a41d682..4d6895bcde0 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh @@ -54,6 +54,7 @@ GPU_SHADER_INTERFACE_INFO(eevee_surf_iface, "interp") .smooth(Type::VEC3, "P") .smooth(Type::VEC3, "N") .smooth(Type::VEC2, "barycentric_coords") + .smooth(Type::VEC3, "curves_tangent") .smooth(Type::VEC3, "curves_binormal") .smooth(Type::FLOAT, "curves_time") .smooth(Type::FLOAT, "curves_time_width") diff --git a/source/blender/draw/engines/overlay/overlay_shader.c b/source/blender/draw/engines/overlay/overlay_shader.c index d63ae6750d6..48146fbddfb 100644 --- a/source/blender/draw/engines/overlay/overlay_shader.c +++ b/source/blender/draw/engines/overlay/overlay_shader.c @@ -893,7 +893,7 @@ GPUShader *OVERLAY_shader_edit_uv_edges_get(void) { OVERLAY_Shaders *sh_data = &e_data.sh_data[0]; if (!sh_data->edit_uv_edges) { - sh_data->edit_uv_edges = GPU_shader_create_from_info_name("overlay_edit_uv_edges_select"); + sh_data->edit_uv_edges = GPU_shader_create_from_info_name("overlay_edit_uv_edges"); } return sh_data->edit_uv_edges; } @@ -903,7 +903,7 @@ GPUShader *OVERLAY_shader_edit_uv_edges_for_edge_select_get(void) OVERLAY_Shaders *sh_data = &e_data.sh_data[0]; if (!sh_data->edit_uv_edges_for_edge_select) { sh_data->edit_uv_edges_for_edge_select = GPU_shader_create_from_info_name( - "overlay_edit_uv_edges"); + "overlay_edit_uv_edges_select"); } return sh_data->edit_uv_edges_for_edge_select; } diff --git a/source/blender/draw/engines/overlay/shaders/edit_uv_edges_geom.glsl b/source/blender/draw/engines/overlay/shaders/edit_uv_edges_geom.glsl index 1012b50e0e1..ac8d33cd727 100644 --- a/source/blender/draw/engines/overlay/shaders/edit_uv_edges_geom.glsl +++ b/source/blender/draw/engines/overlay/shaders/edit_uv_edges_geom.glsl @@ -1,8 +1,5 @@ #pragma BLENDER_REQUIRE(common_overlay_lib.glsl) -layout(lines) in; -layout(triangle_strip, max_vertices = 4) out; - void do_vertex( vec4 pos, float selection_fac, vec2 stipple_start, vec2 stipple_pos, float coord, vec2 offset) { diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h index 4567e470146..cb6006e303a 100644 --- a/source/blender/draw/intern/draw_cache_extract.h +++ b/source/blender/draw/intern/draw_cache_extract.h @@ -328,7 +328,6 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, const float obmat[4][4], bool do_final, bool do_uvedit, - bool use_subsurf_fdots, const Scene *scene, const struct ToolSettings *ts, bool use_hide); diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc index 8a7b4fc9703..ec544d8e786 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.cc +++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc @@ -563,7 +563,6 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, const float obmat[4][4], const bool do_final, const bool do_uvedit, - const bool use_subsurf_fdots, const Scene *scene, const ToolSettings *ts, const bool use_hide) @@ -687,7 +686,7 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, MeshRenderData *mr = mesh_render_data_create( object, me, is_editmode, is_paint_mode, is_mode_active, obmat, do_final, do_uvedit, ts); mr->use_hide = use_hide; - mr->use_subsurf_fdots = use_subsurf_fdots; + mr->use_subsurf_fdots = mr->me && mr->me->runtime.subsurf_face_dot_tags != nullptr; mr->use_final_mesh = do_final; #ifdef DEBUG_TIME @@ -919,7 +918,6 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, const float obmat[4][4], const bool do_final, const bool do_uvedit, - const bool use_subsurf_fdots, const Scene *scene, const ToolSettings *ts, const bool use_hide) @@ -935,7 +933,6 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, obmat, do_final, do_uvedit, - use_subsurf_fdots, scene, ts, use_hide); diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c index 917216b92e6..7dc0244275d 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.c +++ b/source/blender/draw/intern/draw_cache_impl_mesh.c @@ -708,12 +708,18 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Object *object, case CD_MCOL: case CD_PROP_BYTE_COLOR: case CD_PROP_COLOR: { + /* First check Color attributes, when not found check mesh attributes. Geometry nodes + * can generate those layers. */ int vcol_bit = mesh_cd_calc_gpu_layers_vcol_used(&me_query, cd_vdata, cd_ldata, name); if (vcol_bit != -1) { cd_used.vcol |= 1UL << (uint)vcol_bit; + break; } + if (layer != -1 && domain != ATTR_DOMAIN_NUM) { + drw_mesh_attributes_add_request(attributes, type, layer, domain); + } break; } case CD_PROP_FLOAT3: @@ -1231,11 +1237,11 @@ static void sculpt_request_active_vcol(MeshBatchCache *cache, Object *object, Me &me_query.id, render, ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL); if (active_i >= 0) { - cache->cd_used.vcol |= 1UL << (uint)active_i; + cache->cd_needed.vcol |= 1UL << (uint)active_i; } if (render_i >= 0) { - cache->cd_used.vcol |= 1UL << (uint)render_i; + cache->cd_needed.vcol |= 1UL << (uint)render_i; } } @@ -2128,8 +2134,6 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, MDEPS_ASSERT_MAP_INDEX(TRIS_PER_MAT_INDEX); - const bool use_subsurf_fdots = me->runtime.subsurf_face_dot_tags != NULL; - if (do_uvcage) { mesh_buffer_cache_create_requested(task_graph, cache, @@ -2142,7 +2146,6 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, ob->obmat, false, true, - false, scene, ts, true); @@ -2160,7 +2163,6 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, ob->obmat, false, false, - use_subsurf_fdots, scene, ts, true); @@ -2178,7 +2180,6 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, ob->obmat, true, false, - use_subsurf_fdots, ts, use_hide); } @@ -2199,7 +2200,6 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, ob->obmat, true, false, - use_subsurf_fdots, scene, ts, use_hide); diff --git a/source/blender/draw/intern/draw_cache_impl_subdivision.cc b/source/blender/draw/intern/draw_cache_impl_subdivision.cc index 7d245f4158b..8eb0509d615 100644 --- a/source/blender/draw/intern/draw_cache_impl_subdivision.cc +++ b/source/blender/draw/intern/draw_cache_impl_subdivision.cc @@ -1904,7 +1904,6 @@ static bool draw_subdiv_create_requested_buffers(const Scene *scene, const float obmat[4][4], const bool do_final, const bool do_uvedit, - const bool /*use_subsurf_fdots*/, const ToolSettings *ts, const bool /*use_hide*/, OpenSubdiv_EvaluatorCache *evaluator_cache) @@ -2103,7 +2102,6 @@ void DRW_create_subdivision(const Scene *scene, const float obmat[4][4], const bool do_final, const bool do_uvedit, - const bool use_subsurf_fdots, const ToolSettings *ts, const bool use_hide) { @@ -2128,7 +2126,6 @@ void DRW_create_subdivision(const Scene *scene, obmat, do_final, do_uvedit, - use_subsurf_fdots, ts, use_hide, g_evaluator_cache)) { diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index 6ab8d30109e..4bbcf6eaf42 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -1287,7 +1287,7 @@ void DRW_notify_view_update(const DRWUpdateContext *update_ctx) const bool gpencil_engine_needed = drw_gpencil_engine_needed(depsgraph, v3d); - if (G.is_rendering) { + if (G.is_rendering && U.experimental.use_draw_manager_acquire_lock) { return; } diff --git a/source/blender/draw/intern/draw_subdivision.h b/source/blender/draw/intern/draw_subdivision.h index d803fad5052..b7cd520f54f 100644 --- a/source/blender/draw/intern/draw_subdivision.h +++ b/source/blender/draw/intern/draw_subdivision.h @@ -194,7 +194,6 @@ void DRW_create_subdivision(const struct Scene *scene, const float obmat[4][4], const bool do_final, const bool do_uvedit, - const bool use_subsurf_fdots, const ToolSettings *ts, const bool use_hide); diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc index 43aa52f08c8..2c4e7bfa802 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc @@ -387,8 +387,7 @@ static void extract_edituv_points_iter_poly_mesh(const MeshRenderData *mr, for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { const MLoop *ml = &mloop[ml_index]; - const bool real_vert = (mr->extract_type == MR_EXTRACT_MAPPED && (mr->v_origindex) && - mr->v_origindex[ml->v] != ORIGINDEX_NONE); + const bool real_vert = !mr->v_origindex || mr->v_origindex[ml->v] != ORIGINDEX_NONE; edituv_point_add( data, ((mp->flag & ME_HIDE) != 0) || !real_vert, (mp->flag & ME_FACE_SEL) != 0, ml_index); } @@ -449,9 +448,8 @@ static void extract_edituv_points_iter_subdiv_mesh(const DRWSubdivCache *subdiv_ uint end_loop_idx = (subdiv_quad_index + 1) * 4; for (uint i = start_loop_idx; i < end_loop_idx; i++) { const int vert_origindex = subdiv_loop_vert_index[i]; - const bool real_vert = (mr->extract_type == MR_EXTRACT_MAPPED && (mr->v_origindex) && - vert_origindex != -1 && - mr->v_origindex[vert_origindex] != ORIGINDEX_NONE); + const bool real_vert = !mr->v_origindex || (vert_origindex != -1 && + mr->v_origindex[vert_origindex] != ORIGINDEX_NONE); edituv_point_add(data, ((coarse_quad->flag & ME_HIDE) != 0) || !real_vert, (coarse_quad->flag & ME_FACE_SEL) != 0, @@ -543,8 +541,7 @@ static void extract_edituv_fdots_iter_poly_mesh(const MeshRenderData *mr, for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { const MLoop *ml = &mloop[ml_index]; - const bool real_fdot = (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex && - mr->p_origindex[mp_index] != ORIGINDEX_NONE); + const bool real_fdot = !mr->p_origindex || (mr->p_origindex[mp_index] != ORIGINDEX_NONE); const bool subd_fdot = BLI_BITMAP_TEST(facedot_tags, ml->v); edituv_facedot_add(data, ((mp->flag & ME_HIDE) != 0) || !real_fdot || !subd_fdot, @@ -553,8 +550,7 @@ static void extract_edituv_fdots_iter_poly_mesh(const MeshRenderData *mr, } } else { - const bool real_fdot = (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex && - mr->p_origindex[mp_index] != ORIGINDEX_NONE); + const bool real_fdot = !mr->p_origindex || (mr->p_origindex[mp_index] != ORIGINDEX_NONE); edituv_facedot_add( data, ((mp->flag & ME_HIDE) != 0) || !real_fdot, (mp->flag & ME_FACE_SEL) != 0, mp_index); } diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c index fe2fa832477..da5c8b311e7 100644 --- a/source/blender/editors/gpencil/gpencil_select.c +++ b/source/blender/editors/gpencil/gpencil_select.c @@ -2685,6 +2685,7 @@ void GPENCIL_OT_select(wmOperatorType *ot) ot->invoke = gpencil_select_invoke; ot->exec = gpencil_select_exec; ot->poll = gpencil_select_poll; + ot->get_name = ED_select_pick_get_name; /* flag */ ot->flag = OPTYPE_UNDO; diff --git a/source/blender/editors/include/ED_select_utils.h b/source/blender/editors/include/ED_select_utils.h index dbe1b582132..fa02ebe18f6 100644 --- a/source/blender/editors/include/ED_select_utils.h +++ b/source/blender/editors/include/ED_select_utils.h @@ -14,6 +14,7 @@ extern "C" { struct KDTree_1d; struct wmOperator; +struct wmOperatorType; enum { SEL_TOGGLE = 0, @@ -96,16 +97,23 @@ struct SelectPick_Params { /** * Utility to get #eSelectPickMode from booleans for convenience. */ -eSelectOp ED_select_op_from_operator(struct wmOperator *op) +eSelectOp ED_select_op_from_operator(struct PointerRNA *ptr) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT; /** * Initialize `params` from `op`, * these properties are defined by #WM_operator_properties_mouse_select. */ -void ED_select_pick_params_from_operator(struct wmOperator *op, struct SelectPick_Params *params) +void ED_select_pick_params_from_operator(struct PointerRNA *ptr, struct SelectPick_Params *params) ATTR_NONNULL(1, 2); +/** + * Get-name callback for #wmOperatorType.get_name, this is mainly useful so the selection + * action is shown in the status-bar. + */ +const char *ED_select_pick_get_name(struct wmOperatorType *ot, PointerRNA *ptr); +const char *ED_select_circle_get_name(struct wmOperatorType *ot, PointerRNA *ptr); + #ifdef __cplusplus } #endif diff --git a/source/blender/editors/include/ED_uvedit.h b/source/blender/editors/include/ED_uvedit.h index 4c01c75e06c..06134f1d7b5 100644 --- a/source/blender/editors/include/ED_uvedit.h +++ b/source/blender/editors/include/ED_uvedit.h @@ -268,6 +268,10 @@ struct BMLoop **ED_uvedit_selected_verts(const struct Scene *scene, int *r_verts_len); void ED_uvedit_get_aspect(struct Object *obedit, float *r_aspx, float *r_aspy); +void ED_uvedit_get_aspect_from_material(Object *ob, + const int material_index, + float *r_aspx, + float *r_aspy); void ED_uvedit_active_vert_loop_set(struct BMesh *bm, struct BMLoop *l); struct BMLoop *ED_uvedit_active_vert_loop_get(struct BMesh *bm); diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 0ae3cb010ed..3d247a9b5e3 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -184,26 +184,26 @@ enum { /** #uiBut.flag general state flags. */ enum { - /* WARNING: the first 7 flags are internal (see #UI_SELECT definition). */ - UI_BUT_ICON_SUBMENU = 1 << 7, - UI_BUT_ICON_PREVIEW = 1 << 8, + /* WARNING: the first 8 flags are internal (see #UI_SELECT definition). */ + UI_BUT_ICON_SUBMENU = 1 << 8, + UI_BUT_ICON_PREVIEW = 1 << 9, - UI_BUT_NODE_LINK = 1 << 9, - UI_BUT_NODE_ACTIVE = 1 << 10, - UI_BUT_DRAG_LOCK = 1 << 11, + UI_BUT_NODE_LINK = 1 << 10, + UI_BUT_NODE_ACTIVE = 1 << 11, + UI_BUT_DRAG_LOCK = 1 << 12, /** Grayed out and un-editable. */ - UI_BUT_DISABLED = 1 << 12, + UI_BUT_DISABLED = 1 << 13, - UI_BUT_ANIMATED = 1 << 13, - UI_BUT_ANIMATED_KEY = 1 << 14, - UI_BUT_DRIVEN = 1 << 15, - UI_BUT_REDALERT = 1 << 16, + UI_BUT_ANIMATED = 1 << 14, + UI_BUT_ANIMATED_KEY = 1 << 15, + UI_BUT_DRIVEN = 1 << 16, + UI_BUT_REDALERT = 1 << 17, /** Grayed out but still editable. */ - UI_BUT_INACTIVE = 1 << 17, - UI_BUT_LAST_ACTIVE = 1 << 18, - UI_BUT_UNDO = 1 << 19, - UI_BUT_IMMEDIATE = 1 << 20, - UI_BUT_NO_UTF8 = 1 << 21, + UI_BUT_INACTIVE = 1 << 18, + UI_BUT_LAST_ACTIVE = 1 << 19, + UI_BUT_UNDO = 1 << 20, + UI_BUT_IMMEDIATE = 1 << 21, + UI_BUT_NO_UTF8 = 1 << 22, /** For popups, pressing return activates this button, overriding the highlighted button. * For non-popups this is just used as a display hint for the user to let them diff --git a/source/blender/editors/interface/interface.cc b/source/blender/editors/interface/interface.cc index b7098c26bcd..480044118f1 100644 --- a/source/blender/editors/interface/interface.cc +++ b/source/blender/editors/interface/interface.cc @@ -1861,15 +1861,32 @@ bool ui_but_context_poll_operator_ex(bContext *C, const wmOperatorCallParams *optype_params) { bool result; + int old_but_flag = 0; - if (but && but->context) { - CTX_store_set(C, but->context); + if (but) { + old_but_flag = but->flag; + + /* Temporarily make this button override the active one, in case the poll acts on the active + * button. */ + const_cast<uiBut *>(but)->flag |= UI_BUT_ACTIVE_OVERRIDE; + + if (but->context) { + CTX_store_set(C, but->context); + } } result = WM_operator_poll_context(C, optype_params->optype, optype_params->opcontext); - if (but && but->context) { - CTX_store_set(C, nullptr); + if (but) { + BLI_assert_msg((but->flag & ~UI_BUT_ACTIVE_OVERRIDE) == + (old_but_flag & ~UI_BUT_ACTIVE_OVERRIDE), + "Operator polls shouldn't change button flags"); + + const_cast<uiBut *>(but)->flag = old_but_flag; + + if (but->context) { + CTX_store_set(C, nullptr); + } } return result; diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 2c408619fe7..9d7d76f0bdb 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -384,6 +384,12 @@ typedef struct uiHandleButtonData { /* True when alt is held and the preference for displaying tooltips should be ignored. */ bool tooltip_force; + /** + * Behave as if #UI_BUT_DISABLED is set (without drawing grayed out). + * Needed so non-interactive labels can be activated for the purpose of showing tool-tips, + * without them blocking interaction with nodes, see: T97386. + */ + bool disable_force; /* auto open */ bool used_mouse; @@ -1669,7 +1675,7 @@ static void ui_drag_toggle_set(bContext *C, uiDragToggleHandle *drag_info, const */ if (drag_info->is_xy_lock_init == false) { /* first store the buttons original coords */ - uiBut *but = ui_but_find_mouse_over_ex(region, xy_input, true, NULL, NULL); + uiBut *but = ui_but_find_mouse_over_ex(region, xy_input, true, false, NULL, NULL); if (but) { if (but->flag & UI_BUT_DRAG_LOCK) { @@ -1739,7 +1745,7 @@ static int ui_handler_region_drag_toggle(bContext *C, const wmEvent *event, void if (done) { wmWindow *win = CTX_wm_window(C); const ARegion *region = CTX_wm_region(C); - uiBut *but = ui_but_find_mouse_over_ex(region, drag_info->xy_init, true, NULL, NULL); + uiBut *but = ui_but_find_mouse_over_ex(region, drag_info->xy_init, true, false, NULL, NULL); if (but) { ui_apply_but_undo(but); @@ -4324,7 +4330,7 @@ static uiBut *ui_but_list_row_text_activate(bContext *C, uiButtonActivateType activate_type) { ARegion *region = CTX_wm_region(C); - uiBut *labelbut = ui_but_find_mouse_over_ex(region, event->xy, true, NULL, NULL); + uiBut *labelbut = ui_but_find_mouse_over_ex(region, event->xy, true, false, NULL, NULL); if (labelbut && labelbut->type == UI_BTYPE_TEXT && !(labelbut->flag & UI_BUT_DISABLED)) { /* exit listrow */ @@ -4346,14 +4352,14 @@ static uiBut *ui_but_list_row_text_activate(bContext *C, * \{ */ static uiButExtraOpIcon *ui_but_extra_operator_icon_mouse_over_get(uiBut *but, - uiHandleButtonData *data, + ARegion *region, const wmEvent *event) { float xmax = but->rect.xmax; const float icon_size = 0.8f * BLI_rctf_size_y(&but->rect); /* ICON_SIZE_FROM_BUTRECT */ int x = event->xy[0], y = event->xy[1]; - ui_window_to_block(data->region, but->block, &x, &y); + ui_window_to_block(region, but->block, &x, &y); if (!BLI_rctf_isect_pt(&but->rect, x, y)) { return NULL; } @@ -4382,7 +4388,7 @@ static bool ui_do_but_extra_operator_icon(bContext *C, uiHandleButtonData *data, const wmEvent *event) { - uiButExtraOpIcon *op_icon = ui_but_extra_operator_icon_mouse_over_get(but, data, event); + uiButExtraOpIcon *op_icon = ui_but_extra_operator_icon_mouse_over_get(but, data->region, event); if (!op_icon) { return false; @@ -4417,7 +4423,7 @@ static void ui_do_but_extra_operator_icons_mousemove(uiBut *but, op_icon->highlighted = false; } - uiButExtraOpIcon *hovered = ui_but_extra_operator_icon_mouse_over_get(but, data, event); + uiButExtraOpIcon *hovered = ui_but_extra_operator_icon_mouse_over_get(but, data->region, event); if (hovered) { hovered->highlighted = true; @@ -4656,7 +4662,7 @@ static int ui_do_but_TEX( /* pass */ } else { - if (!ui_but_extra_operator_icon_mouse_over_get(but, data, event)) { + if (!ui_but_extra_operator_icon_mouse_over_get(but, data->region, event)) { button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING); } return WM_UI_HANDLER_BREAK; @@ -4782,7 +4788,7 @@ static int ui_do_but_TREEROW(bContext *C, 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, event)) { + 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); @@ -7899,7 +7905,7 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * uiHandleButtonData *data = but->active; int retval = WM_UI_HANDLER_CONTINUE; - const bool is_disabled = but->flag & UI_BUT_DISABLED; + const bool is_disabled = but->flag & UI_BUT_DISABLED || data->disable_force; /* if but->pointype is set, but->poin should be too */ BLI_assert(!but->pointype || but->poin); @@ -8216,7 +8222,7 @@ static ARegion *ui_but_tooltip_init( if (but) { const wmWindow *win = CTX_wm_window(C); uiButExtraOpIcon *extra_icon = ui_but_extra_operator_icon_mouse_over_get( - but, but->active, win->eventstate); + but, but->active ? but->active->region : region, win->eventstate); return UI_tooltip_create_from_button_or_extra_icon(C, region, but, extra_icon, is_label); } @@ -8703,20 +8709,38 @@ static uiBut *ui_context_button_active(const ARegion *region, bool (*but_check_c uiBut *but_found = NULL; while (region) { - uiBut *activebut = NULL; + /* Follow this exact priority (from highest to lowest priority): + * 1) Active-override button (#UI_BUT_ACTIVE_OVERRIDE). + * 2) The real active button. + * 3) The previously active button (#UI_BUT_LAST_ACTIVE). + */ + uiBut *active_but_override = NULL; + uiBut *active_but_real = NULL; + uiBut *active_but_last = NULL; /* find active button */ LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + if (but->flag & UI_BUT_ACTIVE_OVERRIDE) { + active_but_override = but; + } if (but->active) { - activebut = but; + active_but_real = but; } - else if (!activebut && (but->flag & UI_BUT_LAST_ACTIVE)) { - activebut = but; + if (but->flag & UI_BUT_LAST_ACTIVE) { + active_but_last = but; } } } + uiBut *activebut = active_but_override; + if (!activebut) { + activebut = active_but_real; + } + if (!activebut) { + activebut = active_but_last; + } + if (activebut && (but_check_cb == NULL || but_check_cb(activebut))) { uiHandleButtonData *data = activebut->active; @@ -8947,7 +8971,12 @@ static uiBut *ui_but_find_open_event(ARegion *region, const wmEvent *event) static int ui_handle_button_over(bContext *C, const wmEvent *event, ARegion *region) { if (event->type == MOUSEMOVE) { - uiBut *but = ui_but_find_mouse_over(region, event); + const bool labeledit = event->modifier & KM_CTRL; + /* Allow buttons to be activated to show the tool-tip, + * then force-disable them if they're not considered interactive + * so they don't swallow events but can still display tips. */ + const bool for_tooltip = true; + uiBut *but = ui_but_find_mouse_over_ex(region, event->xy, labeledit, for_tooltip, NULL, NULL); if (but) { button_activate_init(C, region, but, BUTTON_ACTIVATE_OVER); @@ -8956,6 +8985,10 @@ static int ui_handle_button_over(bContext *C, const wmEvent *event, ARegion *reg * preferences. */ but->active->tooltip_force = true; } + + if (but->active && !ui_but_is_interactive(but, labeledit)) { + but->active->disable_force = true; + } } } else if (event->type == EVT_BUT_OPEN) { @@ -9435,7 +9468,7 @@ static bool ui_list_is_hovering_draggable_but(bContext *C, int mouse_xy[2]; WM_event_drag_start_xy(event, mouse_xy); - const uiBut *hovered_but = ui_but_find_mouse_over_ex(region, mouse_xy, false, NULL, NULL); + const uiBut *hovered_but = ui_but_find_mouse_over_ex(region, mouse_xy, false, false, NULL, NULL); if (list->dyn_data->custom_drag_optype) { if (ui_but_context_poll_operator(C, list->dyn_data->custom_drag_optype, hovered_but)) { diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index c09ff68bbca..1c79d3218fb 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -74,6 +74,12 @@ enum { UI_SELECT_DRAW = (1 << 5), /** Property search filter is active and the button does not match. */ UI_SEARCH_FILTER_NO_MATCH = (1 << 6), + + /** Temporarily override the active button for lookups in context, regions, etc. (everything + * using #ui_context_button_active()). For example, so that operators normally acting on the + * active button can be polled on non-active buttons to (e.g. for disabling). */ + UI_BUT_ACTIVE_OVERRIDE = (1 << 7), + /* WARNING: rest of #uiBut.flag in UI_interface.h */ }; @@ -1352,6 +1358,7 @@ bool ui_but_is_toggle(const uiBut *but) ATTR_WARN_UNUSED_RESULT; * \note ctrl is kind of a hack currently, * so that non-embossed UI_BTYPE_TEXT button behaves as a label when ctrl is not pressed. */ +bool ui_but_is_interactive_ex(const uiBut *but, const bool labeledit, const bool for_tooltip); bool ui_but_is_interactive(const uiBut *but, bool labeledit) ATTR_WARN_UNUSED_RESULT; bool ui_but_is_popover_once_compat(const uiBut *but) ATTR_WARN_UNUSED_RESULT; bool ui_but_has_array_value(const uiBut *but) ATTR_WARN_UNUSED_RESULT; @@ -1388,6 +1395,7 @@ typedef bool (*uiButFindPollFn)(const uiBut *but, const void *customdata); uiBut *ui_but_find_mouse_over_ex(const struct ARegion *region, const int xy[2], bool labeledit, + bool for_tooltip, const uiButFindPollFn find_poll, const void *find_custom_data) ATTR_NONNULL(1, 2) ATTR_WARN_UNUSED_RESULT; diff --git a/source/blender/editors/interface/interface_query.cc b/source/blender/editors/interface/interface_query.cc index 337b2852d57..ea1fe3923e4 100644 --- a/source/blender/editors/interface/interface_query.cc +++ b/source/blender/editors/interface/interface_query.cc @@ -58,12 +58,23 @@ bool ui_but_is_toggle(const uiBut *but) UI_BTYPE_TREEROW); } -bool ui_but_is_interactive(const uiBut *but, const bool labeledit) +bool ui_but_is_interactive_ex(const uiBut *but, const bool labeledit, const bool for_tooltip) { /* NOTE: #UI_BTYPE_LABEL is included for highlights, this allows drags. */ - if ((but->type == UI_BTYPE_LABEL) && but->dragpoin == nullptr && but->tip_func == nullptr) { - return false; + if (but->type == UI_BTYPE_LABEL) { + if (for_tooltip) { + /* It's important labels are considered interactive for the purpose of showing tooltip. */ + if (but->dragpoin == nullptr && but->tip_func == nullptr) { + return false; + } + } + else { + if (but->dragpoin == nullptr) { + return false; + } + } } + if (ELEM(but->type, UI_BTYPE_ROUNDBOX, UI_BTYPE_SEPR, UI_BTYPE_SEPR_LINE, UI_BTYPE_LISTBOX)) { return false; } @@ -84,6 +95,11 @@ bool ui_but_is_interactive(const uiBut *but, const bool labeledit) return true; } +bool ui_but_is_interactive(const uiBut *but, const bool labeledit) +{ + return ui_but_is_interactive_ex(but, labeledit, false); +} + bool UI_but_is_utf8(const uiBut *but) { if (but->rnaprop) { @@ -266,6 +282,7 @@ static uiBut *ui_but_find(const ARegion *region, uiBut *ui_but_find_mouse_over_ex(const ARegion *region, const int xy[2], const bool labeledit, + const bool for_tooltip, const uiButFindPollFn find_poll, const void *find_custom_data) { @@ -282,7 +299,7 @@ uiBut *ui_but_find_mouse_over_ex(const ARegion *region, if (find_poll && find_poll(but, find_custom_data) == false) { continue; } - if (ui_but_is_interactive(but, labeledit)) { + if (ui_but_is_interactive_ex(but, labeledit, for_tooltip)) { if (but->pie_dir != UI_RADIAL_NONE) { if (ui_but_isect_pie_seg(block, but)) { butover = but; @@ -310,7 +327,8 @@ uiBut *ui_but_find_mouse_over_ex(const ARegion *region, uiBut *ui_but_find_mouse_over(const ARegion *region, const wmEvent *event) { - return ui_but_find_mouse_over_ex(region, event->xy, event->modifier & KM_CTRL, nullptr, nullptr); + return ui_but_find_mouse_over_ex( + region, event->xy, event->modifier & KM_CTRL, false, nullptr, nullptr); } uiBut *ui_but_find_rect_over(const struct ARegion *region, const rcti *rect_px) @@ -414,7 +432,7 @@ static bool ui_but_is_listrow(const uiBut *but, const void *UNUSED(customdata)) uiBut *ui_list_row_find_mouse_over(const ARegion *region, const int xy[2]) { - return ui_but_find_mouse_over_ex(region, xy, false, ui_but_is_listrow, nullptr); + return ui_but_find_mouse_over_ex(region, xy, false, false, ui_but_is_listrow, nullptr); } struct ListRowFindIndexData { @@ -446,7 +464,7 @@ static bool ui_but_is_treerow(const uiBut *but, const void *UNUSED(customdata)) uiBut *ui_tree_row_find_mouse_over(const ARegion *region, const int xy[2]) { - return ui_but_find_mouse_over_ex(region, xy, false, ui_but_is_treerow, nullptr); + 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) diff --git a/source/blender/editors/io/CMakeLists.txt b/source/blender/editors/io/CMakeLists.txt index 418a399db28..ef093a01ff8 100644 --- a/source/blender/editors/io/CMakeLists.txt +++ b/source/blender/editors/io/CMakeLists.txt @@ -9,6 +9,7 @@ set(INC ../../depsgraph ../../io/alembic ../../io/collada + ../../io/common ../../io/gpencil ../../io/usd ../../io/wavefront_obj diff --git a/source/blender/editors/io/io_obj.c b/source/blender/editors/io/io_obj.c index 9156ff15ded..886586ff236 100644 --- a/source/blender/editors/io/io_obj.c +++ b/source/blender/editors/io/io_obj.c @@ -29,6 +29,7 @@ #include "DEG_depsgraph.h" +#include "IO_path_util_types.h" #include "IO_wavefront_obj.h" #include "io_obj.h" @@ -59,6 +60,15 @@ static const EnumPropertyItem io_obj_export_evaluation_mode[] = { "Export objects as they appear in the viewport"}, {0, NULL, 0, NULL, NULL}}; +static const EnumPropertyItem io_obj_path_mode[] = { + {PATH_REFERENCE_AUTO, "AUTO", 0, "Auto", "Use Relative paths with subdirectories only"}, + {PATH_REFERENCE_ABSOLUTE, "ABSOLUTE", 0, "Absolute", "Always write absolute paths"}, + {PATH_REFERENCE_RELATIVE, "RELATIVE", 0, "Relative", "Write relative paths where possible"}, + {PATH_REFERENCE_MATCH, "MATCH", 0, "Match", "Match Absolute/Relative setting with input path"}, + {PATH_REFERENCE_STRIP, "STRIP", 0, "Strip", "Write filename only"}, + {PATH_REFERENCE_COPY, "COPY", 0, "Copy", "Copy the file to the destination path"}, + {0, NULL, 0, NULL, NULL}}; + static int wm_obj_export_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { if (!RNA_struct_property_is_set(op->ptr, "filepath")) { @@ -87,6 +97,7 @@ static int wm_obj_export_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } struct OBJExportParams export_params; + export_params.file_base_for_tests[0] = '\0'; RNA_string_get(op->ptr, "filepath", export_params.filepath); export_params.blen_filepath = CTX_data_main(C)->filepath; export_params.export_animation = RNA_boolean_get(op->ptr, "export_animation"); @@ -103,6 +114,7 @@ static int wm_obj_export_exec(bContext *C, wmOperator *op) export_params.export_uv = RNA_boolean_get(op->ptr, "export_uv"); export_params.export_normals = RNA_boolean_get(op->ptr, "export_normals"); export_params.export_materials = RNA_boolean_get(op->ptr, "export_materials"); + export_params.path_mode = RNA_enum_get(op->ptr, "path_mode"); export_params.export_triangulated_mesh = RNA_boolean_get(op->ptr, "export_triangulated_mesh"); export_params.export_curves_as_nurbs = RNA_boolean_get(op->ptr, "export_curves_as_nurbs"); @@ -119,9 +131,9 @@ static int wm_obj_export_exec(bContext *C, wmOperator *op) static void ui_obj_export_settings(uiLayout *layout, PointerRNA *imfptr) { - const bool export_animation = RNA_boolean_get(imfptr, "export_animation"); const bool export_smooth_groups = RNA_boolean_get(imfptr, "export_smooth_groups"); + const bool export_materials = RNA_boolean_get(imfptr, "export_materials"); uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); @@ -150,6 +162,9 @@ static void ui_obj_export_settings(uiLayout *layout, PointerRNA *imfptr) uiItemR(sub, imfptr, "export_selected_objects", 0, IFACE_("Selected Only"), ICON_NONE); uiItemR(sub, imfptr, "apply_modifiers", 0, IFACE_("Apply Modifiers"), ICON_NONE); uiItemR(sub, imfptr, "export_eval_mode", 0, IFACE_("Properties"), ICON_NONE); + sub = uiLayoutColumn(sub, false); + uiLayoutSetEnabled(sub, export_materials); + uiItemR(sub, imfptr, "path_mode", 0, IFACE_("Path Mode"), ICON_NONE); /* Options for what to write. */ box = uiLayoutBox(layout); @@ -162,6 +177,7 @@ static void ui_obj_export_settings(uiLayout *layout, PointerRNA *imfptr) uiItemR(sub, imfptr, "export_triangulated_mesh", 0, IFACE_("Triangulated Mesh"), ICON_NONE); uiItemR(sub, imfptr, "export_curves_as_nurbs", 0, IFACE_("Curves as NURBS"), ICON_NONE); + /* Grouping options. */ box = uiLayoutBox(layout); uiItemL(box, IFACE_("Grouping"), ICON_GROUP); col = uiLayoutColumn(box, false); @@ -322,6 +338,12 @@ void WM_OT_obj_export(struct wmOperatorType *ot) "Export Materials", "Export MTL library. There must be a Principled-BSDF node for image textures to " "be exported to the MTL file"); + RNA_def_enum(ot->srna, + "path_mode", + io_obj_path_mode, + PATH_REFERENCE_AUTO, + "Path Mode", + "Method used to reference paths"); RNA_def_boolean(ot->srna, "export_triangulated_mesh", false, diff --git a/source/blender/editors/io/io_usd.c b/source/blender/editors/io/io_usd.c index e0616a0cec3..ca9c2de63a4 100644 --- a/source/blender/editors/io/io_usd.c +++ b/source/blender/editors/io/io_usd.c @@ -190,6 +190,20 @@ static void wm_usd_export_draw(bContext *UNUSED(C), wmOperator *op) uiItemR(box, ptr, "use_instancing", 0, NULL, ICON_NONE); } +static bool wm_usd_export_check(bContext *UNUSED(C), wmOperator *op) +{ + char filepath[FILE_MAX]; + RNA_string_get(op->ptr, "filepath", filepath); + + if (!BLI_path_extension_check_n(filepath, ".usd", ".usda", ".usdc", NULL)) { + BLI_path_extension_ensure(filepath, FILE_MAX, ".usdc"); + RNA_string_set(op->ptr, "filepath", filepath); + return true; + } + + return false; +} + void WM_OT_usd_export(struct wmOperatorType *ot) { ot->name = "Export USD"; @@ -200,6 +214,7 @@ void WM_OT_usd_export(struct wmOperatorType *ot) ot->exec = wm_usd_export_exec; ot->poll = WM_operator_winactive; ot->ui = wm_usd_export_draw; + ot->check = wm_usd_export_check; ot->flag = OPTYPE_REGISTER; /* No UNDO possible. */ diff --git a/source/blender/editors/mask/mask_select.c b/source/blender/editors/mask/mask_select.c index 0bd054e1b89..1c84cffbcad 100644 --- a/source/blender/editors/mask/mask_select.c +++ b/source/blender/editors/mask/mask_select.c @@ -402,6 +402,7 @@ void MASK_OT_select(wmOperatorType *ot) ot->exec = select_exec; ot->invoke = select_invoke; ot->poll = ED_maskedit_mask_poll; + ot->get_name = ED_select_pick_get_name; /* flags */ ot->flag = OPTYPE_UNDO; @@ -748,6 +749,7 @@ void MASK_OT_select_circle(wmOperatorType *ot) ot->modal = WM_gesture_circle_modal; ot->exec = circle_select_exec; ot->poll = ED_maskedit_mask_poll; + ot->get_name = ED_select_pick_get_name; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index ee40431c101..5d8cf176b87 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -190,6 +190,22 @@ typedef struct KnifeBVH { } KnifeBVH; +/** Additional per-object data. */ +typedef struct KnifeObjectInfo { + const float (*cagecos)[3]; + + /** + * Optionally allocate triangle indices, these are needed for non-interactive knife + * projection as multiple cuts are made without the BVH being updated. + * Using these indices the it's possible to access `cagecos` even if the face has been cut + * and the loops in `em->looptris` no longer refer to the original triangles, see: + */ + const int (*tri_indices)[3]; + + /** Only assigned for convenient access. */ + BMEditMesh *em; +} KnifeObjectInfo; + /* struct for properties used while drawing */ typedef struct KnifeTool_OpData { ARegion *region; /* Region that knifetool was activated in. */ @@ -203,6 +219,9 @@ typedef struct KnifeTool_OpData { Object **objects; uint objects_len; + /** Array `objects_len` length of additional per-object data. */ + KnifeObjectInfo *objects_info; + MemArena *arena; /* Reused for edge-net filling. */ @@ -220,7 +239,6 @@ typedef struct KnifeTool_OpData { GHash *facetrimap; KnifeBVH bvh; - const float (**cagecos)[3]; BLI_mempool *kverts; BLI_mempool *kedges; @@ -1134,6 +1152,52 @@ static void knife_update_header(bContext *C, wmOperator *op, KnifeTool_OpData *k /** \} */ /* -------------------------------------------------------------------- */ +/** \name Knife Object Info Accessors (#KnifeObjectInfo) + * \{ */ + +static const int *knife_bm_tri_index_get(const KnifeTool_OpData *kcd, + int base_index, + int tri_index, + int tri_index_buf[3]) +{ + const KnifeObjectInfo *obinfo = &kcd->objects_info[base_index]; + if (obinfo->tri_indices) { + return obinfo->tri_indices[tri_index]; + } + for (int i = 0; i < 3; i++) { + tri_index_buf[i] = BM_elem_index_get(obinfo->em->looptris[tri_index][i]->v); + } + return tri_index_buf; +} + +static void knife_bm_tri_cagecos_get(const KnifeTool_OpData *kcd, + int base_index, + int tri_index, + float cos[3][3]) +{ + const KnifeObjectInfo *obinfo = &kcd->objects_info[base_index]; + int tri_ind_buf[3]; + const int *tri_ind = knife_bm_tri_index_get(kcd, base_index, tri_index, tri_ind_buf); + for (int i = 0; i < 3; i++) { + copy_v3_v3(cos[i], obinfo->cagecos[tri_ind[i]]); + } +} + +static void knife_bm_tri_cagecos_get_worldspace(const KnifeTool_OpData *kcd, + int base_index, + int tri_index, + float cos[3][3]) +{ + knife_bm_tri_cagecos_get(kcd, base_index, tri_index, cos); + const Object *ob = kcd->objects[base_index]; + for (int i = 0; i < 3; i++) { + mul_m4_v3(ob->obmat, cos[i]); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Knife BVH Utils * \{ */ @@ -1219,16 +1283,7 @@ static void knife_bvh_init(KnifeTool_OpData *kcd) if (!test_fn_ret) { continue; } - - copy_v3_v3(cos[0], kcd->cagecos[b][BM_elem_index_get(looptris[i][0]->v)]); - copy_v3_v3(cos[1], kcd->cagecos[b][BM_elem_index_get(looptris[i][1]->v)]); - copy_v3_v3(cos[2], kcd->cagecos[b][BM_elem_index_get(looptris[i][2]->v)]); - - /* Convert to world-space. */ - mul_m4_v3(ob->obmat, cos[0]); - mul_m4_v3(ob->obmat, cos[1]); - mul_m4_v3(ob->obmat, cos[2]); - + knife_bm_tri_cagecos_get_worldspace(kcd, b, i, cos); BLI_bvhtree_insert(kcd->bvh.tree, i + tottri, (float *)cos, 3); } @@ -1282,12 +1337,7 @@ static void knife_bvh_raycast_cb(void *userdata, } } - copy_v3_v3(tri_cos[0], kcd->cagecos[b][BM_elem_index_get(ltri[0]->v)]); - copy_v3_v3(tri_cos[1], kcd->cagecos[b][BM_elem_index_get(ltri[1]->v)]); - copy_v3_v3(tri_cos[2], kcd->cagecos[b][BM_elem_index_get(ltri[2]->v)]); - mul_m4_v3(ob->obmat, tri_cos[0]); - mul_m4_v3(ob->obmat, tri_cos[1]); - mul_m4_v3(ob->obmat, tri_cos[2]); + knife_bm_tri_cagecos_get_worldspace(kcd, b, index, tri_cos); isect = (ray->radius > 0.0f ? @@ -1684,7 +1734,7 @@ static KnifeVert *get_bm_knife_vert(KnifeTool_OpData *kcd, BMVert *v, Object *ob BMFace *f; if (BM_elem_index_get(v) >= 0) { - cageco = kcd->cagecos[base_index][BM_elem_index_get(v)]; + cageco = kcd->objects_info[base_index].cagecos[BM_elem_index_get(v)]; } else { cageco = v->co; @@ -2545,12 +2595,8 @@ static bool knife_ray_intersect_face(KnifeTool_OpData *kcd, if (tri[0]->f != f) { break; } - copy_v3_v3(lv[0], kcd->cagecos[base_index][BM_elem_index_get(tri[0]->v)]); - copy_v3_v3(lv[1], kcd->cagecos[base_index][BM_elem_index_get(tri[1]->v)]); - copy_v3_v3(lv[2], kcd->cagecos[base_index][BM_elem_index_get(tri[2]->v)]); - mul_m4_v3(ob->obmat, lv[0]); - mul_m4_v3(ob->obmat, lv[1]); - mul_m4_v3(ob->obmat, lv[2]); + + knife_bm_tri_cagecos_get_worldspace(kcd, base_index, tri_i, lv); /* Using epsilon test in case ray is directly through an internal * tessellation edge and might not hit either tessellation tri with @@ -2605,9 +2651,10 @@ static void calc_ortho_extent(KnifeTool_OpData *kcd) ob = kcd->objects[b]; em = BKE_editmesh_from_object(ob); - if (kcd->cagecos[b]) { + const float(*cagecos)[3] = kcd->objects_info[b].cagecos; + if (cagecos) { for (int i = 0; i < em->bm->totvert; i++) { - copy_v3_v3(ws, kcd->cagecos[b][i]); + copy_v3_v3(ws, cagecos[i]); mul_m4_v3(ob->obmat, ws); minmax_v3v3_v3(min, max, ws); } @@ -3961,10 +4008,13 @@ static void knifetool_undo(KnifeTool_OpData *kcd) /** \} */ /* -------------------------------------------------------------------- */ -/** \name #KnifeTool_OpData (#op->customdata) Init and Free +/** \name #KnifeObjectInfo (#kcd->objects_info) Init and Free * \{ */ -static void knifetool_init_cagecos(KnifeTool_OpData *kcd, Object *ob, uint base_index) +static void knifetool_init_obinfo(KnifeTool_OpData *kcd, + Object *ob, + uint base_index, + bool use_tri_indices) { Scene *scene_eval = (Scene *)DEG_get_evaluated_id(kcd->vc.depsgraph, &kcd->scene->id); @@ -3973,18 +4023,36 @@ static void knifetool_init_cagecos(KnifeTool_OpData *kcd, Object *ob, uint base_ BM_mesh_elem_index_ensure(em_eval->bm, BM_VERT); - kcd->cagecos[base_index] = (const float(*)[3])BKE_editmesh_vert_coords_alloc( + KnifeObjectInfo *obinfo = &kcd->objects_info[base_index]; + obinfo->em = em_eval; + obinfo->cagecos = (const float(*)[3])BKE_editmesh_vert_coords_alloc( kcd->vc.depsgraph, em_eval, scene_eval, obedit_eval, NULL); + + if (use_tri_indices) { + BMLoop *(*looptris)[3] = em_eval->looptris; + int(*tri_indices)[3] = MEM_mallocN(sizeof(int[3]) * em_eval->tottri, __func__); + for (int i = 0; i < em_eval->tottri; i++) { + BMLoop **tri = looptris[i]; + tri_indices[i][0] = BM_elem_index_get(tri[0]->v); + tri_indices[i][1] = BM_elem_index_get(tri[1]->v); + tri_indices[i][2] = BM_elem_index_get(tri[2]->v); + } + obinfo->tri_indices = tri_indices; + } } -static void knifetool_free_cagecos(KnifeTool_OpData *kcd, uint base_index) +static void knifetool_free_obinfo(KnifeTool_OpData *kcd, uint base_index) { - if (kcd->cagecos[base_index]) { - MEM_freeN((void *)kcd->cagecos[base_index]); - kcd->cagecos[base_index] = NULL; - } + MEM_SAFE_FREE(kcd->objects_info[base_index].cagecos); + MEM_SAFE_FREE(kcd->objects_info[base_index].tri_indices); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #KnifeTool_OpData (#op->customdata) Init and Free + * \{ */ + static void knife_init_colors(KnifeColors *colors) { /* Possible BMESH_TODO: add explicit themes or calculate these by @@ -4018,6 +4086,10 @@ static void knifetool_init(bContext *C, const float angle_snapping_increment, const bool is_interactive) { + /* Needed so multiple non-interactive cuts (also called knife-project) + * doesn't access indices of loops that were created by cutting, see: T97153. */ + bool use_tri_indices = !is_interactive; + kcd->vc = *vc; Scene *scene = vc->scene; @@ -4031,11 +4103,11 @@ static void knifetool_init(bContext *C, Object *ob; BMEditMesh *em; - kcd->cagecos = MEM_callocN(sizeof(*kcd->cagecos) * kcd->objects_len, "knife cagecos"); + kcd->objects_info = MEM_callocN(sizeof(*kcd->objects_info) * kcd->objects_len, "knife cagecos"); for (uint b = 0; b < kcd->objects_len; b++) { ob = kcd->objects[b]; em = BKE_editmesh_from_object(ob); - knifetool_init_cagecos(kcd, ob, b); + knifetool_init_obinfo(kcd, ob, b, use_tri_indices); /* Can't usefully select resulting edges in face mode. */ kcd->select_result = (em->selectmode != SCE_SELECT_FACE); @@ -4139,9 +4211,9 @@ static void knifetool_exit_ex(KnifeTool_OpData *kcd) /* Knife BVH cleanup. */ for (int i = 0; i < kcd->objects_len; i++) { - knifetool_free_cagecos(kcd, i); + knifetool_free_obinfo(kcd, i); } - MEM_freeN((void *)kcd->cagecos); + MEM_freeN((void *)kcd->objects_info); knife_bvh_free(kcd); /* Line-hits cleanup. */ @@ -4391,7 +4463,8 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) ED_workspace_status_text(C, NULL); return OPERATOR_CANCELLED; - case KNF_MODAL_CONFIRM: + case KNF_MODAL_CONFIRM: { + const bool changed = (kcd->totkvert != 0); /* finish */ ED_region_tag_redraw(kcd->region); @@ -4400,11 +4473,11 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) ED_workspace_status_text(C, NULL); /* Cancel to prevent undo push for empty cuts. */ - if (kcd->totkvert == 0) { + if (!changed) { return OPERATOR_CANCELLED; } - return OPERATOR_FINISHED; + } case KNF_MODAL_UNDO: if (BLI_stack_is_empty(kcd->undostack)) { ED_region_tag_redraw(kcd->region); diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index 5ef8e573e27..76dcfbd8d36 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -1964,7 +1964,7 @@ static void single_objectdata_action_users( ID *id_act = (ID *)adt->action; if (single_data_needs_duplication(id_act)) { DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - BKE_animdata_duplicate_id_action(bmain, &ob->id, USER_DUP_ACT | USER_DUP_LINKED_ID); + BKE_animdata_duplicate_id_action(bmain, id_obdata, USER_DUP_ACT | USER_DUP_LINKED_ID); } } } diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c index d30aa4dfab1..fae2e6863fa 100644 --- a/source/blender/editors/sculpt_paint/paint_image_2d.c +++ b/source/blender/editors/sculpt_paint/paint_image_2d.c @@ -621,39 +621,38 @@ static void brush_painter_imbuf_partial_update(BrushPainter *painter, } static void brush_painter_2d_tex_mapping(ImagePaintState *s, - ImBuf *canvas, + ImagePaintTile *tile, const int diameter, - const float startpos[2], const float pos[2], const float mouse[2], int mapmode, rctf *mapping) { - float invw = 1.0f / (float)canvas->x; - float invh = 1.0f / (float)canvas->y; - int xmin, ymin, xmax, ymax; - int ipos[2]; + float invw = 1.0f / (float)tile->canvas->x; + float invh = 1.0f / (float)tile->canvas->y; + float start[2]; /* find start coordinate of brush in canvas */ - ipos[0] = (int)floorf((pos[0] - diameter / 2) + 1.0f); - ipos[1] = (int)floorf((pos[1] - diameter / 2) + 1.0f); + start[0] = pos[0] - diameter / 2.0f; + start[1] = pos[1] - diameter / 2.0f; if (mapmode == MTEX_MAP_MODE_STENCIL) { /* map from view coordinates of brush to region coordinates */ - UI_view2d_view_to_region(s->v2d, ipos[0] * invw, ipos[1] * invh, &xmin, &ymin); - UI_view2d_view_to_region( - s->v2d, (ipos[0] + diameter) * invw, (ipos[1] + diameter) * invh, &xmax, &ymax); + float xmin, ymin, xmax, ymax; + UI_view2d_view_to_region_fl(s->v2d, start[0] * invw, start[1] * invh, &xmin, &ymin); + UI_view2d_view_to_region_fl( + s->v2d, (start[0] + diameter) * invw, (start[1] + diameter) * invh, &xmax, &ymax); /* output mapping from brush ibuf x/y to region coordinates */ - mapping->xmin = xmin; - mapping->ymin = ymin; mapping->xmax = (xmax - xmin) / (float)diameter; mapping->ymax = (ymax - ymin) / (float)diameter; + mapping->xmin = xmin + (tile->uv_origin[0] * tile->size[0] * mapping->xmax); + mapping->ymin = ymin + (tile->uv_origin[1] * tile->size[1] * mapping->ymax); } else if (mapmode == MTEX_MAP_MODE_3D) { /* 3D mapping, just mapping to canvas 0..1. */ - mapping->xmin = 2.0f * (ipos[0] * invw - 0.5f); - mapping->ymin = 2.0f * (ipos[1] * invh - 0.5f); + mapping->xmin = 2.0f * (start[0] * invw - 0.5f); + mapping->ymin = 2.0f * (start[1] * invh - 0.5f); mapping->xmax = 2.0f * invw; mapping->ymax = 2.0f * invh; } @@ -665,8 +664,10 @@ static void brush_painter_2d_tex_mapping(ImagePaintState *s, mapping->ymax = 1.0f; } else /* if (mapmode == MTEX_MAP_MODE_TILED) */ { - mapping->xmin = (int)(-diameter * 0.5) + (int)floorf(pos[0]) - (int)floorf(startpos[0]); - mapping->ymin = (int)(-diameter * 0.5) + (int)floorf(pos[1]) - (int)floorf(startpos[1]); + mapping->xmin = (int)(-diameter * 0.5) + (int)floorf(pos[0]) - + (int)floorf(tile->start_paintpos[0]); + mapping->ymin = (int)(-diameter * 0.5) + (int)floorf(pos[1]) - + (int)floorf(tile->start_paintpos[1]); mapping->xmax = 1.0f; mapping->ymax = 1.0f; } @@ -711,14 +712,8 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s, do_partial_update = true; } - brush_painter_2d_tex_mapping(s, - tile->canvas, - diameter, - tile->start_paintpos, - pos, - mouse, - brush->mtex.brush_map_mode, - &painter->tex_mapping); + brush_painter_2d_tex_mapping( + s, tile, diameter, pos, mouse, brush->mtex.brush_map_mode, &painter->tex_mapping); } if (cache->is_maskbrush) { @@ -745,14 +740,8 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s, renew_maxmask) { MEM_SAFE_FREE(cache->tex_mask); - brush_painter_2d_tex_mapping(s, - tile->canvas, - diameter, - tile->start_paintpos, - pos, - mouse, - brush->mask_mtex.brush_map_mode, - &painter->mask_mapping); + brush_painter_2d_tex_mapping( + s, tile, diameter, pos, mouse, brush->mask_mtex.brush_map_mode, &painter->mask_mapping); if (do_partial_update_mask) { brush_painter_mask_imbuf_partial_update(painter, tile, pos, diameter); diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index 1303da71435..e442cd53639 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -83,6 +83,8 @@ #include "RNA_define.h" #include "RNA_enum_types.h" +#include "NOD_shader.h" + #include "IMB_colormanagement.h" //#include "bmesh_tools.h" @@ -6422,6 +6424,17 @@ static const EnumPropertyItem layer_type_items[] = { {0, NULL, 0, NULL, NULL}, }; +static Material *get_or_create_current_material(bContext *C, Object *ob) +{ + Material *ma = BKE_object_material_get(ob, ob->actcol); + if (!ma) { + Main *bmain = CTX_data_main(C); + ma = BKE_material_add(bmain, "Material"); + BKE_object_material_assign(bmain, ob, ma, ob->actcol, BKE_MAT_ASSIGN_USERPREF); + } + return ma; +} + static Image *proj_paint_image_create(wmOperator *op, Main *bmain, bool is_data) { Image *ima; @@ -6459,55 +6472,65 @@ static Image *proj_paint_image_create(wmOperator *op, Main *bmain, bool is_data) return ima; } -static void proj_paint_default_color(wmOperator *op, int type, Material *ma) +/** + * Get a default color for the paint slot layer from a material's Principled BSDF. + * + * \param layer_type: The layer type of the paint slot + * \param ma: The material to attempt using as the default color source. + * If this fails or \p ma is null, a default Principled BSDF is used instead. + */ +static void default_paint_slot_color_get(int layer_type, Material *ma, float color[4]) { - if (RNA_struct_property_is_set(op->ptr, "color")) { - return; - } - - bNode *in_node = ntreeFindType(ma->nodetree, SH_NODE_BSDF_PRINCIPLED); - if (in_node == NULL) { - return; - } - - float color[4]; - - if (type >= LAYER_BASE_COLOR && type < LAYER_NORMAL) { - /* Copy color from node, so result is unchanged after assigning textures. */ - bNodeSocket *in_sock = nodeFindSocket(in_node, SOCK_IN, layer_type_items[type].name); - - switch (in_sock->type) { - case SOCK_FLOAT: { - bNodeSocketValueFloat *socket_data = in_sock->default_value; - copy_v3_fl(color, socket_data->value); - color[3] = 1.0f; - break; + switch (layer_type) { + case LAYER_BASE_COLOR: + case LAYER_SPECULAR: + case LAYER_ROUGHNESS: + case LAYER_METALLIC: { + bNodeTree *ntree = NULL; + bNode *in_node = ma ? ntreeFindType(ma->nodetree, SH_NODE_BSDF_PRINCIPLED) : NULL; + if (!in_node) { + /* An existing material or Principled BSDF node could not be found. + * Copy default color values from a default Principled BSDF instead. */ + ntree = ntreeAddTree(NULL, "Temporary Shader Nodetree", ntreeType_Shader->idname); + in_node = nodeAddStaticNode(NULL, ntree, SH_NODE_BSDF_PRINCIPLED); } - case SOCK_VECTOR: - case SOCK_RGBA: { - bNodeSocketValueRGBA *socket_data = in_sock->default_value; - copy_v3_v3(color, socket_data->value); - color[3] = 1.0f; - break; + bNodeSocket *in_sock = nodeFindSocket(in_node, SOCK_IN, layer_type_items[layer_type].name); + switch (in_sock->type) { + case SOCK_FLOAT: { + bNodeSocketValueFloat *socket_data = in_sock->default_value; + copy_v3_fl(color, socket_data->value); + color[3] = 1.0f; + break; + } + case SOCK_VECTOR: + case SOCK_RGBA: { + bNodeSocketValueRGBA *socket_data = in_sock->default_value; + copy_v3_v3(color, socket_data->value); + color[3] = 1.0f; + break; + } + default: + BLI_assert_unreachable(); + rgba_float_args_set(color, 0.0f, 0.0f, 0.0f, 1.0f); + break; } - default: { - return; + /* Cleanup */ + if (ntree) { + ntreeFreeTree(ntree); + MEM_freeN(ntree); } + return; } + case LAYER_NORMAL: + /* Neutral tangent space normal map. */ + rgba_float_args_set(color, 0.5f, 0.5f, 1.0f, 1.0f); + break; + case LAYER_BUMP: + case LAYER_DISPLACEMENT: + /* Neutral displacement and bump map. */ + rgba_float_args_set(color, 0.5f, 0.5f, 0.5f, 1.0f); + break; } - else if (type == LAYER_NORMAL) { - /* Neutral tangent space normal map. */ - rgba_float_args_set(color, 0.5f, 0.5f, 1.0f, 1.0f); - } - else if (ELEM(type, LAYER_BUMP, LAYER_DISPLACEMENT)) { - /* Neutral displacement and bump map. */ - rgba_float_args_set(color, 0.5f, 0.5f, 0.5f, 1.0f); - } - else { - return; - } - - RNA_float_set_array(op->ptr, "color", color); } static bool proj_paint_add_slot(bContext *C, wmOperator *op) @@ -6521,7 +6544,7 @@ static bool proj_paint_add_slot(bContext *C, wmOperator *op) return false; } - ma = BKE_object_material_get(ob, ob->actcol); + ma = get_or_create_current_material(C, ob); if (ma) { Main *bmain = CTX_data_main(C); @@ -6632,25 +6655,8 @@ static int get_texture_layer_type(wmOperator *op, const char *prop_name) return type; } -static Material *get_or_create_current_material(bContext *C, Object *ob) -{ - Material *ma = BKE_object_material_get(ob, ob->actcol); - if (!ma) { - Main *bmain = CTX_data_main(C); - ma = BKE_material_add(bmain, "Material"); - BKE_object_material_assign(bmain, ob, ma, ob->actcol, BKE_MAT_ASSIGN_USERPREF); - } - return ma; -} - static int texture_paint_add_texture_paint_slot_exec(bContext *C, wmOperator *op) { - Object *ob = ED_object_active_context(C); - Material *ma = get_or_create_current_material(C, ob); - - int type = get_texture_layer_type(op, "type"); - proj_paint_default_color(op, type, ma); - if (proj_paint_add_slot(C, op)) { return OPERATOR_FINISHED; } @@ -6671,17 +6677,21 @@ static int texture_paint_add_texture_paint_slot_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { - /* Get material and default color to display in the popup. */ Object *ob = ED_object_active_context(C); - Material *ma = get_or_create_current_material(C, ob); + Material *ma = BKE_object_material_get(ob, ob->actcol); int type = get_texture_layer_type(op, "type"); - proj_paint_default_color(op, type, ma); + /* Set default name. */ char imagename[MAX_ID_NAME - 2]; get_default_texture_layer_name_for_object(ob, type, (char *)&imagename, sizeof(imagename)); RNA_string_set(op->ptr, "name", imagename); + /* Set default color. Copy the color from nodes, so it matches the existing material. */ + float color[4]; + default_paint_slot_color_get(type, ma, color); + RNA_float_set_array(op->ptr, "color", color); + return WM_operator_props_dialog_popup(C, op, 300); } diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index d2005473512..210cffcbcda 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -923,6 +923,8 @@ PaintStroke *paint_stroke_new(bContext *C, BKE_paint_set_overlay_override(br->overlay_flags); + ups->start_pixel_radius = BKE_brush_size_get(CTX_data_scene(C), br); + return stroke; } diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_color.c b/source/blender/editors/sculpt_paint/sculpt_filter_color.c index c0db587f69a..9bddc2ad855 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_color.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.c @@ -124,8 +124,8 @@ static void color_filter_task_cb(void *__restrict userdata, } case COLOR_FILTER_HUE: rgb_to_hsv_v(orig_color, hsv_color); - hue = hsv_color[0] + fade; - hsv_color[0] = fabs((hsv_color[0] + fade) - hue); + hue = hsv_color[0]; + hsv_color[0] = fmod((hsv_color[0] + fabs(fade)) - hue,1); hsv_to_rgb_v(hsv_color, final_color); break; case COLOR_FILTER_SATURATION: diff --git a/source/blender/editors/space_action/action_select.c b/source/blender/editors/space_action/action_select.c index 38ca9e16004..c0f7b7bd2b7 100644 --- a/source/blender/editors/space_action/action_select.c +++ b/source/blender/editors/space_action/action_select.c @@ -887,6 +887,7 @@ void ACTION_OT_select_circle(wmOperatorType *ot) ot->exec = action_circle_select_exec; ot->poll = ED_operator_action_active; ot->cancel = WM_gesture_circle_cancel; + ot->get_name = ED_select_pick_get_name; /* flags */ ot->flag = OPTYPE_UNDO; diff --git a/source/blender/editors/space_clip/tracking_select.c b/source/blender/editors/space_clip/tracking_select.c index 5f940b817a9..f267d948a53 100644 --- a/source/blender/editors/space_clip/tracking_select.c +++ b/source/blender/editors/space_clip/tracking_select.c @@ -835,6 +835,7 @@ void CLIP_OT_select_circle(wmOperatorType *ot) ot->modal = WM_gesture_circle_modal; ot->exec = circle_select_exec; ot->poll = ED_space_clip_tracking_poll; + ot->get_name = ED_select_pick_get_name; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; diff --git a/source/blender/editors/space_graph/graph_select.c b/source/blender/editors/space_graph/graph_select.c index 39b980ac4c3..b8295150478 100644 --- a/source/blender/editors/space_graph/graph_select.c +++ b/source/blender/editors/space_graph/graph_select.c @@ -1080,6 +1080,7 @@ void GRAPH_OT_select_circle(wmOperatorType *ot) ot->exec = graph_circle_select_exec; ot->poll = graphop_visible_keyframes_poll; ot->cancel = WM_gesture_circle_cancel; + ot->get_name = ED_select_pick_get_name; /* flags */ ot->flag = OPTYPE_UNDO; diff --git a/source/blender/editors/space_node/node_select.cc b/source/blender/editors/space_node/node_select.cc index 1d0097068f1..db523651534 100644 --- a/source/blender/editors/space_node/node_select.cc +++ b/source/blender/editors/space_node/node_select.cc @@ -193,11 +193,6 @@ static bool is_event_over_node_or_socket(bContext *C, const wmEvent *event) return is_position_over_node_or_socket(*snode, mouse); } -static void node_toggle(bNode *node) -{ - nodeSetSelected(node, !(node->flag & SELECT)); -} - void node_socket_select(bNode *node, bNodeSocket &sock) { sock.flag |= SELECT; @@ -510,10 +505,10 @@ void node_select_single(bContext &C, bNode &node) WM_event_add_notifier(&C, NC_NODE | NA_SELECTED, nullptr); } -static int node_mouse_select(bContext *C, - wmOperator *op, - const int mval[2], - bool wait_to_deselect_others) +static bool node_mouse_select(bContext *C, + wmOperator *op, + const int mval[2], + struct SelectPick_Params *params) { Main &bmain = *CTX_data_main(C); SpaceNode &snode = *CTX_wm_space_node(C); @@ -525,36 +520,38 @@ static int node_mouse_select(bContext *C, bNodeSocket *sock = nullptr; bNodeSocket *tsock; float cursor[2]; - int ret_value = OPERATOR_CANCELLED; - const bool extend = RNA_boolean_get(op->ptr, "extend"); /* always do socket_select when extending selection. */ - const bool socket_select = extend || RNA_boolean_get(op->ptr, "socket_select"); - const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all"); - - /* These cases are never modal. */ - if (extend || socket_select) { - wait_to_deselect_others = false; - } + const bool socket_select = (params->sel_op == SEL_OP_XOR) || + RNA_boolean_get(op->ptr, "socket_select"); + bool changed = false; + bool found = false; + bool node_was_selected = false; /* get mouse coordinates in view2d space */ UI_view2d_region_to_view(®ion.v2d, mval[0], mval[1], &cursor[0], &cursor[1]); /* first do socket selection, these generally overlap with nodes. */ if (socket_select) { + /* NOTE: unlike nodes #SelectPick_Params isn't fully supported. */ + const bool extend = (params->sel_op == SEL_OP_XOR); if (node_find_indicated_socket(snode, &node, &sock, cursor, SOCK_IN)) { + found = true; + node_was_selected = node->flag & SELECT; + /* NOTE: SOCK_IN does not take into account the extend case... * This feature is not really used anyway currently? */ node_socket_toggle(node, *sock, true); - ret_value = OPERATOR_FINISHED; + changed = true; } else if (node_find_indicated_socket(snode, &node, &sock, cursor, SOCK_OUT)) { + found = true; + node_was_selected = node->flag & SELECT; + if (sock->flag & SELECT) { if (extend) { node_socket_deselect(node, *sock, true); - } - else { - ret_value = OPERATOR_FINISHED; + changed = true; } } else { @@ -566,6 +563,7 @@ static int node_mouse_select(bContext *C, continue; } node_socket_deselect(node, *tsock, true); + changed = true; } } if (!extend) { @@ -575,69 +573,71 @@ static int node_mouse_select(bContext *C, } for (tsock = (bNodeSocket *)tnode->outputs.first; tsock; tsock = tsock->next) { node_socket_deselect(tnode, *tsock, true); + changed = true; } } } node_socket_select(node, *sock); - ret_value = OPERATOR_FINISHED; + changed = true; } } } if (!sock) { + /* find the closest visible node */ node = node_under_mouse_select(*snode.edittree, (int)cursor[0], (int)cursor[1]); + found = (node != nullptr); + node_was_selected = node && (node->flag & SELECT); - if (extend) { - if (node != nullptr) { - /* If node is selected but not active, we want to make it active, - * but not toggle (deselect) it. */ - if (!((node->flag & SELECT) && (node->flag & NODE_ACTIVE) == 0)) { - node_toggle(node); - } - ret_value = OPERATOR_FINISHED; + if (params->sel_op == SEL_OP_SET) { + if ((found && params->select_passthrough) && (node->flag & SELECT)) { + found = false; } - } - else if (deselect_all && node == nullptr) { - /* Rather than deselecting others, users may want to drag to box-select (drag from empty - * space) or tweak-translate an already selected item. If these cases may apply, delay - * deselection. */ - if (wait_to_deselect_others) { - ret_value = OPERATOR_RUNNING_MODAL; - } - else { - /* Deselect in empty space. */ + else if (found || params->deselect_all) { + /* Deselect everything. */ for (tnode = (bNode *)snode.edittree->nodes.first; tnode; tnode = tnode->next) { nodeSetSelected(tnode, false); } - ret_value = OPERATOR_FINISHED; + changed = true; } } - else if (node != nullptr) { - /* When clicking on an already selected node, we want to wait to deselect - * others and allow the user to start moving the node without that. */ - if (wait_to_deselect_others && (node->flag & SELECT)) { - ret_value = OPERATOR_RUNNING_MODAL; - } - else { - nodeSetSelected(node, true); - for (tnode = (bNode *)snode.edittree->nodes.first; tnode; tnode = tnode->next) { - if (tnode != node) { - nodeSetSelected(tnode, false); - } + if (found) { + switch (params->sel_op) { + case SEL_OP_ADD: { + nodeSetSelected(node, true); + break; + } + case SEL_OP_SUB: { + nodeSetSelected(node, false); + break; + } + case SEL_OP_XOR: { + /* Check active so clicking on an inactive node activates it. */ + bool is_selected = (node->flag & NODE_SELECT) && (node->flag & NODE_ACTIVE); + nodeSetSelected(node, !is_selected); + break; + } + case SEL_OP_SET: { + nodeSetSelected(node, true); + break; + } + case SEL_OP_AND: { + BLI_assert_unreachable(); /* Doesn't make sense for picking. */ + break; } - - ret_value = OPERATOR_FINISHED; } + + changed = true; } } /* update node order */ - if (ret_value != OPERATOR_CANCELLED) { + if (changed || found) { bool active_texture_changed = false; bool viewer_node_changed = false; - if (node != nullptr && ret_value != OPERATOR_RUNNING_MODAL) { + if ((node != nullptr) && (node_was_selected == false || params->select_passthrough == false)) { viewer_node_changed = (node->flag & NODE_DO_OUTPUT) == 0 && node->type == GEO_NODE_VIEWER; ED_node_set_active(&bmain, &snode, snode.edittree, node, &active_texture_changed); } @@ -654,23 +654,35 @@ static int node_mouse_select(bContext *C, WM_event_add_notifier(C, NC_NODE | NA_SELECTED, nullptr); } - return ret_value; + return changed || found; } static int node_select_exec(bContext *C, wmOperator *op) { - const bool wait_to_deselect_others = RNA_boolean_get(op->ptr, "wait_to_deselect_others"); - /* get settings from RNA properties for operator */ int mval[2]; - mval[0] = RNA_int_get(op->ptr, "mouse_x"); - mval[1] = RNA_int_get(op->ptr, "mouse_y"); + RNA_int_get_array(op->ptr, "location", mval); + + struct SelectPick_Params params = {}; + ED_select_pick_params_from_operator(op->ptr, ¶ms); /* perform the select */ - const int ret_value = node_mouse_select(C, op, mval, wait_to_deselect_others); + const bool changed = node_mouse_select(C, op, mval, ¶ms); - /* allow tweak event to work too */ - return ret_value | OPERATOR_PASS_THROUGH; + if (changed) { + return OPERATOR_PASS_THROUGH | OPERATOR_FINISHED; + } + /* Nothing selected, just passthrough. */ + return OPERATOR_PASS_THROUGH | OPERATOR_CANCELLED; +} + +static int node_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + RNA_int_set_array(op->ptr, "location", event->mval); + + const int retval = node_select_exec(C, op); + + return WM_operator_flag_only_pass_through_on_press(retval, event); } void NODE_OT_select(wmOperatorType *ot) @@ -684,24 +696,29 @@ void NODE_OT_select(wmOperatorType *ot) /* api callbacks */ ot->exec = node_select_exec; - ot->invoke = WM_generic_select_invoke; - ot->modal = WM_generic_select_modal; + ot->invoke = node_select_invoke; ot->poll = ED_operator_node_active; + ot->get_name = ED_select_pick_get_name; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ - WM_operator_properties_generic_select(ot); - prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", ""); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); + WM_operator_properties_mouse_select(ot); + + prop = RNA_def_int_vector(ot->srna, + "location", + 2, + NULL, + INT_MIN, + INT_MAX, + "Location", + "Mouse location", + INT_MIN, + INT_MAX); + RNA_def_property_flag(prop, PROP_HIDDEN); + RNA_def_boolean(ot->srna, "socket_select", false, "Socket Select", ""); - prop = RNA_def_boolean(ot->srna, - "deselect_all", - false, - "Deselect On Nothing", - "Deselect all when nothing under the cursor"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); } /** \} */ @@ -871,8 +888,8 @@ void NODE_OT_select_circle(wmOperatorType *ot) ot->invoke = WM_gesture_circle_invoke; ot->exec = node_circleselect_exec; ot->modal = WM_gesture_circle_modal; - ot->poll = ED_operator_node_active; + ot->get_name = ED_select_circle_get_name; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; diff --git a/source/blender/editors/space_outliner/outliner_draw.cc b/source/blender/editors/space_outliner/outliner_draw.cc index d898be4eb2c..36e21cf51a5 100644 --- a/source/blender/editors/space_outliner/outliner_draw.cc +++ b/source/blender/editors/space_outliner/outliner_draw.cc @@ -1808,6 +1808,25 @@ static void outliner_draw_overrides_rna_buts(uiBlock *block, TreeElementOverridesProperty &override_elem = *tree_element_cast<TreeElementOverridesProperty>( te); + if (!override_elem.is_rna_path_valid) { + uiBut *but = uiDefBut(block, + UI_BTYPE_LABEL, + 0, + override_elem.rna_path.c_str(), + x + pad_x, + te->ys + pad_y, + item_max_width, + item_height, + NULL, + 0.0f, + 0.0f, + 0.0f, + 0.0f, + ""); + UI_but_flag_enable(but, UI_BUT_REDALERT); + continue; + } + PointerRNA *ptr = &override_elem.override_rna_ptr; PropertyRNA *prop = &override_elem.override_rna_prop; const PropertyType prop_type = RNA_property_type(prop); @@ -1935,8 +1954,9 @@ static bool outliner_draw_overrides_warning_buts(uiBlock *block, break; } case TSE_LIBRARY_OVERRIDE: { - const bool is_rna_path_valid = (bool)(POINTER_AS_UINT(te->directdata)); - if (!is_rna_path_valid) { + TreeElementOverridesProperty &te_override_prop = + *tree_element_cast<TreeElementOverridesProperty>(te); + if (!te_override_prop.is_rna_path_valid) { item_has_warnings = true; if (do_draw) { tip = TIP_( diff --git a/source/blender/editors/space_outliner/tree/tree_element_overrides.cc b/source/blender/editors/space_outliner/tree/tree_element_overrides.cc index 857f5577e59..3a039da86c2 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_overrides.cc +++ b/source/blender/editors/space_outliner/tree/tree_element_overrides.cc @@ -84,14 +84,13 @@ TreeElementOverridesProperty::TreeElementOverridesProperty(TreeElement &legacy_t TreeElementOverridesData &override_data) : AbstractTreeElement(legacy_te), override_rna_ptr(override_data.override_rna_ptr), - override_rna_prop(override_data.override_rna_prop) + override_rna_prop(override_data.override_rna_prop), + rna_path(override_data.override_property.rna_path), + is_rna_path_valid(override_data.is_rna_path_valid) { BLI_assert(legacy_te.store_elem->type == TSE_LIBRARY_OVERRIDE); legacy_te.name = override_data.override_property.rna_path; - /* Abusing this for now, better way to do it is also pending current refactor of the whole tree - * code to use C++. */ - legacy_te.directdata = POINTER_FROM_UINT(override_data.is_rna_path_valid); } } // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/tree/tree_element_overrides.hh b/source/blender/editors/space_outliner/tree/tree_element_overrides.hh index a2d1409f193..b42e1c37a0f 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_overrides.hh +++ b/source/blender/editors/space_outliner/tree/tree_element_overrides.hh @@ -8,6 +8,8 @@ #include "RNA_types.h" +#include "BLI_string_ref.hh" + #include "tree_element.hh" struct ID; @@ -39,6 +41,9 @@ class TreeElementOverridesProperty final : public AbstractTreeElement { PointerRNA override_rna_ptr; PropertyRNA &override_rna_prop; + StringRefNull rna_path; + bool is_rna_path_valid; + public: TreeElementOverridesProperty(TreeElement &legacy_te, TreeElementOverridesData &override_data); }; diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c index e162968fa3b..939fcfeb7d9 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.c +++ b/source/blender/editors/space_sequencer/sequencer_add.c @@ -136,7 +136,7 @@ static void sequencer_generic_props__internal(wmOperatorType *ot, int flag) ot->srna, "overlap_shuffle_override", false, - "Override Overlap Shuffle Behaviour", + "Override Overlap Shuffle Behavior", "Use the overlap_mode tool settings to determine how to shuffle overlapping strips"); RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c index 4651d5b31a9..4e4e570d331 100644 --- a/source/blender/editors/space_sequencer/sequencer_select.c +++ b/source/blender/editors/space_sequencer/sequencer_select.c @@ -999,6 +999,7 @@ void SEQUENCER_OT_select(wmOperatorType *ot) ot->invoke = sequencer_select_invoke; ot->modal = WM_generic_select_modal; ot->poll = ED_operator_sequencer_active; + ot->get_name = ED_select_pick_get_name; /* Flags. */ ot->flag = OPTYPE_UNDO; diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index ffd33006cc2..d99063a9b26 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -1646,7 +1646,7 @@ static int bone_select_menu_exec(bContext *C, wmOperator *op) const int name_index = RNA_enum_get(op->ptr, "name"); const struct SelectPick_Params params = { - .sel_op = ED_select_op_from_operator(op), + .sel_op = ED_select_op_from_operator(op->ptr), }; View3D *v3d = CTX_wm_view3d(C); @@ -2875,7 +2875,7 @@ static int view3d_select_exec(bContext *C, wmOperator *op) Object *obact = CTX_data_active_object(C); struct SelectPick_Params params = {0}; - ED_select_pick_params_from_operator(op, ¶ms); + ED_select_pick_params_from_operator(op->ptr, ¶ms); bool center = RNA_boolean_get(op->ptr, "center"); bool enumerate = RNA_boolean_get(op->ptr, "enumerate"); @@ -2987,6 +2987,7 @@ void VIEW3D_OT_select(wmOperatorType *ot) ot->invoke = view3d_select_invoke; ot->exec = view3d_select_exec; ot->poll = ED_operator_view3d_active; + ot->get_name = ED_select_pick_get_name; /* flags */ ot->flag = OPTYPE_UNDO; @@ -4739,6 +4740,7 @@ void VIEW3D_OT_select_circle(wmOperatorType *ot) ot->exec = view3d_circle_select_exec; ot->poll = view3d_selectable_data; ot->cancel = view3d_circle_select_cancel; + ot->get_name = ED_select_circle_get_name; /* flags */ ot->flag = OPTYPE_UNDO; diff --git a/source/blender/editors/transform/transform_convert_tracking.c b/source/blender/editors/transform/transform_convert_tracking.c index 45f17512c09..bf9f929dd65 100644 --- a/source/blender/editors/transform/transform_convert_tracking.c +++ b/source/blender/editors/transform/transform_convert_tracking.c @@ -621,7 +621,7 @@ static void flushTransTracking(TransInfo *t) TransData *td; TransData2D *td2d; TransDataTracking *tdt; - int a; + int td_index; if (t->state == TRANS_CANCEL) { cancelTransTracking(t); @@ -630,8 +630,9 @@ static void flushTransTracking(TransInfo *t) TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); /* flush to 2d vector from internally used 3d vector */ - for (a = 0, td = tc->data, td2d = tc->data_2d, tdt = tc->custom.type.data; a < tc->data_len; - a++, td2d++, td++, tdt++) { + for (td_index = 0, td = tc->data, td2d = tc->data_2d, tdt = tc->custom.type.data; + td_index < tc->data_len; + td_index++, td2d++, td++, tdt++) { if (tdt->mode == transDataTracking_ModeTracks) { float loc2d[2]; @@ -655,7 +656,7 @@ static void flushTransTracking(TransInfo *t) if (!tdt->smarkers) { tdt->smarkers = MEM_callocN(sizeof(*tdt->smarkers) * tdt->markersnr, "flushTransTracking markers"); - for (a = 0; a < tdt->markersnr; a++) { + for (int a = 0; a < tdt->markersnr; a++) { copy_v2_v2(tdt->smarkers[a], tdt->markers[a].pos); } } @@ -665,7 +666,7 @@ static void flushTransTracking(TransInfo *t) sub_v2_v2v2(d2, loc2d, tdt->srelative); - for (a = 0; a < tdt->markersnr; a++) { + for (int a = 0; a < tdt->markersnr; a++) { add_v2_v2v2(tdt->markers[a].pos, tdt->smarkers[a], d2); } diff --git a/source/blender/editors/transform/transform_snap_object.cc b/source/blender/editors/transform/transform_snap_object.cc index fade7f47d9c..0505772c668 100644 --- a/source/blender/editors/transform/transform_snap_object.cc +++ b/source/blender/editors/transform/transform_snap_object.cc @@ -168,8 +168,13 @@ struct SnapObjectContext { /** \name Utilities * \{ */ -/* Mesh used for snapping. - * If nullptr the BMesh should be used. */ +/** + * Mesh used for snapping. + * + * - When the return value is null the `BKE_editmesh_from_object(ob_eval)` should be used. + * - In rare cases there is no evaluated mesh available and a null result doesn't imply an + * edit-mesh, so callers need to account for a null edit-mesh too, see: T96536. + */ static const Mesh *mesh_for_snap(Object *ob_eval, eSnapEditType edit_mode_type, bool *r_use_hide) { const Mesh *me_eval = BKE_object_get_evaluated_mesh(ob_eval); @@ -998,6 +1003,9 @@ static void raycast_obj_fn(SnapObjectContext *sctx, const Mesh *me_eval = mesh_for_snap(ob_eval, edit_mode_type, &use_hide); if (me_eval == nullptr) { BMEditMesh *em = BKE_editmesh_from_object(ob_eval); + if (UNLIKELY(!em)) { /* See #mesh_for_snap doc-string. */ + return; + } BLI_assert_msg(em == BKE_editmesh_from_object(DEG_get_original_object(ob_eval)), "Make sure there is only one pointer for looptris"); retval = raycastEditMesh(sctx, @@ -2696,6 +2704,9 @@ static void snap_obj_fn(SnapObjectContext *sctx, const Mesh *me_eval = mesh_for_snap(ob_eval, edit_mode_type, &use_hide); if (me_eval == nullptr) { BMEditMesh *em = BKE_editmesh_from_object(ob_eval); + if (UNLIKELY(!em)) { /* See #mesh_for_snap doc-string. */ + return; + } BLI_assert_msg(em == BKE_editmesh_from_object(DEG_get_original_object(ob_eval)), "Make sure there is only one pointer for looptris"); retval = snapEditMesh( diff --git a/source/blender/editors/util/select_utils.c b/source/blender/editors/util/select_utils.c index 53f9aca8e8d..263ef06718f 100644 --- a/source/blender/editors/util/select_utils.c +++ b/source/blender/editors/util/select_utils.c @@ -119,11 +119,11 @@ bool ED_select_similar_compare_float_tree(const KDTree_1d *tree, return false; } -eSelectOp ED_select_op_from_operator(wmOperator *op) +eSelectOp ED_select_op_from_operator(PointerRNA *ptr) { - const bool extend = RNA_boolean_get(op->ptr, "extend"); - const bool deselect = RNA_boolean_get(op->ptr, "deselect"); - const bool toggle = RNA_boolean_get(op->ptr, "toggle"); + const bool extend = RNA_boolean_get(ptr, "extend"); + const bool deselect = RNA_boolean_get(ptr, "deselect"); + const bool toggle = RNA_boolean_get(ptr, "toggle"); if (extend) { return SEL_OP_ADD; @@ -137,10 +137,56 @@ eSelectOp ED_select_op_from_operator(wmOperator *op) return SEL_OP_SET; } -void ED_select_pick_params_from_operator(wmOperator *op, struct SelectPick_Params *params) +void ED_select_pick_params_from_operator(PointerRNA *ptr, struct SelectPick_Params *params) { memset(params, 0x0, sizeof(*params)); - params->sel_op = ED_select_op_from_operator(op); - params->deselect_all = RNA_boolean_get(op->ptr, "deselect_all"); - params->select_passthrough = RNA_boolean_get(op->ptr, "select_passthrough"); + params->sel_op = ED_select_op_from_operator(ptr); + params->deselect_all = RNA_boolean_get(ptr, "deselect_all"); + params->select_passthrough = RNA_boolean_get(ptr, "select_passthrough"); } + +/* -------------------------------------------------------------------- */ +/** \name Operator Naming Callbacks + * \{ */ + +const char *ED_select_pick_get_name(wmOperatorType *UNUSED(ot), PointerRNA *ptr) +{ + struct SelectPick_Params params = {0}; + ED_select_pick_params_from_operator(ptr, ¶ms); + switch (params.sel_op) { + case SEL_OP_ADD: + return "Select (Extend)"; + case SEL_OP_SUB: + return "Select (Deselect)"; + case SEL_OP_XOR: + return "Select (Toggle)"; + case SEL_OP_AND: + BLI_assert_unreachable(); + ATTR_FALLTHROUGH; + case SEL_OP_SET: + break; + } + return "Select"; +} + +const char *ED_select_circle_get_name(wmOperatorType *UNUSED(ot), PointerRNA *ptr) +{ + /* Matches options in #WM_operator_properties_select_operation_simple */ + const eSelectOp sel_op = RNA_enum_get(ptr, "mode"); + switch (sel_op) { + case SEL_OP_ADD: + return "Circle Select (Extend)"; + case SEL_OP_SUB: + return "Circle Select (Deselect)"; + case SEL_OP_XOR: + ATTR_FALLTHROUGH; + case SEL_OP_AND: + BLI_assert_unreachable(); + ATTR_FALLTHROUGH; + case SEL_OP_SET: + break; + } + return "Circle Select"; +} + +/** \} */ diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c index 1287804d9ee..2c1cdb1d93e 100644 --- a/source/blender/editors/uvedit/uvedit_select.c +++ b/source/blender/editors/uvedit/uvedit_select.c @@ -2624,7 +2624,7 @@ static int uv_select_exec(bContext *C, wmOperator *op) RNA_float_get_array(op->ptr, "location", co); struct SelectPick_Params params = {0}; - ED_select_pick_params_from_operator(op, ¶ms); + ED_select_pick_params_from_operator(op->ptr, ¶ms); const bool changed = uv_mouse_select(C, co, ¶ms); @@ -2659,6 +2659,7 @@ void UV_OT_select(wmOperatorType *ot) ot->exec = uv_select_exec; ot->invoke = uv_select_invoke; ot->poll = ED_operator_uvedit; /* requires space image */ + ot->get_name = ED_select_pick_get_name; /* properties */ PropertyRNA *prop; @@ -3828,6 +3829,7 @@ void UV_OT_select_circle(wmOperatorType *ot) ot->exec = uv_circle_select_exec; ot->poll = ED_operator_uvedit_space_image; /* requires space image */ ot->cancel = WM_gesture_circle_cancel; + ot->get_name = ED_select_circle_get_name; /* flags */ ot->flag = OPTYPE_UNDO; diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c index 34fae2ffb2a..c0ea753ed51 100644 --- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c +++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c @@ -267,26 +267,35 @@ static bool uvedit_have_selection_multi(const Scene *scene, return have_select; } +void ED_uvedit_get_aspect_from_material(Object *ob, + const int material_index, + float *r_aspx, + float *r_aspy) +{ + if (UNLIKELY(material_index < 0 || material_index >= ob->totcol)) { + *r_aspx = 1.0f; + *r_aspy = 1.0f; + return; + } + Image *ima; + ED_object_get_active_image(ob, material_index + 1, &ima, NULL, NULL, NULL); + ED_image_get_uv_aspect(ima, NULL, r_aspx, r_aspy); +} + void ED_uvedit_get_aspect(Object *ob, float *r_aspx, float *r_aspy) { BMEditMesh *em = BKE_editmesh_from_object(ob); BLI_assert(em != NULL); bool sloppy = true; bool selected = false; - BMFace *efa; - Image *ima; - - efa = BM_mesh_active_face_get(em->bm, sloppy, selected); - - if (efa) { - ED_object_get_active_image(ob, efa->mat_nr + 1, &ima, NULL, NULL, NULL); - - ED_image_get_uv_aspect(ima, NULL, r_aspx, r_aspy); - } - else { + BMFace *efa = BM_mesh_active_face_get(em->bm, sloppy, selected); + if (!efa) { *r_aspx = 1.0f; *r_aspy = 1.0f; + return; } + + ED_uvedit_get_aspect_from_material(ob, efa->mat_nr, r_aspx, r_aspy); } static void construct_param_handle_face_add(ParamHandle *handle, @@ -1527,49 +1536,88 @@ static void uv_transform_properties(wmOperatorType *ot, int radius) } } -static void correct_uv_aspect(Object *ob, BMEditMesh *em) +static void shrink_loop_uv_by_aspect_ratio(BMFace *efa, + const int cd_loop_uv_offset, + const float aspect_y) { + BLI_assert(aspect_y != 1.0f); /* Nothing to do, should be handled by caller. */ + BLI_assert(aspect_y > 0.0f); /* Negative aspect ratios are not supported. */ + BMLoop *l; - BMIter iter, liter; - MLoopUV *luv; - BMFace *efa; - float scale, aspx, aspy; + BMIter iter; + BM_ITER_ELEM (l, &iter, efa, BM_LOOPS_OF_FACE) { + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + if (aspect_y > 1.0f) { + /* Reduce round-off error, i.e. `u = (u - 0.5) / aspect_y + 0.5`. */ + luv->uv[0] = luv->uv[0] / aspect_y + (0.5f - 0.5f / aspect_y); + } + else { + /* Reduce round-off error, i.e. `v = (v - 0.5) * aspect_y + 0.5`. */ + luv->uv[1] = luv->uv[1] * aspect_y + (0.5f - 0.5f * aspect_y); + } + } +} +static void correct_uv_aspect(Object *ob, BMEditMesh *em) +{ const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); - + float aspx, aspy; ED_uvedit_get_aspect(ob, &aspx, &aspy); + const float aspect_y = aspx / aspy; + if (aspect_y == 1.0f) { + /* Scaling by 1.0 has no effect. */ + return; + } + BMFace *efa; + BMIter iter; + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + if (BM_elem_flag_test(efa, BM_ELEM_SELECT)) { + shrink_loop_uv_by_aspect_ratio(efa, cd_loop_uv_offset, aspect_y); + } + } +} - if (aspx == aspy) { +static void correct_uv_aspect_per_face(Object *ob, BMEditMesh *em) +{ + const int materials_num = ob->totcol; + if (materials_num == 0) { + /* Without any materials, there is no aspect_y information and nothing to do. */ return; } - if (aspx > aspy) { - scale = aspy / aspx; + float *material_aspect_y = BLI_array_alloca(material_aspect_y, materials_num); + /* Lazily initialize aspect ratio for materials. */ + copy_vn_fl(material_aspect_y, materials_num, -1.0f); - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) { - continue; - } + const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - luv->uv[0] = ((luv->uv[0] - 0.5f) * scale) + 0.5f; - } + BMFace *efa; + BMIter iter; + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) { + continue; } - } - else { - scale = aspx / aspy; - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) { - continue; - } + const int material_index = efa->mat_nr; + if (UNLIKELY(material_index < 0 || material_index >= materials_num)) { + /* The index might be for a material slot which is not currently setup. */ + continue; + } - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - luv->uv[1] = ((luv->uv[1] - 0.5f) * scale) + 0.5f; - } + float aspect_y = material_aspect_y[material_index]; + if (aspect_y == -1.0f) { + /* Lazily initialize aspect ratio for materials. */ + float aspx, aspy; + ED_uvedit_get_aspect_from_material(ob, material_index, &aspx, &aspy); + aspect_y = aspx / aspy; + material_aspect_y[material_index] = aspect_y; } + + if (aspect_y == 1.0f) { + /* Scaling by 1.0 has no effect. */ + continue; + } + shrink_loop_uv_by_aspect_ratio(efa, cd_loop_uv_offset, aspect_y); } } @@ -1613,7 +1661,17 @@ static void uv_map_clip_correct_properties(wmOperatorType *ot) uv_map_clip_correct_properties_ex(ot, true); } -static void uv_map_clip_correct_multi(Object **objects, uint objects_len, wmOperator *op) +/** + * \param per_face_aspect: Calculate the aspect ratio per-face, + * otherwise use a single aspect for all UV's based on the material of the active face. + * TODO: using per-face aspect may split UV islands so more advanced UV projection methods + * such as "Unwrap" & "Smart UV Projections" will need to handle aspect correction themselves. + * For now keep using a single aspect for all faces in this case. + */ +static void uv_map_clip_correct_multi(Object **objects, + uint objects_len, + wmOperator *op, + bool per_face_aspect) { BMFace *efa; BMLoop *l; @@ -1633,9 +1691,14 @@ static void uv_map_clip_correct_multi(Object **objects, uint objects_len, wmOper BMEditMesh *em = BKE_editmesh_from_object(ob); const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); - /* correct for image aspect ratio */ + /* Correct for image aspect ratio. */ if (correct_aspect) { - correct_uv_aspect(ob, em); + if (per_face_aspect) { + correct_uv_aspect_per_face(ob, em); + } + else { + correct_uv_aspect(ob, em); + } } if (scale_to_bounds) { @@ -1678,6 +1741,11 @@ static void uv_map_clip_correct_multi(Object **objects, uint objects_len, wmOper dy = 1.0f / dy; } + if (dx == 1.0f && dy == 1.0f) { + /* Scaling by 1.0 has no effect. */ + return; + } + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { Object *ob = objects[ob_index]; @@ -1702,7 +1770,7 @@ static void uv_map_clip_correct_multi(Object **objects, uint objects_len, wmOper static void uv_map_clip_correct(Object *ob, wmOperator *op) { - uv_map_clip_correct_multi(&ob, 1, op); + uv_map_clip_correct_multi(&ob, 1, op, true); } /** \} */ @@ -2283,7 +2351,9 @@ static int smart_project_exec(bContext *C, wmOperator *op) .use_seams = true, }); - uv_map_clip_correct_multi(objects_changed, object_changed_len, op); + /* #ED_uvedit_pack_islands_multi only supports `per_face_aspect = false`. */ + const bool per_face_aspect = false; + uv_map_clip_correct_multi(objects_changed, object_changed_len, op, per_face_aspect); } MEM_freeN(objects_changed); @@ -2485,7 +2555,7 @@ static int uv_from_view_exec(bContext *C, wmOperator *op) } if (changed_multi) { - uv_map_clip_correct_multi(objects, objects_len, op); + uv_map_clip_correct_multi(objects, objects_len, op, true); } MEM_freeN(objects); diff --git a/source/blender/functions/FN_multi_function_builder.hh b/source/blender/functions/FN_multi_function_builder.hh index a35339b2495..e6dc01eb539 100644 --- a/source/blender/functions/FN_multi_function_builder.hh +++ b/source/blender/functions/FN_multi_function_builder.hh @@ -207,9 +207,9 @@ void execute_materialized(TypeSequence<ParamTags...> /* param_tags */, ( /* Setup information for all parameters. */ [&] { - using ParamTag = ParamTags; - using T = typename ParamTag::base_type; - ArgInfo<ParamTags> &arg_info = std::get<I>(args_info); + typedef ParamTags ParamTag; + typedef typename ParamTag::base_type T; + [[maybe_unused]] ArgInfo<ParamTags> &arg_info = std::get<I>(args_info); if constexpr (ParamTag::category == MFParamCategory::SingleInput) { VArray<T> &varray = *args; if (varray.is_single()) { @@ -246,7 +246,7 @@ void execute_materialized(TypeSequence<ParamTags...> /* param_tags */, [&] { using ParamTag = ParamTags; using T = typename ParamTag::base_type; - ArgInfo<ParamTags> &arg_info = std::get<I>(args_info); + [[maybe_unused]] ArgInfo<ParamTags> &arg_info = std::get<I>(args_info); if constexpr (ParamTag::category == MFParamCategory::SingleInput) { if (arg_info.mode == ArgMode::Single) { /* The single value has been filled into a buffer already reused for every chunk. */ @@ -282,9 +282,9 @@ void execute_materialized(TypeSequence<ParamTags...> /* param_tags */, ( /* Destruct values that have been materialized before. */ [&] { - using ParamTag = ParamTags; - using T = typename ParamTag::base_type; - ArgInfo<ParamTags> &arg_info = std::get<I>(args_info); + typedef ParamTags ParamTag; + typedef typename ParamTag::base_type T; + [[maybe_unused]] ArgInfo<ParamTags> &arg_info = std::get<I>(args_info); if constexpr (ParamTag::category == MFParamCategory::SingleInput) { if (arg_info.mode == ArgMode::Materialized) { T *in_chunk = std::get<I>(buffers_owner).ptr(); @@ -298,9 +298,9 @@ void execute_materialized(TypeSequence<ParamTags...> /* param_tags */, ( /* Destruct buffers for single value inputs. */ [&] { - using ParamTag = ParamTags; - using T = typename ParamTag::base_type; - ArgInfo<ParamTags> &arg_info = std::get<I>(args_info); + typedef ParamTags ParamTag; + typedef typename ParamTag::base_type T; + [[maybe_unused]] ArgInfo<ParamTags> &arg_info = std::get<I>(args_info); if constexpr (ParamTag::category == MFParamCategory::SingleInput) { if (arg_info.mode == ArgMode::Single) { MutableSpan<T> in_chunk = std::get<I>(buffers); @@ -347,8 +347,8 @@ template<typename... ParamTags> class CustomMF : public MultiFunction { ( /* Get all parameters from #params and store them in #retrieved_params. */ [&]() { - using ParamTag = typename TagsSequence::template at_index<I>; - using T = typename ParamTag::base_type; + typedef typename TagsSequence::template at_index<I> ParamTag; + typedef typename ParamTag::base_type T; if constexpr (ParamTag::category == MFParamCategory::SingleInput) { std::get<I>(retrieved_params) = params.readonly_single_input<T>(I); @@ -402,7 +402,7 @@ template<typename... ParamTags> class CustomMF : public MultiFunction { ( /* Loop over all parameter types and add an entry for each in the signature. */ [&] { - using ParamTag = typename TagsSequence::template at_index<I>; + typedef typename TagsSequence::template at_index<I> ParamTag; signature.add(ParamTag(), ""); }(), ...); diff --git a/source/blender/gpu/GPU_capabilities.h b/source/blender/gpu/GPU_capabilities.h index 0d0542aa528..061b850619f 100644 --- a/source/blender/gpu/GPU_capabilities.h +++ b/source/blender/gpu/GPU_capabilities.h @@ -30,6 +30,7 @@ int GPU_max_batch_vertices(void); int GPU_max_vertex_attribs(void); int GPU_max_varying_floats(void); int GPU_max_shader_storage_buffer_bindings(void); +int GPU_max_compute_shader_storage_blocks(void); int GPU_extensions_len(void); const char *GPU_extension_get(int i); @@ -40,6 +41,7 @@ bool GPU_mip_render_workaround(void); bool GPU_depth_blitting_workaround(void); bool GPU_use_main_context_workaround(void); bool GPU_use_hq_normals_workaround(void); +bool GPU_clear_viewport_workaround(void); bool GPU_crappy_amd_driver(void); bool GPU_compute_shader_support(void); diff --git a/source/blender/gpu/intern/gpu_capabilities.cc b/source/blender/gpu/intern/gpu_capabilities.cc index b750dacaca6..6ef015492c7 100644 --- a/source/blender/gpu/intern/gpu_capabilities.cc +++ b/source/blender/gpu/intern/gpu_capabilities.cc @@ -142,6 +142,11 @@ bool GPU_use_hq_normals_workaround() return GCaps.use_hq_normals_workaround; } +bool GPU_clear_viewport_workaround() +{ + return GCaps.clear_viewport_workaround; +} + bool GPU_compute_shader_support() { return GCaps.compute_shader_support; @@ -162,6 +167,12 @@ int GPU_max_shader_storage_buffer_bindings() return GCaps.max_shader_storage_buffer_bindings; } +int GPU_max_compute_shader_storage_blocks() +{ + return GCaps.max_compute_shader_storage_blocks; +} + + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/gpu/intern/gpu_capabilities_private.hh b/source/blender/gpu/intern/gpu_capabilities_private.hh index 4a951eb8458..a17dbe7f8e6 100644 --- a/source/blender/gpu/intern/gpu_capabilities_private.hh +++ b/source/blender/gpu/intern/gpu_capabilities_private.hh @@ -36,6 +36,7 @@ struct GPUCapabilities { int max_vertex_attribs = 0; int max_varying_floats = 0; int max_shader_storage_buffer_bindings = 0; + int max_compute_shader_storage_blocks = 0; int extensions_len = 0; const char *(*extension_get)(int); @@ -51,6 +52,7 @@ struct GPUCapabilities { bool use_main_context_workaround = false; bool broken_amd_driver = false; bool use_hq_normals_workaround = false; + bool clear_viewport_workaround = false; /* Vulkan related workarounds. */ /* Metal related workarounds. */ diff --git a/source/blender/gpu/intern/gpu_codegen.cc b/source/blender/gpu/intern/gpu_codegen.cc index b6b0825a993..f1b46f8bf86 100644 --- a/source/blender/gpu/intern/gpu_codegen.cc +++ b/source/blender/gpu/intern/gpu_codegen.cc @@ -32,6 +32,7 @@ #include "GPU_vertex_format.h" #include "BLI_sys_types.h" /* for intptr_t support */ +#include "BLI_vector.hh" #include "gpu_codegen.h" #include "gpu_material_library.h" @@ -58,18 +59,27 @@ struct GPUCodegenCreateInfo : ShaderCreateInfo { /** Duplicate attribute names to avoid reference the GPUNodeGraph directly. */ char attr_names[16][GPU_MAX_SAFE_ATTR_NAME + 1]; char var_names[16][8]; + blender::Vector<std::array<char, 32>, 16> sampler_names; + + /* Returns the appended name memory location */ + const char *append_sampler_name(const char name[32]) + { + auto index = sampler_names.append_and_get_index(std::array<char, 32>()); + char *name_buffer = sampler_names[index].data(); + memcpy(name_buffer, name, 32); + return name_buffer; + } }; /** Optional generated interface. */ StageInterfaceInfo *interface_generated = nullptr; /** Optional name buffer containing names referenced by StringRefNull. */ - NameBuffer *name_buffer = nullptr; + NameBuffer name_buffer; GPUCodegenCreateInfo(const char *name) : ShaderCreateInfo(name){}; ~GPUCodegenCreateInfo() { delete interface_generated; - MEM_delete(name_buffer); }; }; @@ -288,7 +298,6 @@ void GPUCodegen::generate_attribs() GPUCodegenCreateInfo &info = *create_info; - info.name_buffer = MEM_new<GPUCodegenCreateInfo::NameBuffer>("info.name_buffer"); info.interface_generated = new StageInterfaceInfo("codegen_iface", "var_attrs"); StageInterfaceInfo &iface = *info.interface_generated; info.vertex_out(iface); @@ -302,11 +311,11 @@ void GPUCodegen::generate_attribs() BLI_assert_msg(0, "Too many attributes"); break; } - STRNCPY(info.name_buffer->attr_names[slot], attr->input_name); - SNPRINTF(info.name_buffer->var_names[slot], "v%d", attr->id); + STRNCPY(info.name_buffer.attr_names[slot], attr->input_name); + SNPRINTF(info.name_buffer.var_names[slot], "v%d", attr->id); - blender::StringRefNull attr_name = info.name_buffer->attr_names[slot]; - blender::StringRefNull var_name = info.name_buffer->var_names[slot]; + blender::StringRefNull attr_name = info.name_buffer.attr_names[slot]; + blender::StringRefNull var_name = info.name_buffer.var_names[slot]; eGPUType input_type, iface_type; @@ -348,14 +357,19 @@ void GPUCodegen::generate_resources() /* Textures. */ LISTBASE_FOREACH (GPUMaterialTexture *, tex, &graph.textures) { if (tex->colorband) { - info.sampler(0, ImageType::FLOAT_1D_ARRAY, tex->sampler_name, Frequency::BATCH); + const char *name = info.name_buffer.append_sampler_name(tex->sampler_name); + info.sampler(0, ImageType::FLOAT_1D_ARRAY, name, Frequency::BATCH); } else if (tex->tiled_mapping_name[0] != '\0') { - info.sampler(0, ImageType::FLOAT_2D_ARRAY, tex->sampler_name, Frequency::BATCH); - info.sampler(0, ImageType::FLOAT_1D_ARRAY, tex->tiled_mapping_name, Frequency::BATCH); + const char *name = info.name_buffer.append_sampler_name(tex->sampler_name); + info.sampler(0, ImageType::FLOAT_2D_ARRAY, name, Frequency::BATCH); + + const char *name_mapping = info.name_buffer.append_sampler_name(tex->tiled_mapping_name); + info.sampler(0, ImageType::FLOAT_1D_ARRAY, name_mapping, Frequency::BATCH); } else { - info.sampler(0, ImageType::FLOAT_2D, tex->sampler_name, Frequency::BATCH); + const char *name = info.name_buffer.append_sampler_name(tex->sampler_name); + info.sampler(0, ImageType::FLOAT_2D, name, Frequency::BATCH); } } diff --git a/source/blender/gpu/intern/gpu_index_buffer.cc b/source/blender/gpu/intern/gpu_index_buffer.cc index 54473b3a44d..146461d1dfb 100644 --- a/source/blender/gpu/intern/gpu_index_buffer.cc +++ b/source/blender/gpu/intern/gpu_index_buffer.cc @@ -232,6 +232,7 @@ void IndexBuf::init(uint indices_len, uint32_t *indices, uint min_index, uint ma data_ = indices; index_start_ = 0; index_len_ = indices_len; + is_empty_ = min_index > max_index; #if GPU_TRACK_INDEX_RANGE /* Everything remains 32 bit while building to keep things simple. diff --git a/source/blender/gpu/intern/gpu_index_buffer_private.hh b/source/blender/gpu/intern/gpu_index_buffer_private.hh index 9323a81d393..6ce62ae852e 100644 --- a/source/blender/gpu/intern/gpu_index_buffer_private.hh +++ b/source/blender/gpu/intern/gpu_index_buffer_private.hh @@ -45,6 +45,8 @@ class IndexBuf { bool is_init_ = false; /** Is this object only a reference to a subrange of another IndexBuf. */ bool is_subrange_ = false; + /** True if buffer only contains restart indices. */ + bool is_empty_ = false; union { /** Mapped buffer data. non-NULL indicates not yet sent to VRAM. */ @@ -61,9 +63,12 @@ class IndexBuf { void init_subrange(IndexBuf *elem_src, uint start, uint length); void init_build_on_device(uint index_len); + /* Returns render index count (not precise). */ uint32_t index_len_get() const { - return index_len_; + /* Return 0 to bypass drawing for index buffers full of restart indices. + * They can lead to graphical glitches on some systems. (See T96892) */ + return is_empty_ ? 0 : index_len_; } /* Return size in byte of the drawable data buffer range. Actual buffer size might be bigger. */ size_t size_get() const diff --git a/source/blender/gpu/intern/gpu_node_graph.c b/source/blender/gpu/intern/gpu_node_graph.c index bc7ace792bb..91fb0544cf6 100644 --- a/source/blender/gpu/intern/gpu_node_graph.c +++ b/source/blender/gpu/intern/gpu_node_graph.c @@ -333,6 +333,8 @@ static char attr_prefix_get(CustomDataType type) switch (type) { case CD_TANGENT: return 't'; + case CD_MCOL: + return 'c'; case CD_AUTO_FROM_NAME: return 'a'; case CD_HAIRLENGTH: @@ -611,7 +613,7 @@ bool GPU_link(GPUMaterial *mat, const char *name, ...) va_start(params, name); for (i = 0; i < function->totparam; i++) { - if (function->paramqual[i] != FUNCTION_QUAL_IN) { + if (function->paramqual[i] == FUNCTION_QUAL_OUT) { linkptr = va_arg(params, GPUNodeLink **); gpu_node_output(node, function->paramtype[i], linkptr); } @@ -669,7 +671,7 @@ static bool gpu_stack_link_v(GPUMaterial *material, } for (i = 0; i < function->totparam; i++) { - if (function->paramqual[i] != FUNCTION_QUAL_IN) { + if (function->paramqual[i] == FUNCTION_QUAL_OUT) { if (totout == 0) { linkptr = va_arg(params, GPUNodeLink **); gpu_node_output(node, function->paramtype[i], linkptr); diff --git a/source/blender/gpu/intern/gpu_shader_builtin.c b/source/blender/gpu/intern/gpu_shader_builtin.c index 13238a03688..1100272b712 100644 --- a/source/blender/gpu/intern/gpu_shader_builtin.c +++ b/source/blender/gpu/intern/gpu_shader_builtin.c @@ -367,6 +367,16 @@ GPUShader *GPU_shader_get_builtin_shader_with_config(eGPUBuiltinShader shader, if (sh_cfg == GPU_SHADER_CFG_DEFAULT) { if (stages->create_info != NULL) { *sh_p = GPU_shader_create_from_info_name(stages->create_info); + if (ELEM(shader, + GPU_SHADER_3D_POLYLINE_CLIPPED_UNIFORM_COLOR, + GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR, + GPU_SHADER_3D_POLYLINE_FLAT_COLOR, + GPU_SHADER_3D_POLYLINE_SMOOTH_COLOR)) { + /* Set a default value for `lineSmooth`. + * Ideally this value should be set by the caller. */ + GPU_shader_bind(*sh_p); + GPU_shader_uniform_1i(*sh_p, "lineSmooth", 1); + } } else { *sh_p = GPU_shader_create_from_arrays_named( diff --git a/source/blender/gpu/intern/gpu_shader_dependency.cc b/source/blender/gpu/intern/gpu_shader_dependency.cc index 460b6d32967..f69c56b5f3f 100644 --- a/source/blender/gpu/intern/gpu_shader_dependency.cc +++ b/source/blender/gpu/intern/gpu_shader_dependency.cc @@ -8,6 +8,7 @@ * shader files. */ +#include <algorithm> #include <iomanip> #include <iostream> #include <sstream> @@ -98,6 +99,10 @@ struct GPUSource { /* Limit to shared header files to avoid the temptation to use C++ syntax in .glsl files. */ if (filename.endswith(".h") || filename.endswith(".hh")) { enum_preprocess(); + quote_preprocess(); + } + else { + check_no_quotes(); } if (is_from_material_library()) { @@ -174,6 +179,44 @@ struct GPUSource { } /** + * Some drivers completely forbid quote characters even in unused preprocessor directives. + * We fix the cases where we can't manually patch in `enum_preprocess()`. + * This check ensure none are present in non-patched sources. (see T97545) + */ + void check_no_quotes() + { +#ifdef DEBUG + int64_t pos = -1; + do { + pos = source.find('"', pos + 1); + if (pos == -1) { + break; + } + if (!is_in_comment(source, pos)) { + print_error(source, pos, "Quote characters are forbidden in GLSL files"); + } + } while (true); +#endif + } + + /** + * Some drivers completely forbid string characters even in unused preprocessor directives. + * This fixes the cases we cannot manually patch: Shared headers #includes. (see T97545) + * TODO(fclem): This could be done during the datatoc step. + */ + void quote_preprocess() + { + if (source.find_first_of('"') == -1) { + return; + } + + processed_source = source; + std::replace(processed_source.begin(), processed_source.end(), '"', ' '); + + source = processed_source.c_str(); + } + + /** * Transform C,C++ enum declaration into GLSL compatible defines and constants: * * \code{.cpp} @@ -282,6 +325,7 @@ struct GPUSource { if (last_pos != 0) { output += input.substr(last_pos); } + processed_source = output; source = processed_source.c_str(); }; diff --git a/source/blender/gpu/intern/gpu_viewport.c b/source/blender/gpu/intern/gpu_viewport.c index a0efe12f523..c3118ca320c 100644 --- a/source/blender/gpu/intern/gpu_viewport.c +++ b/source/blender/gpu/intern/gpu_viewport.c @@ -21,6 +21,7 @@ #include "DNA_userdef_types.h" #include "DNA_vec_types.h" +#include "GPU_capabilities.h" #include "GPU_framebuffer.h" #include "GPU_immediate.h" #include "GPU_matrix.h" @@ -126,19 +127,23 @@ static void gpu_viewport_textures_create(GPUViewport *viewport) if (viewport->color_render_tx[0] == NULL) { viewport->color_render_tx[0] = GPU_texture_create_2d( "dtxl_color", UNPACK2(size), 1, GPU_RGBA16F, NULL); - GPU_texture_clear(viewport->color_render_tx[0], GPU_DATA_FLOAT, empty_pixel); viewport->color_overlay_tx[0] = GPU_texture_create_2d( "dtxl_color_overlay", UNPACK2(size), 1, GPU_SRGB8_A8, NULL); - GPU_texture_clear(viewport->color_overlay_tx[0], GPU_DATA_FLOAT, empty_pixel); + if (GPU_clear_viewport_workaround()) { + GPU_texture_clear(viewport->color_render_tx[0], GPU_DATA_FLOAT, empty_pixel); + GPU_texture_clear(viewport->color_overlay_tx[0], GPU_DATA_FLOAT, empty_pixel); + } } if ((viewport->flag & GPU_VIEWPORT_STEREO) != 0 && viewport->color_render_tx[1] == NULL) { viewport->color_render_tx[1] = GPU_texture_create_2d( "dtxl_color_stereo", UNPACK2(size), 1, GPU_RGBA16F, NULL); - GPU_texture_clear(viewport->color_render_tx[1], GPU_DATA_FLOAT, empty_pixel); viewport->color_overlay_tx[1] = GPU_texture_create_2d( "dtxl_color_overlay_stereo", UNPACK2(size), 1, GPU_SRGB8_A8, NULL); - GPU_texture_clear(viewport->color_overlay_tx[1], GPU_DATA_FLOAT, empty_pixel); + if (GPU_clear_viewport_workaround()) { + GPU_texture_clear(viewport->color_render_tx[1], GPU_DATA_FLOAT, empty_pixel); + GPU_texture_clear(viewport->color_overlay_tx[1], GPU_DATA_FLOAT, empty_pixel); + } } /* Can be shared with GPUOffscreen. */ @@ -262,12 +267,15 @@ void GPU_viewport_stereo_composite(GPUViewport *viewport, Stereo3dFormat *stereo return; } /* The composite framebuffer object needs to be created in the window context. */ - GPU_framebuffer_ensure_config(&viewport->stereo_comp_fb, - { - GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_TEXTURE(viewport->color_overlay_tx[0]), - GPU_ATTACHMENT_TEXTURE(viewport->color_render_tx[0]), - }); + GPU_framebuffer_ensure_config( + &viewport->stereo_comp_fb, + { + GPU_ATTACHMENT_NONE, + /* We need the sRGB attachment to be first for GL_FRAMEBUFFER_SRGB to be turned on. + * Note that this is the opposite of what the texture binding is. */ + GPU_ATTACHMENT_TEXTURE(viewport->color_overlay_tx[0]), + GPU_ATTACHMENT_TEXTURE(viewport->color_render_tx[0]), + }); GPUVertFormat *vert_format = immVertexFormat(); uint pos = GPU_vertformat_attr_add(vert_format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); diff --git a/source/blender/gpu/opengl/gl_backend.cc b/source/blender/gpu/opengl/gl_backend.cc index 610fd5d980f..1cd2301aa4e 100644 --- a/source/blender/gpu/opengl/gl_backend.cc +++ b/source/blender/gpu/opengl/gl_backend.cc @@ -419,6 +419,13 @@ static void detect_workarounds() GCaps.shader_storage_buffer_objects_support = false; } + /* Certain Intel/AMD based platforms don't clear the viewport textures. Always clearing leads to + * noticeable performance regressions on other platforms as well. */ + if (GPU_type_matches(GPU_DEVICE_ANY, GPU_OS_MAC, GPU_DRIVER_ANY) || + GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_UNIX, GPU_DRIVER_ANY)) { + GCaps.clear_viewport_workaround = true; + } + /* Metal-related Workarounds. */ /* Minimum Per-Vertex stride is 1 byte for OpenGL. */ @@ -503,6 +510,7 @@ void GLBackend::capabilities_init() glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 2, &GCaps.max_work_group_size[2]); glGetIntegerv(GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS, &GCaps.max_shader_storage_buffer_bindings); + glGetIntegerv(GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS, &GCaps.max_compute_shader_storage_blocks); } GCaps.shader_storage_buffer_objects_support = GLEW_ARB_shader_storage_buffer_object; /* GL specific capabilities. */ diff --git a/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl b/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl index 5c97eada77d..6091a5c834a 100644 --- a/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl +++ b/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl @@ -191,8 +191,8 @@ struct GlobalData { vec3 N; /** Geometric Normal. */ vec3 Ng; - /** Surface default Tangent. */ - vec3 T; + /** Curve Tangent Space. */ + vec3 curve_T, curve_B, curve_N; /** Barycentric coordinates. */ vec2 barycentric_coords; vec3 barycentric_dists; diff --git a/source/blender/gpu/shaders/gpu_shader_image_overlays_stereo_merge_frag.glsl b/source/blender/gpu/shaders/gpu_shader_image_overlays_stereo_merge_frag.glsl index 9b1e6fe9d23..522f6de169d 100644 --- a/source/blender/gpu/shaders/gpu_shader_image_overlays_stereo_merge_frag.glsl +++ b/source/blender/gpu/shaders/gpu_shader_image_overlays_stereo_merge_frag.glsl @@ -5,18 +5,6 @@ #define S3D_INTERLACE_COLUMN 1 #define S3D_INTERLACE_CHECKERBOARD 2 -/* Composite stereo textures */ - -#ifndef USE_GPU_SHADER_CREATE_INFO -uniform sampler2D imageTexture; -uniform sampler2D overlayTexture; - -uniform int stereoDisplaySettings; - -layout(location = 0) out vec4 imageColor; -layout(location = 1) out vec4 overlayColor; -#endif - #define stereo_display_mode (stereoDisplaySettings & ((1 << 3) - 1)) #define stereo_interlace_mode ((stereoDisplaySettings >> 3) & ((1 << 3) - 1)) #define stereo_interlace_swap bool(stereoDisplaySettings >> 6) diff --git a/source/blender/gpu/shaders/infos/gpu_shader_2D_image_overlays_stereo_merge_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_2D_image_overlays_stereo_merge_info.hh index 4b2d59cc159..3cacd0f4b8d 100644 --- a/source/blender/gpu/shaders/infos/gpu_shader_2D_image_overlays_stereo_merge_info.hh +++ b/source/blender/gpu/shaders/infos/gpu_shader_2D_image_overlays_stereo_merge_info.hh @@ -9,8 +9,8 @@ GPU_SHADER_CREATE_INFO(gpu_shader_2D_image_overlays_stereo_merge) .vertex_in(0, Type::VEC2, "pos") - .fragment_out(0, Type::VEC4, "imageColor") - .fragment_out(1, Type::VEC4, "overlayColor") + .fragment_out(0, Type::VEC4, "overlayColor") + .fragment_out(1, Type::VEC4, "imageColor") .sampler(0, ImageType::FLOAT_2D, "imageTexture") .sampler(1, ImageType::FLOAT_2D, "overlayTexture") .push_constant(Type::MAT4, "ModelViewProjectionMatrix") diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_add_shader.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_add_shader.glsl index 99117400c57..3f42b6d9094 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_add_shader.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_add_shader.glsl @@ -1,4 +1,4 @@ -void node_add_shader(Closure shader1, Closure shader2, out Closure shader) +void node_add_shader(inout Closure shader1, inout Closure shader2, out Closure shader) { shader = closure_add(shader1, shader2); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_eevee_specular.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_eevee_specular.glsl index c81880184e3..530907859e9 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_eevee_specular.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_eevee_specular.glsl @@ -64,6 +64,8 @@ void node_eevee_specular(vec4 diffuse, else { result = closure_eval(diffuse_data, reflection_data); } - result = closure_add(result, closure_eval(emission_data)); - result = closure_add(result, closure_eval(transparency_data)); + Closure emission_cl = closure_eval(emission_data); + Closure transparency_cl = closure_eval(transparency_data); + result = closure_add(result, emission_cl); + result = closure_add(result, transparency_cl); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_geometry.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_geometry.glsl index 5e86a4577ee..4c9ff31622f 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_geometry.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_geometry.glsl @@ -18,7 +18,7 @@ void node_geometry(vec3 orco, true_normal = g_data.Ng; if (g_data.is_strand) { - tangent = g_data.T; + tangent = g_data.curve_T; } else { tangent_orco_z(orco, orco); diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_hair.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_hair.glsl index 7bf8795495a..b24f9ab65f0 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_hair.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_hair.glsl @@ -40,7 +40,7 @@ void node_bsdf_hair_principled(vec4 color, hair_data.color = color.rgb; hair_data.offset = offset; hair_data.roughness = vec2(0.0); - hair_data.T = g_data.T; + hair_data.T = g_data.curve_B; result = closure_eval(hair_data); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl index a8b4b039370..2d5114c3bad 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl @@ -5,14 +5,14 @@ void node_hair_info(float hair_length, out float intercept, out float out_length, out float thickness, - out vec3 tangent, + out vec3 normal, out float random) { is_strand = float(g_data.is_strand); intercept = g_data.hair_time; thickness = g_data.hair_thickness; out_length = hair_length; - tangent = g_data.T; + normal = g_data.curve_N; /* TODO: could be precomputed per strand instead. */ random = wang_hash_noise(uint(g_data.hair_strand_id)); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_mix_shader.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_mix_shader.glsl index c303d21d7c1..00cfba3ca12 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_mix_shader.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_mix_shader.glsl @@ -1,4 +1,4 @@ -void node_mix_shader(float fac, Closure shader1, Closure shader2, out Closure shader) +void node_mix_shader(float fac, inout Closure shader1, inout Closure shader2, out Closure shader) { shader = closure_mix(shader1, shader2, fac); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl index 033dc05c57d..2e695fa3e14 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl @@ -169,6 +169,8 @@ void node_bsdf_principled(vec4 base_color, /* Un-optimized case. */ result = closure_eval(diffuse_data, reflection_data, clearcoat_data, refraction_data); } - result = closure_add(result, closure_eval(emission_data)); - result = closure_add(result, closure_eval(transparency_data)); + Closure emission_cl = closure_eval(emission_data); + Closure transparency_cl = closure_eval(transparency_data); + result = closure_add(result, emission_cl); + result = closure_add(result, transparency_cl); } diff --git a/source/blender/imbuf/intern/openexr/openexr_api.cpp b/source/blender/imbuf/intern/openexr/openexr_api.cpp index 9948aaac5da..2281d8d85b3 100644 --- a/source/blender/imbuf/intern/openexr/openexr_api.cpp +++ b/source/blender/imbuf/intern/openexr/openexr_api.cpp @@ -1394,12 +1394,10 @@ static int imb_exr_split_channel_name(ExrChannel *echan, char *layname, char *pa const char *name = echan->m->name.c_str(); const char *end = name + strlen(name); const char *token; - char tokenbuf[EXR_TOT_MAXNAME]; - int len; /* some multilayers have the combined buffer with names A B G R saved */ if (name[1] == 0) { - echan->chan_id = name[0]; + echan->chan_id = BLI_toupper_ascii(name[0]); layname[0] = '\0'; if (ELEM(name[0], 'R', 'G', 'B', 'A')) { @@ -1416,13 +1414,17 @@ static int imb_exr_split_channel_name(ExrChannel *echan, char *layname, char *pa } /* last token is channel identifier */ - len = imb_exr_split_token(name, end, &token); + size_t len = imb_exr_split_token(name, end, &token); if (len == 0) { printf("multilayer read: bad channel name: %s\n", name); return 0; } + + char channelname[EXR_TOT_MAXNAME]; + BLI_strncpy(channelname, token, std::min(len + 1, sizeof(channelname))); + if (len == 1) { - echan->chan_id = token[0]; + echan->chan_id = BLI_toupper_ascii(channelname[0]); } else if (len > 1) { bool ok = false; @@ -1436,36 +1438,35 @@ static int imb_exr_split_channel_name(ExrChannel *echan, char *layname, char *pa * * Here we do some magic to distinguish such cases. */ - if (ELEM(token[1], 'X', 'Y', 'Z') || ELEM(token[1], 'R', 'G', 'B') || - ELEM(token[1], 'U', 'V', 'A')) { - echan->chan_id = token[1]; + const char chan_id = BLI_toupper_ascii(channelname[1]); + if (ELEM(chan_id, 'X', 'Y', 'Z', 'R', 'G', 'B', 'U', 'V', 'A')) { + echan->chan_id = chan_id; ok = true; } } - else if (BLI_strcaseeq(token, "red")) { + else if (BLI_strcaseeq(channelname, "red")) { echan->chan_id = 'R'; ok = true; } - else if (BLI_strcaseeq(token, "green")) { + else if (BLI_strcaseeq(channelname, "green")) { echan->chan_id = 'G'; ok = true; } - else if (BLI_strcaseeq(token, "blue")) { + else if (BLI_strcaseeq(channelname, "blue")) { echan->chan_id = 'B'; ok = true; } - else if (BLI_strcaseeq(token, "alpha")) { + else if (BLI_strcaseeq(channelname, "alpha")) { echan->chan_id = 'A'; ok = true; } - else if (BLI_strcaseeq(token, "depth")) { + else if (BLI_strcaseeq(channelname, "depth")) { echan->chan_id = 'Z'; ok = true; } if (ok == false) { - BLI_strncpy(tokenbuf, token, std::min(len + 1, EXR_TOT_MAXNAME)); - printf("multilayer read: unknown channel token: %s\n", tokenbuf); + printf("multilayer read: unknown channel token: %s\n", channelname); return 0; } } diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.cc b/source/blender/io/alembic/intern/abc_reader_mesh.cc index 2d2dcfb1f42..8c62484028d 100644 --- a/source/blender/io/alembic/intern/abc_reader_mesh.cc +++ b/source/blender/io/alembic/intern/abc_reader_mesh.cc @@ -622,7 +622,7 @@ void AbcMeshReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelec if (read_mesh != mesh) { /* XXX FIXME: after 2.80; mesh->flag isn't copied by #BKE_mesh_nomain_to_mesh(). */ /* read_mesh can be freed by BKE_mesh_nomain_to_mesh(), so get the flag before that happens. */ - short autosmooth = (read_mesh->flag & ME_AUTOSMOOTH); + uint16_t autosmooth = (read_mesh->flag & ME_AUTOSMOOTH); BKE_mesh_nomain_to_mesh(read_mesh, mesh, m_object, &CD_MASK_EVERYTHING, true); mesh->flag |= autosmooth; } diff --git a/source/blender/io/common/CMakeLists.txt b/source/blender/io/common/CMakeLists.txt index b1add38bf01..b5766b44025 100644 --- a/source/blender/io/common/CMakeLists.txt +++ b/source/blender/io/common/CMakeLists.txt @@ -19,10 +19,13 @@ set(SRC intern/dupli_parent_finder.cc intern/dupli_persistent_id.cc intern/object_identifier.cc + intern/path_util.cc intern/string_utils.cc IO_abstract_hierarchy_iterator.h IO_dupli_persistent_id.hh + IO_path_util.hh + IO_path_util_types.h IO_string_utils.hh IO_types.h intern/dupli_parent_finder.hh diff --git a/source/blender/io/common/IO_path_util.hh b/source/blender/io/common/IO_path_util.hh new file mode 100644 index 00000000000..ac2f935523e --- /dev/null +++ b/source/blender/io/common/IO_path_util.hh @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#pragma once + +#include "BLI_string_ref.hh" +#include "BLI_set.hh" + +#include "IO_path_util_types.h" + +namespace blender::io { + +/** + * Return a filepath relative to a destination directory, for use with + * exporters. + * + * When PATH_REFERENCE_COPY mode is used, the file path pair (source + * path, destination path) is added to the `copy_set`. + * + * Equivalent of bpy_extras.io_utils.path_reference. + */ +std::string path_reference(StringRefNull filepath, + StringRefNull base_src, + StringRefNull base_dst, + ePathReferenceMode mode, + Set<std::pair<std::string, std::string>> *copy_set = nullptr); + +/** Execute copying files of path_reference. */ +void path_reference_copy(const Set<std::pair<std::string, std::string>> ©_set); + +} // namespace blender::io diff --git a/source/blender/io/common/IO_path_util_types.h b/source/blender/io/common/IO_path_util_types.h new file mode 100644 index 00000000000..0233f601a81 --- /dev/null +++ b/source/blender/io/common/IO_path_util_types.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#pragma once + +/** Method used to reference paths. Equivalent of bpy_extras.io_utils.path_reference_mode. */ +typedef enum { + /** Use Relative paths with subdirectories only. */ + PATH_REFERENCE_AUTO = 0, + /** Always write absolute paths. */ + PATH_REFERENCE_ABSOLUTE = 1, + /** Write relative paths where possible. */ + PATH_REFERENCE_RELATIVE = 2, + /** Match Absolute/Relative setting with input path. */ + PATH_REFERENCE_MATCH = 3, + /** Filename only. */ + PATH_REFERENCE_STRIP = 4, + /** Copy the file to the destination path. */ + PATH_REFERENCE_COPY = 5, +} ePathReferenceMode; diff --git a/source/blender/io/common/intern/path_util.cc b/source/blender/io/common/intern/path_util.cc new file mode 100644 index 00000000000..2b9a1d67b44 --- /dev/null +++ b/source/blender/io/common/intern/path_util.cc @@ -0,0 +1,81 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "IO_path_util.hh" + +#include "BLI_fileops.h" +#include "BLI_path_util.h" + +namespace blender::io { + +std::string path_reference(StringRefNull filepath, + StringRefNull base_src, + StringRefNull base_dst, + ePathReferenceMode mode, + Set<std::pair<std::string, std::string>> *copy_set) +{ + const bool is_relative = BLI_path_is_rel(filepath.c_str()); + char filepath_abs[PATH_MAX]; + BLI_strncpy(filepath_abs, filepath.c_str(), PATH_MAX); + BLI_path_abs(filepath_abs, base_src.c_str()); + BLI_path_normalize(nullptr, filepath_abs); + + /* Figure out final mode to be used. */ + if (mode == PATH_REFERENCE_MATCH) { + mode = is_relative ? PATH_REFERENCE_RELATIVE : PATH_REFERENCE_ABSOLUTE; + } + else if (mode == PATH_REFERENCE_AUTO) { + mode = BLI_path_contains(base_dst.c_str(), filepath_abs) ? PATH_REFERENCE_RELATIVE : + PATH_REFERENCE_ABSOLUTE; + } + else if (mode == PATH_REFERENCE_COPY) { + char filepath_cpy[PATH_MAX]; + BLI_path_join(filepath_cpy, PATH_MAX, base_dst.c_str(), BLI_path_basename(filepath_abs), nullptr); + copy_set->add(std::make_pair(filepath_abs, filepath_cpy)); + BLI_strncpy(filepath_abs, filepath_cpy, PATH_MAX); + mode = PATH_REFERENCE_RELATIVE; + } + + /* Now we know the final path mode. */ + if (mode == PATH_REFERENCE_ABSOLUTE) { + return filepath_abs; + } + else if (mode == PATH_REFERENCE_RELATIVE) { + char rel_path[PATH_MAX]; + BLI_strncpy(rel_path, filepath_abs, PATH_MAX); + BLI_path_rel(rel_path, base_dst.c_str()); + /* Can't always find relative path (e.g. between different drives). */ + if (!BLI_path_is_rel(rel_path)) { + return filepath_abs; + } + return rel_path + 2; /* Skip blender's internal "//" prefix. */ + } + else if (mode == PATH_REFERENCE_STRIP) { + return BLI_path_basename(filepath_abs); + } + BLI_assert_msg(false, "Invalid path reference mode"); + return filepath_abs; +} + +void path_reference_copy(const Set<std::pair<std::string, std::string>> ©_set) +{ + for (const auto © : copy_set) { + const char *src = copy.first.c_str(); + const char *dst = copy.second.c_str(); + if (!BLI_exists(src)) { + fprintf(stderr, "Missing source file '%s', not copying\n", src); + continue; + } + if (0 == BLI_path_cmp_normalized(src, dst)) { + continue; /* Source and dest are the same. */ + } + if (!BLI_make_existing_file(dst)) { + fprintf(stderr, "Can't make directory for '%s', not copying\n", dst); + continue; + } + if (!BLI_copy(src, dst)) { + fprintf(stderr, "Can't copy '%s' to '%s'\n", src, dst); + continue; + } + } +} + +} // namespace blender::io diff --git a/source/blender/io/usd/intern/usd_reader_mesh.cc b/source/blender/io/usd/intern/usd_reader_mesh.cc index 328b4109616..e2562eca69b 100644 --- a/source/blender/io/usd/intern/usd_reader_mesh.cc +++ b/source/blender/io/usd/intern/usd_reader_mesh.cc @@ -200,7 +200,7 @@ void USDMeshReader::read_object_data(Main *bmain, const double motionSampleTime) if (read_mesh != mesh) { /* FIXME: after 2.80; `mesh->flag` isn't copied by #BKE_mesh_nomain_to_mesh() */ /* read_mesh can be freed by BKE_mesh_nomain_to_mesh(), so get the flag before that happens. */ - short autosmooth = (read_mesh->flag & ME_AUTOSMOOTH); + uint16_t autosmooth = (read_mesh->flag & ME_AUTOSMOOTH); BKE_mesh_nomain_to_mesh(read_mesh, mesh, object_, &CD_MASK_MESH, true); mesh->flag |= autosmooth; } diff --git a/source/blender/io/wavefront_obj/IO_wavefront_obj.h b/source/blender/io/wavefront_obj/IO_wavefront_obj.h index 8b71ec750c0..f7bf678310f 100644 --- a/source/blender/io/wavefront_obj/IO_wavefront_obj.h +++ b/source/blender/io/wavefront_obj/IO_wavefront_obj.h @@ -9,6 +9,7 @@ #include "BKE_context.h" #include "BLI_path_util.h" #include "DEG_depsgraph.h" +#include "IO_path_util_types.h" #ifdef __cplusplus extern "C" { @@ -37,6 +38,8 @@ static const int TOTAL_AXES = 3; struct OBJExportParams { /** Full path to the destination .OBJ file. */ char filepath[FILE_MAX]; + /** Pretend that destination file folder is this, if non-empty. Used only for tests. */ + char file_base_for_tests[FILE_MAX]; /** Full path to current blender file (used for comments in output). */ const char *blen_filepath; @@ -62,6 +65,7 @@ struct OBJExportParams { bool export_materials; bool export_triangulated_mesh; bool export_curves_as_nurbs; + ePathReferenceMode path_mode; /* Grouping options. */ bool export_object_groups; diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc index 194583e71fe..b027df73b1e 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc +++ b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc @@ -13,6 +13,8 @@ #include "BLI_path_util.h" #include "BLI_task.hh" +#include "IO_path_util.hh" + #include "obj_export_mesh.hh" #include "obj_export_mtl.hh" #include "obj_export_nurbs.hh" @@ -530,7 +532,11 @@ void MTLWriter::write_bsdf_properties(const MTLMaterial &mtl_material) void MTLWriter::write_texture_map( const MTLMaterial &mtl_material, - const Map<const eMTLSyntaxElement, tex_map_XX>::Item &texture_map) + const Map<const eMTLSyntaxElement, tex_map_XX>::Item &texture_map, + const char *blen_filedir, + const char *dest_dir, + ePathReferenceMode path_mode, + Set<std::pair<std::string, std::string>> ©_set) { std::string options; /* Option strings should have their own leading spaces. */ @@ -546,7 +552,11 @@ void MTLWriter::write_texture_map( #define SYNTAX_DISPATCH(eMTLSyntaxElement) \ if (texture_map.key == eMTLSyntaxElement) { \ - fmt_handler_.write<eMTLSyntaxElement>(options, texture_map.value.image_path); \ + std::string path = path_reference( \ + texture_map.value.image_path.c_str(), blen_filedir, dest_dir, path_mode, ©_set); \ + /* Always emit forward slashes for cross-platform compatibility. */ \ + std::replace(path.begin(), path.end(), '\\', '/'); \ + fmt_handler_.write<eMTLSyntaxElement>(options, path.c_str()); \ return; \ } @@ -561,25 +571,35 @@ void MTLWriter::write_texture_map( BLI_assert(!"This map type was not written to the file."); } -void MTLWriter::write_materials() +void MTLWriter::write_materials(const char *blen_filepath, + ePathReferenceMode path_mode, + const char *dest_dir) { if (mtlmaterials_.size() == 0) { return; } + + char blen_filedir[PATH_MAX]; + BLI_split_dir_part(blen_filepath, blen_filedir, PATH_MAX); + BLI_path_slash_native(blen_filedir); + BLI_path_normalize(nullptr, blen_filedir); + std::sort(mtlmaterials_.begin(), mtlmaterials_.end(), [](const MTLMaterial &a, const MTLMaterial &b) { return a.name < b.name; }); + Set<std::pair<std::string, std::string>> copy_set; for (const MTLMaterial &mtlmat : mtlmaterials_) { fmt_handler_.write<eMTLSyntaxElement::string>("\n"); fmt_handler_.write<eMTLSyntaxElement::newmtl>(mtlmat.name); write_bsdf_properties(mtlmat); - for (const Map<const eMTLSyntaxElement, tex_map_XX>::Item &texture_map : - mtlmat.texture_maps.items()) { - if (!texture_map.value.image_path.empty()) { - write_texture_map(mtlmat, texture_map); + for (const auto &tex : mtlmat.texture_maps.items()) { + if (tex.value.image_path.empty()) { + continue; } + write_texture_map(mtlmat, tex, blen_filedir, dest_dir, path_mode, copy_set); } } + path_reference_copy(copy_set); } Vector<int> MTLWriter::add_materials(const OBJMesh &mesh_to_export) diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh index 96f7d434338..77da7b44276 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh +++ b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh @@ -9,6 +9,7 @@ #include "DNA_meshdata_types.h" #include "BLI_map.hh" +#include "BLI_set.hh" #include "BLI_vector.hh" #include "IO_wavefront_obj.h" @@ -181,7 +182,9 @@ class MTLWriter : NonMovable, NonCopyable { * For consistency of output from run to run (useful for testing), * the materials are sorted by name before writing. */ - void write_materials(); + void write_materials(const char *blen_filepath, + ePathReferenceMode path_mode, + const char *dest_dir); StringRefNull mtl_file_path() const; /** * Add the materials of the given object to #MTLWriter, de-duplicating @@ -203,6 +206,10 @@ class MTLWriter : NonMovable, NonCopyable { * Write a texture map in the form "map_XX -s 1. 1. 1. -o 0. 0. 0. [-bm 1.] path/to/image". */ void write_texture_map(const MTLMaterial &mtl_material, - const Map<const eMTLSyntaxElement, tex_map_XX>::Item &texture_map); + const Map<const eMTLSyntaxElement, tex_map_XX>::Item &texture_map, + const char *blen_filedir, + const char *dest_dir, + ePathReferenceMode mode, + Set<std::pair<std::string, std::string>> ©_set); }; } // namespace blender::io::obj diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_mtl.cc b/source/blender/io/wavefront_obj/exporter/obj_export_mtl.cc index c48d5a5f7f0..4ed148ec64e 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_mtl.cc +++ b/source/blender/io/wavefront_obj/exporter/obj_export_mtl.cc @@ -113,8 +113,7 @@ static const bNode *get_node_of_type(Span<const nodes::OutputSocketRef *> socket /** * From a texture image shader node, get the image's filepath. - * Returned filepath is stripped of initial "//". If packed image is found, - * only the file "name" is returned. + * If packed image is found, only the file "name" is returned. */ static const char *get_image_filepath(const bNode *tex_node) { @@ -134,9 +133,6 @@ static const char *get_image_filepath(const bNode *tex_node) "directory as the .MTL file.\n", path); } - if (path[0] == '/' && path[1] == '/') { - path += 2; - } return path; } diff --git a/source/blender/io/wavefront_obj/exporter/obj_exporter.cc b/source/blender/io/wavefront_obj/exporter/obj_exporter.cc index 78b709c884a..b6e636b389d 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_exporter.cc +++ b/source/blender/io/wavefront_obj/exporter/obj_exporter.cc @@ -284,7 +284,16 @@ void export_frame(Depsgraph *depsgraph, const OBJExportParams &export_params, co std::move(exportable_as_mesh), *frame_writer, mtl_writer.get(), export_params); if (mtl_writer) { mtl_writer->write_header(export_params.blen_filepath); - mtl_writer->write_materials(); + char dest_dir[PATH_MAX]; + if (export_params.file_base_for_tests[0] == '\0') { + BLI_split_dir_part(export_params.filepath, dest_dir, PATH_MAX); + } + else { + BLI_strncpy(dest_dir, export_params.file_base_for_tests, PATH_MAX); + } + BLI_path_slash_native(dest_dir); + BLI_path_normalize(nullptr, dest_dir); + mtl_writer->write_materials(export_params.blen_filepath, export_params.path_mode, dest_dir); } write_nurbs_curve_objects(std::move(exportable_as_nurbs), *frame_writer); } 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 fc40333c24d..8d560bd2c8c 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc @@ -62,7 +62,7 @@ Object *MeshFromGeometry::create_mesh(Main *bmain, transform_object(obj, import_params); /* FIXME: after 2.80; `mesh->flag` isn't copied by #BKE_mesh_nomain_to_mesh() */ - const short autosmooth = (mesh->flag & ME_AUTOSMOOTH); + const uint16_t autosmooth = (mesh->flag & ME_AUTOSMOOTH); Mesh *dst = static_cast<Mesh *>(obj->data); BKE_mesh_nomain_to_mesh(mesh, dst, obj, &CD_MASK_EVERYTHING, true); dst->flag |= autosmooth; diff --git a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc index f74bfc155dd..8c49af90a82 100644 --- a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc +++ b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc @@ -11,12 +11,15 @@ #include "BKE_appdir.h" #include "BKE_blender_version.h" +#include "BKE_main.h" #include "BLI_fileops.h" #include "BLI_index_range.hh" #include "BLI_string_utf8.h" #include "BLI_vector.hh" +#include "BLO_readfile.h" + #include "DEG_depsgraph.h" #include "obj_export_file_writer.hh" @@ -259,11 +262,12 @@ class obj_exporter_regression_test : public obj_exporter_test { std::string tempdir = std::string(BKE_tempdir_base()); std::string out_file_path = tempdir + BLI_path_basename(golden_obj.c_str()); strncpy(params.filepath, out_file_path.c_str(), FILE_MAX - 1); - params.blen_filepath = blendfile.c_str(); + params.blen_filepath = bfile->main->filepath; + std::string golden_file_path = blender::tests::flags_test_asset_dir() + "/" + golden_obj; + BLI_split_dir_part(golden_file_path.c_str(), params.file_base_for_tests, PATH_MAX); export_frame(depsgraph, params, out_file_path.c_str()); std::string output_str = read_temp_file_in_string(out_file_path); - std::string golden_file_path = blender::tests::flags_test_asset_dir() + "/" + golden_obj; std::string golden_str = read_temp_file_in_string(golden_file_path); bool are_equal = strings_equal_after_first_lines(output_str, golden_str); if (save_failing_test_output && !are_equal) { @@ -432,19 +436,26 @@ TEST_F(obj_exporter_regression_test, cubes_positioned) _export.params); } -/* Note: texture paths in the resulting mtl file currently are always - * as they are stored in the source .blend file; not relative to where - * the export is done. When that is properly fixed, the expected .mtl - * file should be updated. */ -TEST_F(obj_exporter_regression_test, cubes_with_textures) +TEST_F(obj_exporter_regression_test, cubes_with_textures_strip) { OBJExportParamsDefault _export; + _export.params.path_mode = PATH_REFERENCE_STRIP; compare_obj_export_to_golden("io_tests/blend_geometry/cubes_with_textures.blend", "io_tests/obj/cubes_with_textures.obj", "io_tests/obj/cubes_with_textures.mtl", _export.params); } +TEST_F(obj_exporter_regression_test, cubes_with_textures_relative) +{ + OBJExportParamsDefault _export; + _export.params.path_mode = PATH_REFERENCE_RELATIVE; + compare_obj_export_to_golden("io_tests/blend_geometry/cubes_with_textures.blend", + "io_tests/obj/cubes_with_textures_rel.obj", + "io_tests/obj/cubes_with_textures_rel.mtl", + _export.params); +} + TEST_F(obj_exporter_regression_test, suzanne_all_data) { OBJExportParamsDefault _export; diff --git a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh index 6a821e0b1bf..ef27a65fb4b 100644 --- a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh +++ b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh @@ -11,6 +11,7 @@ struct OBJExportParamsDefault { OBJExportParamsDefault() { params.filepath[0] = '\0'; + params.file_base_for_tests[0] = '\0'; params.blen_filepath = ""; params.export_animation = false; params.start_frame = 0; @@ -26,6 +27,7 @@ struct OBJExportParamsDefault { params.export_uv = true; params.export_normals = true; params.export_materials = true; + params.path_mode = PATH_REFERENCE_AUTO; params.export_triangulated_mesh = false; params.export_curves_as_nurbs = false; diff --git a/source/blender/makesdna/DNA_mesh_types.h b/source/blender/makesdna/DNA_mesh_types.h index b98c9dd2886..4a57f60be26 100644 --- a/source/blender/makesdna/DNA_mesh_types.h +++ b/source/blender/makesdna/DNA_mesh_types.h @@ -280,7 +280,7 @@ typedef struct Mesh { /** Various flags used when editing the mesh. */ char editflag; /** Mostly more flags used when editing or displaying the mesh. */ - short flag; + uint16_t flag; /** * The angle for auto smooth in radians. `M_PI` (180 degrees) causes all edges to be smooth. diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index d7926e918a3..bc9b664ab34 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -1264,8 +1264,7 @@ typedef struct UnifiedPaintSettings { * In case of anchored brushes contains the anchored radius */ float pixel_radius; float initial_pixel_radius; - - char _pad[4]; + float start_pixel_radius; /* drawing pressure */ float size_pressure_value; diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 49c16d1b514..7da3b2696fa 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -652,7 +652,7 @@ typedef struct UserDef_Experimental { char enable_eevee_next; char use_sculpt_texture_paint; - char _pad[1]; + char use_draw_manager_acquire_lock; /** `makesdna` does not allow empty structs. */ } UserDef_Experimental; diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index b65e08311fe..b0488bbfa7a 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -1976,7 +1976,7 @@ static void rna_def_ID(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Extra User", - "Indicates wether an extra user is set or not (mainly for internal/debug usages)"); + "Indicates whether an extra user is set or not (mainly for internal/debug usages)"); RNA_def_property_boolean_funcs(prop, NULL, "rna_ID_extra_user_set"); prop = RNA_def_property(srna, "is_embedded_data", PROP_BOOLEAN, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index aa4c4acdd5e..c96163be562 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -5548,7 +5548,12 @@ static char *rna_idp_path(PointerRNA *ptr, if (iter->type == IDP_GROUP) { if (prop->type == PROP_POINTER) { PointerRNA child_ptr = RNA_property_pointer_get(ptr, prop); - BLI_assert(!RNA_pointer_is_null(&child_ptr)); + if (RNA_pointer_is_null(&child_ptr)) { + /* Pointer ID prop might be a 'leaf' in the IDProp group hierarchy, in which case a NULL + * value is perfectly valid. Just means it won't match the searched needle. */ + continue; + } + link.name = iter->name; link.index = -1; if ((path = rna_idp_path(&child_ptr, iter, needle, &link))) { @@ -5570,7 +5575,11 @@ static char *rna_idp_path(PointerRNA *ptr, for (j = 0; j < iter->len; j++, array++) { PointerRNA child_ptr; if (RNA_property_collection_lookup_int(ptr, prop, j, &child_ptr)) { - BLI_assert(!RNA_pointer_is_null(&child_ptr)); + if (RNA_pointer_is_null(&child_ptr)) { + /* Array item ID prop might be a 'leaf' in the IDProp group hierarchy, in which case + * a NULL value is perfectly valid. Just means it won't match the searched needle. */ + continue; + } link.index = j; if ((path = rna_idp_path(&child_ptr, array, needle, &link))) { break; diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index af44fc62e6d..dfd216bc85c 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -6418,6 +6418,10 @@ static void rna_def_userdef_experimental(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "use_sculpt_texture_paint", 1); RNA_def_property_ui_text(prop, "Sculpt Texture Paint", "Use texture painting in Sculpt Mode"); + prop = RNA_def_property(srna, "use_draw_manager_acquire_lock", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "use_draw_manager_acquire_lock", 1); + RNA_def_property_ui_text(prop, "Draw Manager Locking", "Don't lock UI during background rendering"); + prop = RNA_def_property(srna, "use_extended_asset_browser", PROP_BOOLEAN, PROP_NONE); RNA_def_property_ui_text(prop, "Extended Asset Browser", diff --git a/source/blender/modifiers/intern/MOD_meshdeform.c b/source/blender/modifiers/intern/MOD_meshdeform.c index 09e6819a2ae..c2e9e5ebe7d 100644 --- a/source/blender/modifiers/intern/MOD_meshdeform.c +++ b/source/blender/modifiers/intern/MOD_meshdeform.c @@ -587,7 +587,16 @@ static void blendWrite(BlendWriter *writer, const ModifierData *md) int size = mmd->dyngridsize; BLO_write_struct_array(writer, MDefInfluence, mmd->influences_num, mmd->bindinfluences); - BLO_write_int32_array(writer, mmd->verts_num + 1, mmd->bindoffsets); + + /* NOTE: `bindoffset` is abusing `verts_num + 1` as its size, this becomes an incorrect value in + * case `verts_num == 0`, since `bindoffset` is then NULL, not a size 1 allocated array. */ + if (mmd->verts_num > 0) { + BLO_write_int32_array(writer, mmd->verts_num + 1, mmd->bindoffsets); + } + else { + BLI_assert(mmd->bindoffsets == NULL); + } + BLO_write_float3_array(writer, mmd->cage_verts_num, mmd->bindcagecos); BLO_write_struct_array(writer, MDefCell, size * size * size, mmd->dyngrid); BLO_write_struct_array(writer, MDefInfluence, mmd->influences_num, mmd->dyninfluences); @@ -599,7 +608,13 @@ static void blendRead(BlendDataReader *reader, ModifierData *md) MeshDeformModifierData *mmd = (MeshDeformModifierData *)md; BLO_read_data_address(reader, &mmd->bindinfluences); - BLO_read_int32_array(reader, mmd->verts_num + 1, &mmd->bindoffsets); + + /* NOTE: `bindoffset` is abusing `verts_num + 1` as its size, this becomes an incorrect value in + * case `verts_num == 0`, since `bindoffset` is then NULL, not a size 1 allocated array. */ + if (mmd->verts_num > 0) { + BLO_read_int32_array(reader, mmd->verts_num + 1, &mmd->bindoffsets); + } + BLO_read_float3_array(reader, mmd->cage_verts_num, &mmd->bindcagecos); BLO_read_data_address(reader, &mmd->dyngrid); BLO_read_data_address(reader, &mmd->dyninfluences); diff --git a/source/blender/modifiers/intern/MOD_screw.c b/source/blender/modifiers/intern/MOD_screw.c index 05072cf340c..08925e8aeb1 100644 --- a/source/blender/modifiers/intern/MOD_screw.c +++ b/source/blender/modifiers/intern/MOD_screw.c @@ -439,7 +439,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * /* Tag mvert as not loose. */ BLI_BITMAP_ENABLE(vert_tag, med_orig->v1); - BLI_BITMAP_ENABLE(vert_tag, med_orig->v1); + BLI_BITMAP_ENABLE(vert_tag, med_orig->v2); } /* build polygon -> edge map */ @@ -1118,6 +1118,10 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * MEM_freeN(vert_loop_map); } + if ((ltmd->flag & MOD_SCREW_NORMAL_CALC)) { + BKE_mesh_vertex_normals_clear_dirty(result); + } + if ((ltmd->flag & MOD_SCREW_MERGE) && (screw_ofs == 0.0f)) { result = mesh_remove_doubles_on_axis(result, mvert_new, @@ -1128,10 +1132,6 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * ltmd->merge_dist); } - if ((ltmd->flag & MOD_SCREW_NORMAL_CALC)) { - BKE_mesh_vertex_normals_clear_dirty(result); - } - return result; } diff --git a/source/blender/modifiers/intern/MOD_warp.c b/source/blender/modifiers/intern/MOD_warp.c index e95d2983639..79972d1911d 100644 --- a/source/blender/modifiers/intern/MOD_warp.c +++ b/source/blender/modifiers/intern/MOD_warp.c @@ -491,20 +491,20 @@ static void panelRegister(ARegionType *region_type) static void blendWrite(BlendWriter *writer, const ModifierData *md) { - const WarpModifierData *tmd = (const WarpModifierData *)md; + const WarpModifierData *wmd = (const WarpModifierData *)md; - if (tmd->curfalloff) { - BKE_curvemapping_blend_write(writer, tmd->curfalloff); + if (wmd->curfalloff) { + BKE_curvemapping_blend_write(writer, wmd->curfalloff); } } static void blendRead(BlendDataReader *reader, ModifierData *md) { - WarpModifierData *tmd = (WarpModifierData *)md; + WarpModifierData *wmd = (WarpModifierData *)md; - BLO_read_data_address(reader, &tmd->curfalloff); - if (tmd->curfalloff) { - BKE_curvemapping_blend_read(reader, tmd->curfalloff); + BLO_read_data_address(reader, &wmd->curfalloff); + if (wmd->curfalloff) { + BKE_curvemapping_blend_read(reader, wmd->curfalloff); } } diff --git a/source/blender/nodes/shader/nodes/node_shader_vertex_color.cc b/source/blender/nodes/shader/nodes/node_shader_vertex_color.cc index a2a9aa9ad91..cba944c671c 100644 --- a/source/blender/nodes/shader/nodes/node_shader_vertex_color.cc +++ b/source/blender/nodes/shader/nodes/node_shader_vertex_color.cc @@ -42,10 +42,20 @@ static int node_shader_gpu_vertex_color(GPUMaterial *mat, GPUNodeStack *out) { NodeShaderVertexColor *vertexColor = (NodeShaderVertexColor *)node->storage; - /* NOTE: using CD_AUTO_FROM_NAME instead of CD_MCOL or CD_PROP_COLOR as geometry nodes may - * overwrite data which will also change the CustomDataType. This will also make EEVEE and Cycles + /* NOTE: using CD_AUTO_FROM_NAME instead of CD_MCOL or CD_PROP_COLOR for named attributes + * as geometry nodes may overwrite data which will also change the CustomDataType. + * This will also make EEVEE and Cycles * consistent. See T93179. */ - GPUNodeLink *vertexColorLink = GPU_attribute(mat, CD_AUTO_FROM_NAME, vertexColor->layer_name); + + GPUNodeLink *vertexColorLink; + + if (vertexColor->layer_name[0]) { + vertexColorLink = GPU_attribute(mat, CD_AUTO_FROM_NAME, vertexColor->layer_name); + } + else { /* Fall back on active render color attribute. */ + vertexColorLink = GPU_attribute(mat, CD_MCOL, vertexColor->layer_name); + } + return GPU_stack_link(mat, node, "node_vertex_color", in, out, vertexColorLink); } diff --git a/source/blender/python/intern/bpy_utils_units.c b/source/blender/python/intern/bpy_utils_units.c index 3e0698caa50..1e5856a3285 100644 --- a/source/blender/python/intern/bpy_utils_units.c +++ b/source/blender/python/intern/bpy_utils_units.c @@ -35,7 +35,7 @@ static const char *bpyunits_usystem_items[] = { NULL, }; -static const char *bpyunits_ucategorie_items[] = { +static const char *bpyunits_ucategories_items[] = { "NONE", "LENGTH", "AREA", @@ -43,20 +43,26 @@ static const char *bpyunits_ucategorie_items[] = { "MASS", "ROTATION", "TIME", + "TIME_ABSOLUTE", "VELOCITY", "ACCELERATION", "CAMERA", "POWER", + "TEMPERATURE", NULL, }; +BLI_STATIC_ASSERT( + ARRAY_SIZE(bpyunits_ucategories_items) == B_UNIT_TYPE_TOT + 1, + "`bpyunits_ucategories_items` should match `B_UNIT_` enum items in `BKE_units.h`") + /** * These fields are just empty placeholders, actual values get set in initializations functions. * This allows us to avoid many handwriting, and above all, * to keep all systems/categories definition stuff in `BKE_unit.h`. */ static PyStructSequence_Field bpyunits_systems_fields[ARRAY_SIZE(bpyunits_usystem_items)]; -static PyStructSequence_Field bpyunits_categories_fields[ARRAY_SIZE(bpyunits_ucategorie_items)]; +static PyStructSequence_Field bpyunits_categories_fields[ARRAY_SIZE(bpyunits_ucategories_items)]; static PyStructSequence_Desc bpyunits_systems_desc = { "bpy.utils.units.systems", /* name */ @@ -114,7 +120,7 @@ static bool bpyunits_validate(const char *usys_str, const char *ucat_str, int *r return false; } - *r_ucat = BLI_str_index_in_array(ucat_str, bpyunits_ucategorie_items); + *r_ucat = BLI_str_index_in_array(ucat_str, bpyunits_ucategories_items); if (*r_ucat < 0) { PyErr_Format(PyExc_ValueError, "Unknown unit category specified: %.200s.", ucat_str); return false; @@ -356,7 +362,7 @@ PyObject *BPY_utils_units(void) /* bpy.utils.units.categories */ item = py_structseq_from_strings( - &BPyUnitsCategoriesType, &bpyunits_categories_desc, bpyunits_ucategorie_items); + &BPyUnitsCategoriesType, &bpyunits_categories_desc, bpyunits_ucategories_items); PyModule_AddObject(submodule, "categories", item); /* steals ref */ return submodule; diff --git a/source/blender/render/intern/bake.c b/source/blender/render/intern/bake.c index 5953c0f0f8f..bf876163013 100644 --- a/source/blender/render/intern/bake.c +++ b/source/blender/render/intern/bake.c @@ -330,10 +330,10 @@ static bool cast_ray_highpoly(BVHTreeFromMesh *treeData, { int i; int hit_mesh = -1; - float hit_distance = max_ray_distance; - if (hit_distance == 0.0f) { + float hit_distance_squared = max_ray_distance * max_ray_distance; + if (hit_distance_squared == 0.0f) { /* No ray distance set, use maximum. */ - hit_distance = FLT_MAX; + hit_distance_squared = FLT_MAX; } BVHTreeRayHit *hits; @@ -365,16 +365,14 @@ static bool cast_ray_highpoly(BVHTreeFromMesh *treeData, } if (hits[i].index != -1) { - float distance; - float hit_world[3]; - /* distance comparison in world space */ + float hit_world[3]; mul_v3_m4v3(hit_world, highpoly[i].obmat, hits[i].co); - distance = len_squared_v3v3(hit_world, co); + float distance_squared = len_squared_v3v3(hit_world, co); - if (distance < hit_distance) { + if (distance_squared < hit_distance_squared) { hit_mesh = i; - hit_distance = distance; + hit_distance_squared = distance_squared; } } } diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c index a61d73b9f0b..f481f19045d 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c @@ -492,7 +492,8 @@ void WM_gizmomap_draw(wmGizmoMap *gzmap, static void gizmo_draw_select_3d_loop(const bContext *C, wmGizmo **visible_gizmos, - const int visible_gizmos_len) + const int visible_gizmos_len, + bool *r_use_select_bias) { /* TODO(campbell): this depends on depth buffer being written to, @@ -528,6 +529,10 @@ static void gizmo_draw_select_3d_loop(const bContext *C, is_depth_skip_prev = is_depth_skip; } + if (gz->select_bias != 0.0) { + *r_use_select_bias = true; + } + /* pass the selection id shifted by 8 bits. Last 8 bits are used for selected gizmo part id */ gz->type->draw_select(C, gz, select_id << 8); @@ -545,10 +550,7 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos, const int visible_gizmos_len, const bContext *C, const int co[2], - const int hotspot, - const bool use_depth_test, - const bool has_3d_select_bias, - int *r_hits) + const int hotspot) { const wmWindowManager *wm = CTX_wm_manager(C); ScrArea *area = CTX_wm_area(C); @@ -562,76 +564,30 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos, BLI_rcti_init_pt_radius(&rect, co, hotspot); - /* The selection mode is assigned for the following reasons: - * - * - #GPU_SELECT_ALL: Use it to check if there is anything at the cursor location - * (only ever runs once). - * - #GPU_SELECT_PICK_NEAREST: Use if there are more than 1 item at the cursor location, - * pick the nearest one. - * - #GPU_SELECT_PICK_ALL: Use for the same purpose as #GPU_SELECT_PICK_NEAREST - * when the selection depths need to re-ordered based on a bias. - * */ - const eGPUSelectMode gpu_select_mode = - (use_depth_test ? (has_3d_select_bias ? - /* Using select bias means the depths need to be - * re-calculated based on the bias to pick the best. */ - GPU_SELECT_PICK_ALL : - /* No bias, just pick the closest. */ - GPU_SELECT_PICK_NEAREST) : - /* Fast-path (occlusion queries). */ - GPU_SELECT_ALL); - - /* When switching between modes and the mouse pointer is over a gizmo, the highlight test is - * performed before the viewport is fully initialized (region->draw_buffer = NULL). - * When this is the case we should not use depth testing. */ - GPUViewport *gpu_viewport = WM_draw_region_get_viewport(region); - if (use_depth_test && gpu_viewport == NULL) { - return -1; - } + ED_view3d_draw_setup_view( + wm, CTX_wm_window(C), depsgraph, CTX_data_scene(C), region, v3d, NULL, NULL, &rect); - if (GPU_select_is_cached()) { - GPU_select_begin(buffer, ARRAY_SIZE(buffer), &rect, gpu_select_mode, 0); - GPU_select_cache_load_id(); - hits = GPU_select_end(); - } - else { - /* TODO: waiting for the GPU in the middle of the event loop for every - * mouse move is bad for performance, we need to find a solution to not - * use the GPU or draw something once. (see T61474) */ + bool use_select_bias = false; - ED_view3d_draw_setup_view( - wm, CTX_wm_window(C), depsgraph, CTX_data_scene(C), region, v3d, NULL, NULL, &rect); + /* TODO: waiting for the GPU in the middle of the event loop for every + * mouse move is bad for performance, we need to find a solution to not + * use the GPU or draw something once. (see T61474) */ + GPU_select_begin(buffer, ARRAY_SIZE(buffer), &rect, GPU_SELECT_NEAREST_FIRST_PASS, 0); + /* do the drawing */ + gizmo_draw_select_3d_loop(C, visible_gizmos, visible_gizmos_len, &use_select_bias); - /* There is no need to bind to the depth buffer outside this function - * because all future passes the will use the cached depths. */ - GPUFrameBuffer *depth_read_fb = NULL; - if (use_depth_test) { - GPUTexture *depth_tx = GPU_viewport_depth_texture(gpu_viewport); - GPU_framebuffer_ensure_config(&depth_read_fb, - { - GPU_ATTACHMENT_TEXTURE(depth_tx), - GPU_ATTACHMENT_NONE, - }); - GPU_framebuffer_bind(depth_read_fb); - } + hits = GPU_select_end(); - GPU_select_begin(buffer, ARRAY_SIZE(buffer), &rect, gpu_select_mode, 0); - gizmo_draw_select_3d_loop(C, visible_gizmos, visible_gizmos_len); - hits = GPU_select_end(); - - if (use_depth_test) { - GPU_framebuffer_restore(); - GPU_framebuffer_free(depth_read_fb); - } - - ED_view3d_draw_setup_view( - wm, CTX_wm_window(C), depsgraph, CTX_data_scene(C), region, v3d, NULL, NULL, NULL); + if (hits > 0) { + GPU_select_begin(buffer, ARRAY_SIZE(buffer), &rect, GPU_SELECT_NEAREST_SECOND_PASS, hits); + gizmo_draw_select_3d_loop(C, visible_gizmos, visible_gizmos_len, &use_select_bias); + GPU_select_end(); } - /* When selection bias is needed, this function will run again with `use_depth_test` enabled. */ - int hit_found = -1; + ED_view3d_draw_setup_view( + wm, CTX_wm_window(C), depsgraph, CTX_data_scene(C), region, v3d, NULL, NULL, NULL); - if (has_3d_select_bias && use_depth_test && (hits > 1)) { + if (use_select_bias && (hits > 1)) { float co_direction[3]; float co_screen[3] = {co[0], co[1], 0.0f}; ED_view3d_win_to_vector(region, (float[2]){UNPACK2(co)}, co_direction); @@ -643,6 +599,7 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos, GPU_matrix_unproject_3fv(co_screen, rv3d->viewinv, rv3d->winmat, viewport, co_3d_origin); GPUSelectResult *buf_iter = buffer; + int hit_found = -1; float dot_best = FLT_MAX; for (int i = 0; i < hits; i++, buf_iter++) { @@ -662,16 +619,11 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos, hit_found = buf_iter->id; } } - } - else { - const GPUSelectResult *hit_near = GPU_select_buffer_near(buffer, hits); - if (hit_near) { - hit_found = hit_near->id; - } + return hit_found; } - *r_hits = hits; - return hit_found; + const GPUSelectResult *hit_near = GPU_select_buffer_near(buffer, hits); + return hit_near ? hit_near->id : -1; } /** @@ -694,7 +646,6 @@ static wmGizmo *gizmo_find_intersected_3d(bContext *C, /* Search for 3D gizmo's that use the 2D callback for checking intersections. */ bool has_3d = false; - bool has_3d_select_bias = false; { for (int select_id = 0; select_id < visible_gizmos_len; select_id++) { wmGizmo *gz = visible_gizmos[select_id]; @@ -710,9 +661,6 @@ static wmGizmo *gizmo_find_intersected_3d(bContext *C, } else if (gz->type->draw_select != NULL) { has_3d = true; - if (gz->select_bias != 0.0f) { - has_3d_select_bias = true; - } } } } @@ -721,86 +669,39 @@ static wmGizmo *gizmo_find_intersected_3d(bContext *C, * This way we always use the first hit. */ if (has_3d) { - /* NOTE(@campbellbarton): The selection logic here uses a fast-path that exits early - * where possible. This is important as this runs on cursor-motion in the 3D view-port. - * - * - First, don't use the depth buffer at all, use occlusion queries to detect any gizmos. - * If there are no gizmos or only one - early exit, otherwise. - * - * - Bind the depth buffer and use selection picking logic. - * This is much slower than occlusion queries (since it's reading depths while drawing). - * When there is a single gizmo under the cursor (quite common), early exit, otherwise. - * - * - Perform another pass at a reduced size (see: `hotspot_radii`), - * since the result depths are cached this pass is practically free. - * - * Other notes: - * - * - If any of these passes fail, use the nearest result from the previous pass. - * - * - Drawing is only ever done twice. - */ - - /* Order largest to smallest so the first pass can be used as cache for - * later passes (when `use_depth_test == true`). */ - const int hotspot_radii[] = { - 10 * U.pixelsize, - /* This runs on mouse move, careful doing too many tests! */ - 3 * U.pixelsize, - }; - - /* Narrowing may assign zero to `hit`, allow falling back to the previous test. */ - int hit_prev = -1; + /* The depth buffer is needed for for gizmos to obscure eachother. */ + GPUViewport *viewport = WM_draw_region_get_viewport(CTX_wm_region(C)); - bool use_depth_test = false; - bool use_depth_cache = false; - - /* Workaround for MS-Windows & NVidia failing to detect any gizmo undo the cursor unless the - * depth test is enabled, see: T97124. - * NOTE(@campbellbarton): Ideally the exact cause of this could be tracked down, - * disable as I don't have a system to test this configuration. */ - if (GPU_type_matches(GPU_DEVICE_NVIDIA | GPU_DEVICE_SOFTWARE, GPU_OS_WIN, GPU_DRIVER_ANY)) { - use_depth_test = true; + /* When switching between modes and the mouse pointer is over a gizmo, the highlight test is + * performed before the viewport is fully initialized (region->draw_buffer = NULL). + * When this is the case we should not use depth testing. */ + if (viewport == NULL) { + return NULL; } + GPUTexture *depth_tx = GPU_viewport_depth_texture(viewport); + GPUFrameBuffer *depth_read_fb = NULL; + GPU_framebuffer_ensure_config(&depth_read_fb, + { + GPU_ATTACHMENT_TEXTURE(depth_tx), + GPU_ATTACHMENT_NONE, + }); + GPU_framebuffer_bind(depth_read_fb); + const int hotspot_radii[] = { + 3 * U.pixelsize, + /* This runs on mouse move, careful doing too many tests! */ + 10 * U.pixelsize, + }; for (int i = 0; i < ARRAY_SIZE(hotspot_radii); i++) { - - if (use_depth_test && (use_depth_cache == false)) { - GPU_select_cache_begin(); - use_depth_cache = true; - } - - int hit_count; - hit = gizmo_find_intersected_3d_intern(visible_gizmos, - visible_gizmos_len_trim, - C, - co, - hotspot_radii[i], - use_depth_test, - has_3d_select_bias, - &hit_count); - /* Only continue searching when there are multiple options to narrow down. */ - if (hit_count < 2) { + hit = gizmo_find_intersected_3d_intern( + visible_gizmos, visible_gizmos_len_trim, C, co, hotspot_radii[i]); + if (hit != -1) { break; } - - /* Fast path for simple case, one item or nothing. */ - if (use_depth_test == false) { - /* Restart, using depth buffer (slower). */ - use_depth_test = true; - i = -1; - } - hit_prev = hit; - } - /* Narrowing the search area may yield no hits, - * in this case fall back to the previous search. */ - if (hit == -1) { - hit = hit_prev; } - if (use_depth_cache) { - GPU_select_cache_end(); - } + GPU_framebuffer_restore(); + GPU_framebuffer_free(depth_read_fb); if (hit != -1) { const int select_id = hit >> 8; diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c index 02da798495b..d2ade7b0376 100644 --- a/source/blender/windowmanager/intern/wm_draw.c +++ b/source/blender/windowmanager/intern/wm_draw.c @@ -467,7 +467,7 @@ static bool wm_draw_region_bind(bContext *C, ARegion *region, int view) } if (region->draw_buffer->viewport) { - if (G.is_rendering && C != NULL) { + if (G.is_rendering && C != NULL && U.experimental.use_draw_manager_acquire_lock) { Scene *scene = CTX_data_scene(C); RenderEngineType *render_engine_type = RE_engines_find(scene->r.engine); if (RE_engine_is_opengl(render_engine_type)) { diff --git a/source/tools b/source/tools -Subproject c1586ae29595713b597c22f6baa08d6cce42376 +Subproject 53b7c02a062c3d6ec6b38ce670836321b4e78fa diff --git a/tests/python/bl_rigging_symmetrize.py b/tests/python/bl_rigging_symmetrize.py index 3a5a0777d6c..cac5e9b0856 100644 --- a/tests/python/bl_rigging_symmetrize.py +++ b/tests/python/bl_rigging_symmetrize.py @@ -134,9 +134,8 @@ def check_constraints(self, input_arm, expected_arm, bone, exp_bone): "Missmatching constraint boolean in pose.bones[%s].constraints[%s].%s" % ( bone.name, const_name, var)) else: - self.assertAlmostEqual(value, exp_value, - "Missmatching constraint value in pose.bones[%s].constraints[%s].%s" % ( - bone.name, const_name, var)) + msg = "Missmatching constraint value in pose.bones[%s].constraints[%s].%s" % (bone.name, const_name, var) + self.assertAlmostEqual(value, exp_value, places=6, msg=msg) class AbstractAnimationTest: |