diff options
257 files changed, 4561 insertions, 2471 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index c998919622e..80b8bfdbb3d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -366,7 +366,7 @@ if(WIN32 OR APPLE) endif() option(WITH_INPUT_NDOF "Enable NDOF input devices (SpaceNavigator and friends)" ON) if(UNIX AND NOT APPLE) - option(WITH_INSTALL_PORTABLE "Install redistributeable runtime, otherwise install into CMAKE_INSTALL_PREFIX" ON) + option(WITH_INSTALL_PORTABLE "Install redistributable runtime, otherwise install into CMAKE_INSTALL_PREFIX" ON) option(WITH_STATIC_LIBS "Try to link with static libraries, as much as possible, to make blender more portable across distributions" OFF) if(WITH_STATIC_LIBS) option(WITH_BOOST_ICU "Boost uses ICU library (required for linking with static Boost built with libicu)." OFF) diff --git a/intern/cycles/blender/curves.cpp b/intern/cycles/blender/curves.cpp index 04ba7c53878..59e630eef63 100644 --- a/intern/cycles/blender/curves.cpp +++ b/intern/cycles/blender/curves.cpp @@ -707,6 +707,21 @@ static void attr_create_motion(Hair *hair, BL::Attribute &b_attribute, const flo } } +static void attr_create_uv(AttributeSet &attributes, + BL::Curves &b_curves, + BL::Attribute &b_attribute, + const ustring name) +{ + BL::Float2Attribute b_float2_attribute{b_attribute}; + Attribute *attr = attributes.add(ATTR_STD_UV, name); + + float2 *data = attr->data_float2(); + fill_generic_attribute(b_curves, data, ATTR_ELEMENT_CURVE, [&](int i) { + BL::Array<float, 2> v = b_float2_attribute.data[i].vector(); + return make_float2(v[0], v[1]); + }); +} + static void attr_create_generic(Scene *scene, Hair *hair, BL::Curves &b_curves, @@ -715,12 +730,26 @@ static void attr_create_generic(Scene *scene, { AttributeSet &attributes = hair->attributes; static const ustring u_velocity("velocity"); + const bool need_uv = hair->need_attribute(scene, ATTR_STD_UV); + bool have_uv = false; for (BL::Attribute &b_attribute : b_curves.attributes) { const ustring name{b_attribute.name().c_str()}; + const BL::Attribute::domain_enum b_domain = b_attribute.domain(); + const BL::Attribute::data_type_enum b_data_type = b_attribute.data_type(); + if (need_motion && name == u_velocity) { attr_create_motion(hair, b_attribute, motion_scale); + continue; + } + + /* Weak, use first float2 attribute as standard UV. */ + if (need_uv && !have_uv && b_data_type == BL::Attribute::data_type_FLOAT2 && + b_domain == BL::Attribute::domain_CURVE) { + attr_create_uv(attributes, b_curves, b_attribute, name); + have_uv = true; + continue; } if (!hair->need_attribute(scene, name)) { @@ -730,9 +759,6 @@ static void attr_create_generic(Scene *scene, continue; } - const BL::Attribute::domain_enum b_domain = b_attribute.domain(); - const BL::Attribute::data_type_enum b_data_type = b_attribute.data_type(); - AttributeElement element = ATTR_ELEMENT_NONE; switch (b_domain) { case BL::Attribute::domain_POINT: @@ -844,79 +870,84 @@ static void export_hair_curves(Scene *scene, { /* TODO: optimize so we can straight memcpy arrays from Blender? */ + const int num_keys = b_curves.points.length(); + const int num_curves = b_curves.curves.length(); + + hair->resize_curves(num_curves, num_keys); + + float3 *curve_keys = hair->get_curve_keys().data(); + float *curve_radius = hair->get_curve_radius().data(); + int *curve_first_key = hair->get_curve_first_key().data(); + int *curve_shader = hair->get_curve_shader().data(); + /* Add requested attributes. */ - Attribute *attr_intercept = NULL; - Attribute *attr_length = NULL; - Attribute *attr_random = NULL; + float *attr_intercept = NULL; + float *attr_length = NULL; + float *attr_random = NULL; if (hair->need_attribute(scene, ATTR_STD_CURVE_INTERCEPT)) { - attr_intercept = hair->attributes.add(ATTR_STD_CURVE_INTERCEPT); + attr_intercept = hair->attributes.add(ATTR_STD_CURVE_INTERCEPT)->data_float(); } if (hair->need_attribute(scene, ATTR_STD_CURVE_LENGTH)) { - attr_length = hair->attributes.add(ATTR_STD_CURVE_LENGTH); + attr_length = hair->attributes.add(ATTR_STD_CURVE_LENGTH)->data_float(); } if (hair->need_attribute(scene, ATTR_STD_CURVE_RANDOM)) { - attr_random = hair->attributes.add(ATTR_STD_CURVE_RANDOM); + attr_random = hair->attributes.add(ATTR_STD_CURVE_RANDOM)->data_float(); } - /* Reserve memory. */ - const int num_keys = b_curves.points.length(); - const int num_curves = b_curves.curves.length(); - - hair->reserve_curves(num_curves, num_keys); - BL::FloatVectorAttribute b_attr_position = find_curves_position_attribute(b_curves); std::optional<BL::FloatAttribute> b_attr_radius = find_curves_radius_attribute(b_curves); /* Export curves and points. */ - vector<float> points_length; - for (int i = 0; i < num_curves; i++) { const int first_point_index = b_curves.curve_offset_data[i].value(); const int num_points = b_curves.curve_offset_data[i + 1].value() - first_point_index; float3 prev_co = zero_float3(); float length = 0.0f; - if (attr_intercept) { - points_length.clear(); - points_length.reserve(num_points); - } /* Position and radius. */ - for (int i = 0; i < num_points; i++) { - const float3 co = get_float3(b_attr_position.data[first_point_index + i].vector()); - const float radius = b_attr_radius ? b_attr_radius->data[first_point_index + i].value() : - 0.005f; - hair->add_curve_key(co, radius); - - if (attr_intercept) { - if (i > 0) { + for (int j = 0; j < num_points; j++) { + const int point_offset = first_point_index + j; + const float3 co = get_float3(b_attr_position.data[point_offset].vector()); + const float radius = b_attr_radius ? b_attr_radius->data[point_offset].value() : 0.0f; + + curve_keys[point_offset] = co; + curve_radius[point_offset] = radius; + + if (attr_length || attr_intercept) { + if (j > 0) { length += len(co - prev_co); - points_length.push_back(length); } prev_co = co; + + if (attr_intercept) { + attr_intercept[point_offset] = length; + } } } /* Normalized 0..1 attribute along curve. */ - if (attr_intercept) { - for (int i = 0; i < num_points; i++) { - attr_intercept->add((length == 0.0f) ? 0.0f : points_length[i] / length); + if (attr_intercept && length > 0.0f) { + for (int j = 1; j < num_points; j++) { + const int point_offset = first_point_index + j; + attr_intercept[point_offset] /= length; } } + /* Curve length. */ if (attr_length) { - attr_length->add(length); + attr_length[i] = length; } /* Random number per curve. */ if (attr_random != NULL) { - attr_random->add(hash_uint2_to_float(i, 0)); + attr_random[i] = hash_uint2_to_float(i, 0); } /* Curve. */ - const int shader_index = 0; - hair->add_curve(first_point_index, shader_index); + curve_shader[i] = 0; + curve_first_key[i] = first_point_index; } attr_create_generic(scene, hair, b_curves, need_motion, motion_scale); diff --git a/intern/cycles/blender/display_driver.cpp b/intern/cycles/blender/display_driver.cpp index a1bc064be68..61cd88fb433 100644 --- a/intern/cycles/blender/display_driver.cpp +++ b/intern/cycles/blender/display_driver.cpp @@ -913,8 +913,6 @@ void BlenderDisplayDriver::flush() void BlenderDisplayDriver::draw(const Params ¶ms) { /* See do_update_begin() for why no locking is required here. */ - const bool transparent = true; // TODO(sergey): Derive this from Film. - if (use_gl_context_) { gl_context_mutex_.lock(); } @@ -935,10 +933,8 @@ void BlenderDisplayDriver::draw(const Params ¶ms) glWaitSync((GLsync)gl_upload_sync_, 0, GL_TIMEOUT_IGNORED); } - if (transparent) { - glEnable(GL_BLEND); - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - } + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); glActiveTexture(GL_TEXTURE0); @@ -975,9 +971,7 @@ void BlenderDisplayDriver::draw(const Params ¶ms) glDeleteVertexArrays(1, &vertex_array_object); - if (transparent) { - glDisable(GL_BLEND); - } + glDisable(GL_BLEND); gl_render_sync_ = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); glFlush(); diff --git a/intern/cycles/blender/shader.cpp b/intern/cycles/blender/shader.cpp index 4218a9a8a68..113a8e47b6d 100644 --- a/intern/cycles/blender/shader.cpp +++ b/intern/cycles/blender/shader.cpp @@ -794,7 +794,7 @@ static ShaderNode *add_node(Scene *scene, } else { ustring filename = ustring( - image_user_file_path(b_image_user, b_image, b_scene.frame_current())); + image_user_file_path(b_data, b_image_user, b_image, b_scene.frame_current())); image->set_filename(filename); } } @@ -831,7 +831,7 @@ static ShaderNode *add_node(Scene *scene, } else { env->set_filename( - ustring(image_user_file_path(b_image_user, b_image, b_scene.frame_current()))); + ustring(image_user_file_path(b_data, b_image_user, b_image, b_scene.frame_current()))); } } node = env; diff --git a/intern/cycles/blender/sync.cpp b/intern/cycles/blender/sync.cpp index 63e9e1e0e68..429a8e665af 100644 --- a/intern/cycles/blender/sync.cpp +++ b/intern/cycles/blender/sync.cpp @@ -412,7 +412,15 @@ void BlenderSync::sync_integrator(BL::ViewLayer &b_view_layer, bool background) integrator->set_direct_light_sampling_type(direct_light_sampling_type); #endif - const DenoiseParams denoise_params = get_denoise_params(b_scene, b_view_layer, background); + DenoiseParams denoise_params = get_denoise_params(b_scene, b_view_layer, background); + + /* No denoising support for vertex color baking, vertices packed into image + * buffer have no relation to neighbors. */ + if (scene->bake_manager->get_baking() && + b_scene.render().bake().target() != BL::BakeSettings::target_IMAGE_TEXTURES) { + denoise_params.use = false; + } + integrator->set_use_denoise(denoise_params.use); /* Only update denoiser parameters if the denoiser is actually used. This allows to tweak diff --git a/intern/cycles/blender/util.h b/intern/cycles/blender/util.h index 49cecb6d0f3..dbdfbaddaf1 100644 --- a/intern/cycles/blender/util.h +++ b/intern/cycles/blender/util.h @@ -21,7 +21,8 @@ extern "C" { void BKE_image_user_frame_calc(void *ima, void *iuser, int cfra); -void BKE_image_user_file_path_ex(void *iuser, void *ima, char *path, bool resolve_udim); +void BKE_image_user_file_path_ex( + void *bmain, void *iuser, void *ima, char *path, bool resolve_udim, bool resolve_multiview); unsigned char *BKE_image_get_pixels_for_frame(void *image, int frame, int tile); float *BKE_image_get_float_pixels_for_frame(void *image, int frame, int tile); } @@ -281,12 +282,15 @@ static inline int render_resolution_y(BL::RenderSettings &b_render) return b_render.resolution_y() * b_render.resolution_percentage() / 100; } -static inline string image_user_file_path(BL::ImageUser &iuser, BL::Image &ima, int cfra) +static inline string image_user_file_path(BL::BlendData &data, + BL::ImageUser &iuser, + BL::Image &ima, + int cfra) { char filepath[1024]; iuser.tile(0); BKE_image_user_frame_calc(ima.ptr.data, iuser.ptr.data, cfra); - BKE_image_user_file_path_ex(iuser.ptr.data, ima.ptr.data, filepath, false); + BKE_image_user_file_path_ex(data.ptr.data, iuser.ptr.data, ima.ptr.data, filepath, false, true); return string(filepath); } diff --git a/intern/cycles/bvh/bvh.h b/intern/cycles/bvh/bvh.h index be390f8a673..19ebf7f68ba 100644 --- a/intern/cycles/bvh/bvh.h +++ b/intern/cycles/bvh/bvh.h @@ -74,6 +74,13 @@ class BVH { { } + virtual void replace_geometry(const vector<Geometry *> &geometry, + const vector<Object *> &objects) + { + this->geometry = geometry; + this->objects = objects; + } + protected: BVH(const BVHParams ¶ms, const vector<Geometry *> &geometry, diff --git a/intern/cycles/bvh/multi.cpp b/intern/cycles/bvh/multi.cpp index 7211720b56b..d9ee2fce966 100644 --- a/intern/cycles/bvh/multi.cpp +++ b/intern/cycles/bvh/multi.cpp @@ -21,4 +21,12 @@ BVHMulti::~BVHMulti() } } +void BVHMulti::replace_geometry(const vector<Geometry *> &geometry, + const vector<Object *> &objects) +{ + foreach (BVH *bvh, sub_bvhs) { + bvh->replace_geometry(geometry, objects); + } +} + CCL_NAMESPACE_END diff --git a/intern/cycles/bvh/multi.h b/intern/cycles/bvh/multi.h index 824899f3101..aafbfae19e0 100644 --- a/intern/cycles/bvh/multi.h +++ b/intern/cycles/bvh/multi.h @@ -19,6 +19,9 @@ class BVHMulti : public BVH { const vector<Geometry *> &geometry, const vector<Object *> &objects); virtual ~BVHMulti(); + + virtual void replace_geometry(const vector<Geometry *> &geometry, + const vector<Object *> &objects); }; CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt index dfcd75a135e..3f857be0dfa 100644 --- a/intern/cycles/kernel/CMakeLists.txt +++ b/intern/cycles/kernel/CMakeLists.txt @@ -749,10 +749,8 @@ if(WITH_CYCLES_DEVICE_ONEAPI) if (NOT DEFINED CYCLES_ONEAPI_SYCL_OPTIONS_spir64_gen) SET (CYCLES_ONEAPI_SYCL_OPTIONS_spir64_gen "${CYCLES_ONEAPI_SYCL_OPTIONS_spir64}" CACHE STRING "Extra build options for spir64_gen target") endif() - # enabling zebin (graphics binary format with improved compatibility) on Windows only while support on Linux isn't available yet - if(WIN32) - string(PREPEND CYCLES_ONEAPI_SYCL_OPTIONS_spir64_gen "--format zebin ") - endif() + # Enable zebin, a graphics binary format with improved compatibility. + string(PREPEND CYCLES_ONEAPI_SYCL_OPTIONS_spir64_gen "--format zebin ") string(PREPEND CYCLES_ONEAPI_SYCL_OPTIONS_spir64_gen "-device ${CYCLES_ONEAPI_SPIR64_GEN_DEVICES} ") if (WITH_CYCLES_ONEAPI_BINARIES) diff --git a/intern/cycles/kernel/device/cpu/bvh.h b/intern/cycles/kernel/device/cpu/bvh.h index 6c06232a692..d9267e1cd6d 100644 --- a/intern/cycles/kernel/device/cpu/bvh.h +++ b/intern/cycles/kernel/device/cpu/bvh.h @@ -114,27 +114,37 @@ ccl_device_inline bool kernel_embree_is_self_intersection(const KernelGlobals kg const RTCHit *hit, const Ray *ray) { - bool status = false; + int object, prim; + if (hit->instID[0] != RTC_INVALID_GEOMETRY_ID) { - const int oID = hit->instID[0] / 2; - if ((ray->self.object == oID) || (ray->self.light_object == oID)) { + object = hit->instID[0] / 2; + if ((ray->self.object == object) || (ray->self.light_object == object)) { RTCScene inst_scene = (RTCScene)rtcGetGeometryUserData( rtcGetGeometry(kernel_data.device_bvh, hit->instID[0])); - const int pID = hit->primID + - (intptr_t)rtcGetGeometryUserData(rtcGetGeometry(inst_scene, hit->geomID)); - status = intersection_skip_self_shadow(ray->self, oID, pID); + prim = hit->primID + + (intptr_t)rtcGetGeometryUserData(rtcGetGeometry(inst_scene, hit->geomID)); + } + else { + return false; } } else { - const int oID = hit->geomID / 2; - if ((ray->self.object == oID) || (ray->self.light_object == oID)) { - const int pID = hit->primID + (intptr_t)rtcGetGeometryUserData( - rtcGetGeometry(kernel_data.device_bvh, hit->geomID)); - status = intersection_skip_self_shadow(ray->self, oID, pID); + object = hit->geomID / 2; + if ((ray->self.object == object) || (ray->self.light_object == object)) { + prim = hit->primID + + (intptr_t)rtcGetGeometryUserData(rtcGetGeometry(kernel_data.device_bvh, hit->geomID)); } + else { + return false; + } + } + + const bool is_hair = hit->geomID & 1; + if (is_hair) { + prim = kernel_data_fetch(curve_segments, prim).prim; } - return status; + return intersection_skip_self_shadow(ray->self, object, prim); } ccl_device_inline void kernel_embree_convert_hit(KernelGlobals kg, diff --git a/intern/cycles/kernel/device/metal/kernel.metal b/intern/cycles/kernel/device/metal/kernel.metal index 3d173b0d601..3de8c069c30 100644 --- a/intern/cycles/kernel/device/metal/kernel.metal +++ b/intern/cycles/kernel/device/metal/kernel.metal @@ -180,11 +180,6 @@ bool metalrt_shadow_all_hit(constant KernelParamsMetal &launch_params_metal, } # endif - if (intersection_skip_self_shadow(payload.self, object, prim)) { - /* continue search */ - return true; - } - const float u = barycentrics.x; const float v = barycentrics.y; int type = 0; @@ -205,6 +200,11 @@ bool metalrt_shadow_all_hit(constant KernelParamsMetal &launch_params_metal, } # endif + if (intersection_skip_self_shadow(payload.self, object, prim)) { + /* continue search */ + return true; + } + # ifndef __TRANSPARENT_SHADOWS__ /* No transparent shadows support compiled in, make opaque. */ payload.result = true; @@ -346,6 +346,14 @@ inline TReturnType metalrt_visibility_test( } #endif + if (intersection_type == METALRT_HIT_TRIANGLE) { + } +# ifdef __HAIR__ + else { + prim = kernel_data_fetch(curve_segments, prim).prim; + } +# endif + /* Shadow ray early termination. */ if (visibility & PATH_RAY_SHADOW_OPAQUE) { if (intersection_skip_self_shadow(payload.self, object, prim)) { diff --git a/intern/cycles/kernel/device/oneapi/image.h b/intern/cycles/kernel/device/oneapi/image.h index 6681977a675..2417b8eac3b 100644 --- a/intern/cycles/kernel/device/oneapi/image.h +++ b/intern/cycles/kernel/device/oneapi/image.h @@ -81,10 +81,15 @@ ccl_device_inline float4 svm_image_texture_read_2d(int id, int x, int y) x = svm_image_texture_wrap_periodic(x, info.width); y = svm_image_texture_wrap_periodic(y, info.height); } - else { + else if (info.extension == EXTENSION_EXTEND) { x = svm_image_texture_wrap_clamp(x, info.width); y = svm_image_texture_wrap_clamp(y, info.height); } + else { + if (x < 0 || x >= info.width || y < 0 || y >= info.height) { + return make_float4(0.0f, 0.0f, 0.0f, 0.0f); + } + } return svm_image_texture_read(info, x, y, 0); } @@ -99,11 +104,16 @@ ccl_device_inline float4 svm_image_texture_read_3d(int id, int x, int y, int z) y = svm_image_texture_wrap_periodic(y, info.height); z = svm_image_texture_wrap_periodic(z, info.depth); } - else { + else if (info.extension == EXTENSION_EXTEND) { x = svm_image_texture_wrap_clamp(x, info.width); y = svm_image_texture_wrap_clamp(y, info.height); z = svm_image_texture_wrap_clamp(z, info.depth); } + else { + if (x < 0 || x >= info.width || y < 0 || y >= info.height || z < 0 || z >= info.depth) { + return make_float4(0.0f, 0.0f, 0.0f, 0.0f); + } + } return svm_image_texture_read(info, x, y, z); } @@ -128,12 +138,6 @@ ccl_device float4 kernel_tex_image_interp(KernelGlobals, int id, float x, float { const TextureInfo &info = kernel_data_fetch(texture_info, id); - if (info.extension == EXTENSION_CLIP) { - if (x < 0.0f || y < 0.0f || x > 1.0f || y > 1.0f) { - return make_float4(0.0f, 0.0f, 0.0f, 0.0f); - } - } - if (info.interpolation == INTERPOLATION_CLOSEST) { /* Closest interpolation. */ int ix, iy; @@ -315,12 +319,6 @@ ccl_device float4 kernel_tex_image_interp_3d(KernelGlobals, int id, float3 P, in } #endif else { - if (info.extension == EXTENSION_CLIP) { - if (x < 0.0f || y < 0.0f || z < 0.0f || x > 1.0f || y > 1.0f || z > 1.0f) { - return make_float4(0.0f, 0.0f, 0.0f, 0.0f); - } - } - x *= info.width; y *= info.height; z *= info.depth; diff --git a/intern/cycles/kernel/device/optix/bvh.h b/intern/cycles/kernel/device/optix/bvh.h index d1d342f6034..fb9907709ce 100644 --- a/intern/cycles/kernel/device/optix/bvh.h +++ b/intern/cycles/kernel/device/optix/bvh.h @@ -143,14 +143,10 @@ extern "C" __global__ void __anyhit__kernel_optix_shadow_all_hit() } # endif - ccl_private Ray *const ray = get_payload_ptr_6<Ray>(); - if (intersection_skip_self_shadow(ray->self, object, prim)) { - return optixIgnoreIntersection(); - } - float u = 0.0f, v = 0.0f; int type = 0; if (optixIsTriangleHit()) { + /* Triangle. */ const float2 barycentrics = optixGetTriangleBarycentrics(); u = barycentrics.x; v = barycentrics.y; @@ -158,6 +154,7 @@ extern "C" __global__ void __anyhit__kernel_optix_shadow_all_hit() } # ifdef __HAIR__ else if ((optixGetHitKind() & (~PRIMITIVE_MOTION)) != PRIMITIVE_POINT) { + /* Curve. */ u = __uint_as_float(optixGetAttribute_0()); v = __uint_as_float(optixGetAttribute_1()); @@ -174,11 +171,17 @@ extern "C" __global__ void __anyhit__kernel_optix_shadow_all_hit() } # endif else { + /* Point. */ type = kernel_data_fetch(objects, object).primitive_type; u = 0.0f; v = 0.0f; } + ccl_private Ray *const ray = get_payload_ptr_6<Ray>(); + if (intersection_skip_self_shadow(ray->self, object, prim)) { + return optixIgnoreIntersection(); + } + # ifndef __TRANSPARENT_SHADOWS__ /* No transparent shadows support compiled in, make opaque. */ optixSetPayload_5(true); @@ -307,7 +310,17 @@ extern "C" __global__ void __anyhit__kernel_optix_visibility_test() } #endif - const int prim = optixGetPrimitiveIndex(); + int prim = optixGetPrimitiveIndex(); + if (optixIsTriangleHit()) { + /* Triangle. */ + } +#ifdef __HAIR__ + else if ((optixGetHitKind() & (~PRIMITIVE_MOTION)) != PRIMITIVE_POINT) { + /* Curve. */ + prim = kernel_data_fetch(curve_segments, prim).prim; + } +#endif + ccl_private Ray *const ray = get_payload_ptr_6<Ray>(); if (visibility & PATH_RAY_SHADOW_OPAQUE) { diff --git a/intern/cycles/kernel/geom/motion_triangle_shader.h b/intern/cycles/kernel/geom/motion_triangle_shader.h index 236e737b785..413a61b380a 100644 --- a/intern/cycles/kernel/geom/motion_triangle_shader.h +++ b/intern/cycles/kernel/geom/motion_triangle_shader.h @@ -68,8 +68,8 @@ ccl_device_noinline void motion_triangle_shader_setup(KernelGlobals kg, sd->N = Ng; /* Compute derivatives of P w.r.t. uv. */ #ifdef __DPDU__ - sd->dPdu = (verts[0] - verts[2]); - sd->dPdv = (verts[1] - verts[2]); + sd->dPdu = (verts[1] - verts[0]); + sd->dPdv = (verts[2] - verts[0]); #endif /* Compute smooth normal. */ if (sd->shader & SHADER_SMOOTH_NORMAL) { @@ -89,7 +89,7 @@ ccl_device_noinline void motion_triangle_shader_setup(KernelGlobals kg, float u = sd->u; float v = sd->v; float w = 1.0f - u - v; - sd->N = (u * normals[0] + v * normals[1] + w * normals[2]); + sd->N = (w * normals[0] + u * normals[1] + v * normals[2]); } } diff --git a/intern/cycles/kernel/integrator/shade_surface.h b/intern/cycles/kernel/integrator/shade_surface.h index 1514b3956ad..70b20a93b6a 100644 --- a/intern/cycles/kernel/integrator/shade_surface.h +++ b/intern/cycles/kernel/integrator/shade_surface.h @@ -43,11 +43,9 @@ ccl_device_forceinline bool integrate_surface_holdout(KernelGlobals kg, if (((sd->flag & SD_HOLDOUT) || (sd->object_flag & SD_OBJECT_HOLDOUT_MASK)) && (path_flag & PATH_RAY_TRANSPARENT_BACKGROUND)) { const float3 holdout_weight = shader_holdout_apply(kg, sd); - if (kernel_data.background.transparent) { - const float3 throughput = INTEGRATOR_STATE(state, path, throughput); - const float transparent = average(holdout_weight * throughput); - kernel_accum_holdout(kg, state, path_flag, transparent, render_buffer); - } + const float3 throughput = INTEGRATOR_STATE(state, path, throughput); + const float transparent = average(holdout_weight * throughput); + kernel_accum_holdout(kg, state, path_flag, transparent, render_buffer); if (isequal(holdout_weight, one_float3())) { return false; } diff --git a/intern/cycles/kernel/integrator/volume_stack.h b/intern/cycles/kernel/integrator/volume_stack.h index 97a0f0f386c..675e1927fc0 100644 --- a/intern/cycles/kernel/integrator/volume_stack.h +++ b/intern/cycles/kernel/integrator/volume_stack.h @@ -39,7 +39,7 @@ ccl_device void volume_stack_enter_exit(KernelGlobals kg, break; } - if (entry.object == sd->object) { + if (entry.object == sd->object && entry.shader == sd->shader) { /* Shift back next stack entries. */ do { entry = stack_read(i + 1); @@ -61,7 +61,7 @@ ccl_device void volume_stack_enter_exit(KernelGlobals kg, } /* Already in the stack? then we have nothing to do. */ - if (entry.object == sd->object) { + if (entry.object == sd->object && entry.shader == sd->shader) { return; } } diff --git a/intern/cycles/scene/geometry.cpp b/intern/cycles/scene/geometry.cpp index 67ff118692e..ae8dcaa43b6 100644 --- a/intern/cycles/scene/geometry.cpp +++ b/intern/cycles/scene/geometry.cpp @@ -217,8 +217,7 @@ void Geometry::compute_bvh(Device *device, if (bvh && !need_update_rebuild) { progress->set_status(msg, "Refitting BVH"); - bvh->geometry = geometry; - bvh->objects = objects; + bvh->replace_geometry(geometry, objects); device->build_bvh(bvh, *progress, true); } diff --git a/intern/ghost/CMakeLists.txt b/intern/ghost/CMakeLists.txt index c681dc368bb..0ac3a234946 100644 --- a/intern/ghost/CMakeLists.txt +++ b/intern/ghost/CMakeLists.txt @@ -5,6 +5,7 @@ set(INC . ../clog ../glew-mx + ../../source/blender/blenlib ../../source/blender/imbuf ../../source/blender/makesdna ) @@ -25,6 +26,7 @@ set(SRC intern/GHOST_ISystemPaths.cpp intern/GHOST_ModifierKeys.cpp intern/GHOST_Path-api.cpp + intern/GHOST_PathUtils.cpp intern/GHOST_Rect.cpp intern/GHOST_System.cpp intern/GHOST_TimerManager.cpp @@ -59,6 +61,7 @@ set(SRC intern/GHOST_EventTrackpad.h intern/GHOST_EventWheel.h intern/GHOST_ModifierKeys.h + intern/GHOST_PathUtils.h intern/GHOST_System.h intern/GHOST_SystemPaths.h intern/GHOST_TimerManager.h diff --git a/intern/ghost/intern/GHOST_DropTargetX11.cpp b/intern/ghost/intern/GHOST_DropTargetX11.cpp index 252a8bfd095..4da3c7c996d 100644 --- a/intern/ghost/intern/GHOST_DropTargetX11.cpp +++ b/intern/ghost/intern/GHOST_DropTargetX11.cpp @@ -7,6 +7,7 @@ #include "GHOST_DropTargetX11.h" #include "GHOST_Debug.h" +#include "GHOST_PathUtils.h" #include "GHOST_utildefines.h" #include <cassert> @@ -97,89 +98,10 @@ GHOST_DropTargetX11::~GHOST_DropTargetX11() } } -/* Based on: https://stackoverflow.com/a/2766963/432509 */ - -using DecodeState_e = enum DecodeState_e { - /** Searching for an ampersand to convert. */ - STATE_SEARCH = 0, - /** Convert the two proceeding characters from hex. */ - STATE_CONVERTING -}; - -void GHOST_DropTargetX11::UrlDecode(char *decodedOut, int bufferSize, const char *encodedIn) -{ - unsigned int i; - unsigned int len = strlen(encodedIn); - DecodeState_e state = STATE_SEARCH; - int j; - unsigned int asciiCharacter; - char tempNumBuf[3] = {0}; - bool bothDigits = true; - - memset(decodedOut, 0, bufferSize); - - for (i = 0; i < len; ++i) { - switch (state) { - case STATE_SEARCH: - if (encodedIn[i] != '%') { - strncat(decodedOut, &encodedIn[i], 1); - assert((int)strlen(decodedOut) < bufferSize); - break; - } - - /* We are now converting */ - state = STATE_CONVERTING; - break; - - case STATE_CONVERTING: - bothDigits = true; - - /* Create a buffer to hold the hex. For example, if %20, this - * buffer would hold 20 (in ASCII) */ - memset(tempNumBuf, 0, sizeof(tempNumBuf)); - - /* Conversion complete (i.e. don't convert again next iter) */ - state = STATE_SEARCH; - - strncpy(tempNumBuf, &encodedIn[i], 2); - - /* Ensure both characters are hexadecimal */ - - for (j = 0; j < 2; ++j) { - if (!isxdigit(tempNumBuf[j])) { - bothDigits = false; - } - } - - if (!bothDigits) { - break; - } - /* Convert two hexadecimal characters into one character */ - sscanf(tempNumBuf, "%x", &asciiCharacter); - - /* Ensure we aren't going to overflow */ - assert((int)strlen(decodedOut) < bufferSize); - - /* Concatenate this character onto the output */ - strncat(decodedOut, (char *)&asciiCharacter, 1); - - /* Skip the next character */ - i++; - break; - } - } -} - char *GHOST_DropTargetX11::FileUrlDecode(char *fileUrl) { if (strncmp(fileUrl, "file://", 7) == 0) { - /* assume one character of encoded URL can be expanded to 4 chars max */ - int decodedSize = 4 * strlen(fileUrl) + 1; - char *decodedPath = (char *)malloc(decodedSize); - - UrlDecode(decodedPath, decodedSize, fileUrl + 7); - - return decodedPath; + return GHOST_URL_decode_alloc(fileUrl + 7); } return nullptr; diff --git a/intern/ghost/intern/GHOST_DropTargetX11.h b/intern/ghost/intern/GHOST_DropTargetX11.h index f0ef27697e1..db73ddff70f 100644 --- a/intern/ghost/intern/GHOST_DropTargetX11.h +++ b/intern/ghost/intern/GHOST_DropTargetX11.h @@ -65,14 +65,6 @@ class GHOST_DropTargetX11 { void *getURIListGhostData(unsigned char *dropBuffer, int dropBufferSize); /** - * Decode URL (i.e. converts `file:///a%20b/test` to `file:///a b/test`) - * \param decodedOut: - buffer for decoded URL. - * \param bufferSize: - size of output buffer. - * \param encodedIn: - input encoded buffer to be decoded. - */ - void UrlDecode(char *decodedOut, int bufferSize, const char *encodedIn); - - /** * Fully decode file URL (i.e. converts `file:///a%20b/test` to `/a b/test`) * \param fileUrl: - file path URL to be fully decoded. * \return decoded file path (result should be free-d). diff --git a/intern/ghost/intern/GHOST_PathUtils.cpp b/intern/ghost/intern/GHOST_PathUtils.cpp new file mode 100644 index 00000000000..3b57480039a --- /dev/null +++ b/intern/ghost/intern/GHOST_PathUtils.cpp @@ -0,0 +1,101 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2010 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup GHOST + */ + +#include <cassert> +#include <cctype> +#include <cstdio> +#include <cstdlib> +#include <cstring> + +#include "GHOST_PathUtils.h" +#include "GHOST_Types.h" + +/* Based on: https://stackoverflow.com/a/2766963/432509 */ + +using DecodeState_e = enum DecodeState_e { + /** Searching for an ampersand to convert. */ + STATE_SEARCH = 0, + /** Convert the two proceeding characters from hex. */ + STATE_CONVERTING +}; + +void GHOST_URL_decode(char *buf_dst, int buf_dst_size, const char *buf_src) +{ + const unsigned int buf_src_len = strlen(buf_src); + DecodeState_e state = STATE_SEARCH; + unsigned int ascii_character; + char temp_num_buf[3] = {0}; + + memset(buf_dst, 0, buf_dst_size); + + for (unsigned int i = 0; i < buf_src_len; i++) { + switch (state) { + case STATE_SEARCH: { + if (buf_src[i] != '%') { + strncat(buf_dst, &buf_src[i], 1); + assert((int)strlen(buf_dst) < buf_dst_size); + break; + } + + /* We are now converting. */ + state = STATE_CONVERTING; + break; + } + case STATE_CONVERTING: { + bool both_digits = true; + + /* Create a buffer to hold the hex. For example, if `%20`, + * this buffer would hold 20 (in ASCII). */ + memset(temp_num_buf, 0, sizeof(temp_num_buf)); + + /* Conversion complete (i.e. don't convert again next iteration). */ + state = STATE_SEARCH; + + strncpy(temp_num_buf, &buf_src[i], 2); + + /* Ensure both characters are hexadecimal. */ + for (int j = 0; j < 2; j++) { + if (!isxdigit(temp_num_buf[j])) { + both_digits = false; + } + } + + if (!both_digits) { + break; + } + /* Convert two hexadecimal characters into one character. */ + sscanf(temp_num_buf, "%x", &ascii_character); + + /* Ensure we aren't going to overflow. */ + assert((int)strlen(buf_dst) < buf_dst_size); + + /* Concatenate this character onto the output. */ + strncat(buf_dst, (char *)&ascii_character, 1); + + /* Skip the next character. */ + i++; + break; + } + } + } +} + +char *GHOST_URL_decode_alloc(const char *buf_src) +{ + /* Assume one character of encoded URL can be expanded to 4 chars max. */ + const size_t decoded_size_max = 4 * strlen(buf_src) + 1; + char *buf_dst = (char *)malloc(decoded_size_max); + GHOST_URL_decode(buf_dst, decoded_size_max, buf_src); + const size_t decoded_size = strlen(buf_dst) + 1; + if (decoded_size != decoded_size_max) { + char *buf_dst_trim = (char *)malloc(decoded_size); + memcpy(buf_dst_trim, buf_dst, decoded_size); + free(buf_dst); + buf_dst = buf_dst_trim; + } + return buf_dst; +} diff --git a/intern/ghost/intern/GHOST_PathUtils.h b/intern/ghost/intern/GHOST_PathUtils.h new file mode 100644 index 00000000000..26a31d1f5c6 --- /dev/null +++ b/intern/ghost/intern/GHOST_PathUtils.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2010 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup GHOST + */ + +#pragma once + +/** + * Decode URL (i.e. converts `file:///a%20b/test` to `file:///a b/test`) + * + * \param buf_dst: Buffer for decoded URL. + * \param buf_dst_maxlen: Size of output buffer. + * \param buf_src: Input encoded buffer to be decoded. + */ +void GHOST_URL_decode(char *buf_dst, int buf_dst_size, const char *buf_src); +/** + * A version of #GHOST_URL_decode that allocates the string & returns it. + * + * \param buf_src: Input encoded buffer to be decoded. + * \return The decoded output buffer. + */ +char *GHOST_URL_decode_alloc(const char *buf_src); diff --git a/intern/ghost/intern/GHOST_SystemWayland.cpp b/intern/ghost/intern/GHOST_SystemWayland.cpp index ebb52bf08cb..af841d16dc6 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.cpp +++ b/intern/ghost/intern/GHOST_SystemWayland.cpp @@ -11,6 +11,7 @@ #include "GHOST_EventDragnDrop.h" #include "GHOST_EventKey.h" #include "GHOST_EventWheel.h" +#include "GHOST_PathUtils.h" #include "GHOST_TimerManager.h" #include "GHOST_WindowManager.h" #include "GHOST_utildefines.h" @@ -1138,6 +1139,7 @@ static void data_device_handle_enter(void *data, input_t *input = static_cast<input_t *>(data); std::lock_guard lock{input->data_offer_dnd_mutex}; + delete input->data_offer_dnd; input->data_offer_dnd = static_cast<data_offer_t *>(wl_data_offer_get_user_data(id)); data_offer_t *data_offer = input->data_offer_dnd; @@ -1197,8 +1199,6 @@ static void data_device_handle_drop(void *data, struct wl_data_device * /*wl_dat input_t *input = static_cast<input_t *>(data); std::lock_guard lock{input->data_offer_dnd_mutex}; - CLOG_INFO(LOG, 2, "drop"); - data_offer_t *data_offer = input->data_offer_dnd; const std::string mime_receive = *std::find_first_of(mime_preference_order.begin(), @@ -1206,6 +1206,8 @@ static void data_device_handle_drop(void *data, struct wl_data_device * /*wl_dat data_offer->types.begin(), data_offer->types.end()); + CLOG_INFO(LOG, 2, "drop mime_recieve=%s", mime_receive.c_str()); + auto read_uris_fn = [](input_t *const input, data_offer_t *data_offer, wl_surface *surface, @@ -1214,9 +1216,15 @@ static void data_device_handle_drop(void *data, struct wl_data_device * /*wl_dat const std::string data = read_pipe(data_offer, mime_receive, nullptr); + CLOG_INFO( + LOG, 2, "drop_read_uris mime_receive=%s, data=%s", mime_receive.c_str(), data.c_str()); + wl_data_offer_finish(data_offer->id); wl_data_offer_destroy(data_offer->id); + if (input->data_offer_dnd == data_offer) { + input->data_offer_dnd = nullptr; + } delete data_offer; data_offer = nullptr; @@ -1224,7 +1232,9 @@ static void data_device_handle_drop(void *data, struct wl_data_device * /*wl_dat if (mime_receive == mime_text_uri) { static constexpr const char *file_proto = "file://"; - static constexpr const char *crlf = "\r\n"; + /* NOTE: some applications CRLF (`\r\n`) GTK3 for e.g. & others don't `pcmanfm-qt`. + * So support both, once `\n` is found, strip the preceding `\r` if found. */ + static constexpr const char *lf = "\n"; GHOST_WindowWayland *win = ghost_wl_surface_user_data(surface); std::vector<std::string> uris; @@ -1233,13 +1243,18 @@ static void data_device_handle_drop(void *data, struct wl_data_device * /*wl_dat while (true) { pos = data.find(file_proto, pos); const size_t start = pos + sizeof(file_proto) - 1; - pos = data.find(crlf, pos); - const size_t end = pos; + pos = data.find(lf, pos); if (pos == std::string::npos) { break; } + /* Account for 'CRLF' case. */ + size_t end = pos; + if (data[end - 1] == '\r') { + end -= 1; + } uris.push_back(data.substr(start, end - start)); + CLOG_INFO(LOG, 2, "drop_read_uris pos=%zu, text_uri=\"%s\"", start, uris.back().c_str()); } GHOST_TStringArray *flist = static_cast<GHOST_TStringArray *>( @@ -1247,10 +1262,10 @@ static void data_device_handle_drop(void *data, struct wl_data_device * /*wl_dat flist->count = int(uris.size()); flist->strings = static_cast<uint8_t **>(malloc(uris.size() * sizeof(uint8_t *))); for (size_t i = 0; i < uris.size(); i++) { - flist->strings[i] = static_cast<uint8_t *>(malloc((uris[i].size() + 1) * sizeof(uint8_t))); - memcpy(flist->strings[i], uris[i].data(), uris[i].size() + 1); + flist->strings[i] = (uint8_t *)GHOST_URL_decode_alloc(uris[i].c_str()); } + CLOG_INFO(LOG, 2, "drop_read_uris_fn file_count=%d", flist->count); const wl_fixed_t scale = win->scale(); system->pushEvent(new GHOST_EventDragnDrop(system->getMilliSeconds(), GHOST_kEventDraggingDropDone, @@ -1263,12 +1278,13 @@ static void data_device_handle_drop(void *data, struct wl_data_device * /*wl_dat else if (ELEM(mime_receive, mime_text_plain, mime_text_utf8)) { /* TODO: enable use of internal functions 'txt_insert_buf' and * 'text_update_edited' to behave like dropped text was pasted. */ + CLOG_INFO(LOG, 2, "drop_read_uris_fn (text_plain, text_utf8), unhandled!"); } wl_display_roundtrip(system->display()); }; /* Pass in `input->focus_dnd` instead of accessing it from `input` since the leave callback - * (#data_device_leave) will clear the value once this function starts. */ + * (#data_device_handle_leave) will clear the value once this function starts. */ std::thread read_thread(read_uris_fn, input, data_offer, input->focus_dnd, mime_receive); read_thread.detach(); } diff --git a/intern/guardedalloc/MEM_guardedalloc.h b/intern/guardedalloc/MEM_guardedalloc.h index fca19fb9731..64987548a11 100644 --- a/intern/guardedalloc/MEM_guardedalloc.h +++ b/intern/guardedalloc/MEM_guardedalloc.h @@ -199,6 +199,15 @@ extern size_t (*MEM_get_peak_memory)(void) ATTR_WARN_UNUSED_RESULT; #ifndef NDEBUG extern const char *(*MEM_name_ptr)(void *vmemh); +/** + * Change the debugging name/string assigned to the memory allocated at \a vmemh. Only affects the + * guarded allocator. The name must be a static string, because only a pointer to it is stored! + * + * Handy when debugging leaking memory allocated by some often called, generic function with a + * unspecific name. A caller with more info can set a more specific name, and see which call to the + * generic function allocates the leaking memory. + */ +extern void (*MEM_name_ptr_set)(void *vmemh, const char *str) ATTR_NONNULL(); #endif /** diff --git a/intern/guardedalloc/intern/mallocn.c b/intern/guardedalloc/intern/mallocn.c index f7979168799..63f06ced31d 100644 --- a/intern/guardedalloc/intern/mallocn.c +++ b/intern/guardedalloc/intern/mallocn.c @@ -49,6 +49,7 @@ size_t (*MEM_get_peak_memory)(void) = MEM_lockfree_get_peak_memory; #ifndef NDEBUG const char *(*MEM_name_ptr)(void *vmemh) = MEM_lockfree_name_ptr; +void (*MEM_name_ptr_set)(void *vmemh, const char *str) = MEM_lockfree_name_ptr_set; #endif void *aligned_malloc(size_t size, size_t alignment) @@ -128,6 +129,7 @@ void MEM_use_lockfree_allocator(void) #ifndef NDEBUG MEM_name_ptr = MEM_lockfree_name_ptr; + MEM_name_ptr_set = MEM_lockfree_name_ptr_set; #endif } @@ -159,5 +161,6 @@ void MEM_use_guarded_allocator(void) #ifndef NDEBUG MEM_name_ptr = MEM_guarded_name_ptr; + MEM_name_ptr_set = MEM_guarded_name_ptr_set; #endif } diff --git a/intern/guardedalloc/intern/mallocn_guarded_impl.c b/intern/guardedalloc/intern/mallocn_guarded_impl.c index 8bf1680e6f8..cd4b99ecde8 100644 --- a/intern/guardedalloc/intern/mallocn_guarded_impl.c +++ b/intern/guardedalloc/intern/mallocn_guarded_impl.c @@ -1199,4 +1199,18 @@ const char *MEM_guarded_name_ptr(void *vmemh) return "MEM_guarded_name_ptr(NULL)"; } + +void MEM_guarded_name_ptr_set(void *vmemh, const char *str) +{ + if (!vmemh) { + return; + } + + MemHead *memh = vmemh; + memh--; + memh->name = str; + if (memh->prev) { + MEMNEXT(memh->prev)->nextname = str; + } +} #endif /* NDEBUG */ diff --git a/intern/guardedalloc/intern/mallocn_intern.h b/intern/guardedalloc/intern/mallocn_intern.h index f8b16ff6ddf..ce5683a04ae 100644 --- a/intern/guardedalloc/intern/mallocn_intern.h +++ b/intern/guardedalloc/intern/mallocn_intern.h @@ -131,6 +131,7 @@ void MEM_lockfree_reset_peak_memory(void); size_t MEM_lockfree_get_peak_memory(void) ATTR_WARN_UNUSED_RESULT; #ifndef NDEBUG const char *MEM_lockfree_name_ptr(void *vmemh); +void MEM_lockfree_name_ptr_set(void *vmemh, const char *str); #endif /* Prototypes for fully guarded allocator functions */ @@ -174,6 +175,7 @@ void MEM_guarded_reset_peak_memory(void); size_t MEM_guarded_get_peak_memory(void) ATTR_WARN_UNUSED_RESULT; #ifndef NDEBUG const char *MEM_guarded_name_ptr(void *vmemh); +void MEM_guarded_name_ptr_set(void *vmemh, const char *str); #endif #ifdef __cplusplus diff --git a/intern/guardedalloc/intern/mallocn_lockfree_impl.c b/intern/guardedalloc/intern/mallocn_lockfree_impl.c index 300e2000a14..b5ee539ff4d 100644 --- a/intern/guardedalloc/intern/mallocn_lockfree_impl.c +++ b/intern/guardedalloc/intern/mallocn_lockfree_impl.c @@ -426,4 +426,8 @@ const char *MEM_lockfree_name_ptr(void *vmemh) return "MEM_lockfree_name_ptr(NULL)"; } + +void MEM_lockfree_name_ptr_set(void *UNUSED(vmemh), const char *UNUSED(str)) +{ +} #endif /* NDEBUG */ diff --git a/release/datafiles/fonts/MaterialIcons-Variable.woff2 b/release/datafiles/fonts/MaterialIcons-Variable.woff2 Binary files differdeleted file mode 100644 index 048802a6454..00000000000 --- a/release/datafiles/fonts/MaterialIcons-Variable.woff2 +++ /dev/null diff --git a/release/scripts/modules/gpu_extras/presets.py b/release/scripts/modules/gpu_extras/presets.py index 222d9032cfd..eba8d6c161d 100644 --- a/release/scripts/modules/gpu_extras/presets.py +++ b/release/scripts/modules/gpu_extras/presets.py @@ -24,7 +24,7 @@ def draw_circle_2d(position, color, radius, *, segments=None): ) if segments is None: - max_pixel_error = 0.25 # TODO: multiply 0.5 by display dpi + max_pixel_error = 0.25 # TODO: multiply 0.5 by display dpi segments = int(ceil(pi / acos(1.0 - max_pixel_error / radius))) segments = max(segments, 8) segments = min(segments, 1000) diff --git a/release/scripts/startup/bl_operators/geometry_nodes.py b/release/scripts/startup/bl_operators/geometry_nodes.py index ea4d40bb778..1f573543e7b 100644 --- a/release/scripts/startup/bl_operators/geometry_nodes.py +++ b/release/scripts/startup/bl_operators/geometry_nodes.py @@ -3,9 +3,11 @@ import bpy from bpy.types import Operator +from bpy.app.translations import pgettext_data as data_ + def geometry_node_group_empty_new(): - group = bpy.data.node_groups.new("Geometry Nodes", 'GeometryNodeTree') + group = bpy.data.node_groups.new(data_("Geometry Nodes"), 'GeometryNodeTree') group.inputs.new('NodeSocketGeometry', "Geometry") group.outputs.new('NodeSocketGeometry', "Geometry") input_node = group.nodes.new('NodeGroupInput') @@ -45,7 +47,7 @@ class NewGeometryNodesModifier(Operator): return geometry_modifier_poll(context) def execute(self, context): - modifier = context.object.modifiers.new("GeometryNodes", "NODES") + modifier = context.object.modifiers.new(data_("GeometryNodes"), "NODES") if not modifier: return {'CANCELLED'} diff --git a/release/scripts/startup/bl_operators/presets.py b/release/scripts/startup/bl_operators/presets.py index cde4348977f..6bfce948412 100644 --- a/release/scripts/startup/bl_operators/presets.py +++ b/release/scripts/startup/bl_operators/presets.py @@ -11,11 +11,13 @@ from bpy.props import ( StringProperty, ) +from bpy.app.translations import pgettext_data as data_ + # For preset popover menu WindowManager.preset_name = StringProperty( name="Preset Name", description="Name for new preset", - default="New Preset" + default=data_("New Preset") ) diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py index 3ab124bf4cf..7e7dbbc387e 100644 --- a/release/scripts/startup/bl_operators/wm.py +++ b/release/scripts/startup/bl_operators/wm.py @@ -2483,8 +2483,8 @@ class BatchRenameAction(bpy.types.PropertyGroup): ) # Weak, add/remove as properties. - op_add: BoolProperty() - op_remove: BoolProperty() + op_add: BoolProperty(name="Add") + op_remove: BoolProperty(name="Remove") class WM_OT_batch_rename(Operator): @@ -2570,7 +2570,7 @@ class WM_OT_batch_rename(Operator): if only_selected else scene.sequence_editor.sequences_all, "name", - "Strip(s)", + iface_("Strip(s)"), ) elif space_type == 'NODE_EDITOR': data_type_test = 'NODE' @@ -2582,7 +2582,7 @@ class WM_OT_batch_rename(Operator): if only_selected else list(space.node_tree.nodes), "name", - "Node(s)", + iface_("Node(s)"), ) elif space_type == 'OUTLINER': data_type_test = 'COLLECTION' @@ -2594,7 +2594,7 @@ class WM_OT_batch_rename(Operator): if only_selected else scene.collection.children_recursive, "name", - "Collection(s)", + iface_("Collection(s)"), ) else: if mode == 'POSE' or (mode == 'WEIGHT_PAINT' and context.pose_object): @@ -2607,7 +2607,7 @@ class WM_OT_batch_rename(Operator): if only_selected else [pbone.bone for ob in context.objects_in_mode_unique_data for pbone in ob.pose.bones], "name", - "Bone(s)", + iface_("Bone(s)"), ) elif mode == 'EDIT_ARMATURE': data_type_test = 'BONE' @@ -2619,24 +2619,24 @@ class WM_OT_batch_rename(Operator): if only_selected else [ebone for ob in context.objects_in_mode_unique_data for ebone in ob.data.edit_bones], "name", - "Edit Bone(s)", + iface_("Edit Bone(s)"), ) if check_context: return 'OBJECT' object_data_type_attrs_map = { - 'MESH': ("meshes", "Mesh(es)", bpy.types.Mesh), - 'CURVE': ("curves", "Curve(s)", bpy.types.Curve), - 'META': ("metaballs", "Metaball(s)", bpy.types.MetaBall), - 'VOLUME': ("volumes", "Volume(s)", bpy.types.Volume), - 'GPENCIL': ("grease_pencils", "Grease Pencil(s)", bpy.types.GreasePencil), - 'ARMATURE': ("armatures", "Armature(s)", bpy.types.Armature), - 'LATTICE': ("lattices", "Lattice(s)", bpy.types.Lattice), - 'LIGHT': ("lights", "Light(s)", bpy.types.Light), - 'LIGHT_PROBE': ("light_probes", "Light Probe(s)", bpy.types.LightProbe), - 'CAMERA': ("cameras", "Camera(s)", bpy.types.Camera), - 'SPEAKER': ("speakers", "Speaker(s)", bpy.types.Speaker), + 'MESH': ("meshes", iface_("Mesh(es)"), bpy.types.Mesh), + 'CURVE': ("curves", iface_("Curve(s)"), bpy.types.Curve), + 'META': ("metaballs", iface_("Metaball(s)"), bpy.types.MetaBall), + 'VOLUME': ("volumes", iface_("Volume(s)"), bpy.types.Volume), + 'GPENCIL': ("grease_pencils", iface_("Grease Pencil(s)"), bpy.types.GreasePencil), + 'ARMATURE': ("armatures", iface_("Armature(s)"), bpy.types.Armature), + 'LATTICE': ("lattices", iface_("Lattice(s)"), bpy.types.Lattice), + 'LIGHT': ("lights", iface_("Light(s)"), bpy.types.Light), + 'LIGHT_PROBE': ("light_probes", iface_("Light Probe(s)"), bpy.types.LightProbe), + 'CAMERA': ("cameras", iface_("Camera(s)"), bpy.types.Camera), + 'SPEAKER': ("speakers", iface_("Speaker(s)"), bpy.types.Speaker), } # Finish with space types. @@ -2654,7 +2654,7 @@ class WM_OT_batch_rename(Operator): if only_selected else [id for id in bpy.data.objects if id.library is None], "name", - "Object(s)", + iface_("Object(s)"), ) elif data_type == 'COLLECTION': data = ( @@ -2669,7 +2669,7 @@ class WM_OT_batch_rename(Operator): if only_selected else [id for id in bpy.data.collections if id.library is None], "name", - "Collection(s)", + iface_("Collection(s)"), ) elif data_type == 'MATERIAL': data = ( @@ -2688,7 +2688,7 @@ class WM_OT_batch_rename(Operator): if only_selected else [id for id in bpy.data.materials if id.library is None], "name", - "Material(s)", + iface_("Material(s)"), ) elif data_type in object_data_type_attrs_map.keys(): attr, descr, ty = object_data_type_attrs_map[data_type] @@ -2913,7 +2913,7 @@ class WM_OT_batch_rename(Operator): row.prop(action, "op_remove", text="", icon='REMOVE') row.prop(action, "op_add", text="", icon='ADD') - layout.label(text="Rename %d %s" % (len(self._data[0]), self._data[2])) + layout.label(text=iface_("Rename %d %s") % (len(self._data[0]), self._data[2])) def check(self, context): changed = False @@ -2974,7 +2974,7 @@ class WM_OT_batch_rename(Operator): change_len += 1 total_len += 1 - self.report({'INFO'}, "Renamed %d of %d %s" % (change_len, total_len, descr)) + self.report({'INFO'}, tip_("Renamed %d of %d %s") % (change_len, total_len, descr)) return {'FINISHED'} diff --git a/release/scripts/startup/bl_ui/properties_render.py b/release/scripts/startup/bl_ui/properties_render.py index 8a637b00362..dafe32c5e5d 100644 --- a/release/scripts/startup/bl_ui/properties_render.py +++ b/release/scripts/startup/bl_ui/properties_render.py @@ -223,7 +223,7 @@ class RENDER_PT_motion_blur_curve(RenderButtonsPanel, Panel): class RENDER_PT_eevee_depth_of_field(RenderButtonsPanel, Panel): bl_label = "Depth of Field" bl_options = {'DEFAULT_CLOSED'} - COMPAT_ENGINES = {'BLENDER_EEVEE', 'BLENDER_EEVEE_NEXT'} + COMPAT_ENGINES = {'BLENDER_EEVEE'} @classmethod def poll(cls, context): @@ -248,6 +248,32 @@ class RENDER_PT_eevee_depth_of_field(RenderButtonsPanel, Panel): col.prop(props, "bokeh_overblur") +class RENDER_PT_eevee_next_depth_of_field(RenderButtonsPanel, Panel): + bl_label = "Depth of Field" + bl_options = {'DEFAULT_CLOSED'} + COMPAT_ENGINES = {'BLENDER_EEVEE_NEXT'} + + @classmethod + def poll(cls, context): + return (context.engine in cls.COMPAT_ENGINES) + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + scene = context.scene + props = scene.eevee + + col = layout.column() + col.prop(props, "bokeh_max_size") + col.prop(props, "bokeh_threshold") + col.prop(props, "bokeh_neighbor_max") + col.prop(props, "use_bokeh_jittered") + + col = layout.column() + col.active = props.use_bokeh_jittered + col.prop(props, "bokeh_overblur") + + class RENDER_PT_eevee_bloom(RenderButtonsPanel, Panel): bl_label = "Bloom" bl_options = {'DEFAULT_CLOSED'} @@ -801,6 +827,7 @@ classes = ( RENDER_PT_eevee_ambient_occlusion, RENDER_PT_eevee_bloom, RENDER_PT_eevee_depth_of_field, + RENDER_PT_eevee_next_depth_of_field, RENDER_PT_eevee_subsurface_scattering, RENDER_PT_eevee_screen_space_reflections, RENDER_PT_eevee_motion_blur, diff --git a/release/scripts/startup/bl_ui/properties_scene.py b/release/scripts/startup/bl_ui/properties_scene.py index 2e2d7fbe261..33aea4f4d77 100644 --- a/release/scripts/startup/bl_ui/properties_scene.py +++ b/release/scripts/startup/bl_ui/properties_scene.py @@ -13,6 +13,8 @@ from bl_ui.properties_physics_common import ( effector_weights_ui, ) +from bpy.app.translations import pgettext_iface as iface_ + class SCENE_UL_keying_set_paths(UIList): def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, _index): @@ -82,17 +84,17 @@ class SceneKeyingSetsPanel: @staticmethod def draw_keyframing_settings(context, layout, ks, ksp): SceneKeyingSetsPanel._draw_keyframing_setting( - context, layout, ks, ksp, "Needed", + context, layout, ks, ksp, iface_("Needed"), "use_insertkey_override_needed", "use_insertkey_needed", userpref_fallback="use_keyframe_insert_needed", ) SceneKeyingSetsPanel._draw_keyframing_setting( - context, layout, ks, ksp, "Visual", + context, layout, ks, ksp, iface_("Visual"), "use_insertkey_override_visual", "use_insertkey_visual", userpref_fallback="use_visual_keying", ) SceneKeyingSetsPanel._draw_keyframing_setting( - context, layout, ks, ksp, "XYZ to RGB", + context, layout, ks, ksp, iface_("XYZ to RGB"), "use_insertkey_override_xyz_to_rgb", "use_insertkey_xyz_to_rgb", ) diff --git a/release/scripts/startup/bl_ui/space_toolsystem_common.py b/release/scripts/startup/bl_ui/space_toolsystem_common.py index 0c796b899af..39dfdd0eecb 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_common.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_common.py @@ -5,6 +5,7 @@ from bpy.types import ( ) from bpy.app.translations import pgettext_tip as tip_ +from bpy.app.translations import pgettext_iface as iface_ __all__ = ( "ToolDef", @@ -794,7 +795,7 @@ class ToolSelectPanelHelper: # Note: we could show 'item.text' here but it makes the layout jitter when switching tools. # Add some spacing since the icon is currently assuming regular small icon size. if show_tool_icon_always: - layout.label(text=" " + item.label, icon_value=icon_value) + layout.label(text=" " + iface_(item.label, "Operator"), icon_value=icon_value) layout.separator() else: if context.space_data.show_region_toolbar: @@ -825,7 +826,7 @@ class ToolSelectPanelHelper: row.label(text="Drag:") row = split.row() row.context_pointer_set("tool", tool) - row.popover(panel="TOPBAR_PT_tool_fallback", text=label) + row.popover(panel="TOPBAR_PT_tool_fallback", text=iface_(label, "Operator")) return tool diff --git a/release/scripts/startup/bl_ui/space_topbar.py b/release/scripts/startup/bl_ui/space_topbar.py index 31ecd67eb08..da089ea23b0 100644 --- a/release/scripts/startup/bl_ui/space_topbar.py +++ b/release/scripts/startup/bl_ui/space_topbar.py @@ -2,6 +2,8 @@ import bpy from bpy.types import Header, Menu, Panel +from bpy.app.translations import pgettext_iface as iface_ + class TOPBAR_HT_upper_bar(Header): bl_space_type = 'TOPBAR' @@ -363,7 +365,7 @@ class TOPBAR_MT_file_new(Menu): for d in paths: props = layout.operator( "wm.read_homefile", - text=bpy.path.display_name(d), + text=bpy.path.display_name(iface_(d)), icon=icon, ) props.app_template = d diff --git a/source/blender/blenfont/intern/blf.c b/source/blender/blenfont/intern/blf.c index a1fcc17ca3f..36475321d4c 100644 --- a/source/blender/blenfont/intern/blf.c +++ b/source/blender/blenfont/intern/blf.c @@ -122,7 +122,7 @@ bool BLF_has_glyph(int fontid, unsigned int unicode) { FontBLF *font = blf_get(fontid); if (font) { - return FT_Get_Char_Index(font->face, unicode) != FT_Err_Ok; + return blf_get_char_index(font, unicode) != FT_Err_Ok; } return false; } diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index eb974f33994..17145bdbe99 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -17,6 +17,7 @@ #include <ft2build.h> #include FT_FREETYPE_H +#include FT_CACHE_H /* FreeType Cache. */ #include FT_GLYPH_H #include FT_MULTIPLE_MASTERS_H /* Variable font support. */ #include FT_TRUETYPE_IDS_H /* Codepoint coverage constants. */ @@ -54,7 +55,10 @@ BatchBLF g_batch; /* freetype2 handle ONLY for this file! */ -static FT_Library ft_lib; +static FT_Library ft_lib = NULL; +static FTC_Manager ftc_manager = NULL; +static FTC_CMapCache ftc_charmap_cache = NULL; + static SpinLock ft_lib_mutex; static SpinLock blf_glyph_cache_mutex; @@ -65,19 +69,66 @@ static ft_pix blf_font_height_max_ft_pix(struct FontBLF *font); static ft_pix blf_font_width_max_ft_pix(struct FontBLF *font); /* -------------------------------------------------------------------- */ +/** \name FreeType Caching + * \{ */ + +/* Called when a face is removed. FreeType will call FT_Done_Face itself. */ +static void blf_face_finalizer(void *object) +{ + FT_Face face = object; + FontBLF *font = (FontBLF *)face->generic.data; + font->face = NULL; +} + +/* Called in response to FTC_Manager_LookupFace. Add a face to our font. */ +static FT_Error blf_cache_face_requester(FTC_FaceID faceID, + FT_Library lib, + FT_Pointer UNUSED(reqData), + FT_Face *face) +{ + FontBLF *font = (FontBLF *)faceID; + int err = FT_Err_Cannot_Open_Resource; + + BLI_spin_lock(font->ft_lib_mutex); + + if (font->filepath) { + err = FT_New_Face(lib, font->filepath, 0, face); + } + else if (font->mem) { + err = FT_New_Memory_Face(lib, font->mem, (FT_Long)font->mem_size, 0, face); + } + + BLI_spin_unlock(font->ft_lib_mutex); + + if (err == FT_Err_Ok) { + font->face = *face; + font->face->generic.data = font; + font->face->generic.finalizer = blf_face_finalizer; + } + + return err; +} + +/* Use cache, not blf_get_char_index, to return glyph id from charcode. */ +uint blf_get_char_index(struct FontBLF *font, uint charcode) +{ + return FTC_CMapCache_Lookup(ftc_charmap_cache, font, -1, charcode); +} + +/* -------------------------------------------------------------------- */ /** \name FreeType Utilities (Internal) * \{ */ -/* Convert a FreeType 26.6 value representing an unscaled design size to factional pixels. */ +/* Convert a FreeType 26.6 value representing an unscaled design size to fractional pixels. */ static ft_pix blf_unscaled_F26Dot6_to_pixels(FontBLF *font, FT_Pos value) { /* Scale value by font size using integer-optimized multiplication. */ - FT_Long scaled = FT_MulFix(value, font->face->size->metrics.x_scale); + FT_Long scaled = FT_MulFix(value, font->ft_size->metrics.x_scale); /* Copied from FreeType's FT_Get_Kerning (with FT_KERNING_DEFAULT), scaling down */ /* kerning distances at small ppem values so that they don't become too big. */ - if (font->face->size->metrics.x_ppem < 25) { - scaled = FT_MulDiv(scaled, font->face->size->metrics.x_ppem, 25); + if (font->ft_size->metrics.x_ppem < 25) { + scaled = FT_MulDiv(scaled, font->ft_size->metrics.x_ppem, 25); } return (ft_pix)scaled; @@ -296,7 +347,7 @@ BLI_INLINE ft_pix blf_kerning(FontBLF *font, const GlyphBLF *g_prev, const Glyph /* Small adjust if there is hinting. */ adjustment += g->lsb_delta - ((g_prev) ? g_prev->rsb_delta : 0); - if (FT_HAS_KERNING(font->face) && g_prev) { + if (FT_HAS_KERNING(font) && g_prev) { FT_Vector delta = {KERNING_ENTRY_UNSET}; /* Get unscaled kerning value from our cache if ASCII. */ @@ -305,7 +356,7 @@ BLI_INLINE ft_pix blf_kerning(FontBLF *font, const GlyphBLF *g_prev, const Glyph } /* If not ASCII or not found in cache, ask FreeType for kerning. */ - if (UNLIKELY(delta.x == KERNING_ENTRY_UNSET)) { + if (UNLIKELY(font->face && delta.x == KERNING_ENTRY_UNSET)) { /* Note that this function sets delta values to zero on any error. */ FT_Get_Kerning(font->face, g_prev->idx, g->idx, FT_KERNING_UNSCALED, &delta); } @@ -925,8 +976,7 @@ static void blf_font_wrap_apply(FontBLF *font, int lines = 0; ft_pix pen_x_next = 0; - /* Space between lines needs to be aligned to the pixel grid (T97310). */ - ft_pix line_height = FT_PIX_FLOOR(blf_font_height_max_ft_pix(font)); + ft_pix line_height = blf_font_height_max_ft_pix(font); GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); @@ -1090,7 +1140,7 @@ int blf_font_count_missing_chars(FontBLF *font, } else { c = BLI_str_utf8_as_unicode_step(str, str_len, &i); - if (FT_Get_Char_Index((font)->face, c) == 0) { + if (blf_get_char_index(font, c) == 0) { missing++; } } @@ -1107,18 +1157,8 @@ int blf_font_count_missing_chars(FontBLF *font, static ft_pix blf_font_height_max_ft_pix(FontBLF *font) { - ft_pix height_max; - FT_Face face = font->face; - if (FT_IS_SCALABLE(face)) { - height_max = ft_pix_from_int((int)(face->ascender - face->descender) * - (int)face->size->metrics.y_ppem) / - (ft_pix)face->units_per_EM; - } - else { - height_max = (ft_pix)face->size->metrics.height; - } - /* can happen with size 1 fonts */ - return MAX2(height_max, ft_pix_from_int(1)); + /* Metrics.height is rounded to pixel. Force minimum of one pixel. */ + return MAX2((ft_pix)font->ft_size->metrics.height, ft_pix_from_int(1)); } int blf_font_height_max(FontBLF *font) @@ -1128,18 +1168,8 @@ int blf_font_height_max(FontBLF *font) static ft_pix blf_font_width_max_ft_pix(FontBLF *font) { - ft_pix width_max; - const FT_Face face = font->face; - if (FT_IS_SCALABLE(face)) { - width_max = ft_pix_from_int((int)(face->bbox.xMax - face->bbox.xMin) * - (int)face->size->metrics.x_ppem) / - (ft_pix)face->units_per_EM; - } - else { - width_max = (ft_pix)face->size->metrics.max_advance; - } - /* can happen with size 1 fonts */ - return MAX2(width_max, ft_pix_from_int(1)); + /* Metrics.max_advance is rounded to pixel. Force minimum of one pixel. */ + return MAX2((ft_pix)font->ft_size->metrics.max_advance, ft_pix_from_int(1)); } int blf_font_width_max(FontBLF *font) @@ -1149,12 +1179,12 @@ int blf_font_width_max(FontBLF *font) int blf_font_descender(FontBLF *font) { - return ft_pix_to_int((ft_pix)font->face->size->metrics.descender); + return ft_pix_to_int((ft_pix)font->ft_size->metrics.descender); } int blf_font_ascender(FontBLF *font) { - return ft_pix_to_int((ft_pix)font->face->size->metrics.ascender); + return ft_pix_to_int((ft_pix)font->ft_size->metrics.ascender); } char *blf_display_name(FontBLF *font) @@ -1176,13 +1206,31 @@ int blf_font_init(void) memset(&g_batch, 0, sizeof(g_batch)); BLI_spin_init(&ft_lib_mutex); BLI_spin_init(&blf_glyph_cache_mutex); - return FT_Init_FreeType(&ft_lib); + int err = FT_Init_FreeType(&ft_lib); + if (err == FT_Err_Ok) { + err = FTC_Manager_New(ft_lib, + BLF_CACHE_MAX_FACES, + BLF_CACHE_MAX_SIZES, + BLF_CACHE_BYTES, + blf_cache_face_requester, + NULL, + &ftc_manager); + if (err == FT_Err_Ok) { + err = FTC_CMapCache_New(ftc_manager, &ftc_charmap_cache); + } + } + return err; } void blf_font_exit(void) { - FT_Done_FreeType(ft_lib); BLI_spin_end(&ft_lib_mutex); + if (ftc_manager) { + FTC_Manager_Done(ftc_manager); + } + if (ft_lib) { + FT_Done_FreeType(ft_lib); + } BLI_spin_end(&blf_glyph_cache_mutex); blf_batch_draw_exit(); } @@ -1261,12 +1309,7 @@ bool blf_ensure_face(FontBLF *font) FT_Error err; - if (font->filepath) { - err = FT_New_Face(ft_lib, font->filepath, 0, &font->face); - } - if (font->mem) { - err = FT_New_Memory_Face(ft_lib, font->mem, (FT_Long)font->mem_size, 0, &font->face); - } + err = FTC_Manager_LookupFace(ftc_manager, font, &font->face); if (err) { if (ELEM(err, FT_Err_Unknown_File_Format, FT_Err_Unimplemented_Feature)) { @@ -1306,7 +1349,9 @@ bool blf_ensure_face(FontBLF *font) } } - if (FT_HAS_MULTIPLE_MASTERS(font->face)) { + font->face_flags = font->face->face_flags; + + if (FT_HAS_MULTIPLE_MASTERS(font)) { FT_Get_MM_Var(font->face, &(font->variations)); } @@ -1319,11 +1364,11 @@ bool blf_ensure_face(FontBLF *font) font->UnicodeRanges[3] = (uint)os2_table->ulUnicodeRange4; } - if (FT_IS_FIXED_WIDTH(font->face)) { + if (FT_IS_FIXED_WIDTH(font)) { font->flags |= BLF_MONOSPACED; } - if (FT_HAS_KERNING(font->face) && !font->kerning_cache) { + if (FT_HAS_KERNING(font) && !font->kerning_cache) { /* Create kerning cache table and fill with value indicating "unset". */ font->kerning_cache = MEM_mallocN(sizeof(KerningCacheBLF), __func__); for (uint i = 0; i < KERNING_CACHE_TABLE_SIZE; i++) { @@ -1438,7 +1483,9 @@ void blf_font_attach_from_mem(FontBLF *font, const unsigned char *mem, const siz open.flags = FT_OPEN_MEMORY; open.memory_base = (const FT_Byte *)mem; open.memory_size = (FT_Long)mem_size; - FT_Attach_Stream(font->face, &open); + if (blf_ensure_face(font)) { + FT_Attach_Stream(font->face, &open); + } } void blf_font_free(FontBLF *font) @@ -1454,7 +1501,8 @@ void blf_font_free(FontBLF *font) } if (font->face) { - FT_Done_Face(font->face); + FTC_Manager_RemoveFaceID(ftc_manager, font); + font->face = NULL; } if (font->filepath) { MEM_freeN(font->filepath); @@ -1478,21 +1526,27 @@ bool blf_font_size(FontBLF *font, float size, unsigned int dpi) } /* FreeType uses fixed-point integers in 64ths. */ - FT_F26Dot6 ft_size = lroundf(size * 64.0f); + FT_UInt ft_size = round_fl_to_uint(size * 64.0f); /* Adjust our new size to be on even 64ths. */ size = (float)ft_size / 64.0f; - if (font->size != size || font->dpi != dpi) { - if (FT_Set_Char_Size(font->face, 0, ft_size, dpi, dpi) == FT_Err_Ok) { - font->size = size; - font->dpi = dpi; - } - else { - printf("The current font does not support the size, %f and DPI, %u\n", size, dpi); - return false; - } + FTC_ScalerRec scaler = {0}; + scaler.face_id = font; + scaler.width = 0; + scaler.height = ft_size; + scaler.pixel = 0; + scaler.x_res = dpi; + scaler.y_res = dpi; + + if (FTC_Manager_LookupSize(ftc_manager, &scaler, &font->ft_size) != FT_Err_Ok) { + printf("The current font don't support the size, %f and dpi, %u\n", size, dpi); + return false; } + font->size = size; + font->dpi = dpi; + font->ft_size->generic.data = (void *)font; + return true; } diff --git a/source/blender/blenfont/intern/blf_glyph.c b/source/blender/blenfont/intern/blf_glyph.c index 48ddbc9f920..780b75c6296 100644 --- a/source/blender/blenfont/intern/blf_glyph.c +++ b/source/blender/blenfont/intern/blf_glyph.c @@ -94,16 +94,16 @@ static GlyphCacheBLF *blf_glyph_cache_new(FontBLF *font) memset(gc->bucket, 0, sizeof(gc->bucket)); /* Determine ideal fixed-width size for monospaced output. */ - FT_UInt gindex = FT_Get_Char_Index(font->face, U'0'); - if (gindex) { + FT_UInt gindex = blf_get_char_index(font, U'0'); + if (gindex && font->face) { FT_Fixed advance = 0; FT_Get_Advance(font->face, gindex, FT_LOAD_NO_HINTING, &advance); /* Use CSS 'ch unit' width, advance of zero character. */ gc->fixed_width = (int)(advance >> 16); } else { - /* Font does not contain "0" so use CSS fallback of 1/2 of em. */ - gc->fixed_width = (int)((font->face->size->metrics.height / 2) >> 6); + /* Font does not have a face or does not contain "0" so use CSS fallback of 1/2 of em. */ + gc->fixed_width = (int)((font->ft_size->metrics.height / 2) >> 6); } if (gc->fixed_width < 1) { gc->fixed_width = 1; @@ -565,7 +565,7 @@ static bool blf_font_has_coverage_bit(FontBLF *font, int coverage_bit) */ static FT_UInt blf_glyph_index_from_charcode(FontBLF **font, const uint charcode) { - FT_UInt glyph_index = FT_Get_Char_Index((*font)->face, charcode); + FT_UInt glyph_index = blf_get_char_index(*font, charcode); if (glyph_index) { return glyph_index; } @@ -584,12 +584,10 @@ static FT_UInt blf_glyph_index_from_charcode(FontBLF **font, const uint charcode continue; } if (coverage_bit < 0 || blf_font_has_coverage_bit(f, coverage_bit)) { - if (blf_ensure_face(f)) { - glyph_index = FT_Get_Char_Index(f->face, charcode); - if (glyph_index) { - *font = f; - return glyph_index; - } + glyph_index = blf_get_char_index(f, charcode); + if (glyph_index) { + *font = f; + return glyph_index; } } } @@ -599,8 +597,8 @@ static FT_UInt blf_glyph_index_from_charcode(FontBLF **font, const uint charcode #endif /* Not found in the stack, return from Last Resort if there is one. */ - if (last_resort && blf_ensure_face(last_resort)) { - glyph_index = FT_Get_Char_Index(last_resort->face, charcode); + if (last_resort) { + glyph_index = blf_get_char_index(last_resort, charcode); if (glyph_index) { *font = last_resort; return glyph_index; @@ -789,8 +787,8 @@ static bool blf_glyph_transform_weight(FT_GlyphSlot glyph, float factor, bool mo { if (glyph->format == FT_GLYPH_FORMAT_OUTLINE) { /* Fake bold if the font does not have this variable axis. */ - const FT_Pos average_width = FT_MulFix(glyph->face->units_per_EM, - glyph->face->size->metrics.x_scale); + const FontBLF *font = (FontBLF *)glyph->face->generic.data; + const FT_Pos average_width = font->ft_size->metrics.height; FT_Pos change = (FT_Pos)((float)average_width * factor * 0.1f); FT_Outline_EmboldenXY(&glyph->outline, change, change / 2); if (monospaced) { @@ -849,7 +847,8 @@ static bool blf_glyph_transform_width(FT_GlyphSlot glyph, float factor) static bool blf_glyph_transform_spacing(FT_GlyphSlot glyph, float factor) { if (glyph->advance.x > 0) { - const long int size = glyph->face->size->metrics.height; + const FontBLF *font = (FontBLF *)glyph->face->generic.data; + const long int size = font->ft_size->metrics.height; glyph->advance.x += (FT_Pos)(factor * (float)size / 6.0f); return true; } @@ -898,13 +897,7 @@ static FT_GlyphSlot blf_glyph_render(FontBLF *settings_font, int fixed_width) { if (glyph_font != settings_font) { - FT_Set_Char_Size(glyph_font->face, - 0, - ((FT_F26Dot6)(settings_font->size)) * 64, - settings_font->dpi, - settings_font->dpi); - glyph_font->size = settings_font->size; - glyph_font->dpi = settings_font->dpi; + blf_font_size(glyph_font, settings_font->size, settings_font->dpi); } /* We need to keep track if changes are still needed. */ @@ -961,7 +954,7 @@ static FT_GlyphSlot blf_glyph_render(FontBLF *settings_font, /* Fallback glyph transforms, but only if required and not yet done. */ if (weight != 0.0f && !weight_done) { - blf_glyph_transform_weight(glyph, weight, glyph->face->face_flags & FT_FACE_FLAG_FIXED_WIDTH); + blf_glyph_transform_weight(glyph, weight, FT_IS_FIXED_WIDTH(glyph_font)); } if (slant != 0.0f && !slant_done) { blf_glyph_transform_slant(glyph, slant); @@ -990,11 +983,9 @@ GlyphBLF *blf_glyph_ensure(FontBLF *font, GlyphCacheBLF *gc, uint charcode) FontBLF *font_with_glyph = font; FT_UInt glyph_index = blf_glyph_index_from_charcode(&font_with_glyph, charcode); - /* Glyphs are dynamically created as needed by font rendering. this means that - * to make font rendering thread safe we have to do locking here. note that this - * must be a lock for the whole library and not just per font, because the font - * renderer uses a shared buffer internally. */ - BLI_spin_lock(font_with_glyph->ft_lib_mutex); + if (!blf_ensure_face(font_with_glyph)) { + return NULL; + } FT_GlyphSlot glyph = blf_glyph_render( font, font_with_glyph, glyph_index, charcode, gc->fixed_width); @@ -1004,7 +995,6 @@ GlyphBLF *blf_glyph_ensure(FontBLF *font, GlyphCacheBLF *gc, uint charcode) g = blf_glyph_cache_add_glyph(font, gc, glyph, charcode, glyph_index); } - BLI_spin_unlock(font_with_glyph->ft_lib_mutex); return g; } diff --git a/source/blender/blenfont/intern/blf_internal.h b/source/blender/blenfont/intern/blf_internal.h index 6207edb0107..8ff00d05e02 100644 --- a/source/blender/blenfont/intern/blf_internal.h +++ b/source/blender/blenfont/intern/blf_internal.h @@ -14,9 +14,16 @@ struct ResultBLF; struct rctf; struct rcti; -/* Max number of fonts in memory. Take care that every font has a glyph cache per size/dpi, +/* Max number of FontBLFs in memory. Take care that every font has a glyph cache per size/dpi, * so we don't need load the same font with different size, just load one and call BLF_size. */ -#define BLF_MAX_FONT 32 +#define BLF_MAX_FONT 64 + +/* Maximum number of opened FT_Face objects managed by cache. 0 is default of 2. */ +#define BLF_CACHE_MAX_FACES 0 +/* Maximum number of opened FT_Size objects managed by cache. 0 is default of 4 */ +#define BLF_CACHE_MAX_SIZES 0 +/* Maximum number of bytes to use for cached data nodes. 0 is default of 200,000. */ +#define BLF_CACHE_BYTES 0 extern struct FontBLF *global_font[BLF_MAX_FONT]; @@ -39,6 +46,8 @@ void blf_font_exit(void); bool blf_font_id_is_valid(int fontid); +uint blf_get_char_index(struct FontBLF *font, uint charcode); + bool blf_ensure_face(struct FontBLF *font); void blf_draw_buffer__start(struct FontBLF *font); diff --git a/source/blender/blenfont/intern/blf_internal_types.h b/source/blender/blenfont/intern/blf_internal_types.h index 018cef4540f..007b717ab93 100644 --- a/source/blender/blenfont/intern/blf_internal_types.h +++ b/source/blender/blenfont/intern/blf_internal_types.h @@ -329,6 +329,12 @@ typedef struct FontBLF { /* freetype2 face. */ FT_Face face; + /* FreeType size is separated from face when using their caching subsystem. */ + FT_Size ft_size; + + /* Copy of the font->face->face_flags, in case we don't have a face loaded. */ + FT_Long face_flags; + /* data for buffer usage (drawing into a texture buffer) */ FontBufInfoBLF buf_info; diff --git a/source/blender/blenkernel/BKE_attribute_math.hh b/source/blender/blenkernel/BKE_attribute_math.hh index 01c2ef988f2..770937688d7 100644 --- a/source/blender/blenkernel/BKE_attribute_math.hh +++ b/source/blender/blenkernel/BKE_attribute_math.hh @@ -188,10 +188,28 @@ template<typename T> class SimpleMixer { * \param default_value: Output value for an element that has not been affected by a #mix_in. */ SimpleMixer(MutableSpan<T> buffer, T default_value = {}) + : SimpleMixer(buffer, buffer.index_range(), default_value) + { + } + + /** + * \param mask: Only initialize these indices. Other indices in the buffer will be invalid. + */ + SimpleMixer(MutableSpan<T> buffer, const IndexMask mask, T default_value = {}) : buffer_(buffer), default_value_(default_value), total_weights_(buffer.size(), 0.0f) { BLI_STATIC_ASSERT(std::is_trivial_v<T>, ""); - memset(buffer_.data(), 0, sizeof(T) * buffer_.size()); + mask.foreach_index([&](const int64_t i) { buffer_[i] = default_value_; }); + } + + /** + * Set a #value into the element with the given #index. + */ + void set(const int64_t index, const T &value, const float weight = 1.0f) + { + BLI_assert(weight >= 0.0f); + buffer_[index] = value * weight; + total_weights_[index] = weight; } /** @@ -209,7 +227,12 @@ template<typename T> class SimpleMixer { */ void finalize() { - for (const int64_t i : buffer_.index_range()) { + this->finalize(IndexMask(buffer_.size())); + } + + void finalize(const IndexMask mask) + { + mask.foreach_index([&](const int64_t i) { const float weight = total_weights_[i]; if (weight > 0.0f) { buffer_[i] *= 1.0f / weight; @@ -217,7 +240,7 @@ template<typename T> class SimpleMixer { else { buffer_[i] = default_value_; } - } + }); } }; @@ -237,9 +260,25 @@ class BooleanPropagationMixer { /** * \param buffer: Span where the interpolated values should be stored. */ - BooleanPropagationMixer(MutableSpan<bool> buffer) : buffer_(buffer) + BooleanPropagationMixer(MutableSpan<bool> buffer) + : BooleanPropagationMixer(buffer, buffer.index_range()) + { + } + + /** + * \param mask: Only initialize these indices. Other indices in the buffer will be invalid. + */ + BooleanPropagationMixer(MutableSpan<bool> buffer, const IndexMask mask) : buffer_(buffer) + { + mask.foreach_index([&](const int64_t i) { buffer_[i] = false; }); + } + + /** + * Set a #value into the element with the given #index. + */ + void set(const int64_t index, const bool value, [[maybe_unused]] const float weight = 1.0f) { - buffer_.fill(false); + buffer_[index] = value; } /** @@ -256,6 +295,10 @@ class BooleanPropagationMixer { void finalize() { } + + void finalize(const IndexMask /*mask*/) + { + } }; /** @@ -277,8 +320,27 @@ class SimpleMixerWithAccumulationType { public: SimpleMixerWithAccumulationType(MutableSpan<T> buffer, T default_value = {}) + : SimpleMixerWithAccumulationType(buffer, buffer.index_range(), default_value) + { + } + + /** + * \param mask: Only initialize these indices. Other indices in the buffer will be invalid. + */ + SimpleMixerWithAccumulationType(MutableSpan<T> buffer, + const IndexMask mask, + T default_value = {}) : buffer_(buffer), default_value_(default_value), accumulation_buffer_(buffer.size()) { + mask.foreach_index([&](const int64_t index) { buffer_[index] = default_value_; }); + } + + void set(const int64_t index, const T &value, const float weight = 1.0f) + { + const AccumulationT converted_value = static_cast<AccumulationT>(value); + Item &item = accumulation_buffer_[index]; + item.value = converted_value * weight; + item.weight = weight; } void mix_in(const int64_t index, const T &value, const float weight = 1.0f) @@ -291,7 +353,12 @@ class SimpleMixerWithAccumulationType { void finalize() { - for (const int64_t i : buffer_.index_range()) { + this->finalize(buffer_.index_range()); + } + + void finalize(const IndexMask mask) + { + mask.foreach_index([&](const int64_t i) { const Item &item = accumulation_buffer_[i]; if (item.weight > 0.0f) { const float weight_inv = 1.0f / item.weight; @@ -301,7 +368,7 @@ class SimpleMixerWithAccumulationType { else { buffer_[i] = default_value_; } - } + }); } }; @@ -314,8 +381,16 @@ class ColorGeometry4fMixer { public: ColorGeometry4fMixer(MutableSpan<ColorGeometry4f> buffer, ColorGeometry4f default_color = ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f)); + /** + * \param mask: Only initialize these indices. Other indices in the buffer will be invalid. + */ + ColorGeometry4fMixer(MutableSpan<ColorGeometry4f> buffer, + IndexMask mask, + ColorGeometry4f default_color = ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f)); + void set(int64_t index, const ColorGeometry4f &color, float weight = 1.0f); void mix_in(int64_t index, const ColorGeometry4f &color, float weight = 1.0f); void finalize(); + void finalize(IndexMask mask); }; class ColorGeometry4bMixer { @@ -328,8 +403,16 @@ class ColorGeometry4bMixer { public: ColorGeometry4bMixer(MutableSpan<ColorGeometry4b> buffer, ColorGeometry4b default_color = ColorGeometry4b(0, 0, 0, 255)); + /** + * \param mask: Only initialize these indices. Other indices in the buffer will be invalid. + */ + ColorGeometry4bMixer(MutableSpan<ColorGeometry4b> buffer, + IndexMask mask, + ColorGeometry4b default_color = ColorGeometry4b(0, 0, 0, 255)); + void set(int64_t index, const ColorGeometry4b &color, float weight = 1.0f); void mix_in(int64_t index, const ColorGeometry4b &color, float weight = 1.0f); void finalize(); + void finalize(IndexMask mask); }; template<typename T> struct DefaultMixerStruct { @@ -381,12 +464,12 @@ template<> struct DefaultMixerStruct<int8_t> { using type = SimpleMixerWithAccumulationType<int8_t, float, float_to_int8_t>; }; -template<typename T> struct DefaultPropatationMixerStruct { +template<typename T> struct DefaultPropagationMixerStruct { /* Use void by default. This can be checked for in `if constexpr` statements. */ using type = typename DefaultMixerStruct<T>::type; }; -template<> struct DefaultPropatationMixerStruct<bool> { +template<> struct DefaultPropagationMixerStruct<bool> { using type = BooleanPropagationMixer; }; @@ -396,7 +479,7 @@ template<> struct DefaultPropatationMixerStruct<bool> { * (the default mixing for booleans). */ template<typename T> -using DefaultPropatationMixer = typename DefaultPropatationMixerStruct<T>::type; +using DefaultPropagationMixer = typename DefaultPropagationMixerStruct<T>::type; /* Utility to get a good default mixer for a given type. This is `void` when there is no default * mixer for the given type. */ diff --git a/source/blender/blenkernel/BKE_customdata.h b/source/blender/blenkernel/BKE_customdata.h index 2b96b0c0aab..38686a32505 100644 --- a/source/blender/blenkernel/BKE_customdata.h +++ b/source/blender/blenkernel/BKE_customdata.h @@ -426,6 +426,7 @@ void *CustomData_get_layer(const struct CustomData *data, int type); void *CustomData_get_layer_n(const struct CustomData *data, int type, int n); void *CustomData_get_layer_named(const struct CustomData *data, int type, const char *name); int CustomData_get_offset(const struct CustomData *data, int type); +int CustomData_get_offset_named(const CustomData *data, int type, const char *name); int CustomData_get_n_offset(const struct CustomData *data, int type, int n); int CustomData_get_layer_index(const struct CustomData *data, int type); diff --git a/source/blender/blenkernel/BKE_displist.h b/source/blender/blenkernel/BKE_displist.h index 7a21e85e310..cdca740555a 100644 --- a/source/blender/blenkernel/BKE_displist.h +++ b/source/blender/blenkernel/BKE_displist.h @@ -61,7 +61,6 @@ typedef struct DispList { int totindex; /* indexed array drawing surfaces */ } DispList; -void BKE_displist_copy(struct ListBase *lbn, const struct ListBase *lb); DispList *BKE_displist_find(struct ListBase *lb, int type); void BKE_displist_normals_add(struct ListBase *lb); void BKE_displist_count(const struct ListBase *lb, int *totvert, int *totface, int *tottri); diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h index e3c249e56f9..eb43ce823ac 100644 --- a/source/blender/blenkernel/BKE_image.h +++ b/source/blender/blenkernel/BKE_image.h @@ -237,11 +237,13 @@ void BKE_image_ensure_viewer_views(const struct RenderData *rd, */ void BKE_image_user_frame_calc(struct Image *ima, struct ImageUser *iuser, int cfra); int BKE_image_user_frame_get(const struct ImageUser *iuser, int cfra, bool *r_is_in_range); -void BKE_image_user_file_path(struct ImageUser *iuser, struct Image *ima, char *path); -void BKE_image_user_file_path_ex(struct ImageUser *iuser, - struct Image *ima, +void BKE_image_user_file_path(const struct ImageUser *iuser, const struct Image *ima, char *path); +void BKE_image_user_file_path_ex(const struct Main *bmain, + const struct ImageUser *iuser, + const struct Image *ima, char *path, - bool resolve_udim); + const bool resolve_udim, + const bool resolve_multiview); void BKE_image_editors_update_frame(const struct Main *bmain, int cfra); /** @@ -259,15 +261,15 @@ struct RenderPass *BKE_image_multilayer_index(struct RenderResult *rr, struct Im /** * Sets index offset for multi-view files. */ -void BKE_image_multiview_index(struct Image *ima, struct ImageUser *iuser); +void BKE_image_multiview_index(const struct Image *ima, struct ImageUser *iuser); /** * For multi-layer images as well as for render-viewer * and because rendered results use fake layer/passes, don't correct for wrong indices here. */ -bool BKE_image_is_multilayer(struct Image *ima); -bool BKE_image_is_multiview(struct Image *ima); -bool BKE_image_is_stereo(struct Image *ima); +bool BKE_image_is_multilayer(const struct Image *ima); +bool BKE_image_is_multiview(const struct Image *ima); +bool BKE_image_is_stereo(const struct Image *ima); struct RenderResult *BKE_image_acquire_renderresult(struct Scene *scene, struct Image *ima); void BKE_image_release_renderresult(struct Scene *scene, struct Image *ima); @@ -364,14 +366,7 @@ bool BKE_image_remove_tile(struct Image *ima, struct ImageTile *tile); void BKE_image_reassign_tile(struct Image *ima, struct ImageTile *tile, int new_tile_number); void BKE_image_sort_tiles(struct Image *ima); -bool BKE_image_fill_tile(struct Image *ima, - struct ImageTile *tile, - int width, - int height, - const float color[4], - int gen_type, - int planes, - bool is_float); +bool BKE_image_fill_tile(struct Image *ima, struct ImageTile *tile); typedef enum { UDIM_TILE_FORMAT_NONE = 0, @@ -423,13 +418,13 @@ int BKE_image_get_tile_from_pos(struct Image *ima, void BKE_image_get_tile_uv(const struct Image *ima, const int tile_number, float r_uv[2]); /** - * Return the tile_number for the closest UDIM tile. + * Return the tile_number for the closest UDIM tile to `co`. */ int BKE_image_find_nearest_tile_with_offset(const struct Image *image, const float co[2], - float r_uv_offset[2]) ATTR_NONNULL(1, 2, 3); + float r_uv_offset[2]) ATTR_NONNULL(2, 3); int BKE_image_find_nearest_tile(const struct Image *image, const float co[2]) - ATTR_NONNULL(1, 2) ATTR_WARN_UNUSED_RESULT; + ATTR_NONNULL(2) ATTR_WARN_UNUSED_RESULT; void BKE_image_get_size(struct Image *image, struct ImageUser *iuser, int *r_width, int *r_height); void BKE_image_get_size_fl(struct Image *image, struct ImageUser *iuser, float r_size[2]); diff --git a/source/blender/blenkernel/BKE_lib_id.h b/source/blender/blenkernel/BKE_lib_id.h index f4265dfd004..148e6ec2791 100644 --- a/source/blender/blenkernel/BKE_lib_id.h +++ b/source/blender/blenkernel/BKE_lib_id.h @@ -400,13 +400,10 @@ bool id_single_user(struct bContext *C, struct ID *id, struct PointerRNA *ptr, struct PropertyRNA *prop); + +/** Test whether given `id` can be copied or not. */ bool BKE_id_copy_is_allowed(const struct ID *id); /** - * Invokes the appropriate copy method for the block and returns the result in - * #ID.newid, unless test. Returns true if the block can be copied. - */ -struct ID *BKE_id_copy(struct Main *bmain, const struct ID *id); -/** * Generic entry point for copying a data-block (new API). * * \note Copy is generally only affecting the given data-block @@ -430,14 +427,37 @@ struct ID *BKE_id_copy(struct Main *bmain, const struct ID *id); */ struct ID *BKE_id_copy_ex(struct Main *bmain, const struct ID *id, struct ID **r_newid, int flag); /** - * Invokes the appropriate copy method for the block and returns the result in - * newid, unless test. Returns true if the block can be copied. + * Invoke the appropriate copy method for the block and return the new id as result. + * + * See #BKE_id_copy_ex for details. + */ +struct ID *BKE_id_copy(struct Main *bmain, const struct ID *id); + +/** + * Invoke the appropriate copy method for the block and return the new id as result. + * + * Unlike #BKE_id_copy, it does set the #ID.newid pointer of the given `id` to the copied one. + * + * It is designed as a basic common helper for the higher-level 'duplicate' operations (aka 'deep + * copy' of data-blocks and some of their dependency ones), see e.g. #BKE_object_duplicate. + * + * Currently, it only handles the given ID, and their shape keys and actions if any, according to + * the given `duplicate_flags`. + * + * \param duplicate_flags is of type #eDupli_ID_Flags, see #UserDef.dupflag. Currently only + * `USER_DUP_LINKED_ID` and `USER_DUP_ACT` have an effect here. + * \param copy_flags flags passed to #BKE_id_copy_ex. */ struct ID *BKE_id_copy_for_duplicate(struct Main *bmain, struct ID *id, uint duplicate_flags, int copy_flags); +/* Special version of BKE_id_copy which is safe from using evaluated id as source with a copy + * result appearing in the main database. + * Takes care of the referenced data-blocks consistency. */ +struct ID *BKE_id_copy_for_use_in_bmain(struct Main *bmain, const struct ID *id); + /** * Does a mere memory swap over the whole IDs data (including type-specific memory). * \note Most internal ID data itself is not swapped (only IDProperties are). diff --git a/source/blender/blenkernel/BKE_lib_override.h b/source/blender/blenkernel/BKE_lib_override.h index 9ad5a32e6f0..f933946164c 100644 --- a/source/blender/blenkernel/BKE_lib_override.h +++ b/source/blender/blenkernel/BKE_lib_override.h @@ -60,7 +60,7 @@ void BKE_lib_override_library_clear(struct IDOverrideLibrary *override, bool do_ void BKE_lib_override_library_free(struct IDOverrideLibrary **override, bool do_id_user); /** - * Return the actual #IDOverrideLibrary data 'controlling' the given `id`, and the acutal ID owning + * Return the actual #IDOverrideLibrary data 'controlling' the given `id`, and the actual ID owning * it. * * \note This is especially useful when `id` is a non-real override (e.g. embedded ID like a master diff --git a/source/blender/blenkernel/BKE_mesh_mapping.h b/source/blender/blenkernel/BKE_mesh_mapping.h index f77f268a584..67f1bbbfd72 100644 --- a/source/blender/blenkernel/BKE_mesh_mapping.h +++ b/source/blender/blenkernel/BKE_mesh_mapping.h @@ -17,11 +17,10 @@ struct MLoopUV; struct MPoly; struct MVert; -/* map from uv vertex to face (for select linked, stitch, uv suburf) */ - /* UvVertMap */ #define STD_UV_CONNECT_LIMIT 0.0001f +/* Map from uv vertex to face. Used by select linked, uv subsurf and obj exporter. */ typedef struct UvVertMap { struct UvMapVert **vert; struct UvMapVert *buf; @@ -52,17 +51,26 @@ typedef struct UvElement { unsigned int island; } UvElement; -/* UvElementMap is a container for UvElements of a mesh. It stores some UvElements belonging to the - * same uv island in sequence and the number of uvs per island so it is possible to access all uvs - * belonging to an island directly by iterating through the buffer. +/** UvElementMap is a container for UvElements of a BMesh. + * + * It simplifies access to UV information and ensures the + * different UV selection modes are respected. + * + * If islands are calculated, it also stores UvElements + * belonging to the same uv island in sequence and + * the number of uvs per island. */ typedef struct UvElementMap { - /* address UvElements by their vertex */ - struct UvElement **vert; - /* UvElement Store */ - struct UvElement *buf; - /* Total number of UVs in the layer. Useful to know */ - int totalUVs; + /** UvElement Storage. */ + struct UvElement *storage; + /** Total number of UVs. */ + int total_uvs; + /** Total number of unique UVs. */ + int total_unique_uvs; + + /* If Non-NULL, address UvElements by `BM_elem_index_get(BMVert*)`. */ + struct UvElement **vertex; + /* Number of Islands in the mesh */ int totalIslands; /* Stores the starting index in buf where each island begins */ diff --git a/source/blender/blenkernel/BKE_mesh_sample.hh b/source/blender/blenkernel/BKE_mesh_sample.hh index 0b7d1a1835f..fb01083f334 100644 --- a/source/blender/blenkernel/BKE_mesh_sample.hh +++ b/source/blender/blenkernel/BKE_mesh_sample.hh @@ -13,7 +13,6 @@ #include "DNA_meshdata_types.h" #include "BKE_attribute.h" -#include "BKE_attribute.hh" struct Mesh; struct BVHTreeFromMesh; @@ -27,22 +26,22 @@ namespace blender::bke::mesh_surface_sample { void sample_point_attribute(const Mesh &mesh, Span<int> looptri_indices, Span<float3> bary_coords, - const GVArray &data_in, - const IndexMask mask, - GMutableSpan data_out); + const GVArray &src, + IndexMask mask, + GMutableSpan dst); void sample_corner_attribute(const Mesh &mesh, Span<int> looptri_indices, Span<float3> bary_coords, - const GVArray &data_in, - const IndexMask mask, - GMutableSpan data_out); + const GVArray &src, + IndexMask mask, + GMutableSpan dst); void sample_face_attribute(const Mesh &mesh, Span<int> looptri_indices, - const GVArray &data_in, - const IndexMask mask, - GMutableSpan data_out); + const GVArray &src, + IndexMask mask, + GMutableSpan dst); enum class eAttributeMapMode { INTERPOLATED, @@ -57,7 +56,6 @@ enum class eAttributeMapMode { * these are computed lazily when needed and re-used. */ class MeshAttributeInterpolator { - private: const Mesh *mesh_; const IndexMask mask_; const Span<float3> positions_; @@ -68,18 +66,14 @@ class MeshAttributeInterpolator { public: MeshAttributeInterpolator(const Mesh *mesh, - const IndexMask mask, - const Span<float3> positions, - const Span<int> looptri_indices); + IndexMask mask, + Span<float3> positions, + Span<int> looptri_indices); void sample_data(const GVArray &src, eAttrDomain domain, eAttributeMapMode mode, - const GMutableSpan dst); - - void sample_attribute(const GAttributeReader &src_attribute, - GSpanAttributeWriter &dst_attribute, - eAttributeMapMode mode); + GMutableSpan dst); protected: Span<float3> ensure_barycentric_coords(); diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh index 767018ae0bb..27542aa3586 100644 --- a/source/blender/blenkernel/BKE_spline.hh +++ b/source/blender/blenkernel/BKE_spline.hh @@ -360,7 +360,7 @@ class BezierSpline final : public Spline { * Returns non-owning access to an array of values containing the information necessary to * interpolate values from the original control points to evaluated points. The control point * index is the integer part of each value, and the factor used for interpolating to the next - * control point is the remaining factional part. + * control point is the remaining fractional part. */ blender::Span<float> evaluated_mappings() const; blender::Span<blender::float3> evaluated_positions() const final; diff --git a/source/blender/blenkernel/intern/armature_update.c b/source/blender/blenkernel/intern/armature_update.c index 2db4c086e04..6d7aed239e7 100644 --- a/source/blender/blenkernel/intern/armature_update.c +++ b/source/blender/blenkernel/intern/armature_update.c @@ -288,7 +288,7 @@ static int position_tail_on_spline(bSplineIKConstraint *ik_data, int max_seg_idx = BKE_anim_path_get_array_size(cache) - 1; /* Make an initial guess of where our intersection point will be. - * If the curve was a straight line, then the faction passed in r_new_curve_pos + * If the curve was a straight line, then the fraction passed in r_new_curve_pos * would be the correct location. * So make it our first initial guess. */ diff --git a/source/blender/blenkernel/intern/attribute.cc b/source/blender/blenkernel/intern/attribute.cc index 639e190a2c6..b6d39486313 100644 --- a/source/blender/blenkernel/intern/attribute.cc +++ b/source/blender/blenkernel/intern/attribute.cc @@ -20,9 +20,12 @@ #include "DNA_pointcloud_types.h" #include "BLI_index_range.hh" +#include "BLI_string.h" #include "BLI_string_utf8.h" #include "BLI_string_utils.h" +#include "BLT_translation.h" + #include "BKE_attribute.h" #include "BKE_attribute.hh" #include "BKE_curves.hh" @@ -201,7 +204,14 @@ bool BKE_id_attribute_calc_unique_name(ID *id, const char *name, char *outname) { AttrUniqueData data{id}; - BLI_strncpy_utf8(outname, name, MAX_CUSTOMDATA_LAYER_NAME); + /* Set default name if none specified. + * NOTE: We only call IFACE_() if needed to avoid locale lookup overhead. */ + if (!name || name[0] == '\0') { + BLI_strncpy(outname, IFACE_("Attribute"), MAX_CUSTOMDATA_LAYER_NAME); + } + else { + BLI_strncpy_utf8(outname, name, MAX_CUSTOMDATA_LAYER_NAME); + } return BLI_uniquename_cb( unique_name_cb, &data, nullptr, '.', outname, MAX_CUSTOMDATA_LAYER_NAME); diff --git a/source/blender/blenkernel/intern/attribute_math.cc b/source/blender/blenkernel/intern/attribute_math.cc index c38df2a2969..d8102b4eeb8 100644 --- a/source/blender/blenkernel/intern/attribute_math.cc +++ b/source/blender/blenkernel/intern/attribute_math.cc @@ -4,13 +4,31 @@ namespace blender::attribute_math { -ColorGeometry4fMixer::ColorGeometry4fMixer(MutableSpan<ColorGeometry4f> output_buffer, +ColorGeometry4fMixer::ColorGeometry4fMixer(MutableSpan<ColorGeometry4f> buffer, ColorGeometry4f default_color) - : buffer_(output_buffer), - default_color_(default_color), - total_weights_(output_buffer.size(), 0.0f) + : ColorGeometry4fMixer(buffer, buffer.index_range(), default_color) +{ +} + +ColorGeometry4fMixer::ColorGeometry4fMixer(MutableSpan<ColorGeometry4f> buffer, + const IndexMask mask, + const ColorGeometry4f default_color) + : buffer_(buffer), default_color_(default_color), total_weights_(buffer.size(), 0.0f) { - buffer_.fill(ColorGeometry4f(0.0f, 0.0f, 0.0f, 0.0f)); + const ColorGeometry4f zero{0.0f, 0.0f, 0.0f, 0.0f}; + mask.foreach_index([&](const int64_t i) { buffer_[i] = zero; }); +} + +void ColorGeometry4fMixer::set(const int64_t index, + const ColorGeometry4f &color, + const float weight) +{ + BLI_assert(weight >= 0.0f); + buffer_[index].r = color.r * weight; + buffer_[index].g = color.g * weight; + buffer_[index].b = color.b * weight; + buffer_[index].a = color.a * weight; + total_weights_[index] = weight; } void ColorGeometry4fMixer::mix_in(const int64_t index, @@ -28,7 +46,12 @@ void ColorGeometry4fMixer::mix_in(const int64_t index, void ColorGeometry4fMixer::finalize() { - for (const int64_t i : buffer_.index_range()) { + this->finalize(buffer_.index_range()); +} + +void ColorGeometry4fMixer::finalize(const IndexMask mask) +{ + mask.foreach_index([&](const int64_t i) { const float weight = total_weights_[i]; ColorGeometry4f &output_color = buffer_[i]; if (weight > 0.0f) { @@ -41,16 +64,37 @@ void ColorGeometry4fMixer::finalize() else { output_color = default_color_; } - } + }); } ColorGeometry4bMixer::ColorGeometry4bMixer(MutableSpan<ColorGeometry4b> buffer, - ColorGeometry4b default_color) + const ColorGeometry4b default_color) + : ColorGeometry4bMixer(buffer, buffer.index_range(), default_color) +{ +} + +ColorGeometry4bMixer::ColorGeometry4bMixer(MutableSpan<ColorGeometry4b> buffer, + const IndexMask mask, + const ColorGeometry4b default_color) : buffer_(buffer), default_color_(default_color), total_weights_(buffer.size(), 0.0f), accumulation_buffer_(buffer.size(), float4(0, 0, 0, 0)) { + const ColorGeometry4b zero{0, 0, 0, 0}; + mask.foreach_index([&](const int64_t i) { buffer_[i] = zero; }); +} + +void ColorGeometry4bMixer::ColorGeometry4bMixer::set(int64_t index, + const ColorGeometry4b &color, + const float weight) +{ + BLI_assert(weight >= 0.0f); + accumulation_buffer_[index][0] = color.r * weight; + accumulation_buffer_[index][1] = color.g * weight; + accumulation_buffer_[index][2] = color.b * weight; + accumulation_buffer_[index][3] = color.a * weight; + total_weights_[index] = weight; } void ColorGeometry4bMixer::mix_in(int64_t index, const ColorGeometry4b &color, float weight) @@ -66,7 +110,12 @@ void ColorGeometry4bMixer::mix_in(int64_t index, const ColorGeometry4b &color, f void ColorGeometry4bMixer::finalize() { - for (const int64_t i : buffer_.index_range()) { + this->finalize(buffer_.index_range()); +} + +void ColorGeometry4bMixer::finalize(const IndexMask mask) +{ + mask.foreach_index([&](const int64_t i) { const float weight = total_weights_[i]; const float4 &accum_value = accumulation_buffer_[i]; ColorGeometry4b &output_color = buffer_[i]; @@ -80,7 +129,7 @@ void ColorGeometry4bMixer::finalize() else { output_color = default_color_; } - } + }); } } // namespace blender::attribute_math diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index b71bcef229a..934c3053a02 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -710,10 +710,11 @@ void BKE_collection_new_name_get(Collection *collection_parent, char *rname) char *name; if (!collection_parent) { - name = BLI_strdup("Collection"); + name = BLI_strdup(DATA_("Collection")); } else if (collection_parent->flag & COLLECTION_IS_MASTER) { - name = BLI_sprintfN("Collection %d", BLI_listbase_count(&collection_parent->children) + 1); + name = BLI_sprintfN(DATA_("Collection %d"), + BLI_listbase_count(&collection_parent->children) + 1); } else { const int number = BLI_listbase_count(&collection_parent->children) + 1; diff --git a/source/blender/blenkernel/intern/curve_nurbs.cc b/source/blender/blenkernel/intern/curve_nurbs.cc index 3ab6fb01ea5..62d5682da0f 100644 --- a/source/blender/blenkernel/intern/curve_nurbs.cc +++ b/source/blender/blenkernel/intern/curve_nurbs.cc @@ -4,8 +4,9 @@ * \ingroup bke */ -#include "BKE_attribute_math.hh" +#include "BLI_task.hh" +#include "BKE_attribute_math.hh" #include "BKE_curves.hh" namespace blender::bke::curves::nurbs { @@ -192,16 +193,16 @@ static void interpolate_to_evaluated(const BasisCache &basis_cache, { attribute_math::DefaultMixer<T> mixer{dst}; - for (const int i : dst.index_range()) { - Span<float> point_weights = basis_cache.weights.as_span().slice(i * order, order); - - for (const int j : point_weights.index_range()) { - const int point_index = (basis_cache.start_indices[i] + j) % src.size(); - mixer.mix_in(i, src[point_index], point_weights[j]); + threading::parallel_for(dst.index_range(), 128, [&](const IndexRange range) { + for (const int i : range) { + Span<float> point_weights = basis_cache.weights.as_span().slice(i * order, order); + for (const int j : point_weights.index_range()) { + const int point_index = (basis_cache.start_indices[i] + j) % src.size(); + mixer.mix_in(i, src[point_index], point_weights[j]); + } } - } - - mixer.finalize(); + mixer.finalize(range); + }); } template<typename T> @@ -213,17 +214,18 @@ static void interpolate_to_evaluated_rational(const BasisCache &basis_cache, { attribute_math::DefaultMixer<T> mixer{dst}; - for (const int i : dst.index_range()) { - Span<float> point_weights = basis_cache.weights.as_span().slice(i * order, order); + threading::parallel_for(dst.index_range(), 128, [&](const IndexRange range) { + for (const int i : range) { + Span<float> point_weights = basis_cache.weights.as_span().slice(i * order, order); - for (const int j : point_weights.index_range()) { - const int point_index = (basis_cache.start_indices[i] + j) % src.size(); - const float weight = point_weights[j] * control_weights[point_index]; - mixer.mix_in(i, src[point_index], weight); + for (const int j : point_weights.index_range()) { + const int point_index = (basis_cache.start_indices[i] + j) % src.size(); + const float weight = point_weights[j] * control_weights[point_index]; + mixer.mix_in(i, src[point_index], weight); + } } - } - - mixer.finalize(); + mixer.finalize(range); + }); } void interpolate_to_evaluated(const BasisCache &basis_cache, diff --git a/source/blender/blenkernel/intern/curves.cc b/source/blender/blenkernel/intern/curves.cc index baa9c32a9ff..f90cf48090c 100644 --- a/source/blender/blenkernel/intern/curves.cc +++ b/source/blender/blenkernel/intern/curves.cc @@ -204,7 +204,7 @@ IDTypeInfo IDType_ID_CV = { /*main_listbase_index */ INDEX_ID_CV, /*struct_size */ sizeof(Curves), /*name */ "Curves", - /*name_plural */ "curves", + /*name_plural */ "hair_curves", /*translation_context */ BLT_I18NCONTEXT_ID_CURVES, /*flags */ IDTYPE_FLAGS_APPEND_IS_REUSABLE, /*asset_type_info */ nullptr, diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc index 7fb4e37f956..c37634b5d3d 100644 --- a/source/blender/blenkernel/intern/curves_geometry.cc +++ b/source/blender/blenkernel/intern/curves_geometry.cc @@ -13,6 +13,7 @@ #include "BLI_index_mask_ops.hh" #include "BLI_length_parameterize.hh" #include "BLI_math_rotation.hh" +#include "BLI_task.hh" #include "DNA_curves_types.h" @@ -1467,12 +1468,15 @@ static void adapt_curve_domain_point_to_curve_impl(const CurvesGeometry &curves, MutableSpan<T> r_values) { attribute_math::DefaultMixer<T> mixer(r_values); - for (const int i_curve : IndexRange(curves.curves_num())) { - for (const int i_point : curves.points_for_curve(i_curve)) { - mixer.mix_in(i_curve, old_values[i_point]); + + threading::parallel_for(curves.curves_range(), 128, [&](const IndexRange range) { + for (const int i_curve : range) { + for (const int i_point : curves.points_for_curve(i_curve)) { + mixer.mix_in(i_curve, old_values[i_point]); + } } - } - mixer.finalize(); + mixer.finalize(range); + }); } /** diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index 75f9a67e992..969b9903a39 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -165,7 +165,7 @@ struct LayerTypeInfo { void (*initminmax)(void *min, void *max); void (*add)(void *data1, const void *data2); void (*dominmax)(const void *data1, void *min, void *max); - void (*copyvalue)(const void *source, void *dest, const int mixmode, const float mixfactor); + void (*copyvalue)(const void *source, void *dest, int mixmode, const float mixfactor); /** a function to read data from a cdf file */ bool (*read)(CDataFile *cdf, void *data, int count); @@ -187,7 +187,7 @@ struct LayerTypeInfo { /** \name Callbacks for (#MDeformVert, #CD_MDEFORMVERT) * \{ */ -static void layerCopy_mdeformvert(const void *source, void *dest, int count) +static void layerCopy_mdeformvert(const void *source, void *dest, const int count) { int i, size = sizeof(MDeformVert); @@ -209,7 +209,7 @@ static void layerCopy_mdeformvert(const void *source, void *dest, int count) } } -static void layerFree_mdeformvert(void *data, int count, int size) +static void layerFree_mdeformvert(void *data, const int count, const int size) { for (int i = 0; i < count; i++) { MDeformVert *dvert = static_cast<MDeformVert *>(POINTER_OFFSET(data, i * size)); @@ -222,38 +222,10 @@ static void layerFree_mdeformvert(void *data, int count, int size) } } -/* copy just zeros in this case */ -static void layerCopy_bmesh_elem_py_ptr(const void *UNUSED(source), void *dest, int count) -{ - const int size = sizeof(void *); - - for (int i = 0; i < count; i++) { - void **ptr = (void **)POINTER_OFFSET(dest, i * size); - *ptr = nullptr; - } -} - -#ifndef WITH_PYTHON -void bpy_bm_generic_invalidate(struct BPy_BMGeneric *UNUSED(self)) -{ - /* dummy */ -} -#endif - -static void layerFree_bmesh_elem_py_ptr(void *data, int count, int size) -{ - for (int i = 0; i < count; i++) { - void **ptr = (void **)POINTER_OFFSET(data, i * size); - if (*ptr) { - bpy_bm_generic_invalidate(static_cast<BPy_BMGeneric *>(*ptr)); - } - } -} - static void layerInterp_mdeformvert(const void **sources, const float *weights, const float *UNUSED(sub_weights), - int count, + const int count, void *dest) { /* a single linked list of MDeformWeight's @@ -347,7 +319,7 @@ static void layerInterp_mdeformvert(const void **sources, static void layerInterp_normal(const void **sources, const float *weights, const float *UNUSED(sub_weights), - int count, + const int count, void *dest) { /* NOTE: This is linear interpolation, which is not optimal for vectors. @@ -355,8 +327,8 @@ static void layerInterp_normal(const void **sources, * so for now it will do... */ float no[3] = {0.0f}; - while (count--) { - madd_v3_v3fl(no, (const float *)sources[count], weights[count]); + for (const int i : IndexRange(count)) { + madd_v3_v3fl(no, (const float *)sources[i], weights[i]); } /* Weighted sum of normalized vectors will **not** be normalized, even if weights are. */ @@ -406,7 +378,7 @@ static void layerCopyValue_normal(const void *source, /** \name Callbacks for (#MTFace, #CD_MTFACE) * \{ */ -static void layerCopy_tface(const void *source, void *dest, int count) +static void layerCopy_tface(const void *source, void *dest, const int count) { const MTFace *source_tf = (const MTFace *)source; MTFace *dest_tf = (MTFace *)dest; @@ -415,8 +387,11 @@ static void layerCopy_tface(const void *source, void *dest, int count) } } -static void layerInterp_tface( - const void **sources, const float *weights, const float *sub_weights, int count, void *dest) +static void layerInterp_tface(const void **sources, + const float *weights, + const float *sub_weights, + const int count, + void *dest) { MTFace *tf = static_cast<MTFace *>(dest); float uv[4][2] = {{0.0f}}; @@ -456,7 +431,7 @@ static void layerSwap_tface(void *data, const int *corner_indices) memcpy(tf->uv, uv, sizeof(tf->uv)); } -static void layerDefault_tface(void *data, int count) +static void layerDefault_tface(void *data, const int count) { static MTFace default_tf = {{{0, 0}, {1, 0}, {1, 1}, {0, 1}}}; MTFace *tf = (MTFace *)data; @@ -477,7 +452,7 @@ static int layerMaxNum_tface() /** \name Callbacks for (#MFloatProperty, #CD_PROP_FLOAT) * \{ */ -static void layerCopy_propFloat(const void *source, void *dest, int count) +static void layerCopy_propFloat(const void *source, void *dest, const int count) { memcpy(dest, source, sizeof(MFloatProperty) * count); } @@ -485,7 +460,7 @@ static void layerCopy_propFloat(const void *source, void *dest, int count) static void layerInterp_propFloat(const void **sources, const float *weights, const float *UNUSED(sub_weights), - int count, + const int count, void *dest) { float result = 0.0f; @@ -520,7 +495,7 @@ static bool layerValidate_propFloat(void *data, const uint totitems, const bool /** \name Callbacks for (#MIntProperty, #CD_PROP_INT32) * \{ */ -static void layerCopy_propInt(const void *source, void *dest, int count) +static void layerCopy_propInt(const void *source, void *dest, const int count) { memcpy(dest, source, sizeof(MIntProperty) * count); } @@ -528,7 +503,7 @@ static void layerCopy_propInt(const void *source, void *dest, int count) static void layerInterp_propInt(const void **sources, const float *weights, const float *UNUSED(sub_weights), - int count, + const int count, void *dest) { float result = 0.0f; @@ -547,7 +522,7 @@ static void layerInterp_propInt(const void **sources, /** \name Callbacks for (#MStringProperty, #CD_PROP_STRING) * \{ */ -static void layerCopy_propString(const void *source, void *dest, int count) +static void layerCopy_propString(const void *source, void *dest, const int count) { memcpy(dest, source, sizeof(MStringProperty) * count); } @@ -558,7 +533,7 @@ static void layerCopy_propString(const void *source, void *dest, int count) /** \name Callbacks for (#OrigSpaceFace, #CD_ORIGSPACE) * \{ */ -static void layerCopy_origspace_face(const void *source, void *dest, int count) +static void layerCopy_origspace_face(const void *source, void *dest, const int count) { const OrigSpaceFace *source_tf = (const OrigSpaceFace *)source; OrigSpaceFace *dest_tf = (OrigSpaceFace *)dest; @@ -568,8 +543,11 @@ static void layerCopy_origspace_face(const void *source, void *dest, int count) } } -static void layerInterp_origspace_face( - const void **sources, const float *weights, const float *sub_weights, int count, void *dest) +static void layerInterp_origspace_face(const void **sources, + const float *weights, + const float *sub_weights, + const int count, + void *dest) { OrigSpaceFace *osf = static_cast<OrigSpaceFace *>(dest); float uv[4][2] = {{0.0f}}; @@ -606,7 +584,7 @@ static void layerSwap_origspace_face(void *data, const int *corner_indices) memcpy(osf->uv, uv, sizeof(osf->uv)); } -static void layerDefault_origspace_face(void *data, int count) +static void layerDefault_origspace_face(void *data, const int count) { static OrigSpaceFace default_osf = {{{0, 0}, {1, 0}, {1, 1}, {0, 1}}}; OrigSpaceFace *osf = (OrigSpaceFace *)data; @@ -652,7 +630,7 @@ static void layerSwap_mdisps(void *data, const int *ci) } } -static void layerCopy_mdisps(const void *source, void *dest, int count) +static void layerCopy_mdisps(const void *source, void *dest, const int count) { const MDisps *s = static_cast<const MDisps *>(source); MDisps *d = static_cast<MDisps *>(dest); @@ -673,7 +651,7 @@ static void layerCopy_mdisps(const void *source, void *dest, int count) } } -static void layerFree_mdisps(void *data, int count, int UNUSED(size)) +static void layerFree_mdisps(void *data, const int count, const int UNUSED(size)) { MDisps *d = static_cast<MDisps *>(data); @@ -691,7 +669,7 @@ static void layerFree_mdisps(void *data, int count, int UNUSED(size)) } } -static bool layerRead_mdisps(CDataFile *cdf, void *data, int count) +static bool layerRead_mdisps(CDataFile *cdf, void *data, const int count) { MDisps *d = static_cast<MDisps *>(data); @@ -709,7 +687,7 @@ static bool layerRead_mdisps(CDataFile *cdf, void *data, int count) return true; } -static bool layerWrite_mdisps(CDataFile *cdf, const void *data, int count) +static bool layerWrite_mdisps(CDataFile *cdf, const void *data, const int count) { const MDisps *d = static_cast<const MDisps *>(data); @@ -723,7 +701,7 @@ static bool layerWrite_mdisps(CDataFile *cdf, const void *data, int count) return true; } -static size_t layerFilesize_mdisps(CDataFile *UNUSED(cdf), const void *data, int count) +static size_t layerFilesize_mdisps(CDataFile *UNUSED(cdf), const void *data, const int count) { const MDisps *d = static_cast<const MDisps *>(data); size_t size = 0; @@ -738,6 +716,40 @@ static size_t layerFilesize_mdisps(CDataFile *UNUSED(cdf), const void *data, int /** \} */ /* -------------------------------------------------------------------- */ +/** \name Callbacks for (#CD_BM_ELEM_PYPTR) + * \{ */ + +/* copy just zeros in this case */ +static void layerCopy_bmesh_elem_py_ptr(const void *UNUSED(source), void *dest, const int count) +{ + const int size = sizeof(void *); + + for (int i = 0; i < count; i++) { + void **ptr = (void **)POINTER_OFFSET(dest, i * size); + *ptr = nullptr; + } +} + +#ifndef WITH_PYTHON +void bpy_bm_generic_invalidate(struct BPy_BMGeneric *UNUSED(self)) +{ + /* dummy */ +} +#endif + +static void layerFree_bmesh_elem_py_ptr(void *data, const int count, const int size) +{ + for (int i = 0; i < count; i++) { + void **ptr = (void **)POINTER_OFFSET(data, i * size); + if (*ptr) { + bpy_bm_generic_invalidate(static_cast<BPy_BMGeneric *>(*ptr)); + } + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Callbacks for (`float`, #CD_PAINT_MASK) * \{ */ @@ -762,7 +774,7 @@ static void layerInterp_paint_mask(const void **sources, /** \name Callbacks for (#GridPaintMask, #CD_GRID_PAINT_MASK) * \{ */ -static void layerCopy_grid_paint_mask(const void *source, void *dest, int count) +static void layerCopy_grid_paint_mask(const void *source, void *dest, const int count) { const GridPaintMask *s = static_cast<const GridPaintMask *>(source); GridPaintMask *d = static_cast<GridPaintMask *>(dest); @@ -779,7 +791,7 @@ static void layerCopy_grid_paint_mask(const void *source, void *dest, int count) } } -static void layerFree_grid_paint_mask(void *data, int count, int UNUSED(size)) +static void layerFree_grid_paint_mask(void *data, const int count, const int UNUSED(size)) { GridPaintMask *gpm = static_cast<GridPaintMask *>(data); @@ -867,7 +879,7 @@ static bool layerEqual_mloopcol(const void *data1, const void *data2) return r * r + g * g + b * b + a * a < 0.001f; } -static void layerMultiply_mloopcol(void *data, float fac) +static void layerMultiply_mloopcol(void *data, const float fac) { MLoopCol *m = static_cast<MLoopCol *>(data); @@ -936,7 +948,7 @@ static void layerInitMinMax_mloopcol(void *vmin, void *vmax) max->a = 0; } -static void layerDefault_mloopcol(void *data, int count) +static void layerDefault_mloopcol(void *data, const int count) { MLoopCol default_mloopcol = {255, 255, 255, 255}; MLoopCol *mlcol = (MLoopCol *)data; @@ -1016,7 +1028,7 @@ static bool layerEqual_mloopuv(const void *data1, const void *data2) return len_squared_v2v2(luv1->uv, luv2->uv) < 0.00001f; } -static void layerMultiply_mloopuv(void *data, float fac) +static void layerMultiply_mloopuv(void *data, const float fac) { MLoopUV *luv = static_cast<MLoopUV *>(data); @@ -1110,7 +1122,7 @@ static bool layerEqual_mloop_origspace(const void *data1, const void *data2) return len_squared_v2v2(luv1->uv, luv2->uv) < 0.00001f; } -static void layerMultiply_mloop_origspace(void *data, float fac) +static void layerMultiply_mloop_origspace(void *data, const float fac) { OrigSpaceLoop *luv = static_cast<OrigSpaceLoop *>(data); @@ -1162,8 +1174,11 @@ static void layerInterp_mloop_origspace(const void **sources, } /* --- end copy */ -static void layerInterp_mcol( - const void **sources, const float *weights, const float *sub_weights, int count, void *dest) +static void layerInterp_mcol(const void **sources, + const float *weights, + const float *sub_weights, + const int count, + void *dest) { MCol *mc = static_cast<MCol *>(dest); struct { @@ -1222,7 +1237,7 @@ static void layerSwap_mcol(void *data, const int *corner_indices) memcpy(mcol, col, sizeof(col)); } -static void layerDefault_mcol(void *data, int count) +static void layerDefault_mcol(void *data, const int count) { static MCol default_mcol = {255, 255, 255, 255}; MCol *mcol = (MCol *)data; @@ -1232,7 +1247,7 @@ static void layerDefault_mcol(void *data, int count) } } -static void layerDefault_origindex(void *data, int count) +static void layerDefault_origindex(void *data, const int count) { copy_vn_i((int *)data, count, ORIGINDEX_NONE); } @@ -1290,7 +1305,7 @@ static void layerInterp_shapekey(const void **sources, /** \name Callbacks for (#MVertSkin, #CD_MVERT_SKIN) * \{ */ -static void layerDefault_mvert_skin(void *data, int count) +static void layerDefault_mvert_skin(void *data, const int count) { MVertSkin *vs = static_cast<MVertSkin *>(data); @@ -1300,7 +1315,7 @@ static void layerDefault_mvert_skin(void *data, int count) } } -static void layerCopy_mvert_skin(const void *source, void *dest, int count) +static void layerCopy_mvert_skin(const void *source, void *dest, const int count) { memcpy(dest, source, sizeof(MVertSkin) * count); } @@ -1352,7 +1367,7 @@ static void layerSwap_flnor(void *data, const int *corner_indices) /** \name Callbacks for (`int`, #CD_FACEMAP) * \{ */ -static void layerDefault_fmap(void *data, int count) +static void layerDefault_fmap(void *data, const int count) { int *fmap_num = (int *)data; for (int i = 0; i < count; i++) { @@ -1428,7 +1443,7 @@ static bool layerEqual_propcol(const void *data1, const void *data2) return tot < 0.001f; } -static void layerMultiply_propcol(void *data, float fac) +static void layerMultiply_propcol(void *data, const float fac) { MPropCol *m = static_cast<MPropCol *>(data); mul_v4_fl(m->color, fac); @@ -1458,7 +1473,7 @@ static void layerInitMinMax_propcol(void *vmin, void *vmax) copy_v4_fl(max->color, FLT_MIN); } -static void layerDefault_propcol(void *data, int count) +static void layerDefault_propcol(void *data, const int count) { /* Default to white, full alpha. */ MPropCol default_propcol = {{1.0f, 1.0f, 1.0f, 1.0f}}; @@ -1510,7 +1525,7 @@ static void layerInterp_propfloat3(const void **sources, copy_v3_v3((float *)dest, &result.x); } -static void layerMultiply_propfloat3(void *data, float fac) +static void layerMultiply_propfloat3(void *data, const float fac) { vec3f *vec = static_cast<vec3f *>(data); vec->x *= fac; @@ -1563,7 +1578,7 @@ static void layerInterp_propfloat2(const void **sources, copy_v2_v2((float *)dest, &result.x); } -static void layerMultiply_propfloat2(void *data, float fac) +static void layerMultiply_propfloat2(void *data, const float fac) { vec2f *vec = static_cast<vec2f *>(data); vec->x *= fac; @@ -2354,9 +2369,9 @@ static CustomData shallow_copy_remove_non_bmesh_attributes(const CustomData &src bool CustomData_merge_mesh_to_bmesh(const CustomData *source, CustomData *dest, - eCustomDataMask mask, - eCDAllocType alloctype, - int totelem) + const eCustomDataMask mask, + const eCDAllocType alloctype, + const int totelem) { CustomData source_copy = shallow_copy_remove_non_bmesh_attributes(*source); const bool result = CustomData_merge(&source_copy, dest, mask, alloctype, totelem); @@ -2364,7 +2379,7 @@ bool CustomData_merge_mesh_to_bmesh(const CustomData *source, return result; } -void CustomData_realloc(CustomData *data, int totelem) +void CustomData_realloc(CustomData *data, const int totelem) { BLI_assert(totelem >= 0); for (int i = 0; i < data->totlayer; i++) { @@ -2397,16 +2412,16 @@ void CustomData_copy(const CustomData *source, void CustomData_copy_mesh_to_bmesh(const CustomData *source, CustomData *dest, - eCustomDataMask mask, - eCDAllocType alloctype, - int totelem) + const eCustomDataMask mask, + const eCDAllocType alloctype, + const int totelem) { CustomData source_copy = shallow_copy_remove_non_bmesh_attributes(*source); CustomData_copy(&source_copy, dest, mask, alloctype, totelem); MEM_SAFE_FREE(source_copy.layers); } -static void customData_free_layer__internal(CustomDataLayer *layer, int totelem) +static void customData_free_layer__internal(CustomDataLayer *layer, const int totelem) { const LayerTypeInfo *typeInfo; @@ -2441,7 +2456,7 @@ void CustomData_reset(CustomData *data) copy_vn_i(data->typemap, CD_NUMTYPES, -1); } -void CustomData_free(CustomData *data, int totelem) +void CustomData_free(CustomData *data, const int totelem) { for (int i = 0; i < data->totlayer; i++) { customData_free_layer__internal(&data->layers[i], totelem); @@ -2455,7 +2470,7 @@ void CustomData_free(CustomData *data, int totelem) CustomData_reset(data); } -void CustomData_free_typemask(CustomData *data, int totelem, eCustomDataMask mask) +void CustomData_free_typemask(CustomData *data, const int totelem, eCustomDataMask mask) { for (int i = 0; i < data->totlayer; i++) { CustomDataLayer *layer = &data->layers[i]; @@ -2490,7 +2505,7 @@ static void customData_update_offsets(CustomData *data) } /* to use when we're in the middle of modifying layers */ -static int CustomData_get_layer_index__notypemap(const CustomData *data, int type) +static int CustomData_get_layer_index__notypemap(const CustomData *data, const int type) { for (int i = 0; i < data->totlayer; i++) { if (data->layers[i].type == type) { @@ -2504,13 +2519,13 @@ static int CustomData_get_layer_index__notypemap(const CustomData *data, int typ /* -------------------------------------------------------------------- */ /* index values to access the layers (offset from the layer start) */ -int CustomData_get_layer_index(const CustomData *data, int type) +int CustomData_get_layer_index(const CustomData *data, const int type) { BLI_assert(customdata_typemap_is_valid(data)); return data->typemap[type]; } -int CustomData_get_layer_index_n(const CustomData *data, int type, int n) +int CustomData_get_layer_index_n(const CustomData *data, const int type, const int n) { BLI_assert(n >= 0); int i = CustomData_get_layer_index(data, type); @@ -2523,7 +2538,7 @@ int CustomData_get_layer_index_n(const CustomData *data, int type, int n) return i; } -int CustomData_get_named_layer_index(const CustomData *data, int type, const char *name) +int CustomData_get_named_layer_index(const CustomData *data, const int type, const char *name) { for (int i = 0; i < data->totlayer; i++) { if (data->layers[i].type == type) { @@ -2536,28 +2551,28 @@ int CustomData_get_named_layer_index(const CustomData *data, int type, const cha return -1; } -int CustomData_get_active_layer_index(const CustomData *data, int type) +int CustomData_get_active_layer_index(const CustomData *data, const int type) { const int layer_index = data->typemap[type]; BLI_assert(customdata_typemap_is_valid(data)); return (layer_index != -1) ? layer_index + data->layers[layer_index].active : -1; } -int CustomData_get_render_layer_index(const CustomData *data, int type) +int CustomData_get_render_layer_index(const CustomData *data, const int type) { const int layer_index = data->typemap[type]; BLI_assert(customdata_typemap_is_valid(data)); return (layer_index != -1) ? layer_index + data->layers[layer_index].active_rnd : -1; } -int CustomData_get_clone_layer_index(const CustomData *data, int type) +int CustomData_get_clone_layer_index(const CustomData *data, const int type) { const int layer_index = data->typemap[type]; BLI_assert(customdata_typemap_is_valid(data)); return (layer_index != -1) ? layer_index + data->layers[layer_index].active_clone : -1; } -int CustomData_get_stencil_layer_index(const CustomData *data, int type) +int CustomData_get_stencil_layer_index(const CustomData *data, const int type) { const int layer_index = data->typemap[type]; BLI_assert(customdata_typemap_is_valid(data)); @@ -2567,7 +2582,7 @@ int CustomData_get_stencil_layer_index(const CustomData *data, int type) /* -------------------------------------------------------------------- */ /* index values per layer type */ -int CustomData_get_named_layer(const CustomData *data, int type, const char *name) +int CustomData_get_named_layer(const CustomData *data, const int type, const char *name) { const int named_index = CustomData_get_named_layer_index(data, type, name); const int layer_index = data->typemap[type]; @@ -2575,28 +2590,28 @@ int CustomData_get_named_layer(const CustomData *data, int type, const char *nam return (named_index != -1) ? named_index - layer_index : -1; } -int CustomData_get_active_layer(const CustomData *data, int type) +int CustomData_get_active_layer(const CustomData *data, const int type) { const int layer_index = data->typemap[type]; BLI_assert(customdata_typemap_is_valid(data)); return (layer_index != -1) ? data->layers[layer_index].active : -1; } -int CustomData_get_render_layer(const CustomData *data, int type) +int CustomData_get_render_layer(const CustomData *data, const int type) { const int layer_index = data->typemap[type]; BLI_assert(customdata_typemap_is_valid(data)); return (layer_index != -1) ? data->layers[layer_index].active_rnd : -1; } -int CustomData_get_clone_layer(const CustomData *data, int type) +int CustomData_get_clone_layer(const CustomData *data, const int type) { const int layer_index = data->typemap[type]; BLI_assert(customdata_typemap_is_valid(data)); return (layer_index != -1) ? data->layers[layer_index].active_clone : -1; } -int CustomData_get_stencil_layer(const CustomData *data, int type) +int CustomData_get_stencil_layer(const CustomData *data, const int type) { const int layer_index = data->typemap[type]; BLI_assert(customdata_typemap_is_valid(data)); @@ -2610,7 +2625,7 @@ const char *CustomData_get_active_layer_name(const CustomData *data, const int t return layer_index < 0 ? nullptr : data->layers[layer_index].name; } -void CustomData_set_layer_active(CustomData *data, int type, int n) +void CustomData_set_layer_active(CustomData *data, const int type, const int n) { for (int i = 0; i < data->totlayer; i++) { if (data->layers[i].type == type) { @@ -2619,7 +2634,7 @@ void CustomData_set_layer_active(CustomData *data, int type, int n) } } -void CustomData_set_layer_render(CustomData *data, int type, int n) +void CustomData_set_layer_render(CustomData *data, const int type, const int n) { for (int i = 0; i < data->totlayer; i++) { if (data->layers[i].type == type) { @@ -2628,7 +2643,7 @@ void CustomData_set_layer_render(CustomData *data, int type, int n) } } -void CustomData_set_layer_clone(CustomData *data, int type, int n) +void CustomData_set_layer_clone(CustomData *data, const int type, const int n) { for (int i = 0; i < data->totlayer; i++) { if (data->layers[i].type == type) { @@ -2637,7 +2652,7 @@ void CustomData_set_layer_clone(CustomData *data, int type, int n) } } -void CustomData_set_layer_stencil(CustomData *data, int type, int n) +void CustomData_set_layer_stencil(CustomData *data, const int type, const int n) { for (int i = 0; i < data->totlayer; i++) { if (data->layers[i].type == type) { @@ -2646,7 +2661,7 @@ void CustomData_set_layer_stencil(CustomData *data, int type, int n) } } -void CustomData_set_layer_active_index(CustomData *data, int type, int n) +void CustomData_set_layer_active_index(CustomData *data, const int type, const int n) { const int layer_index = data->typemap[type]; BLI_assert(customdata_typemap_is_valid(data)); @@ -2658,7 +2673,7 @@ void CustomData_set_layer_active_index(CustomData *data, int type, int n) } } -void CustomData_set_layer_render_index(CustomData *data, int type, int n) +void CustomData_set_layer_render_index(CustomData *data, const int type, const int n) { const int layer_index = data->typemap[type]; BLI_assert(customdata_typemap_is_valid(data)); @@ -2670,7 +2685,7 @@ void CustomData_set_layer_render_index(CustomData *data, int type, int n) } } -void CustomData_set_layer_clone_index(CustomData *data, int type, int n) +void CustomData_set_layer_clone_index(CustomData *data, const int type, const int n) { const int layer_index = data->typemap[type]; BLI_assert(customdata_typemap_is_valid(data)); @@ -2682,7 +2697,7 @@ void CustomData_set_layer_clone_index(CustomData *data, int type, int n) } } -void CustomData_set_layer_stencil_index(CustomData *data, int type, int n) +void CustomData_set_layer_stencil_index(CustomData *data, const int type, const int n) { const int layer_index = data->typemap[type]; BLI_assert(customdata_typemap_is_valid(data)); @@ -2694,7 +2709,7 @@ void CustomData_set_layer_stencil_index(CustomData *data, int type, int n) } } -void CustomData_set_layer_flag(CustomData *data, int type, int flag) +void CustomData_set_layer_flag(CustomData *data, const int type, const int flag) { for (int i = 0; i < data->totlayer; i++) { if (data->layers[i].type == type) { @@ -2703,7 +2718,7 @@ void CustomData_set_layer_flag(CustomData *data, int type, int flag) } } -void CustomData_clear_layer_flag(CustomData *data, int type, int flag) +void CustomData_clear_layer_flag(CustomData *data, const int type, const int flag) { const int nflag = ~flag; @@ -2714,7 +2729,7 @@ void CustomData_clear_layer_flag(CustomData *data, int type, int flag) } } -static bool customData_resize(CustomData *data, int amount) +static bool customData_resize(CustomData *data, const int amount) { CustomDataLayer *tmp = static_cast<CustomDataLayer *>( MEM_calloc_arrayN((data->maxlayer + amount), sizeof(*tmp), __func__)); @@ -2733,15 +2748,14 @@ static bool customData_resize(CustomData *data, int amount) } static CustomDataLayer *customData_add_layer__internal(CustomData *data, - int type, - eCDAllocType alloctype, + const int type, + const eCDAllocType alloctype, void *layerdata, - int totelem, + const int totelem, const char *name) { const LayerTypeInfo *typeInfo = layerType_getInfo(type); - int flag = 0, index = data->totlayer; - void *newlayerdata = nullptr; + int flag = 0; /* Passing a layer-data to copy from with an alloctype that won't copy is * most likely a bug */ @@ -2751,6 +2765,7 @@ static CustomDataLayer *customData_add_layer__internal(CustomData *data, return &data->layers[CustomData_get_layer_index(data, type)]; } + void *newlayerdata = nullptr; if (ELEM(alloctype, CD_ASSIGN, CD_REFERENCE)) { newlayerdata = layerdata; } @@ -2786,6 +2801,7 @@ static CustomDataLayer *customData_add_layer__internal(CustomData *data, flag |= CD_FLAG_NOFREE; } + int index = data->totlayer; if (index >= data->maxlayer) { if (!customData_resize(data, CUSTOMDATA_GROW)) { if (newlayerdata != layerdata) { @@ -2802,14 +2818,16 @@ static CustomDataLayer *customData_add_layer__internal(CustomData *data, data->layers[index] = data->layers[index - 1]; } + CustomDataLayer &new_layer = data->layers[index]; + /* Clear remaining data on the layer. The original data on the layer has been moved to another * index. Without this, it can happen that information from the previous layer at that index * leaks into the new layer. */ - memset(data->layers + index, 0, sizeof(CustomDataLayer)); + memset(&new_layer, 0, sizeof(CustomDataLayer)); - data->layers[index].type = type; - data->layers[index].flag = flag; - data->layers[index].data = newlayerdata; + new_layer.type = type; + new_layer.flag = flag; + new_layer.data = newlayerdata; /* Set default name if none exists. Note we only call DATA_() once * we know there is a default name, to avoid overhead of locale lookups @@ -2819,24 +2837,24 @@ static CustomDataLayer *customData_add_layer__internal(CustomData *data, } if (name) { - BLI_strncpy(data->layers[index].name, name, sizeof(data->layers[index].name)); + BLI_strncpy(new_layer.name, name, sizeof(new_layer.name)); CustomData_set_layer_unique_name(data, index); } else { - data->layers[index].name[0] = '\0'; + new_layer.name[0] = '\0'; } if (index > 0 && data->layers[index - 1].type == type) { - data->layers[index].active = data->layers[index - 1].active; - data->layers[index].active_rnd = data->layers[index - 1].active_rnd; - data->layers[index].active_clone = data->layers[index - 1].active_clone; - data->layers[index].active_mask = data->layers[index - 1].active_mask; + new_layer.active = data->layers[index - 1].active; + new_layer.active_rnd = data->layers[index - 1].active_rnd; + new_layer.active_clone = data->layers[index - 1].active_clone; + new_layer.active_mask = data->layers[index - 1].active_mask; } else { - data->layers[index].active = 0; - data->layers[index].active_rnd = 0; - data->layers[index].active_clone = 0; - data->layers[index].active_mask = 0; + new_layer.active = 0; + new_layer.active_rnd = 0; + new_layer.active_clone = 0; + new_layer.active_mask = 0; } customData_update_offsets(data); @@ -2845,7 +2863,7 @@ static CustomDataLayer *customData_add_layer__internal(CustomData *data, } void *CustomData_add_layer( - CustomData *data, int type, eCDAllocType alloctype, void *layerdata, int totelem) + CustomData *data, const int type, eCDAllocType alloctype, void *layerdata, const int totelem) { const LayerTypeInfo *typeInfo = layerType_getInfo(type); @@ -2861,10 +2879,10 @@ void *CustomData_add_layer( } void *CustomData_add_layer_named(CustomData *data, - int type, - eCDAllocType alloctype, + const int type, + const eCDAllocType alloctype, void *layerdata, - int totelem, + const int totelem, const char *name) { CustomDataLayer *layer = customData_add_layer__internal( @@ -2879,10 +2897,10 @@ void *CustomData_add_layer_named(CustomData *data, } void *CustomData_add_layer_anonymous(CustomData *data, - int type, - eCDAllocType alloctype, + const int type, + const eCDAllocType alloctype, void *layerdata, - int totelem, + const int totelem, const AnonymousAttributeID *anonymous_id) { const char *name = BKE_anonymous_attribute_id_internal_name(anonymous_id); @@ -2899,7 +2917,7 @@ void *CustomData_add_layer_anonymous(CustomData *data, return layer->data; } -bool CustomData_free_layer(CustomData *data, int type, int totelem, int index) +bool CustomData_free_layer(CustomData *data, const int type, const int totelem, const int index) { const int index_first = CustomData_get_layer_index(data, type); const int n = index - index_first; @@ -2963,7 +2981,7 @@ bool CustomData_free_layer_named(CustomData *data, const char *name, const int t return false; } -bool CustomData_free_layer_active(CustomData *data, int type, int totelem) +bool CustomData_free_layer_active(CustomData *data, const int type, const int totelem) { const int index = CustomData_get_active_layer_index(data, type); if (index == -1) { @@ -2972,7 +2990,7 @@ bool CustomData_free_layer_active(CustomData *data, int type, int totelem) return CustomData_free_layer(data, type, totelem, index); } -void CustomData_free_layers(CustomData *data, int type, int totelem) +void CustomData_free_layers(CustomData *data, const int type, const int totelem) { const int index = CustomData_get_layer_index(data, type); while (CustomData_free_layer(data, type, totelem, index)) { @@ -2980,12 +2998,12 @@ void CustomData_free_layers(CustomData *data, int type, int totelem) } } -bool CustomData_has_layer(const CustomData *data, int type) +bool CustomData_has_layer(const CustomData *data, const int type) { return (CustomData_get_layer_index(data, type) != -1); } -int CustomData_number_of_layers(const CustomData *data, int type) +int CustomData_number_of_layers(const CustomData *data, const int type) { int number = 0; @@ -2998,7 +3016,7 @@ int CustomData_number_of_layers(const CustomData *data, int type) return number; } -int CustomData_number_of_layers_typemask(const CustomData *data, eCustomDataMask mask) +int CustomData_number_of_layers_typemask(const CustomData *data, const eCustomDataMask mask) { int number = 0; @@ -3088,7 +3106,7 @@ void *CustomData_duplicate_referenced_layer_anonymous(CustomData *data, return nullptr; } -void CustomData_duplicate_referenced_layers(CustomData *data, int totelem) +void CustomData_duplicate_referenced_layers(CustomData *data, const int totelem) { for (int i = 0; i < data->totlayer; i++) { CustomDataLayer *layer = &data->layers[i]; @@ -3096,7 +3114,7 @@ void CustomData_duplicate_referenced_layers(CustomData *data, int totelem) } } -bool CustomData_is_referenced_layer(CustomData *data, int type) +bool CustomData_is_referenced_layer(CustomData *data, const int type) { /* get the layer index of the first layer of type */ int layer_index = CustomData_get_active_layer_index(data, type); @@ -3109,7 +3127,7 @@ bool CustomData_is_referenced_layer(CustomData *data, int type) return (layer->flag & CD_FLAG_NOFREE) != 0; } -void CustomData_free_temporary(CustomData *data, int totelem) +void CustomData_free_temporary(CustomData *data, const int totelem) { int i, j; bool changed = false; @@ -3141,7 +3159,7 @@ void CustomData_free_temporary(CustomData *data, int totelem) } } -void CustomData_set_only_copy(const CustomData *data, eCustomDataMask mask) +void CustomData_set_only_copy(const CustomData *data, const eCustomDataMask mask) { for (int i = 0; i < data->totlayer; i++) { if (!(mask & CD_TYPE_AS_MASK(data->layers[i].type))) { @@ -3150,7 +3168,10 @@ void CustomData_set_only_copy(const CustomData *data, eCustomDataMask mask) } } -void CustomData_copy_elements(int type, void *src_data_ofs, void *dst_data_ofs, int count) +void CustomData_copy_elements(const int type, + void *src_data_ofs, + void *dst_data_ofs, + const int count) { const LayerTypeInfo *typeInfo = layerType_getInfo(type); @@ -3164,11 +3185,11 @@ void CustomData_copy_elements(int type, void *src_data_ofs, void *dst_data_ofs, void CustomData_copy_data_layer(const CustomData *source, CustomData *dest, - int src_layer_index, - int dst_layer_index, - int src_index, - int dst_index, - int count) + const int src_layer_index, + const int dst_layer_index, + const int src_index, + const int dst_index, + const int count) { const LayerTypeInfo *typeInfo; @@ -3202,8 +3223,11 @@ void CustomData_copy_data_layer(const CustomData *source, } } -void CustomData_copy_data_named( - const CustomData *source, CustomData *dest, int source_index, int dest_index, int count) +void CustomData_copy_data_named(const CustomData *source, + CustomData *dest, + const int source_index, + const int dest_index, + const int count) { /* copies a layer at a time */ for (int src_i = 0; src_i < source->totlayer; src_i++) { @@ -3218,8 +3242,11 @@ void CustomData_copy_data_named( } } -void CustomData_copy_data( - const CustomData *source, CustomData *dest, int source_index, int dest_index, int count) +void CustomData_copy_data(const CustomData *source, + CustomData *dest, + const int source_index, + const int dest_index, + const int count) { /* copies a layer at a time */ int dest_i = 0; @@ -3274,7 +3301,7 @@ void CustomData_copy_layer_type_data(const CustomData *source, count); } -void CustomData_free_elem(CustomData *data, int index, int count) +void CustomData_free_elem(CustomData *data, const int index, const int count) { for (int i = 0; i < data->totlayer; i++) { if (!(data->layers[i].flag & CD_FLAG_NOFREE)) { @@ -3374,7 +3401,7 @@ void CustomData_interp(const CustomData *source, } } -void CustomData_swap_corners(CustomData *data, int index, const int *corner_indices) +void CustomData_swap_corners(CustomData *data, const int index, const int *corner_indices) { for (int i = 0; i < data->totlayer; i++) { const LayerTypeInfo *typeInfo = layerType_getInfo(data->layers[i].type); @@ -3414,7 +3441,7 @@ void CustomData_swap(CustomData *data, const int index_a, const int index_b) } } -void *CustomData_get(const CustomData *data, int index, int type) +void *CustomData_get(const CustomData *data, const int index, const int type) { BLI_assert(index >= 0); @@ -3430,7 +3457,7 @@ void *CustomData_get(const CustomData *data, int index, int type) return POINTER_OFFSET(data->layers[layer_index].data, offset); } -void *CustomData_get_n(const CustomData *data, int type, int index, int n) +void *CustomData_get_n(const CustomData *data, const int type, const int index, const int n) { BLI_assert(index >= 0 && n >= 0); @@ -3444,7 +3471,7 @@ void *CustomData_get_n(const CustomData *data, int type, int index, int n) return POINTER_OFFSET(data->layers[layer_index + n].data, offset); } -void *CustomData_get_layer(const CustomData *data, int type) +void *CustomData_get_layer(const CustomData *data, const int type) { /* get the layer index of the active layer of type */ int layer_index = CustomData_get_active_layer_index(data, type); @@ -3455,7 +3482,7 @@ void *CustomData_get_layer(const CustomData *data, int type) return data->layers[layer_index].data; } -void *CustomData_get_layer_n(const CustomData *data, int type, int n) +void *CustomData_get_layer_n(const CustomData *data, const int type, const int n) { /* get the layer index of the active layer of type */ int layer_index = CustomData_get_layer_index_n(data, type, n); @@ -3466,7 +3493,7 @@ void *CustomData_get_layer_n(const CustomData *data, int type, int n) return data->layers[layer_index].data; } -void *CustomData_get_layer_named(const CustomData *data, int type, const char *name) +void *CustomData_get_layer_named(const CustomData *data, const int type, const char *name) { int layer_index = CustomData_get_named_layer_index(data, type, name); if (layer_index == -1) { @@ -3476,7 +3503,7 @@ void *CustomData_get_layer_named(const CustomData *data, int type, const char *n return data->layers[layer_index].data; } -int CustomData_get_offset(const CustomData *data, int type) +int CustomData_get_offset(const CustomData *data, const int type) { /* get the layer index of the active layer of type */ int layer_index = CustomData_get_active_layer_index(data, type); @@ -3487,7 +3514,7 @@ int CustomData_get_offset(const CustomData *data, int type) return data->layers[layer_index].offset; } -int CustomData_get_n_offset(const CustomData *data, int type, int n) +int CustomData_get_n_offset(const CustomData *data, const int type, const int n) { /* get the layer index of the active layer of type */ int layer_index = CustomData_get_layer_index_n(data, type, n); @@ -3498,7 +3525,20 @@ int CustomData_get_n_offset(const CustomData *data, int type, int n) return data->layers[layer_index].offset; } -bool CustomData_set_layer_name(const CustomData *data, int type, int n, const char *name) +int CustomData_get_offset_named(const CustomData *data, int type, const char *name) +{ + int layer_index = CustomData_get_named_layer_index(data, type, name); + if (layer_index == -1) { + return -1; + } + + return data->layers[layer_index].offset; +} + +bool CustomData_set_layer_name(const CustomData *data, + const int type, + const int n, + const char *name) { /* get the layer index of the first layer of type */ const int layer_index = CustomData_get_layer_index_n(data, type, n); @@ -3512,14 +3552,14 @@ bool CustomData_set_layer_name(const CustomData *data, int type, int n, const ch return true; } -const char *CustomData_get_layer_name(const CustomData *data, int type, int n) +const char *CustomData_get_layer_name(const CustomData *data, const int type, const int n) { const int layer_index = CustomData_get_layer_index_n(data, type, n); return (layer_index == -1) ? nullptr : data->layers[layer_index].name; } -void *CustomData_set_layer(const CustomData *data, int type, void *ptr) +void *CustomData_set_layer(const CustomData *data, const int type, void *ptr) { /* get the layer index of the first layer of type */ int layer_index = CustomData_get_active_layer_index(data, type); @@ -3533,7 +3573,7 @@ void *CustomData_set_layer(const CustomData *data, int type, void *ptr) return ptr; } -void *CustomData_set_layer_n(const CustomData *data, int type, int n, void *ptr) +void *CustomData_set_layer_n(const CustomData *data, const int type, const int n, void *ptr) { /* get the layer index of the first layer of type */ int layer_index = CustomData_get_layer_index_n(data, type, n); @@ -3546,7 +3586,7 @@ void *CustomData_set_layer_n(const CustomData *data, int type, int n, void *ptr) return ptr; } -void CustomData_set(const CustomData *data, int index, int type, const void *source) +void CustomData_set(const CustomData *data, const int index, const int type, const void *source) { void *dest = CustomData_get(data, index, type); const LayerTypeInfo *typeInfo = layerType_getInfo(type); @@ -3598,7 +3638,7 @@ void CustomData_bmesh_update_active_layers(CustomData *fdata, CustomData *ldata) } } -void CustomData_bmesh_init_pool(CustomData *data, int totelem, const char htype) +void CustomData_bmesh_init_pool(CustomData *data, const int totelem, const char htype) { int chunksize; @@ -3800,7 +3840,7 @@ void CustomData_bmesh_free_block_data_exclude_by_type(CustomData *data, } } -static void CustomData_bmesh_set_default_n(CustomData *data, void **block, int n) +static void CustomData_bmesh_set_default_n(CustomData *data, void **block, const int n) { int offset = data->layers[n].offset; const LayerTypeInfo *typeInfo = layerType_getInfo(data->layers[n].type); @@ -3895,7 +3935,7 @@ void CustomData_bmesh_copy_data(const CustomData *source, CustomData_bmesh_copy_data_exclude_by_type(source, dest, src_block, dest_block, 0); } -void *CustomData_bmesh_get(const CustomData *data, void *block, int type) +void *CustomData_bmesh_get(const CustomData *data, void *block, const int type) { /* get the layer index of the first layer of type */ int layer_index = CustomData_get_active_layer_index(data, type); @@ -3906,7 +3946,7 @@ void *CustomData_bmesh_get(const CustomData *data, void *block, int type) return POINTER_OFFSET(block, data->layers[layer_index].offset); } -void *CustomData_bmesh_get_n(const CustomData *data, void *block, int type, int n) +void *CustomData_bmesh_get_n(const CustomData *data, void *block, const int type, const int n) { /* get the layer index of the first layer of type */ int layer_index = CustomData_get_layer_index(data, type); @@ -3917,7 +3957,7 @@ void *CustomData_bmesh_get_n(const CustomData *data, void *block, int type, int return POINTER_OFFSET(block, data->layers[layer_index + n].offset); } -void *CustomData_bmesh_get_layer_n(const CustomData *data, void *block, int n) +void *CustomData_bmesh_get_layer_n(const CustomData *data, void *block, const int n) { if (n < 0 || n >= data->totlayer) { return nullptr; @@ -3926,7 +3966,7 @@ void *CustomData_bmesh_get_layer_n(const CustomData *data, void *block, int n) return POINTER_OFFSET(block, data->layers[n].offset); } -bool CustomData_layer_has_math(const CustomData *data, int layer_n) +bool CustomData_layer_has_math(const CustomData *data, const int layer_n) { const LayerTypeInfo *typeInfo = layerType_getInfo(data->layers[layer_n].type); @@ -3938,7 +3978,7 @@ bool CustomData_layer_has_math(const CustomData *data, int layer_n) return false; } -bool CustomData_layer_has_interp(const CustomData *data, int layer_n) +bool CustomData_layer_has_interp(const CustomData *data, const int layer_n) { const LayerTypeInfo *typeInfo = layerType_getInfo(data->layers[layer_n].type); @@ -4059,7 +4099,7 @@ void CustomData_data_dominmax(int type, const void *data, void *min, void *max) } } -void CustomData_data_multiply(int type, void *data, float fac) +void CustomData_data_multiply(int type, void *data, const float fac) { const LayerTypeInfo *typeInfo = layerType_getInfo(type); @@ -4077,7 +4117,7 @@ void CustomData_data_add(int type, void *data1, const void *data2) } } -void CustomData_bmesh_set(const CustomData *data, void *block, int type, const void *source) +void CustomData_bmesh_set(const CustomData *data, void *block, const int type, const void *source) { void *dest = CustomData_bmesh_get(data, block, type); const LayerTypeInfo *typeInfo = layerType_getInfo(type); @@ -4094,7 +4134,8 @@ void CustomData_bmesh_set(const CustomData *data, void *block, int type, const v } } -void CustomData_bmesh_set_n(CustomData *data, void *block, int type, int n, const void *source) +void CustomData_bmesh_set_n( + CustomData *data, void *block, const int type, const int n, const void *source) { void *dest = CustomData_bmesh_get_n(data, block, type, n); const LayerTypeInfo *typeInfo = layerType_getInfo(type); @@ -4111,7 +4152,7 @@ void CustomData_bmesh_set_n(CustomData *data, void *block, int type, int n, cons } } -void CustomData_bmesh_set_layer_n(CustomData *data, void *block, int n, const void *source) +void CustomData_bmesh_set_layer_n(CustomData *data, void *block, const int n, const void *source) { void *dest = CustomData_bmesh_get_layer_n(data, block, n); const LayerTypeInfo *typeInfo = layerType_getInfo(data->layers[n].type); @@ -4365,7 +4406,7 @@ int CustomData_layertype_layers_max(const int type) return typeInfo->layers_max(); } -static bool cd_layer_find_dupe(CustomData *data, const char *name, int type, int index) +static bool cd_layer_find_dupe(CustomData *data, const char *name, const int type, const int index) { /* see if there is a duplicate */ for (int i = 0; i < data->totlayer; i++) { @@ -4400,7 +4441,7 @@ static bool customdata_unique_check(void *arg, const char *name) return cd_layer_find_dupe(data_arg->data, name, data_arg->type, data_arg->index); } -void CustomData_set_layer_unique_name(CustomData *data, int index) +void CustomData_set_layer_unique_name(CustomData *data, const int index) { CustomDataLayer *nlayer = &data->layers[index]; const LayerTypeInfo *typeInfo = layerType_getInfo(nlayer->type); @@ -4445,7 +4486,7 @@ void CustomData_validate_layer_name(const CustomData *data, } } -bool CustomData_verify_versions(CustomData *data, int index) +bool CustomData_verify_versions(CustomData *data, const int index) { const LayerTypeInfo *typeInfo; CustomDataLayer *layer = &data->layers[index]; @@ -4604,7 +4645,7 @@ void CustomData_external_reload(CustomData *data, } } -void CustomData_external_read(CustomData *data, ID *id, eCustomDataMask mask, int totelem) +void CustomData_external_read(CustomData *data, ID *id, eCustomDataMask mask, const int totelem) { CustomDataExternal *external = data->external; CustomDataLayer *layer; @@ -4678,7 +4719,7 @@ void CustomData_external_read(CustomData *data, ID *id, eCustomDataMask mask, in } void CustomData_external_write( - CustomData *data, ID *id, eCustomDataMask mask, int totelem, int free) + CustomData *data, ID *id, eCustomDataMask mask, const int totelem, const int free) { CustomDataExternal *external = data->external; int update = 0; @@ -4780,8 +4821,11 @@ void CustomData_external_write( cdf_free(cdf); } -void CustomData_external_add( - CustomData *data, ID *UNUSED(id), int type, int UNUSED(totelem), const char *filepath) +void CustomData_external_add(CustomData *data, + ID *UNUSED(id), + const int type, + const int UNUSED(totelem), + const char *filepath) { CustomDataExternal *external = data->external; @@ -4805,7 +4849,7 @@ void CustomData_external_add( layer->flag |= CD_FLAG_EXTERNAL | CD_FLAG_IN_MEMORY; } -void CustomData_external_remove(CustomData *data, ID *id, int type, int totelem) +void CustomData_external_remove(CustomData *data, ID *id, const int type, const int totelem) { CustomDataExternal *external = data->external; @@ -4829,7 +4873,7 @@ void CustomData_external_remove(CustomData *data, ID *id, int type, int totelem) } } -bool CustomData_external_test(CustomData *data, int type) +bool CustomData_external_test(CustomData *data, const int type) { int layer_index = CustomData_get_active_layer_index(data, type); if (layer_index == -1) { @@ -5130,7 +5174,10 @@ void CustomData_data_transfer(const MeshPairRemap *me_remap, /** \name Custom Data IO * \{ */ -static void write_mdisps(BlendWriter *writer, int count, const MDisps *mdlist, int external) +static void write_mdisps(BlendWriter *writer, + const int count, + const MDisps *mdlist, + const int external) { if (mdlist) { BLO_write_struct_array(writer, MDisps, count, mdlist); @@ -5230,7 +5277,10 @@ void CustomData_blend_write(BlendWriter *writer, } } -static void blend_read_mdisps(BlendDataReader *reader, int count, MDisps *mdisps, int external) +static void blend_read_mdisps(BlendDataReader *reader, + const int count, + MDisps *mdisps, + const int external) { if (mdisps) { for (int i = 0; i < count; i++) { @@ -5272,7 +5322,7 @@ static void blend_read_paint_mask(BlendDataReader *reader, } } -void CustomData_blend_read(BlendDataReader *reader, CustomData *data, int count) +void CustomData_blend_read(BlendDataReader *reader, CustomData *data, const int count) { BLO_read_data_address(reader, &data->layers); diff --git a/source/blender/blenkernel/intern/data_transfer.c b/source/blender/blenkernel/intern/data_transfer.c index 17a74b5564a..be686635d3e 100644 --- a/source/blender/blenkernel/intern/data_transfer.c +++ b/source/blender/blenkernel/intern/data_transfer.c @@ -70,8 +70,6 @@ void BKE_object_data_transfer_dttypes_to_cdmask(const int dtdata_types, r_data_masks->lmask |= CD_MASK_MLOOPUV; } else if (cddata_type == CD_FAKE_LNOR) { - r_data_masks->vmask |= CD_MASK_NORMAL; - r_data_masks->pmask |= CD_MASK_NORMAL; r_data_masks->lmask |= CD_MASK_NORMAL | CD_MASK_CUSTOMLOOPNORMAL; } } diff --git a/source/blender/blenkernel/intern/displist.cc b/source/blender/blenkernel/intern/displist.cc index 823fce52b50..b1017a78e15 100644 --- a/source/blender/blenkernel/intern/displist.cc +++ b/source/blender/blenkernel/intern/displist.cc @@ -86,19 +86,6 @@ DispList *BKE_displist_find(ListBase *lb, int type) return nullptr; } -void BKE_displist_copy(ListBase *lbn, const ListBase *lb) -{ - BKE_displist_free(lbn); - - LISTBASE_FOREACH (const DispList *, dl, lb) { - DispList *dln = (DispList *)MEM_dupallocN(dl); - BLI_addtail(lbn, dln); - dln->verts = (float *)MEM_dupallocN(dl->verts); - dln->nors = (float *)MEM_dupallocN(dl->nors); - dln->index = (int *)MEM_dupallocN(dl->index); - } -} - void BKE_displist_normals_add(ListBase *lb) { float *vdata, *ndata, nor[3]; diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c index 8a36cfe14c6..81bca5fc911 100644 --- a/source/blender/blenkernel/intern/gpencil.c +++ b/source/blender/blenkernel/intern/gpencil.c @@ -675,7 +675,7 @@ bGPDlayer *BKE_gpencil_layer_addnew(bGPdata *gpd, } /* auto-name */ - BLI_strncpy(gpl->info, name, sizeof(gpl->info)); + BLI_strncpy(gpl->info, DATA_(name), sizeof(gpl->info)); BLI_uniquename(&gpd->layers, gpl, (gpd->flag & GP_DATA_ANNOTATIONS) ? DATA_("Note") : DATA_("GP_Layer"), diff --git a/source/blender/blenkernel/intern/image.cc b/source/blender/blenkernel/intern/image.cc index ae5eead2547..d915a9db76c 100644 --- a/source/blender/blenkernel/intern/image.cc +++ b/source/blender/blenkernel/intern/image.cc @@ -627,6 +627,16 @@ void BKE_image_free_data(Image *ima) image_free_data(&ima->id); } +static ImageTile *imagetile_alloc(int tile_number) +{ + ImageTile *tile = MEM_cnew<ImageTile>("Image Tile"); + tile->tile_number = tile_number; + tile->gen_x = 1024; + tile->gen_y = 1024; + tile->gen_type = IMA_GENTYPE_GRID; + return tile; +} + /* only image block itself */ static void image_init(Image *ima, short source, short type) { @@ -641,8 +651,7 @@ static void image_init(Image *ima, short source, short type) ima->flag |= IMA_VIEW_AS_RENDER; } - ImageTile *tile = MEM_cnew<ImageTile>("Image Tiles"); - tile->tile_number = 1001; + ImageTile *tile = imagetile_alloc(1001); BLI_addtail(&ima->tiles, tile); if (type == IMA_TYPE_R_RESULT) { @@ -785,7 +794,7 @@ bool BKE_image_has_opengl_texture(Image *ima) return false; } -static int image_get_tile_number_from_iuser(Image *ima, const ImageUser *iuser) +static int image_get_tile_number_from_iuser(const Image *ima, const ImageUser *iuser) { BLI_assert(ima != nullptr && ima->tiles.first); ImageTile *tile = static_cast<ImageTile *>(ima->tiles.first); @@ -863,37 +872,89 @@ void BKE_image_get_tile_uv(const Image *ima, const int tile_number, float r_uv[2 } } +/** Linear distance between #x and the unit interval. */ +static float distance_to_unit_interval(float x) +{ + /* The unit interval is between 0 and 1. + * Within the interval, return 0. + * Outside the interval, return the distance to the nearest boundary. + * Intuitively, the function looks like: + * \ | | / + * __\|___|/__ + * 0 1 + */ + + if (x <= 0.0f) { + return -x; /* Distance to left border. */ + } + if (x <= 1.0f) { + return 0.0f; /* Inside unit interval. */ + } + return x - 1.0f; /* Distance to right border. */ +} + +/** Distance squared between #co and the unit square with lower-left starting at #udim. */ +static float distance_squared_to_udim(const float co[2], const float udim[2]) +{ + float delta[2]; + sub_v2_v2v2(delta, co, udim); + delta[0] = distance_to_unit_interval(delta[0]); + delta[1] = distance_to_unit_interval(delta[1]); + return len_squared_v2(delta); +} + +static bool nearest_udim_tile_tie_break(const float best_dist_sq, + const float best_uv[2], + const float dist_sq, + const float uv[2]) +{ + if (best_dist_sq == dist_sq) { /* Exact same distance? Tie-break. */ + if (best_uv[0] == uv[0]) { /* Exact same U? Tie-break. */ + return (uv[1] > best_uv[1]); /* Higher than previous candidate? */ + } + return (uv[0] > best_uv[0]); /* Further right than previous candidate? */ + } + return (dist_sq < best_dist_sq); /* Closer than previous candidate? */ +} + int BKE_image_find_nearest_tile_with_offset(const Image *image, const float co[2], float r_uv_offset[2]) { - /* Distance squared to the closest UDIM tile. */ - float dist_best_sq = FLT_MAX; - float uv_offset_best[2] = {0, 0}; + /* NOTE: If the co-ordinates are integers, take special care to break ties. */ + + zero_v2(r_uv_offset); int tile_number_best = -1; - const float co_offset[2] = {co[0] - 0.5f, co[1] - 0.5f}; + if (image->source != IMA_SRC_TILED) { + return tile_number_best; + } + + /* Distance squared to the closest UDIM tile. */ + float dist_best_sq = FLT_MAX; LISTBASE_FOREACH (const ImageTile *, tile, &image->tiles) { float uv_offset[2]; BKE_image_get_tile_uv(image, tile->tile_number, uv_offset); - /* Distance squared between co[2] and center of UDIM tile. */ - const float dist_sq = len_squared_v2v2(uv_offset, co_offset); + /* Distance squared between #co and closest point on UDIM tile. */ + const float dist_sq = distance_squared_to_udim(co, uv_offset); - if (dist_sq < dist_best_sq) { + if (dist_sq == 0) { /* Either inside in the UDIM, or on its boundary. */ + if (floorf(co[0]) == uv_offset[0] && floorf(co[1]) == uv_offset[1]) { + /* Within the half-open interval of the UDIM. */ + copy_v2_v2(r_uv_offset, uv_offset); + return tile_number_best; + } + } + + if (nearest_udim_tile_tie_break(dist_best_sq, r_uv_offset, dist_sq, uv_offset)) { + /* Tile is better than previous best, update. */ dist_best_sq = dist_sq; + copy_v2_v2(r_uv_offset, uv_offset); tile_number_best = tile->tile_number; - copy_v2_v2(uv_offset_best, uv_offset); - - if (dist_best_sq < 0.5f * 0.5f) { - break; /* No other tile can be closer. */ - } } } - if (tile_number_best != -1) { - copy_v2_v2(r_uv_offset, uv_offset_best); - } return tile_number_best; } @@ -910,7 +971,7 @@ static void image_init_color_management(Image *ima) BKE_image_user_file_path(nullptr, ima, name); - /* will set input color space to image format default's */ + /* Will set input color space to image format default's. */ ibuf = IMB_loadiffname(name, IB_test | IB_alphamode_detect, ima->colorspace_settings.name); if (ibuf) { @@ -1048,73 +1109,70 @@ static void image_buf_fill_isolated(void *usersata_v) } } -static ImBuf *add_ibuf_size(unsigned int width, - unsigned int height, - const char *name, - int depth, - int floatbuf, - short gen_type, - const float color[4], - ColorManagedColorspaceSettings *colorspace_settings) +static ImBuf *add_ibuf_for_tile(Image *ima, ImageTile *tile) { ImBuf *ibuf; unsigned char *rect = nullptr; float *rect_float = nullptr; float fill_color[4]; + const bool floatbuf = (tile->gen_flag & IMA_GEN_FLOAT) != 0; if (floatbuf) { - ibuf = IMB_allocImBuf(width, height, depth, IB_rectfloat); + ibuf = IMB_allocImBuf(tile->gen_x, tile->gen_y, tile->gen_depth, IB_rectfloat); - if (colorspace_settings->name[0] == '\0') { + if (ima->colorspace_settings.name[0] == '\0') { const char *colorspace = IMB_colormanagement_role_colorspace_name_get( COLOR_ROLE_DEFAULT_FLOAT); - STRNCPY(colorspace_settings->name, colorspace); + STRNCPY(ima->colorspace_settings.name, colorspace); } if (ibuf != nullptr) { rect_float = ibuf->rect_float; - IMB_colormanagement_check_is_data(ibuf, colorspace_settings->name); + IMB_colormanagement_check_is_data(ibuf, ima->colorspace_settings.name); } - if (IMB_colormanagement_space_name_is_data(colorspace_settings->name)) { - copy_v4_v4(fill_color, color); + if (IMB_colormanagement_space_name_is_data(ima->colorspace_settings.name)) { + copy_v4_v4(fill_color, tile->gen_color); } else { /* The input color here should ideally be linear already, but for now * we just convert and postpone breaking the API for later. */ - srgb_to_linearrgb_v4(fill_color, color); + srgb_to_linearrgb_v4(fill_color, tile->gen_color); } } else { - ibuf = IMB_allocImBuf(width, height, depth, IB_rect); + ibuf = IMB_allocImBuf(tile->gen_x, tile->gen_y, tile->gen_depth, IB_rect); - if (colorspace_settings->name[0] == '\0') { + if (ima->colorspace_settings.name[0] == '\0') { const char *colorspace = IMB_colormanagement_role_colorspace_name_get( COLOR_ROLE_DEFAULT_BYTE); - STRNCPY(colorspace_settings->name, colorspace); + STRNCPY(ima->colorspace_settings.name, colorspace); } if (ibuf != nullptr) { rect = (unsigned char *)ibuf->rect; - IMB_colormanagement_assign_rect_colorspace(ibuf, colorspace_settings->name); + IMB_colormanagement_assign_rect_colorspace(ibuf, ima->colorspace_settings.name); } - copy_v4_v4(fill_color, color); + copy_v4_v4(fill_color, tile->gen_color); } if (!ibuf) { return nullptr; } - STRNCPY(ibuf->name, name); + STRNCPY(ibuf->name, ima->filepath); + + /* Mark the tile itself as having been generated. */ + tile->gen_flag |= IMA_GEN_TILE; ImageFillData data; - data.gen_type = gen_type; - data.width = width; - data.height = height; + data.gen_type = tile->gen_type; + data.width = tile->gen_x; + data.height = tile->gen_y; data.rect = rect; data.rect_float = rect_float; copy_v4_v4(data.fill_color, fill_color); @@ -1153,12 +1211,16 @@ Image *BKE_image_add_generated(Main *bmain, /* NOTE: leave `ima->filepath` unset, * setting it to a dummy value may write to an invalid file-path. */ - ima->gen_x = width; - ima->gen_y = height; - ima->gen_type = gen_type; - ima->gen_flag |= (floatbuf ? IMA_GEN_FLOAT : 0); - ima->gen_depth = depth; - copy_v4_v4(ima->gen_color, color); + + /* The generation info is always stored in the tiles. The first tile + * will be used for non-tiled images. */ + ImageTile *tile = static_cast<ImageTile *>(ima->tiles.first); + tile->gen_x = width; + tile->gen_y = height; + tile->gen_type = gen_type; + tile->gen_flag |= (floatbuf ? IMA_GEN_FLOAT : 0); + tile->gen_depth = depth; + copy_v4_v4(tile->gen_color, color); if (is_data) { STRNCPY(ima->colorspace_settings.name, @@ -1167,8 +1229,7 @@ Image *BKE_image_add_generated(Main *bmain, for (view_id = 0; view_id < 2; view_id++) { ImBuf *ibuf; - ibuf = add_ibuf_size( - width, height, ima->filepath, depth, floatbuf, gen_type, color, &ima->colorspace_settings); + ibuf = add_ibuf_for_tile(ima, tile); int index = tiled ? 0 : IMA_NO_INDEX; int entry = tiled ? 1001 : 0; image_assign_ibuf(ima, ibuf, stereo3d ? view_id : index, entry); @@ -2949,6 +3010,28 @@ static void image_free_tile(Image *ima, ImageTile *tile) } } +static bool image_remove_tile(Image *ima, ImageTile *tile) +{ + if (BLI_listbase_is_single(&ima->tiles)) { + /* Can't remove the last remaining tile. */ + return false; + } + + image_free_tile(ima, tile); + BLI_remlink(&ima->tiles, tile); + MEM_freeN(tile); + + return true; +} + +static void image_remove_all_tiles(Image *ima) +{ + /* Remove all but the final tile. */ + while (image_remove_tile(ima, static_cast<ImageTile *>(ima->tiles.last))) { + ; + } +} + void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal) { if (ima == nullptr) { @@ -2975,11 +3058,12 @@ void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal) } if (ima->source == IMA_SRC_GENERATED) { - if (ima->gen_x == 0 || ima->gen_y == 0) { + ImageTile *base_tile = BKE_image_get_tile(ima, 0); + if (base_tile->gen_x == 0 || base_tile->gen_y == 0) { ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, IMA_NO_INDEX, 0, nullptr); if (ibuf) { - ima->gen_x = ibuf->x; - ima->gen_y = ibuf->y; + base_tile->gen_x = ibuf->x; + base_tile->gen_y = ibuf->y; IMB_freeImBuf(ibuf); } } @@ -2996,16 +3080,40 @@ void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal) if (ima->source != IMA_SRC_TILED) { /* Free all but the first tile. */ + image_remove_all_tiles(ima); + + /* If the remaining tile is generated, we need to again ensure that we + * wouldn't continue to use the old filepath. + * + * Otherwise, if this used to be a UDIM image, get the concrete filepath associated + * with the remaining tile and use that as the new filepath. */ ImageTile *base_tile = BKE_image_get_tile(ima, 0); - BLI_assert(base_tile == ima->tiles.first); - for (ImageTile *tile = base_tile->next, *tile_next; tile; tile = tile_next) { - tile_next = tile->next; - image_free_tile(ima, tile); - MEM_freeN(tile); + if ((base_tile->gen_flag & IMA_GEN_TILE) != 0) { + ima->filepath[0] = '\0'; + } + else if (BKE_image_is_filename_tokenized(ima->filepath)) { + const bool was_relative = BLI_path_is_rel(ima->filepath); + + eUDIM_TILE_FORMAT tile_format; + char *udim_pattern = BKE_image_get_tile_strformat(ima->filepath, &tile_format); + BKE_image_set_filepath_from_tile_number( + ima->filepath, udim_pattern, tile_format, base_tile->tile_number); + MEM_freeN(udim_pattern); + + if (was_relative) { + const char *relbase = ID_BLEND_PATH(bmain, &ima->id); + BLI_path_rel(ima->filepath, relbase); + } } - base_tile->next = nullptr; + + /* If the remaining tile was not number 1001, we need to reassign it so that + * ibuf lookups from the cache still succeed. */ base_tile->tile_number = 1001; - ima->tiles.last = base_tile; + } + else { + /* When changing to UDIM, attempt to tokenize the filepath. */ + char *filename = (char *)BLI_path_basename(ima->filepath); + BKE_image_ensure_tile_token(filename); } /* image buffers for non-sequence multilayer will share buffers with RenderResult, @@ -3021,6 +3129,7 @@ void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal) image_tag_frame_recalc(ima, nullptr, iuser, ima); } BKE_image_walk_all_users(bmain, ima, image_tag_frame_recalc); + BKE_image_partial_update_mark_full_update(ima); break; @@ -3072,9 +3181,7 @@ void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal) * to account for how the two sets might or might not overlap. To be complete, we start * the refresh process by clearing all existing tiles, stopping when there's only 1 tile * left. */ - while (BKE_image_remove_tile(ima, static_cast<ImageTile *>(ima->tiles.last))) { - ; - } + image_remove_all_tiles(ima); int remaining_tile_number = ((ImageTile *)ima->tiles.first)->tile_number; bool needs_final_cleanup = true; @@ -3257,8 +3364,7 @@ ImageTile *BKE_image_add_tile(struct Image *ima, int tile_number, const char *la } } - ImageTile *tile = MEM_cnew<ImageTile>("image new tile"); - tile->tile_number = tile_number; + ImageTile *tile = imagetile_alloc(tile_number); if (next_tile) { BLI_insertlinkbefore(&ima->tiles, next_tile, tile); @@ -3293,16 +3399,7 @@ bool BKE_image_remove_tile(struct Image *ima, ImageTile *tile) return false; } - if (BLI_listbase_is_single(&ima->tiles)) { - /* Can't remove the last remaining tile. */ - return false; - } - - image_free_tile(ima, tile); - BLI_remlink(&ima->tiles, tile); - MEM_freeN(tile); - - return true; + return image_remove_tile(ima, tile); } void BKE_image_reassign_tile(struct Image *ima, ImageTile *tile, int new_tile_number) @@ -3364,14 +3461,7 @@ void BKE_image_sort_tiles(struct Image *ima) BLI_listbase_sort(&ima->tiles, tile_sort_cb); } -bool BKE_image_fill_tile(struct Image *ima, - ImageTile *tile, - int width, - int height, - const float color[4], - int gen_type, - int planes, - bool is_float) +bool BKE_image_fill_tile(struct Image *ima, ImageTile *tile) { if (ima == nullptr || tile == nullptr || ima->source != IMA_SRC_TILED) { return false; @@ -3379,8 +3469,7 @@ bool BKE_image_fill_tile(struct Image *ima, image_free_tile(ima, tile); - ImBuf *tile_ibuf = add_ibuf_size( - width, height, ima->filepath, planes, is_float, gen_type, color, &ima->colorspace_settings); + ImBuf *tile_ibuf = add_ibuf_for_tile(ima, tile); if (tile_ibuf != nullptr) { image_assign_ibuf(ima, tile_ibuf, 0, tile->tile_number); @@ -3562,7 +3651,7 @@ RenderPass *BKE_image_multilayer_index(RenderResult *rr, ImageUser *iuser) return rpass; } -void BKE_image_multiview_index(Image *ima, ImageUser *iuser) +void BKE_image_multiview_index(const Image *ima, ImageUser *iuser) { if (iuser) { bool is_stereo = BKE_image_is_stereo(ima) && (iuser->flag & IMA_SHOW_STEREO); @@ -3583,7 +3672,7 @@ void BKE_image_multiview_index(Image *ima, ImageUser *iuser) /* if layer or pass changes, we need an index for the imbufs list */ /* note it is called for rendered results, but it doesn't use the index! */ -bool BKE_image_is_multilayer(Image *ima) +bool BKE_image_is_multilayer(const Image *ima) { if (ELEM(ima->source, IMA_SRC_FILE, IMA_SRC_SEQUENCE, IMA_SRC_TILED)) { if (ima->type == IMA_TYPE_MULTILAYER) { @@ -3598,13 +3687,13 @@ bool BKE_image_is_multilayer(Image *ima) return false; } -bool BKE_image_is_multiview(Image *ima) +bool BKE_image_is_multiview(const Image *ima) { ImageView *view = static_cast<ImageView *>(ima->views.first); return (view && (view->next || view->name[0])); } -bool BKE_image_is_stereo(Image *ima) +bool BKE_image_is_stereo(const Image *ima) { return BKE_image_is_multiview(ima) && (BLI_findstring(&ima->views, STEREO_LEFT_NAME, offsetof(ImageView, name)) && @@ -4553,14 +4642,22 @@ static ImBuf *image_acquire_ibuf(Image *ima, ImageUser *iuser, void **r_lock) } } else if (ima->source == IMA_SRC_TILED) { - if (ima->type == IMA_TYPE_IMAGE) { - /* Regular files, ibufs in flip-book, allows saving */ - ibuf = image_load_image_file(ima, iuser, entry, 0, false); + /* Nothing was cached. Check to see if the tile should be generated. */ + ImageTile *tile = BKE_image_get_tile(ima, entry); + if ((tile->gen_flag & IMA_GEN_TILE) != 0) { + ibuf = add_ibuf_for_tile(ima, tile); + image_assign_ibuf(ima, ibuf, 0, entry); } - /* no else; on load the ima type can change */ - if (ima->type == IMA_TYPE_MULTILAYER) { - /* Only 1 layer/pass stored in imbufs, no EXR-handle anim storage, no saving. */ - ibuf = image_load_sequence_multilayer(ima, iuser, entry, 0); + else { + if (ima->type == IMA_TYPE_IMAGE) { + /* Regular files, ibufs in flip-book, allows saving */ + ibuf = image_load_image_file(ima, iuser, entry, 0, false); + } + /* no else; on load the ima type can change */ + if (ima->type == IMA_TYPE_MULTILAYER) { + /* Only 1 layer/pass stored in imbufs, no EXR-handle anim storage, no saving. */ + ibuf = image_load_sequence_multilayer(ima, iuser, entry, 0); + } } } else if (ima->source == IMA_SRC_FILE) { @@ -4578,23 +4675,17 @@ static ImBuf *image_acquire_ibuf(Image *ima, ImageUser *iuser, void **r_lock) else if (ima->source == IMA_SRC_GENERATED) { /* Generated is: `ibuf` is allocated dynamically. */ /* UV test-grid or black or solid etc. */ - if (ima->gen_x == 0) { - ima->gen_x = 1024; + ImageTile *base_tile = BKE_image_get_tile(ima, 0); + if (base_tile->gen_x == 0) { + base_tile->gen_x = 1024; } - if (ima->gen_y == 0) { - ima->gen_y = 1024; + if (base_tile->gen_y == 0) { + base_tile->gen_y = 1024; } - if (ima->gen_depth == 0) { - ima->gen_depth = 24; + if (base_tile->gen_depth == 0) { + base_tile->gen_depth = 24; } - ibuf = add_ibuf_size(ima->gen_x, - ima->gen_y, - ima->filepath, - ima->gen_depth, - (ima->gen_flag & IMA_GEN_FLOAT) != 0, - ima->gen_type, - ima->gen_color, - &ima->colorspace_settings); + ibuf = add_ibuf_for_tile(ima, base_tile); image_assign_ibuf(ima, ibuf, index, 0); } else if (ima->source == IMA_SRC_VIEWER) { @@ -4937,10 +5028,12 @@ static void image_editors_update_frame(Image *ima, ImageUser *iuser, void *customdata) { - int cfra = *(int *)customdata; + if (ima && BKE_image_is_animated(ima)) { + if ((iuser->flag & IMA_ANIM_ALWAYS) || (iuser->flag & IMA_NEED_FRAME_RECALC)) { + int cfra = *(int *)customdata; - if ((iuser->flag & IMA_ANIM_ALWAYS) || (iuser->flag & IMA_NEED_FRAME_RECALC)) { - BKE_image_user_frame_calc(ima, iuser, cfra); + BKE_image_user_frame_calc(ima, iuser, cfra); + } } } @@ -5000,14 +5093,19 @@ void BKE_image_user_id_eval_animation(Depsgraph *depsgraph, ID *id) image_walk_id_all_users(id, skip_nested_nodes, depsgraph, image_user_id_eval_animation); } -void BKE_image_user_file_path(ImageUser *iuser, Image *ima, char *filepath) +void BKE_image_user_file_path(const ImageUser *iuser, const Image *ima, char *filepath) { - BKE_image_user_file_path_ex(iuser, ima, filepath, true); + BKE_image_user_file_path_ex(G_MAIN, iuser, ima, filepath, true, true); } -void BKE_image_user_file_path_ex(ImageUser *iuser, Image *ima, char *filepath, bool resolve_udim) +void BKE_image_user_file_path_ex(const Main *bmain, + const ImageUser *iuser, + const Image *ima, + char *filepath, + const bool resolve_udim, + const bool resolve_multiview) { - if (BKE_image_is_multiview(ima)) { + if (resolve_multiview && BKE_image_is_multiview(ima)) { ImageView *iv = static_cast<ImageView *>(BLI_findlink(&ima->views, iuser->view)); if (iv->filepath[0]) { BLI_strncpy(filepath, iv->filepath, FILE_MAX); @@ -5040,7 +5138,7 @@ void BKE_image_user_file_path_ex(ImageUser *iuser, Image *ima, char *filepath, b } } - BLI_path_abs(filepath, ID_BLEND_PATH_FROM_GLOBAL(&ima->id)); + BLI_path_abs(filepath, ID_BLEND_PATH(bmain, &ima->id)); } bool BKE_image_has_alpha(Image *image) @@ -5514,7 +5612,7 @@ bool BKE_image_clear_renderslot(Image *ima, ImageUser *iuser, int slot) } RenderSlot *render_slot = static_cast<RenderSlot *>(BLI_findlink(&ima->renderslots, slot)); - if (!slot) { + if (!render_slot) { return false; } if (render_slot->render) { diff --git a/source/blender/blenkernel/intern/image_save.cc b/source/blender/blenkernel/intern/image_save.cc index cd86d3f7087..d366e9362e8 100644 --- a/source/blender/blenkernel/intern/image_save.cc +++ b/source/blender/blenkernel/intern/image_save.cc @@ -144,13 +144,9 @@ bool BKE_image_save_options_init(ImageSaveOptions *opts, opts->im_format.color_management = R_IMF_COLOR_MANAGEMENT_FOLLOW_SCENE; - if (ibuf->name[0] == '\0' || ima->source == IMA_SRC_TILED) { - BLI_strncpy(opts->filepath, ima->filepath, sizeof(opts->filepath)); - BLI_path_abs(opts->filepath, ID_BLEND_PATH_FROM_GLOBAL(&ima->id)); - } - else { - BLI_strncpy(opts->filepath, ibuf->name, sizeof(opts->filepath)); - } + /* Compute filepath, but don't resolve multiview and UDIM which are handled + * by the image saving code itself. */ + BKE_image_user_file_path_ex(bmain, iuser, ima, opts->filepath, false, false); /* sanitize all settings */ @@ -320,6 +316,8 @@ static void image_save_post(ReportList *reports, if (ELEM(ima->source, IMA_SRC_GENERATED, IMA_SRC_VIEWER)) { ima->source = IMA_SRC_FILE; ima->type = IMA_TYPE_IMAGE; + ImageTile *base_tile = BKE_image_get_tile(ima, 0); + base_tile->gen_flag &= ~IMA_GEN_TILE; } /* Update image file color space when saving to another color space. */ @@ -666,8 +664,11 @@ bool BKE_image_save( } } - /* Set the image path only if all tiles were ok. */ + /* Set the image path and clear the per-tile generated flag only if all tiles were ok. */ if (ok) { + LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { + tile->gen_flag &= ~IMA_GEN_TILE; + } image_save_update_filepath(ima, opts->filepath, opts); } MEM_freeN(udim_pattern); diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index 3778e308db6..5a394a05d86 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -59,6 +59,7 @@ #include "DEG_depsgraph.h" #include "DEG_depsgraph_build.h" +#include "DEG_depsgraph_query.h" #include "RNA_access.h" @@ -708,6 +709,58 @@ ID *BKE_id_copy_for_duplicate(Main *bmain, return id->newid; } +static int foreach_assign_id_to_orig_callback(LibraryIDLinkCallbackData *cb_data) +{ + ID **id_p = cb_data->id_pointer; + + if (*id_p) { + ID *id = *id_p; + *id_p = DEG_get_original_id(id); + + /* If the ID changes increase the user count. + * + * This means that the reference to evaluated ID has been changed with a reference to the + * original ID which implies that the user count of the original ID is increased. + * + * The evaluated IDs do not maintain their user counter, so do not change it to avoid issues + * with the user counter going negative. */ + if (*id_p != id) { + if ((cb_data->cb_flag & IDWALK_CB_USER) != 0) { + id_us_plus(*id_p); + } + } + } + + return IDWALK_RET_NOP; +} + +ID *BKE_id_copy_for_use_in_bmain(Main *bmain, const ID *id) +{ + ID *newid = BKE_id_copy(bmain, id); + + if (newid == NULL) { + return newid; + } + + /* Assign ID references directly used by the given ID to their original complementary parts. + * + * For example, when is called on an evaluated object will assign object->data to its original + * pointer, the evaluated object->data will be kept unchanged. */ + BKE_library_foreach_ID_link(NULL, newid, foreach_assign_id_to_orig_callback, NULL, IDWALK_NOP); + + /* Shape keys reference on evaluated ID is preserved to keep driver paths available, but the key + * data is likely to be invalid now due to modifiers, so clear the shape key reference avoiding + * any possible shape corruption. */ + if (DEG_is_evaluated_id(id)) { + Key **key_p = BKE_key_from_id_p(newid); + if (key_p) { + *key_p = NULL; + } + } + + return newid; +} + /** * Does a mere memory swap over the whole IDs data (including type-specific memory). * \note Most internal ID data itself is not swapped (only IDProperties are). diff --git a/source/blender/blenkernel/intern/lib_override.cc b/source/blender/blenkernel/intern/lib_override.cc index 5618d0f6aac..758a317e415 100644 --- a/source/blender/blenkernel/intern/lib_override.cc +++ b/source/blender/blenkernel/intern/lib_override.cc @@ -82,8 +82,8 @@ static void lib_override_library_property_operation_clear( BLI_INLINE void lib_override_object_posemode_transfer(ID *id_dst, ID *id_src) { if (GS(id_src->name) == ID_OB && GS(id_dst->name) == ID_OB) { - Object *ob_src = (Object *)id_src; - Object *ob_dst = (Object *)id_dst; + Object *ob_src = reinterpret_cast<Object *>(id_src); + Object *ob_dst = reinterpret_cast<Object *>(id_dst); if (ob_src->type == OB_ARMATURE && (ob_src->mode & OB_MODE_POSE) != 0) { ob_dst->restore_mode = ob_dst->mode; ob_dst->mode |= OB_MODE_POSE; @@ -187,7 +187,7 @@ void BKE_lib_override_library_copy(ID *dst_id, const ID *src_id, const bool do_f * Otherwise, source is only an override template, it then becomes reference of dest ID. */ dst_id->override_library->reference = src_id->override_library->reference ? src_id->override_library->reference : - (ID *)src_id; + const_cast<ID *>(src_id); id_us_plus(dst_id->override_library->reference); dst_id->override_library->hierarchy_root = src_id->override_library->hierarchy_root; @@ -683,7 +683,7 @@ static void lib_override_group_tag_data_object_to_collection_init_collection_pro LinkNodePair **collections_linkedlist_p; if (!BLI_ghash_ensure_p(data->linked_object_to_instantiating_collections, ob, - (void ***)&collections_linkedlist_p)) { + reinterpret_cast<void ***>(&collections_linkedlist_p))) { *collections_linkedlist_p = static_cast<LinkNodePair *>( BLI_memarena_calloc(data->mem_arena, sizeof(**collections_linkedlist_p))); } @@ -724,7 +724,7 @@ static void lib_override_hierarchy_dependencies_recursive_tag_from(LibOverrideGr ID *id = data->id_root; const bool is_override = data->is_override; - if ((*(uint *)&id->tag & data->tag) == 0) { + if ((*reinterpret_cast<uint *>(&id->tag) & data->tag) == 0) { /* This ID is not tagged, no reason to proceed further to its parents. */ return; } @@ -782,7 +782,7 @@ static bool lib_override_hierarchy_dependencies_recursive_tag(LibOverrideGroupTa if (entry->tags & MAINIDRELATIONS_ENTRY_TAGS_PROCESSED_TO) { /* This ID has already been processed. */ - return (*(uint *)&id->tag & data->tag) != 0; + return (*reinterpret_cast<uint *>(&id->tag) & data->tag) != 0; } /* This way we won't process again that ID, should we encounter it again through another * relationship hierarchy. */ @@ -815,11 +815,11 @@ static bool lib_override_hierarchy_dependencies_recursive_tag(LibOverrideGroupTa * * This will cover e.g. the case where user override an armature, and would expect the mesh * object deformed by that armature to also be overridden. */ - if ((*(uint *)&id->tag & data->tag) != 0 && !is_resync) { + if ((*reinterpret_cast<uint *>(&id->tag) & data->tag) != 0 && !is_resync) { lib_override_hierarchy_dependencies_recursive_tag_from(data); } - return (*(uint *)&id->tag & data->tag) != 0; + return (*reinterpret_cast<uint *>(&id->tag) & data->tag) != 0; } static void lib_override_linked_group_tag_recursive(LibOverrideGroupTagData *data) @@ -1224,9 +1224,9 @@ static void lib_override_library_create_post_process(Main *bmain, switch (GS(id_root->name)) { case ID_GR: { Object *ob_reference = id_instance_hint != nullptr && GS(id_instance_hint->name) == ID_OB ? - (Object *)id_instance_hint : + reinterpret_cast<Object *>(id_instance_hint) : nullptr; - Collection *collection_new = ((Collection *)id_root->newid); + Collection *collection_new = (reinterpret_cast<Collection *>(id_root->newid)); if (is_resync && BKE_collection_is_in_scene(collection_new)) { break; } @@ -1236,11 +1236,11 @@ static void lib_override_library_create_post_process(Main *bmain, else if (id_instance_hint != nullptr) { BLI_assert(GS(id_instance_hint->name) == ID_GR); BKE_collection_add_from_collection( - bmain, scene, ((Collection *)id_instance_hint), collection_new); + bmain, scene, (reinterpret_cast<Collection *>(id_instance_hint)), collection_new); } else { BKE_collection_add_from_collection( - bmain, scene, ((Collection *)id_root), collection_new); + bmain, scene, (reinterpret_cast<Collection *>(id_root)), collection_new); } BLI_assert(BKE_collection_is_in_scene(collection_new)); @@ -1249,9 +1249,10 @@ static void lib_override_library_create_post_process(Main *bmain, break; } case ID_OB: { - Object *ob_new = (Object *)id_root->newid; + Object *ob_new = reinterpret_cast<Object *>(id_root->newid); if (BLI_gset_lookup(all_objects_in_scene, ob_new) == nullptr) { - BKE_collection_object_add_from(bmain, scene, (Object *)id_root, ob_new); + BKE_collection_object_add_from( + bmain, scene, reinterpret_cast<Object *>(id_root), ob_new); all_objects_in_scene = BKE_scene_objects_as_gset(scene, all_objects_in_scene); } break; @@ -1264,7 +1265,7 @@ static void lib_override_library_create_post_process(Main *bmain, /* We need to ensure all new overrides of objects are properly instantiated. */ Collection *default_instantiating_collection = residual_storage; LISTBASE_FOREACH (Object *, ob, &bmain->objects) { - Object *ob_new = (Object *)ob->id.newid; + Object *ob_new = reinterpret_cast<Object *>(ob->id.newid); if (ob_new == nullptr || (ID_IS_LINKED(ob_new) && ob_new->id.lib != owner_library)) { continue; } @@ -1298,7 +1299,7 @@ static void lib_override_library_create_post_process(Main *bmain, case ID_OB: { /* Add the other objects to one of the collections instantiating the * root object, or scene's master collection if none found. */ - Object *ob_ref = (Object *)id_ref; + Object *ob_ref = reinterpret_cast<Object *>(id_ref); LISTBASE_FOREACH (Collection *, collection, &bmain->collections) { if (BKE_collection_has_object(collection, ob_ref) && (view_layer != nullptr ? @@ -1328,8 +1329,10 @@ static void lib_override_library_create_post_process(Main *bmain, ID *id_ref = id_root->newid != nullptr ? 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); + BKE_collection_add_from_collection(bmain, + scene, + reinterpret_cast<Collection *>(id_ref), + default_instantiating_collection); break; default: /* Add to master collection. */ @@ -1802,14 +1805,15 @@ static bool lib_override_library_resync(Main *bmain, if (GS(reference_id->name) != GS(id->name)) { switch (GS(id->name)) { case ID_KE: - reference_id = (ID *)BKE_key_from_id(reference_id); + reference_id = reinterpret_cast<ID *>(BKE_key_from_id(reference_id)); break; case ID_GR: BLI_assert(GS(reference_id->name) == ID_SCE); - reference_id = (ID *)((Scene *)reference_id)->master_collection; + reference_id = reinterpret_cast<ID *>( + reinterpret_cast<Scene *>(reference_id)->master_collection); break; case ID_NT: - reference_id = (ID *)ntreeFromID(id); + reference_id = reinterpret_cast<ID *>(ntreeFromID(id)); break; default: break; @@ -2310,7 +2314,7 @@ static bool lib_override_resync_tagging_finalize_recurse( BLI_assert(id_root->override_library != nullptr); LinkNodePair **id_resync_roots_p; - if (!BLI_ghash_ensure_p(id_roots, id_root, (void ***)&id_resync_roots_p)) { + if (!BLI_ghash_ensure_p(id_roots, id_root, reinterpret_cast<void ***>(&id_resync_roots_p))) { *id_resync_roots_p = MEM_cnew<LinkNodePair>(__func__); } @@ -2485,8 +2489,8 @@ static void lib_override_library_main_resync_on_library_indirect_level( 2, "Resyncing all dependencies under root %s (%p), first one being '%s'...", id_root->name, - library, - ((ID *)id_resync_roots->list->link)->name); + reinterpret_cast<void *>(library), + reinterpret_cast<ID *>(id_resync_roots->list->link)->name); const bool success = lib_override_library_resync(bmain, scene, view_layer, @@ -2580,7 +2584,7 @@ static int lib_override_sort_libraries_func(LibraryIDLinkCallbackData *cb_data) if (owner_library_indirect_level >= id->lib->temp_index) { id->lib->temp_index = owner_library_indirect_level + 1; - *(bool *)cb_data->user_data = true; + *reinterpret_cast<bool *>(cb_data->user_data) = true; } } return IDWALK_RET_NOP; @@ -2738,7 +2742,7 @@ void BKE_lib_override_library_make_local(ID *id) } if (GS(id->name) == ID_SCE) { - Collection *master_collection = ((Scene *)id)->master_collection; + Collection *master_collection = reinterpret_cast<Scene *>(id)->master_collection; if (master_collection != nullptr) { master_collection->id.flag &= ~LIB_EMBEDDED_DATA_LIB_OVERRIDE; } @@ -3120,9 +3124,9 @@ bool BKE_lib_override_library_status_check_local(Main *bmain, ID *local) /* Our beloved pose's bone cross-data pointers. Usually, depsgraph evaluation would * ensure this is valid, but in some situations (like hidden collections etc.) this won't * be the case, so we need to take care of this ourselves. */ - Object *ob_local = (Object *)local; + Object *ob_local = reinterpret_cast<Object *>(local); if (ob_local->type == OB_ARMATURE) { - Object *ob_reference = (Object *)local->override_library->reference; + Object *ob_reference = reinterpret_cast<Object *>(local->override_library->reference); BLI_assert(ob_local->data != nullptr); BLI_assert(ob_reference->data != nullptr); BKE_pose_ensure(bmain, ob_local, static_cast<bArmature *>(ob_local->data), true); @@ -3180,9 +3184,9 @@ bool BKE_lib_override_library_status_check_reference(Main *bmain, ID *local) /* Our beloved pose's bone cross-data pointers. Usually, depsgraph evaluation would * ensure this is valid, but in some situations (like hidden collections etc.) this won't * be the case, so we need to take care of this ourselves. */ - Object *ob_local = (Object *)local; + Object *ob_local = reinterpret_cast<Object *>(local); if (ob_local->type == OB_ARMATURE) { - Object *ob_reference = (Object *)local->override_library->reference; + Object *ob_reference = reinterpret_cast<Object *>(local->override_library->reference); BLI_assert(ob_local->data != nullptr); BLI_assert(ob_reference->data != nullptr); BKE_pose_ensure(bmain, ob_local, static_cast<bArmature *>(ob_local->data), true); @@ -3228,9 +3232,9 @@ bool BKE_lib_override_library_operations_create(Main *bmain, ID *local) /* Our beloved pose's bone cross-data pointers. Usually, depsgraph evaluation would * ensure this is valid, but in some situations (like hidden collections etc.) this won't * be the case, so we need to take care of this ourselves. */ - Object *ob_local = (Object *)local; + Object *ob_local = reinterpret_cast<Object *>(local); if (ob_local->type == OB_ARMATURE) { - Object *ob_reference = (Object *)local->override_library->reference; + Object *ob_reference = reinterpret_cast<Object *>(local->override_library->reference); BLI_assert(ob_local->data != nullptr); BLI_assert(ob_reference->data != nullptr); BKE_pose_ensure(bmain, ob_local, static_cast<bArmature *>(ob_local->data), true); @@ -3284,7 +3288,7 @@ static void lib_override_library_operations_create_cb(TaskPool *__restrict pool, if (BKE_lib_override_library_operations_create(create_data->bmain, id)) { /* Technically no need for atomic, all jobs write the same value and we only care if one did * it. But play safe and avoid implicit assumptions. */ - atomic_fetch_and_or_uint8((uint8_t *)&create_data->changed, true); + atomic_fetch_and_or_uint8(reinterpret_cast<uint8_t *>(&create_data->changed), true); } } @@ -3324,7 +3328,7 @@ bool BKE_lib_override_library_main_operations_create(Main *bmain, const bool for /* Usual issue with pose, it's quiet rare but sometimes they may not be up to date when this * function is called. */ if (GS(id->name) == ID_OB) { - Object *ob = (Object *)id; + Object *ob = reinterpret_cast<Object *>(id); if (ob->type == OB_ARMATURE) { BLI_assert(ob->data != nullptr); BKE_pose_ensure(bmain, ob, static_cast<bArmature *>(ob->data), true); @@ -3768,7 +3772,7 @@ bool BKE_lib_override_library_id_is_user_deletable(Main *bmain, ID *id) if (GS(id->name) != ID_OB) { return true; } - Object *ob = (Object *)id; + Object *ob = reinterpret_cast<Object *>(id); LISTBASE_FOREACH (Collection *, collection, &bmain->collections) { if (!ID_IS_OVERRIDE_LIBRARY(collection)) { continue; @@ -3839,7 +3843,7 @@ ID *BKE_lib_override_library_operations_store_start(Main *bmain, * (and possibly all over Blender code). * Not impossible to do, but would rather see first is extra useless usual user handling is * actually a (performances) issue here, before doing it. */ - storage_id = BKE_id_copy((Main *)override_storage, local); + storage_id = BKE_id_copy(reinterpret_cast<Main *>(override_storage), local); if (storage_id != nullptr) { PointerRNA rnaptr_reference, rnaptr_final, rnaptr_storage; diff --git a/source/blender/blenkernel/intern/mesh_remap.c b/source/blender/blenkernel/intern/mesh_remap.c index 3a37c29c1d0..5313cc39646 100644 --- a/source/blender/blenkernel/intern/mesh_remap.c +++ b/source/blender/blenkernel/intern/mesh_remap.c @@ -312,15 +312,10 @@ void BKE_mesh_remap_calc_source_cddata_masks_from_map_modes(const int UNUSED(ver { /* vert, edge and poly mapping modes never need extra cddata from source object. */ const bool need_lnors_src = (loop_mode & MREMAP_USE_LOOP) && (loop_mode & MREMAP_USE_NORMAL); - const bool need_pnors_src = need_lnors_src || - ((loop_mode & MREMAP_USE_POLY) && (loop_mode & MREMAP_USE_NORMAL)); if (need_lnors_src) { r_cddata_mask->lmask |= CD_MASK_NORMAL; } - if (need_pnors_src) { - r_cddata_mask->pmask |= CD_MASK_NORMAL; - } } void BKE_mesh_remap_init(MeshPairRemap *map, const int items_num) diff --git a/source/blender/blenkernel/intern/mesh_sample.cc b/source/blender/blenkernel/intern/mesh_sample.cc index dd09a3d6917..e54f2e6d687 100644 --- a/source/blender/blenkernel/intern/mesh_sample.cc +++ b/source/blender/blenkernel/intern/mesh_sample.cc @@ -16,9 +16,9 @@ template<typename T> BLI_NOINLINE static void sample_point_attribute(const Mesh &mesh, const Span<int> looptri_indices, const Span<float3> bary_coords, - const VArray<T> &data_in, + const VArray<T> &src, const IndexMask mask, - const MutableSpan<T> data_out) + const MutableSpan<T> dst) { const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh), BKE_mesh_runtime_looptri_len(&mesh)}; @@ -32,30 +32,30 @@ BLI_NOINLINE static void sample_point_attribute(const Mesh &mesh, const int v1_index = mesh.mloop[looptri.tri[1]].v; const int v2_index = mesh.mloop[looptri.tri[2]].v; - const T v0 = data_in[v0_index]; - const T v1 = data_in[v1_index]; - const T v2 = data_in[v2_index]; + const T v0 = src[v0_index]; + const T v1 = src[v1_index]; + const T v2 = src[v2_index]; const T interpolated_value = attribute_math::mix3(bary_coord, v0, v1, v2); - data_out[i] = interpolated_value; + dst[i] = interpolated_value; } } void sample_point_attribute(const Mesh &mesh, const Span<int> looptri_indices, const Span<float3> bary_coords, - const GVArray &data_in, + const GVArray &src, const IndexMask mask, - const GMutableSpan data_out) + const GMutableSpan dst) { - BLI_assert(data_in.size() == mesh.totvert); - BLI_assert(data_in.type() == data_out.type()); + BLI_assert(src.size() == mesh.totvert); + BLI_assert(src.type() == dst.type()); - const CPPType &type = data_in.type(); + const CPPType &type = src.type(); attribute_math::convert_to_static_type(type, [&](auto dummy) { using T = decltype(dummy); sample_point_attribute<T>( - mesh, looptri_indices, bary_coords, data_in.typed<T>(), mask, data_out.typed<T>()); + mesh, looptri_indices, bary_coords, src.typed<T>(), mask, dst.typed<T>()); }); } @@ -63,9 +63,9 @@ template<typename T> BLI_NOINLINE static void sample_corner_attribute(const Mesh &mesh, const Span<int> looptri_indices, const Span<float3> bary_coords, - const VArray<T> &data_in, + const VArray<T> &src, const IndexMask mask, - const MutableSpan<T> data_out) + const MutableSpan<T> dst) { const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh), BKE_mesh_runtime_looptri_len(&mesh)}; @@ -79,39 +79,39 @@ BLI_NOINLINE static void sample_corner_attribute(const Mesh &mesh, const int loop_index_1 = looptri.tri[1]; const int loop_index_2 = looptri.tri[2]; - const T v0 = data_in[loop_index_0]; - const T v1 = data_in[loop_index_1]; - const T v2 = data_in[loop_index_2]; + const T v0 = src[loop_index_0]; + const T v1 = src[loop_index_1]; + const T v2 = src[loop_index_2]; const T interpolated_value = attribute_math::mix3(bary_coord, v0, v1, v2); - data_out[i] = interpolated_value; + dst[i] = interpolated_value; } } void sample_corner_attribute(const Mesh &mesh, const Span<int> looptri_indices, const Span<float3> bary_coords, - const GVArray &data_in, + const GVArray &src, const IndexMask mask, - const GMutableSpan data_out) + const GMutableSpan dst) { - BLI_assert(data_in.size() == mesh.totloop); - BLI_assert(data_in.type() == data_out.type()); + BLI_assert(src.size() == mesh.totloop); + BLI_assert(src.type() == dst.type()); - const CPPType &type = data_in.type(); + const CPPType &type = src.type(); attribute_math::convert_to_static_type(type, [&](auto dummy) { using T = decltype(dummy); sample_corner_attribute<T>( - mesh, looptri_indices, bary_coords, data_in.typed<T>(), mask, data_out.typed<T>()); + mesh, looptri_indices, bary_coords, src.typed<T>(), mask, dst.typed<T>()); }); } template<typename T> void sample_face_attribute(const Mesh &mesh, const Span<int> looptri_indices, - const VArray<T> &data_in, + const VArray<T> &src, const IndexMask mask, - const MutableSpan<T> data_out) + const MutableSpan<T> dst) { const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh), BKE_mesh_runtime_looptri_len(&mesh)}; @@ -120,23 +120,23 @@ void sample_face_attribute(const Mesh &mesh, const int looptri_index = looptri_indices[i]; const MLoopTri &looptri = looptris[looptri_index]; const int poly_index = looptri.poly; - data_out[i] = data_in[poly_index]; + dst[i] = src[poly_index]; } } void sample_face_attribute(const Mesh &mesh, const Span<int> looptri_indices, - const GVArray &data_in, + const GVArray &src, const IndexMask mask, - const GMutableSpan data_out) + const GMutableSpan dst) { - BLI_assert(data_in.size() == mesh.totpoly); - BLI_assert(data_in.type() == data_out.type()); + BLI_assert(src.size() == mesh.totpoly); + BLI_assert(src.type() == dst.type()); - const CPPType &type = data_in.type(); + const CPPType &type = src.type(); attribute_math::convert_to_static_type(type, [&](auto dummy) { using T = decltype(dummy); - sample_face_attribute<T>(mesh, looptri_indices, data_in.typed<T>(), mask, data_out.typed<T>()); + sample_face_attribute<T>(mesh, looptri_indices, src.typed<T>(), mask, dst.typed<T>()); }); } @@ -219,45 +219,31 @@ void MeshAttributeInterpolator::sample_data(const GVArray &src, if (ELEM(domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CORNER)) { switch (mode) { case eAttributeMapMode::INTERPOLATED: - weights = ensure_barycentric_coords(); + weights = this->ensure_barycentric_coords(); break; case eAttributeMapMode::NEAREST: - weights = ensure_nearest_weights(); + weights = this->ensure_nearest_weights(); break; } } /* Interpolate the source attributes on the surface. */ switch (domain) { - case ATTR_DOMAIN_POINT: { + case ATTR_DOMAIN_POINT: sample_point_attribute(*mesh_, looptri_indices_, weights, src, mask_, dst); break; - } - case ATTR_DOMAIN_FACE: { + case ATTR_DOMAIN_FACE: sample_face_attribute(*mesh_, looptri_indices_, src, mask_, dst); break; - } - case ATTR_DOMAIN_CORNER: { + case ATTR_DOMAIN_CORNER: sample_corner_attribute(*mesh_, looptri_indices_, weights, src, mask_, dst); break; - } - case ATTR_DOMAIN_EDGE: { + case ATTR_DOMAIN_EDGE: /* Not yet supported. */ break; - } - default: { + default: BLI_assert_unreachable(); break; - } - } -} - -void MeshAttributeInterpolator::sample_attribute(const GAttributeReader &src_attribute, - GSpanAttributeWriter &dst_attribute, - eAttributeMapMode mode) -{ - if (src_attribute && dst_attribute) { - this->sample_data(src_attribute.varray, src_attribute.domain, mode, dst_attribute.span); } } diff --git a/source/blender/blenkernel/intern/mesh_tessellate.cc b/source/blender/blenkernel/intern/mesh_tessellate.cc index ab0aeb88ebc..de4c60b28db 100644 --- a/source/blender/blenkernel/intern/mesh_tessellate.cc +++ b/source/blender/blenkernel/intern/mesh_tessellate.cc @@ -282,7 +282,8 @@ static void mesh_recalc_looptri__multi_threaded(const MLoop *mloop, { struct TessellationUserTLS tls_data_dummy = {nullptr}; - struct TessellationUserData data {}; + struct TessellationUserData data { + }; data.mloop = mloop; data.mpoly = mpoly; data.mvert = mvert; diff --git a/source/blender/blenkernel/intern/mesh_validate.cc b/source/blender/blenkernel/intern/mesh_validate.cc index 9b2697ecc84..5bcbdb399e4 100644 --- a/source/blender/blenkernel/intern/mesh_validate.cc +++ b/source/blender/blenkernel/intern/mesh_validate.cc @@ -1001,10 +1001,6 @@ bool BKE_mesh_validate_all_customdata(CustomData *vdata, CustomData_MeshMasks mask = {0}; if (check_meshmask) { mask = CD_MASK_MESH; - /* Normal data isn't in the mask since it is derived data, - * but it is valid and should not be removed. */ - mask.vmask |= CD_MASK_NORMAL; - mask.pmask |= CD_MASK_NORMAL; } is_valid &= mesh_validate_customdata( diff --git a/source/blender/blenkernel/intern/object_update.c b/source/blender/blenkernel/intern/object_update.c index 8ff02c7e698..1f7df2773dc 100644 --- a/source/blender/blenkernel/intern/object_update.c +++ b/source/blender/blenkernel/intern/object_update.c @@ -149,10 +149,6 @@ void BKE_object_handle_data_update(Depsgraph *depsgraph, Scene *scene, Object *o cddata_masks.pmask |= CD_MASK_PROP_ALL; cddata_masks.lmask |= CD_MASK_PROP_ALL; - /* Also copy over normal layers to avoid recomputation. */ - cddata_masks.pmask |= CD_MASK_NORMAL; - cddata_masks.vmask |= CD_MASK_NORMAL; - /* Make sure Freestyle edge/face marks appear in DM for render (see T40315). * Due to Line Art implementation, edge marks should also be shown in viewport. */ #ifdef WITH_FREESTYLE diff --git a/source/blender/blenlib/BLI_math_rotation.h b/source/blender/blenlib/BLI_math_rotation.h index 3987c9daf0a..b8ab74d95ff 100644 --- a/source/blender/blenlib/BLI_math_rotation.h +++ b/source/blender/blenlib/BLI_math_rotation.h @@ -189,7 +189,7 @@ void mat3_to_quat_is_ok(float q[4], const float mat[3][3]); * \endcode * * \param numerator: An integer factor in [0..denominator] (inclusive). - * \param denominator: The faction denominator (typically the number of segments of the circle). + * \param denominator: The fraction denominator (typically the number of segments of the circle). * \param r_sin: The resulting sine. * \param r_cos: The resulting cosine. */ diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 6fad67eb217..b880f0513b8 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -359,8 +359,7 @@ static void oldnewmap_insert(OldNewMap *onm, const void *oldaddr, void *newaddr, oldnewmap_insert_or_replace(onm, entry); } -static void oldnewmap_lib_insert( - FileData *fd, const void *oldaddr, ID *newaddr, int nr) +static void oldnewmap_lib_insert(FileData *fd, const void *oldaddr, ID *newaddr, int nr) { oldnewmap_insert(fd->libmap, oldaddr, newaddr, nr); } diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c index 9ab744337a8..ff72bfe95b8 100644 --- a/source/blender/blenloader/intern/versioning_290.c +++ b/source/blender/blenloader/intern/versioning_290.c @@ -1697,7 +1697,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) } } - /* Add subpanels for FModifiers, which requires a field to store expansion. */ + /* Add sub-panels for FModifiers, which requires a field to store expansion. */ if (!DNA_struct_elem_find(fd->filesdna, "FModifier", "short", "ui_expand_flag")) { LISTBASE_FOREACH (bAction *, act, &bmain->actions) { LISTBASE_FOREACH (FCurve *, fcu, &act->curves) { diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 49b14dc84a6..b98f8996a2c 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -3318,5 +3318,19 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) */ { /* Keep this block, even when empty. */ + + /* Image generation information transferred to tiles. */ + if (!DNA_struct_elem_find(fd->filesdna, "ImageTile", "int", "gen_x")) { + for (Image *ima = bmain->images.first; ima; ima = ima->id.next) { + for (ImageTile *tile = ima->tiles.first; tile; tile = tile->next) { + tile->gen_x = ima->gen_x; + tile->gen_y = ima->gen_y; + tile->gen_type = ima->gen_type; + tile->gen_flag = ima->gen_flag; + tile->gen_depth = ima->gen_depth; + copy_v4_v4(tile->gen_color, ima->gen_color); + } + } + } } } diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c index a2e3fd5346e..113fc244086 100644 --- a/source/blender/blenloader/intern/versioning_defaults.c +++ b/source/blender/blenloader/intern/versioning_defaults.c @@ -45,6 +45,7 @@ #include "BKE_layer.h" #include "BKE_lib_id.h" #include "BKE_main.h" +#include "BKE_main_namemap.h" #include "BKE_material.h" #include "BKE_mesh.h" #include "BKE_node.h" @@ -55,6 +56,8 @@ #include "BLO_readfile.h" +#include "BLT_translation.h" + #include "versioning_common.h" /* Make preferences read-only, use versioning_userdef.c. */ @@ -63,8 +66,9 @@ static bool blo_is_builtin_template(const char *app_template) { /* For all builtin templates shipped with Blender. */ - return (!app_template || - STR_ELEM(app_template, "2D_Animation", "Sculpting", "VFX", "Video_Editing")); + return ( + !app_template || + STR_ELEM(app_template, N_("2D_Animation"), N_("Sculpting"), N_("VFX"), N_("Video_Editing"))); } static void blo_update_defaults_screen(bScreen *screen, @@ -488,6 +492,7 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) if (layout->screen) { bScreen *screen = layout->screen; if (!STREQ(screen->id.name + 2, workspace->id.name + 2)) { + BKE_main_namemap_remove_name(bmain, &screen->id, screen->id.name + 2); BLI_strncpy(screen->id.name + 2, workspace->id.name + 2, sizeof(screen->id.name) - 2); BLI_libblock_ensure_unique_name(bmain, screen->id.name); } @@ -576,14 +581,14 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) /* Materials */ for (Material *ma = bmain->materials.first; ma; ma = ma->id.next) { /* Update default material to be a bit more rough. */ - ma->roughness = 0.4f; + ma->roughness = 0.5f; if (ma->nodetree) { LISTBASE_FOREACH (bNode *, node, &ma->nodetree->nodes) { if (node->type == SH_NODE_BSDF_PRINCIPLED) { bNodeSocket *roughness_socket = nodeFindSocket(node, SOCK_IN, "Roughness"); bNodeSocketValueFloat *roughness_data = roughness_socket->default_value; - roughness_data->value = 0.4f; + roughness_data->value = 0.5f; node->custom2 = SHD_SUBSURFACE_RANDOM_WALK; BKE_ntree_update_tag_node_property(ma->nodetree, node); } diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c index fa852cdd6da..aa2c93f7c5a 100644 --- a/source/blender/bmesh/tools/bmesh_bevel.c +++ b/source/blender/bmesh/tools/bmesh_bevel.c @@ -437,16 +437,6 @@ static bool nearly_parallel_normalized(const float d1[3], const float d2[3]) return compare_ff(fabsf(direction_dot), 1.0f, BEVEL_EPSILON_ANG_DOT); } -/** - * calculate the determinant of a matrix formed by three vectors - * \return dot(a, cross(b, c)) = determinant(a, b, c) - */ -static float determinant_v3v3v3(const float a[3], const float b[3], const float c[3]) -{ - return a[0] * b[1] * c[2] + a[1] * b[2] * c[0] + a[2] * b[0] * c[1] - a[0] * b[2] * c[1] - - a[1] * b[0] * c[2] - a[2] * b[1] * c[0]; -} - /* Make a new BoundVert of the given kind, inserting it at the end of the circular linked * list with entry point bv->boundstart, and return it. */ static BoundVert *add_new_bound_vert(MemArena *mem_arena, VMesh *vm, const float co[3]) @@ -4134,114 +4124,44 @@ static VMesh *cubic_subdiv(BevelParams *bp, VMesh *vm_in) VMesh *vm_out = new_adj_vmesh(bp->mem_arena, n_boundary, ns_out, vm_in->boundstart); /* First we adjust the boundary vertices of the input mesh, storing in output mesh. */ - BoundVert *bndv = vm_in->boundstart; for (int i = 0; i < n_boundary; i++) { - float co1[3], co2[3], acc[3]; - EdgeHalf *e = bndv->elast; - /* Generate tangents. This is hacked together and would ideally be done elsewhere and then only - * used here. */ - float tangent[3], tangent2[3], normal[3]; - bool convex = true; - bool orthogonal = false; - float stretch = 0.0f; - if (e) { - /* Projection direction is direction of the edge. */ - sub_v3_v3v3(tangent, e->e->v1->co, e->e->v2->co); - if (e->is_rev) { - negate_v3(tangent); - } - normalize_v3(tangent); - if (bndv->is_arc_start || bndv->is_patch_start) { - BMFace *face = e->fnext; - if (face) { - copy_v3_v3(normal, face->no); - } - else { - zero_v3(normal); - } - madd_v3_v3v3fl(co2, bndv->profile.middle, normal, 0.1f); - } - if (bndv->is_arc_start || bp->affect_type == BEVEL_AFFECT_VERTICES) { - EdgeHalf *e1 = bndv->next->elast; - BLI_assert(e1); - sub_v3_v3v3(tangent2, e1->e->v1->co, e1->e->v2->co); - if (e1->is_rev) { - negate_v3(tangent2); - } - normalize_v3(tangent2); - - convex = determinant_v3v3v3(tangent2, tangent, normal) < 0; - - add_v3_v3(tangent2, tangent); - normalize_v3(tangent2); - copy_v3_v3(tangent, tangent2); - } - /* Calculate a factor which determines how much the interpolated mesh is - * going to be stretched out into the direction of the tangent. - * It is currently using the difference along the tangent of the - * central point on the profile and the current center vertex position. */ - get_profile_point(bp, &bndv->profile, ns_in2, ns_in, co); - stretch = dot_v3v3(tangent, mesh_vert(vm_in, i, ns_in2, ns_in2)->co) - dot_v3v3(tangent, co); - stretch = fabsf(stretch); - /* Scale the tangent by stretch. The divide by ns_in2 comes from the Levin Paper. */ - mul_v3_fl(tangent, stretch / ns_in2); - orthogonal = bndv->is_patch_start; - } - else if (bndv->prev->is_patch_start) { - /* If this is the second edge of a patch and therefore #e is NULL, - * then e->fprev has to be used/not NULL. */ - BLI_assert(bndv->prev->elast); - BMFace *face = bndv->prev->elast->fnext; - if (face) { - copy_v3_v3(normal, face->no); - } - else { - zero_v3(normal); - } - orthogonal = true; - } - else { - /** Should only come here from make_cube_corner_adj_vmesh. */ - sub_v3_v3v3(co1, mesh_vert(vm_in, i, 0, 0)->co, mesh_vert(vm_in, i, 0, 1)->co); - sub_v3_v3v3(co2, mesh_vert(vm_in, i, 0, 1)->co, mesh_vert(vm_in, i, 0, 2)->co); - cross_v3_v3v3(tangent, co1, co2); - /** The following constant is chosen to best match the old results. */ - normalize_v3_length(tangent, 1.5f / ns_out); - } - /** Copy corner vertex. */ copy_v3_v3(mesh_vert(vm_out, i, 0, 0)->co, mesh_vert(vm_in, i, 0, 0)->co); - /** Copy the rest of the boundary vertices. */ for (int k = 1; k < ns_in; k++) { copy_v3_v3(co, mesh_vert(vm_in, i, 0, k)->co); - copy_v3_v3(co1, mesh_vert(vm_in, i, 0, k - 1)->co); - copy_v3_v3(co2, mesh_vert(vm_in, i, 0, k + 1)->co); - - add_v3_v3v3(acc, co1, co2); - if (bndv->is_arc_start) { - sub_v3_v3(co1, co); - sub_v3_v3(co2, co); - normalize_v3(co1); - normalize_v3(co2); - add_v3_v3v3(tangent, co1, co2); - /* This is an empirical formula to make the result look good. */ - normalize_v3(tangent); - float dot = convex ? fminf(0, dot_v3v3(tangent2, tangent)) : 1.0f; - mul_v3_fl(tangent, stretch / ns_in * dot); - } - else if (orthogonal) { - sub_v3_v3(co1, co); - cross_v3_v3v3(tangent, normal, co1); - /* This is an empirical formula to make the result look good. */ - normalize_v3_length(tangent, -bp->offset * 0.7071f / ns_in); - } - mul_v3_fl(co, 2.0f); - madd_v3_v3fl(co, acc, -0.25f); - madd_v3_v3fl(co, mesh_vert(vm_in, i, 1, k)->co, -0.5f); - add_v3_v3(co, tangent); + /* Smooth boundary rule. Custom profiles shouldn't be smoothed. */ + if (bp->profile_type != BEVEL_PROFILE_CUSTOM) { + float co1[3], co2[3], acc[3]; + copy_v3_v3(co1, mesh_vert(vm_in, i, 0, k - 1)->co); + copy_v3_v3(co2, mesh_vert(vm_in, i, 0, k + 1)->co); + + add_v3_v3v3(acc, co1, co2); + madd_v3_v3fl(acc, co, -2.0f); + madd_v3_v3fl(co, acc, -1.0f / 6.0f); + } copy_v3_v3(mesh_vert_canon(vm_out, i, 0, 2 * k)->co, co); } + } + /* Now adjust odd boundary vertices in output mesh, based on even ones. */ + BoundVert *bndv = vm_out->boundstart; + for (int i = 0; i < n_boundary; i++) { + for (int k = 1; k < ns_out; k += 2) { + get_profile_point(bp, &bndv->profile, k, ns_out, co); + + /* Smooth if using a non-custom profile. */ + if (bp->profile_type != BEVEL_PROFILE_CUSTOM) { + float co1[3], co2[3], acc[3]; + copy_v3_v3(co1, mesh_vert_canon(vm_out, i, 0, k - 1)->co); + copy_v3_v3(co2, mesh_vert_canon(vm_out, i, 0, k + 1)->co); + + add_v3_v3v3(acc, co1, co2); + madd_v3_v3fl(acc, co, -2.0f); + madd_v3_v3fl(co, acc, -1.0f / 6.0f); + } + + copy_v3_v3(mesh_vert_canon(vm_out, i, 0, k)->co, co); + } bndv = bndv->next; } vmesh_copy_equiv_verts(vm_out); @@ -4249,7 +4169,7 @@ static VMesh *cubic_subdiv(BevelParams *bp, VMesh *vm_in) /* Copy adjusted verts back into vm_in. */ for (int i = 0; i < n_boundary; i++) { for (int k = 0; k < ns_in; k++) { - copy_v3_v3(mesh_vert_canon(vm_in, i, 0, k)->co, mesh_vert_canon(vm_out, i, 0, 2 * k)->co); + copy_v3_v3(mesh_vert(vm_in, i, 0, k)->co, mesh_vert(vm_out, i, 0, 2 * k)->co); } } @@ -4334,7 +4254,7 @@ static VMesh *cubic_subdiv(BevelParams *bp, VMesh *vm_in) vmesh_copy_equiv_verts(vm_out); /* The center vertex is special. */ - gamma = sabin_gamma(n_boundary) * 0.5f; + gamma = sabin_gamma(n_boundary); beta = -gamma; /* Accumulate edge verts in co1, face verts in co2. */ float co1[3], co2[3]; diff --git a/source/blender/depsgraph/DEG_depsgraph_build.h b/source/blender/depsgraph/DEG_depsgraph_build.h index 763d2d29035..ac6ab5c7666 100644 --- a/source/blender/depsgraph/DEG_depsgraph_build.h +++ b/source/blender/depsgraph/DEG_depsgraph_build.h @@ -161,8 +161,8 @@ void DEG_add_generic_id_relation(struct DepsNodeHandle *node_handle, * This function will take care of checking which operation is required to * have transformation for the modifier, taking into account possible simulation solvers. */ -void DEG_add_modifier_to_transform_relation(struct DepsNodeHandle *node_handle, - const char *description); +void DEG_add_depends_on_transform_relation(struct DepsNodeHandle *node_handle, + const char *description); /** * Adds relations from the given component of a given object to the given node diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index f36d94c7563..fc5e5189e82 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -239,13 +239,8 @@ DepsgraphRelationBuilder::DepsgraphRelationBuilder(Main *bmain, { } -TimeSourceNode *DepsgraphRelationBuilder::get_node(const TimeSourceKey &key) const +TimeSourceNode *DepsgraphRelationBuilder::get_node(const TimeSourceKey & /*key*/) const { - if (key.id) { - /* XXX TODO */ - return nullptr; - } - return graph_->time_source; } @@ -298,8 +293,8 @@ bool DepsgraphRelationBuilder::has_node(const OperationKey &key) const return find_node(key) != nullptr; } -void DepsgraphRelationBuilder::add_modifier_to_transform_relation(const DepsNodeHandle *handle, - const char *description) +void DepsgraphRelationBuilder::add_depends_on_transform_relation(const DepsNodeHandle *handle, + const char *description) { IDNode *id_node = handle->node->owner->owner; ID *id = id_node->id_orig; @@ -1977,7 +1972,6 @@ void DepsgraphRelationBuilder::build_rigidbody(Scene *scene) void DepsgraphRelationBuilder::build_particle_systems(Object *object) { - TimeSourceKey time_src_key; OperationKey obdata_ubereval_key(&object->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL); OperationKey eval_init_key( &object->id, NodeType::PARTICLE_SYSTEM, OperationCode::PARTICLE_SYSTEM_INIT); @@ -2263,12 +2257,7 @@ void DepsgraphRelationBuilder::build_object_data_geometry(Object *object) // add geometry collider relations } /* Make sure uber update is the last in the dependencies. */ - if (object->type != OB_ARMATURE) { - /* Armatures does no longer require uber node. */ - OperationKey obdata_ubereval_key( - &object->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL); - add_relation(geom_init_key, obdata_ubereval_key, "Object Geometry UberEval"); - } + add_relation(geom_init_key, obdata_ubereval_key, "Object Geometry UberEval"); if (object->type == OB_MBALL) { Object *mom = BKE_mball_basis_find(scene_, object); ComponentKey mom_geom_key(&mom->id, NodeType::GEOMETRY); @@ -3097,7 +3086,6 @@ void DepsgraphRelationBuilder::build_copy_on_write_relations(IDNode *id_node) return; } - TimeSourceKey time_source_key; OperationKey copy_on_write_key(id_orig, NodeType::COPY_ON_WRITE, OperationCode::COPY_ON_WRITE); /* XXX: This is a quick hack to make Alt-A to work. */ // add_relation(time_source_key, copy_on_write_key, "Fluxgate capacitor hack"); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.h b/source/blender/depsgraph/intern/builder/deg_builder_relations.h index 7a78280f1f0..db237303027 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.h @@ -85,51 +85,113 @@ struct RootPChanMap; struct TimeSourceNode; struct TimeSourceKey { - TimeSourceKey(); - TimeSourceKey(ID *id); + TimeSourceKey() = default; string identifier() const; - - ID *id; }; struct ComponentKey { - ComponentKey(); - ComponentKey(ID *id, NodeType type, const char *name = ""); + ComponentKey() = default; + + inline ComponentKey(const ID *id, NodeType type, const char *name = "") + : id(id), type(type), name(name) + { + } string identifier() const; - ID *id; - NodeType type; - const char *name; + const ID *id = nullptr; + NodeType type = NodeType::UNDEFINED; + const char *name = ""; }; struct OperationKey { - OperationKey(); - OperationKey(ID *id, NodeType component_type, const char *name, int name_tag = -1); - OperationKey( - ID *id, NodeType component_type, const char *component_name, const char *name, int name_tag); + OperationKey() = default; + + inline OperationKey(const ID *id, NodeType component_type, const char *name, int name_tag = -1) + : id(id), + component_type(component_type), + component_name(""), + opcode(OperationCode::OPERATION), + name(name), + name_tag(name_tag) + { + } + + OperationKey(const ID *id, + NodeType component_type, + const char *component_name, + const char *name, + int name_tag) + : id(id), + component_type(component_type), + component_name(component_name), + opcode(OperationCode::OPERATION), + name(name), + name_tag(name_tag) + { + } + + OperationKey(const ID *id, NodeType component_type, OperationCode opcode) + : id(id), + component_type(component_type), + component_name(""), + opcode(opcode), + name(""), + name_tag(-1) + { + } - OperationKey(ID *id, NodeType component_type, OperationCode opcode); - OperationKey(ID *id, NodeType component_type, const char *component_name, OperationCode opcode); + OperationKey(const ID *id, + NodeType component_type, + const char *component_name, + OperationCode opcode) + : id(id), + component_type(component_type), + component_name(component_name), + opcode(opcode), + name(""), + name_tag(-1) + { + } - OperationKey( - ID *id, NodeType component_type, OperationCode opcode, const char *name, int name_tag = -1); - OperationKey(ID *id, + OperationKey(const ID *id, + NodeType component_type, + OperationCode opcode, + const char *name, + int name_tag = -1) + : id(id), + component_type(component_type), + component_name(""), + opcode(opcode), + name(name), + name_tag(name_tag) + { + } + + OperationKey(const ID *id, NodeType component_type, const char *component_name, OperationCode opcode, const char *name, - int name_tag = -1); + int name_tag = -1) + : id(id), + component_type(component_type), + component_name(component_name), + opcode(opcode), + name(name), + name_tag(name_tag) + { + } string identifier() const; - ID *id; - NodeType component_type; - const char *component_name; - OperationCode opcode; - const char *name; - int name_tag; + const ID *id = nullptr; + NodeType component_type = NodeType::UNDEFINED; + const char *component_name = ""; + OperationCode opcode = OperationCode::OPERATION; + const char *name = ""; + int name_tag = -1; }; struct RNAPathKey { @@ -177,7 +239,7 @@ class DepsgraphRelationBuilder : public DepsgraphBuilder { /* Adds relation from proper transformation operation to the modifier. * Takes care of checking for possible physics solvers modifying position * of this object. */ - void add_modifier_to_transform_relation(const DepsNodeHandle *handle, const char *description); + void add_depends_on_transform_relation(const DepsNodeHandle *handle, const char *description); void add_customdata_mask(Object *object, const DEGCustomDataMeshMasks &customdata_masks); void add_special_eval_flag(ID *id, uint32_t flag); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations_keys.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations_keys.cc index eeaab623482..8506a97c408 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations_keys.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_keys.cc @@ -14,14 +14,6 @@ namespace blender::deg { //////////////////////////////////////////////////////////////////////////////// /* Time source. */ -TimeSourceKey::TimeSourceKey() : id(nullptr) -{ -} - -TimeSourceKey::TimeSourceKey(ID *id) : id(id) -{ -} - string TimeSourceKey::identifier() const { return string("TimeSourceKey"); @@ -30,15 +22,6 @@ string TimeSourceKey::identifier() const //////////////////////////////////////////////////////////////////////////////// // Component. -ComponentKey::ComponentKey() : id(nullptr), type(NodeType::UNDEFINED), name("") -{ -} - -ComponentKey::ComponentKey(ID *id, NodeType type, const char *name) - : id(id), type(type), name(name) -{ -} - string ComponentKey::identifier() const { const char *idname = (id) ? id->name : "<None>"; @@ -55,86 +38,6 @@ string ComponentKey::identifier() const //////////////////////////////////////////////////////////////////////////////// // Operation. -OperationKey::OperationKey() - : id(nullptr), - component_type(NodeType::UNDEFINED), - component_name(""), - opcode(OperationCode::OPERATION), - name(""), - name_tag(-1) -{ -} - -OperationKey::OperationKey(ID *id, NodeType component_type, const char *name, int name_tag) - : id(id), - component_type(component_type), - component_name(""), - opcode(OperationCode::OPERATION), - name(name), - name_tag(name_tag) -{ -} - -OperationKey::OperationKey( - ID *id, NodeType component_type, const char *component_name, const char *name, int name_tag) - : id(id), - component_type(component_type), - component_name(component_name), - opcode(OperationCode::OPERATION), - name(name), - name_tag(name_tag) -{ -} - -OperationKey::OperationKey(ID *id, NodeType component_type, OperationCode opcode) - : id(id), - component_type(component_type), - component_name(""), - opcode(opcode), - name(""), - name_tag(-1) -{ -} - -OperationKey::OperationKey(ID *id, - NodeType component_type, - const char *component_name, - OperationCode opcode) - : id(id), - component_type(component_type), - component_name(component_name), - opcode(opcode), - name(""), - name_tag(-1) -{ -} - -OperationKey::OperationKey( - ID *id, NodeType component_type, OperationCode opcode, const char *name, int name_tag) - : id(id), - component_type(component_type), - component_name(""), - opcode(opcode), - name(name), - name_tag(name_tag) -{ -} - -OperationKey::OperationKey(ID *id, - NodeType component_type, - const char *component_name, - OperationCode opcode, - const char *name, - int name_tag) - : id(id), - component_type(component_type), - component_name(component_name), - opcode(opcode), - name(name), - name_tag(name_tag) -{ -} - string OperationKey::identifier() const { string result = string("OperationKey("); diff --git a/source/blender/depsgraph/intern/depsgraph_build.cc b/source/blender/depsgraph/intern/depsgraph_build.cc index a207c13d646..6da290d6c4e 100644 --- a/source/blender/depsgraph/intern/depsgraph_build.cc +++ b/source/blender/depsgraph/intern/depsgraph_build.cc @@ -199,11 +199,11 @@ void DEG_add_generic_id_relation(struct DepsNodeHandle *node_handle, deg_node_handle->builder->add_node_handle_relation(operation_key, deg_node_handle, description); } -void DEG_add_modifier_to_transform_relation(struct DepsNodeHandle *node_handle, - const char *description) +void DEG_add_depends_on_transform_relation(struct DepsNodeHandle *node_handle, + const char *description) { deg::DepsNodeHandle *deg_node_handle = get_node_handle(node_handle); - deg_node_handle->builder->add_modifier_to_transform_relation(deg_node_handle, description); + deg_node_handle->builder->add_depends_on_transform_relation(deg_node_handle, description); } void DEG_add_special_eval_flag(struct DepsNodeHandle *node_handle, ID *id, uint32_t flag) diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc index d95e871d6c7..cc742b98866 100644 --- a/source/blender/depsgraph/intern/depsgraph_tag.cc +++ b/source/blender/depsgraph/intern/depsgraph_tag.cc @@ -227,7 +227,9 @@ void depsgraph_tag_to_component_opcode(const ID *id, case ID_RECALC_PROVISION_29: case ID_RECALC_PROVISION_30: case ID_RECALC_PROVISION_31: - BLI_assert_msg(0, "Should not happen"); + /* Silently ignore. + * The bits might be passed here from ID_RECALC_ALL. This is not a code-mistake, but just the + * way how the recalc flags are handled. */ break; } } @@ -757,7 +759,10 @@ const char *DEG_update_tag_as_string(IDRecalcFlag flag) case ID_RECALC_PROVISION_29: case ID_RECALC_PROVISION_30: case ID_RECALC_PROVISION_31: - BLI_assert_msg(0, "Should not happen"); + /* Silently return nullptr, indicating that there is no string representation. + * + * This is needed due to the way how logging for ID_RECALC_ALL works: it iterates over all + * bits and converts then to string. */ return nullptr; } return nullptr; diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index e907c17e9d1..c34a6daa126 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -363,6 +363,7 @@ set(GLSL_SRC engines/eevee_next/shaders/eevee_attributes_lib.glsl engines/eevee_next/shaders/eevee_camera_lib.glsl + engines/eevee_next/shaders/eevee_colorspace_lib.glsl engines/eevee_next/shaders/eevee_depth_of_field_accumulator_lib.glsl engines/eevee_next/shaders/eevee_depth_of_field_bokeh_lut_comp.glsl engines/eevee_next/shaders/eevee_depth_of_field_downsample_comp.glsl diff --git a/source/blender/draw/engines/eevee_next/eevee_defines.hh b/source/blender/draw/engines/eevee_next/eevee_defines.hh index 8240af14203..c1e901845f1 100644 --- a/source/blender/draw/engines/eevee_next/eevee_defines.hh +++ b/source/blender/draw/engines/eevee_next/eevee_defines.hh @@ -57,9 +57,11 @@ #define DOF_TILES_DILATE_GROUP_SIZE 8 #define DOF_BOKEH_LUT_SIZE 32 #define DOF_MAX_SLIGHT_FOCUS_RADIUS 5 -#define DOF_REDUCE_GROUP_SIZE 8 +#define DOF_SLIGHT_FOCUS_SAMPLE_MAX 16 +#define DOF_MIP_COUNT 4 +#define DOF_REDUCE_GROUP_SIZE (1 << (DOF_MIP_COUNT - 1)) #define DOF_DEFAULT_GROUP_SIZE 32 +#define DOF_STABILIZE_GROUP_SIZE 16 #define DOF_FILTER_GROUP_SIZE 8 #define DOF_GATHER_GROUP_SIZE DOF_TILES_SIZE #define DOF_RESOLVE_GROUP_SIZE (DOF_TILES_SIZE * 2) -#define DOF_MIP_MAX 4 diff --git a/source/blender/draw/engines/eevee_next/eevee_depth_of_field.cc b/source/blender/draw/engines/eevee_next/eevee_depth_of_field.cc index 26129192d9e..3700076153e 100644 --- a/source/blender/draw/engines/eevee_next/eevee_depth_of_field.cc +++ b/source/blender/draw/engines/eevee_next/eevee_depth_of_field.cc @@ -55,14 +55,11 @@ void DepthOfField::init() /* Reminder: These are parameters not interpolated by motion blur. */ int update = 0; int sce_flag = sce_eevee.flag; - update += assign_if_different(do_hq_slight_focus_, - (sce_flag & SCE_EEVEE_DOF_HQ_SLIGHT_FOCUS) != 0); update += assign_if_different(do_jitter_, (sce_flag & SCE_EEVEE_DOF_JITTER) != 0); update += assign_if_different(user_overblur_, sce_eevee.bokeh_overblur / 100.0f); update += assign_if_different(fx_max_coc_, sce_eevee.bokeh_max_size); update += assign_if_different(data_.scatter_color_threshold, sce_eevee.bokeh_threshold); update += assign_if_different(data_.scatter_neighbor_max_color, sce_eevee.bokeh_neighbor_max); - update += assign_if_different(data_.denoise_factor, sce_eevee.bokeh_denoise_fac); update += assign_if_different(data_.bokeh_blades, float(camera->dof.aperture_blades)); if (update > 0) { inst_.sampling.reset(); @@ -145,7 +142,7 @@ void DepthOfField::sync() } /* Disable post fx if result wouldn't be noticeable. */ - if (fx_max_coc_ < 0.5f) { + if (fx_max_coc_ <= 0.5f) { fx_radius = 0.0f; } @@ -162,18 +159,15 @@ void DepthOfField::sync() /* TODO(fclem): Once we render into multiple view, we will need to use the maximum resolution. */ int2 max_render_res = inst_.film.render_extent_get(); int2 half_res = math::divide_ceil(max_render_res, int2(2)); - int2 reduce_size = math::ceil_to_multiple(half_res, int2(1 < (DOF_MIP_MAX - 1))); + int2 reduce_size = math::ceil_to_multiple(half_res, int2(DOF_REDUCE_GROUP_SIZE)); data_.gather_uv_fac = 1.0f / float2(reduce_size); /* Now that we know the maximum render resolution of every view, using depth of field, allocate * the reduced buffers. Color needs to be signed format here. See note in shader for * explanation. Do not use texture pool because of needs mipmaps. */ - reduced_color_tx_.ensure_2d(GPU_RGBA16F, reduce_size, nullptr, DOF_MIP_MAX); - reduced_coc_tx_.ensure_2d(GPU_R16F, reduce_size, nullptr, DOF_MIP_MAX); - GPU_texture_wrap_mode(reduced_color_tx_, false, false); - GPU_texture_wrap_mode(reduced_coc_tx_, false, false); - + reduced_color_tx_.ensure_2d(GPU_RGBA16F, reduce_size, nullptr, DOF_MIP_COUNT); + reduced_coc_tx_.ensure_2d(GPU_R16F, reduce_size, nullptr, DOF_MIP_COUNT); reduced_color_tx_.ensure_mip_views(); reduced_coc_tx_.ensure_mip_views(); @@ -276,16 +270,28 @@ void DepthOfField::setup_pass_sync() void DepthOfField::stabilize_pass_sync() { + RenderBuffers &render_buffers = inst_.render_buffers; + VelocityModule &velocity = inst_.velocity; + stabilize_ps_ = DRW_pass_create("Dof.stabilize_ps_", DRW_STATE_NO_DRAW); GPUShader *sh = inst_.shaders.static_shader_get(DOF_STABILIZE); DRWShadingGroup *grp = DRW_shgroup_create(sh, stabilize_ps_); + DRW_shgroup_uniform_block_ref(grp, "camera_prev", &(*velocity.camera_steps[STEP_PREVIOUS])); + DRW_shgroup_uniform_block_ref(grp, "camera_curr", &(*velocity.camera_steps[STEP_CURRENT])); + /* This is only for temporal stability. The next step is not needed. */ + DRW_shgroup_uniform_block_ref(grp, "camera_next", &(*velocity.camera_steps[STEP_PREVIOUS])); DRW_shgroup_uniform_texture_ref_ex(grp, "coc_tx", &setup_coc_tx_, no_filter); DRW_shgroup_uniform_texture_ref_ex(grp, "color_tx", &setup_color_tx_, no_filter); + DRW_shgroup_uniform_texture_ref_ex(grp, "velocity_tx", &render_buffers.vector_tx, no_filter); + DRW_shgroup_uniform_texture_ref_ex(grp, "in_history_tx", &stabilize_input_, with_filter); + DRW_shgroup_uniform_texture_ref_ex(grp, "depth_tx", &render_buffers.depth_tx, no_filter); + DRW_shgroup_uniform_bool(grp, "use_history", &stabilize_valid_history_, 1); DRW_shgroup_uniform_block(grp, "dof_buf", data_); DRW_shgroup_uniform_image(grp, "out_coc_img", reduced_coc_tx_.mip_view(0)); DRW_shgroup_uniform_image(grp, "out_color_img", reduced_color_tx_.mip_view(0)); + DRW_shgroup_uniform_image_ref(grp, "out_history_img", &stabilize_output_tx_); DRW_shgroup_call_compute_ref(grp, dispatch_stabilize_size_); - DRW_shgroup_barrier(grp, GPU_BARRIER_TEXTURE_FETCH); + DRW_shgroup_barrier(grp, GPU_BARRIER_TEXTURE_FETCH | GPU_BARRIER_SHADER_IMAGE_ACCESS); } void DepthOfField::downsample_pass_sync() @@ -319,8 +325,6 @@ void DepthOfField::reduce_pass_sync() DRW_shgroup_uniform_image(grp, "out_coc_lod1_img", reduced_coc_tx_.mip_view(1)); DRW_shgroup_uniform_image(grp, "out_coc_lod2_img", reduced_coc_tx_.mip_view(2)); DRW_shgroup_uniform_image(grp, "out_coc_lod3_img", reduced_coc_tx_.mip_view(3)); - /* Sync writes to inout_color_lod0_img from stabilize_ps_. */ - DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_IMAGE_ACCESS); DRW_shgroup_call_compute_ref(grp, dispatch_reduce_size_); /* NOTE: Command buffer barrier is done automatically by the GPU backend. */ DRW_shgroup_barrier(grp, GPU_BARRIER_TEXTURE_FETCH | GPU_BARRIER_SHADER_STORAGE); @@ -354,7 +358,6 @@ void DepthOfField::tiles_dilate_pass_sync() DRW_shgroup_uniform_image_ref(grp, "in_tiles_bg_img", &tiles_bg_tx_.previous()); DRW_shgroup_uniform_image_ref(grp, "out_tiles_fg_img", &tiles_fg_tx_.current()); DRW_shgroup_uniform_image_ref(grp, "out_tiles_bg_img", &tiles_bg_tx_.current()); - DRW_shgroup_uniform_bool(grp, "dilate_slight_focus", &tiles_dilate_slight_focus_, 1); DRW_shgroup_uniform_int(grp, "ring_count", &tiles_dilate_ring_count_, 1); DRW_shgroup_uniform_int(grp, "ring_width_multiplier", &tiles_dilate_ring_width_mul_, 1); DRW_shgroup_call_compute_ref(grp, dispatch_tiles_dilate_size_); @@ -425,6 +428,10 @@ void DepthOfField::scatter_pass_sync() DRW_shgroup_uniform_texture_ref(grp, "bokeh_lut_tx", &bokeh_scatter_lut_tx_); DRW_shgroup_uniform_texture_ref(grp, "occlusion_tx", &occlusion_tx_); DRW_shgroup_call_procedural_indirect(grp, GPU_PRIM_TRI_STRIP, nullptr, scatter_buf); + if (pass == 0) { + /* Avoid background gather pass writing to the occlusion_tx mid pass. */ + DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_IMAGE_ACCESS); + } } } @@ -453,14 +460,14 @@ void DepthOfField::resolve_pass_sync() resolve_ps_ = DRW_pass_create("Dof.resolve_ps_", DRW_STATE_NO_DRAW); bool use_lut = bokeh_lut_ps_ != nullptr; - eShaderType sh_type = do_hq_slight_focus_ ? (use_lut ? DOF_RESOLVE_LUT_HQ : DOF_RESOLVE_HQ) : - (use_lut ? DOF_RESOLVE_LUT : DOF_RESOLVE); + eShaderType sh_type = use_lut ? DOF_RESOLVE_LUT : DOF_RESOLVE; GPUShader *sh = inst_.shaders.static_shader_get(sh_type); DRWShadingGroup *grp = DRW_shgroup_create(sh, resolve_ps_); inst_.sampling.bind_resources(grp); DRW_shgroup_uniform_block(grp, "dof_buf", data_); DRW_shgroup_uniform_texture_ref_ex(grp, "depth_tx", &render_buffers.depth_tx, no_filter); DRW_shgroup_uniform_texture_ref_ex(grp, "color_tx", &input_color_tx_, no_filter); + DRW_shgroup_uniform_texture_ref_ex(grp, "stable_color_tx", &resolve_stable_color_tx_, no_filter); DRW_shgroup_uniform_texture_ref_ex(grp, "color_bg_tx", &color_bg_tx_.current(), with_filter); DRW_shgroup_uniform_texture_ref_ex(grp, "color_fg_tx", &color_fg_tx_.current(), with_filter); DRW_shgroup_uniform_image_ref(grp, "in_tiles_fg_img", &tiles_fg_tx_.current()); @@ -482,7 +489,29 @@ void DepthOfField::resolve_pass_sync() /** \name Post-FX Rendering. * \{ */ -void DepthOfField::render(GPUTexture **input_tx, GPUTexture **output_tx) +/* Similar to Film::update_sample_table() but with constant filter radius and constant sample + * count. */ +void DepthOfField::update_sample_table() +{ + float2 subpixel_offset = inst_.film.pixel_jitter_get(); + /* Since the film jitter is in full-screen res, divide by 2 to get the jitter in half res. */ + subpixel_offset *= 0.5; + + /* Same offsets as in dof_spatial_filtering(). */ + const std::array<int2, 4> plus_offsets = {int2(-1, 0), int2(0, -1), int2(1, 0), int2(0, 1)}; + + const float radius = 1.5f; + int i = 0; + for (int2 offset : plus_offsets) { + float2 pixel_ofs = float2(offset) - subpixel_offset; + data_.filter_samples_weight[i++] = film_filter_weight(radius, math::length_squared(pixel_ofs)); + } + data_.filter_center_weight = film_filter_weight(radius, math::length_squared(subpixel_offset)); +} + +void DepthOfField::render(GPUTexture **input_tx, + GPUTexture **output_tx, + DepthOfFieldBuffer &dof_buffer) { if (fx_radius_ == 0.0f) { return; @@ -522,6 +551,8 @@ void DepthOfField::render(GPUTexture **input_tx, GPUTexture **output_tx) /* TODO(fclem): Make this dependent of the quality of the gather pass. */ data_.scatter_coc_threshold = 4.0f; + update_sample_table(); + data_.push_update(); } @@ -530,7 +561,7 @@ void DepthOfField::render(GPUTexture **input_tx, GPUTexture **output_tx) int2 tile_res = math::divide_ceil(half_res, int2(DOF_TILES_SIZE)); dispatch_setup_size_ = int3(math::divide_ceil(half_res, int2(DOF_DEFAULT_GROUP_SIZE)), 1); - dispatch_stabilize_size_ = int3(math::divide_ceil(half_res, int2(DOF_DEFAULT_GROUP_SIZE)), 1); + dispatch_stabilize_size_ = int3(math::divide_ceil(half_res, int2(DOF_STABILIZE_GROUP_SIZE)), 1); dispatch_downsample_size_ = int3(math::divide_ceil(quarter_res, int2(DOF_DEFAULT_GROUP_SIZE)), 1); dispatch_reduce_size_ = int3(math::divide_ceil(half_res, int2(DOF_REDUCE_GROUP_SIZE)), 1); @@ -551,31 +582,48 @@ void DepthOfField::render(GPUTexture **input_tx, GPUTexture **output_tx) { DRW_stats_group_start("Setup"); + { + bokeh_gather_lut_tx_.acquire(int2(DOF_BOKEH_LUT_SIZE), GPU_RG16F); + bokeh_scatter_lut_tx_.acquire(int2(DOF_BOKEH_LUT_SIZE), GPU_R16F); + bokeh_resolve_lut_tx_.acquire(int2(DOF_MAX_SLIGHT_FOCUS_RADIUS * 2 + 1), GPU_R16F); - bokeh_gather_lut_tx_.acquire(int2(DOF_BOKEH_LUT_SIZE), GPU_RG16F); - bokeh_scatter_lut_tx_.acquire(int2(DOF_BOKEH_LUT_SIZE), GPU_R16F); - bokeh_resolve_lut_tx_.acquire(int2(DOF_MAX_SLIGHT_FOCUS_RADIUS * 2 + 1), GPU_R16F); - - DRW_draw_pass(bokeh_lut_ps_); + DRW_draw_pass(bokeh_lut_ps_); + } + { + setup_color_tx_.acquire(half_res, GPU_RGBA16F); + setup_coc_tx_.acquire(half_res, GPU_R16F); - setup_color_tx_.acquire(half_res, GPU_RGBA16F); - setup_coc_tx_.acquire(half_res, GPU_RG16F); + DRW_draw_pass(setup_ps_); + } + { + stabilize_output_tx_.acquire(half_res, GPU_RGBA16F); + stabilize_valid_history_ = !dof_buffer.stabilize_history_tx_.ensure_2d(GPU_RGBA16F, + half_res); - DRW_draw_pass(setup_ps_); + if (stabilize_valid_history_ == false) { + /* Avoid uninitialized memory that can contain NaNs. */ + dof_buffer.stabilize_history_tx_.clear(float4(0.0f)); + } - /* Outputs to reduced_*_tx_ mip 0. */ - DRW_draw_pass(stabilize_ps_); + stabilize_input_ = dof_buffer.stabilize_history_tx_; + /* Outputs to reduced_*_tx_ mip 0. */ + DRW_draw_pass(stabilize_ps_); - /* Used by stabilize pass. */ - setup_color_tx_.release(); + /* WATCH(fclem): Swap Texture an TextureFromPool internal GPUTexture in order to reuse + * the one that we just consumed. */ + TextureFromPool::swap(stabilize_output_tx_, dof_buffer.stabilize_history_tx_); + /* Used by stabilize pass. */ + stabilize_output_tx_.release(); + setup_color_tx_.release(); + } { DRW_stats_group_start("Tile Prepare"); /* WARNING: If format changes, make sure dof_tile_* GLSL constants are properly encoded. */ - tiles_fg_tx_.previous().acquire(tile_res, GPU_RGBA16F); + tiles_fg_tx_.previous().acquire(tile_res, GPU_R11F_G11F_B10F); tiles_bg_tx_.previous().acquire(tile_res, GPU_R11F_G11F_B10F); - tiles_fg_tx_.current().acquire(tile_res, GPU_RGBA16F); + tiles_fg_tx_.current().acquire(tile_res, GPU_R11F_G11F_B10F); tiles_bg_tx_.current().acquire(tile_res, GPU_R11F_G11F_B10F); DRW_draw_pass(tiles_flatten_ps_); @@ -592,9 +640,6 @@ void DepthOfField::render(GPUTexture **input_tx, GPUTexture **output_tx) /* This algorithm produce the exact dilation radius by dividing it in multiple passes. */ int dilation_radius = 0; while (dilation_radius < dilation_end_radius) { - /* Dilate slight focus only on first iteration. */ - tiles_dilate_slight_focus_ = (dilation_radius == 0) ? 1 : 0; - int remainder = dilation_end_radius - dilation_radius; /* Do not step over any unvisited tile. */ int max_multiplier = dilation_radius + 1; @@ -640,6 +685,7 @@ void DepthOfField::render(GPUTexture **input_tx, GPUTexture **output_tx) SwapChain<TextureFromPool, 2> &color_tx = is_background ? color_bg_tx_ : color_fg_tx_; SwapChain<TextureFromPool, 2> &weight_tx = is_background ? weight_bg_tx_ : weight_fg_tx_; + Framebuffer &scatter_fb = is_background ? scatter_bg_fb_ : scatter_fg_fb_; DRWPass *gather_ps = is_background ? gather_bg_ps_ : gather_fg_ps_; DRWPass *filter_ps = is_background ? filter_bg_ps_ : filter_fg_ps_; DRWPass *scatter_ps = is_background ? scatter_bg_ps_ : scatter_fg_ps_; @@ -666,9 +712,9 @@ void DepthOfField::render(GPUTexture **input_tx, GPUTexture **output_tx) GPU_memory_barrier(GPU_BARRIER_FRAMEBUFFER); - scatter_fb_.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(color_tx.current())); + scatter_fb.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(color_tx.current())); - GPU_framebuffer_bind(scatter_fb_); + GPU_framebuffer_bind(scatter_fb); DRW_draw_pass(scatter_ps); /* Used by scatter pass. */ @@ -694,6 +740,8 @@ void DepthOfField::render(GPUTexture **input_tx, GPUTexture **output_tx) { DRW_stats_group_start("Resolve"); + resolve_stable_color_tx_ = dof_buffer.stabilize_history_tx_; + DRW_draw_pass(resolve_ps_); color_bg_tx_.current().release(); diff --git a/source/blender/draw/engines/eevee_next/eevee_depth_of_field.hh b/source/blender/draw/engines/eevee_next/eevee_depth_of_field.hh index 12d9e7ebd9f..8c291b241bd 100644 --- a/source/blender/draw/engines/eevee_next/eevee_depth_of_field.hh +++ b/source/blender/draw/engines/eevee_next/eevee_depth_of_field.hh @@ -29,6 +29,17 @@ class Instance; /** \name Depth of field * \{ */ +struct DepthOfFieldBuffer { + /** + * Per view history texture for stabilize pass. + * Swapped with stabilize_output_tx_ in order to reuse the previous history during DoF + * processing. + * Note this should be private as its inner working only concerns the Depth Of Field + * implementation. The view itself should not touch it. + */ + Texture stabilize_history_tx_ = {"dof_taa"}; +}; + class DepthOfField { private: class Instance &inst_; @@ -58,6 +69,9 @@ class DepthOfField { Texture reduced_color_tx_ = {"dof_reduced_color"}; /** Stabilization (flicker attenuation) of Color and CoC output of the setup pass. */ + TextureFromPool stabilize_output_tx_ = {"dof_taa"}; + GPUTexture *stabilize_input_ = nullptr; + bool1 stabilize_valid_history_ = false; int3 dispatch_stabilize_size_ = int3(-1); DRWPass *stabilize_ps_ = nullptr; @@ -81,7 +95,6 @@ class DepthOfField { DRWPass *tiles_flatten_ps_ = nullptr; /** Dilates the min & max CoCs to cover maximum COC values. */ - bool1 tiles_dilate_slight_focus_ = false; int tiles_dilate_ring_count_ = -1; int tiles_dilate_ring_width_mul_ = -1; int3 dispatch_tiles_dilate_size_ = int3(-1); @@ -109,11 +122,13 @@ class DepthOfField { DRWPass *filter_bg_ps_ = nullptr; /** Scatter convolution: A quad is emitted for every 4 bright enough half pixels. */ - Framebuffer scatter_fb_ = {"dof_scatter"}; + Framebuffer scatter_fg_fb_ = {"dof_scatter_fg"}; + Framebuffer scatter_bg_fb_ = {"dof_scatter_bg"}; DRWPass *scatter_fg_ps_ = nullptr; DRWPass *scatter_bg_ps_ = nullptr; /** Recombine the results and also perform a slight out of focus gather. */ + GPUTexture *resolve_stable_color_tx_ = nullptr; int3 dispatch_resolve_size_ = int3(-1); DRWPass *resolve_ps_ = nullptr; @@ -122,8 +137,6 @@ class DepthOfField { /** Scene settings that are immutable. */ float user_overblur_; float fx_max_coc_; - /** Use Hiqh Quality (expensive) in-focus gather pass. */ - bool do_hq_slight_focus_; /** Use jittered depth of field where we randomize camera location. */ bool do_jitter_; @@ -136,9 +149,6 @@ class DepthOfField { /** Extent of the input buffer. */ int2 extent_; - /** Reduce pass info. */ - int reduce_steps_; - public: DepthOfField(Instance &inst) : inst_(inst){}; ~DepthOfField(){}; @@ -156,7 +166,7 @@ class DepthOfField { * Will swap input and output texture if rendering happens. The actual output of this function * is in input_tx. */ - void render(GPUTexture **input_tx, GPUTexture **output_tx); + void render(GPUTexture **input_tx, GPUTexture **output_tx, DepthOfFieldBuffer &dof_buffer); bool postfx_enabled() const { @@ -176,8 +186,10 @@ class DepthOfField { void scatter_pass_sync(); void hole_fill_pass_sync(); void resolve_pass_sync(); + + void update_sample_table(); }; /** \} */ -} // namespace blender::eevee
\ No newline at end of file +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_film.cc b/source/blender/draw/engines/eevee_next/eevee_film.cc index e12b434e8e7..b3fbe088471 100644 --- a/source/blender/draw/engines/eevee_next/eevee_film.cc +++ b/source/blender/draw/engines/eevee_next/eevee_film.cc @@ -483,7 +483,7 @@ void Film::update_sample_table() data_.samples_weight_total = 1.0f; data_.samples_len = 1; } - /* NOTE: Threshold determined by hand until we don't hit the assert bellow. */ + /* NOTE: Threshold determined by hand until we don't hit the assert below. */ else if (data_.filter_radius < 2.20f) { /* Small filter Size. */ int closest_index = 0; diff --git a/source/blender/draw/engines/eevee_next/eevee_film.hh b/source/blender/draw/engines/eevee_next/eevee_film.hh index 0d443876d03..3e368782d31 100644 --- a/source/blender/draw/engines/eevee_next/eevee_film.hh +++ b/source/blender/draw/engines/eevee_next/eevee_film.hh @@ -40,7 +40,7 @@ class Film { private: Instance &inst_; - /** Incomming combined buffer with post fx applied (motion blur + depth of field). */ + /** Incoming combined buffer with post FX applied (motion blur + depth of field). */ GPUTexture *combined_final_tx_ = nullptr; /** Main accumulation textures containing every render-pass except depth and combined. */ diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.cc b/source/blender/draw/engines/eevee_next/eevee_shader.cc index 209aff9c168..357f2796a7e 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.cc +++ b/source/blender/draw/engines/eevee_next/eevee_shader.cc @@ -99,23 +99,19 @@ const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_ case DOF_GATHER_FOREGROUND_LUT: return "eevee_depth_of_field_gather_foreground_lut"; case DOF_GATHER_FOREGROUND: - return "eevee_depth_of_field_gather_foreground"; + return "eevee_depth_of_field_gather_foreground_no_lut"; case DOF_GATHER_BACKGROUND_LUT: return "eevee_depth_of_field_gather_background_lut"; case DOF_GATHER_BACKGROUND: - return "eevee_depth_of_field_gather_background"; + return "eevee_depth_of_field_gather_background_no_lut"; case DOF_GATHER_HOLE_FILL: return "eevee_depth_of_field_hole_fill"; case DOF_REDUCE: return "eevee_depth_of_field_reduce"; case DOF_RESOLVE: - return "eevee_depth_of_field_resolve_lq"; - case DOF_RESOLVE_HQ: - return "eevee_depth_of_field_resolve_hq"; + return "eevee_depth_of_field_resolve_no_lut"; case DOF_RESOLVE_LUT: - return "eevee_depth_of_field_resolve_lq_lut"; - case DOF_RESOLVE_LUT_HQ: - return "eevee_depth_of_field_resolve_hq_lut"; + return "eevee_depth_of_field_resolve_lut"; case DOF_SETUP: return "eevee_depth_of_field_setup"; case DOF_SCATTER: diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.hh b/source/blender/draw/engines/eevee_next/eevee_shader.hh index 13f2022e0d7..dd6b9c9d4ab 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader.hh @@ -38,8 +38,6 @@ enum eShaderType { DOF_GATHER_FOREGROUND, DOF_GATHER_HOLE_FILL, DOF_REDUCE, - DOF_RESOLVE_HQ, - DOF_RESOLVE_LUT_HQ, DOF_RESOLVE_LUT, DOF_RESOLVE, DOF_SCATTER, diff --git a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh index 07957cd2c8c..fe36cb1a17c 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh @@ -372,8 +372,6 @@ struct DepthOfFieldData { float scatter_color_threshold; float scatter_neighbor_max_color; int scatter_sprite_per_row; - /** Firefly removing factor. */ - float denoise_factor; /** Number of side the bokeh shape has. */ float bokeh_blades; /** Rotation of the bokeh shape. */ @@ -384,6 +382,9 @@ struct DepthOfFieldData { float coc_abs_max; /** Copy of camera type. */ eCameraType camera_type; + /** Weights of spatial filtering in stabilize pass. Not array to avoid alignment restriction. */ + float4 filter_samples_weight; + float filter_center_weight; /** Max number of sprite in the scatter pass for each ground. */ int scatter_max_rect; diff --git a/source/blender/draw/engines/eevee_next/eevee_view.cc b/source/blender/draw/engines/eevee_next/eevee_view.cc index 68c855b9bc5..c195f68380c 100644 --- a/source/blender/draw/engines/eevee_next/eevee_view.cc +++ b/source/blender/draw/engines/eevee_next/eevee_view.cc @@ -151,7 +151,7 @@ GPUTexture *ShadingView::render_postfx(GPUTexture *input_tx) GPUTexture *output_tx = postfx_tx_; /* Swapping is done internally. Actual output is set to the next input. */ - inst_.depth_of_field.render(&input_tx, &output_tx); + inst_.depth_of_field.render(&input_tx, &output_tx, dof_buffer_); inst_.motion_blur.render(&input_tx, &output_tx); return input_tx; diff --git a/source/blender/draw/engines/eevee_next/eevee_view.hh b/source/blender/draw/engines/eevee_next/eevee_view.hh index ee169bf418e..65f27aba795 100644 --- a/source/blender/draw/engines/eevee_next/eevee_view.hh +++ b/source/blender/draw/engines/eevee_next/eevee_view.hh @@ -44,6 +44,7 @@ class ShadingView { /** Raytracing persistent buffers. Only opaque and refraction can have surface tracing. */ // RaytraceBuffer rt_buffer_opaque_; // RaytraceBuffer rt_buffer_refract_; + DepthOfFieldBuffer dof_buffer_; Framebuffer prepass_fb_; Framebuffer combined_fb_; diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_colorspace_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_colorspace_lib.glsl new file mode 100644 index 00000000000..d5fdaae6fc1 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_colorspace_lib.glsl @@ -0,0 +1,37 @@ + +/* -------------------------------------------------------------------- */ +/** \name YCoCg + * \{ */ + +vec3 colorspace_YCoCg_from_scene_linear(vec3 rgb_color) +{ + const mat3 colorspace_tx = transpose(mat3(vec3(1, 2, 1), /* Y */ + vec3(2, 0, -2), /* Co */ + vec3(-1, 2, -1))); /* Cg */ + return colorspace_tx * rgb_color; +} + +vec4 colorspace_YCoCg_from_scene_linear(vec4 rgba_color) +{ + return vec4(colorspace_YCoCg_from_scene_linear(rgba_color.rgb), rgba_color.a); +} + +vec3 colorspace_scene_linear_from_YCoCg(vec3 ycocg_color) +{ + float Y = ycocg_color.x; + float Co = ycocg_color.y; + float Cg = ycocg_color.z; + + vec3 rgb_color; + rgb_color.r = Y + Co - Cg; + rgb_color.g = Y + Cg; + rgb_color.b = Y - Co - Cg; + return rgb_color * 0.25; +} + +vec4 colorspace_scene_linear_from_YCoCg(vec4 ycocg_color) +{ + return vec4(colorspace_scene_linear_from_YCoCg(ycocg_color.rgb), ycocg_color.a); +} + +/** \} */ diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_accumulator_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_accumulator_lib.glsl index 15c1073309a..57f229feedb 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_accumulator_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_accumulator_lib.glsl @@ -6,6 +6,7 @@ **/ #pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_colorspace_lib.glsl) #pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl) #pragma BLENDER_REQUIRE(eevee_depth_of_field_lib.glsl) @@ -339,10 +340,10 @@ void dof_gather_accumulate_resolve(int total_sample_count, out_weight = 0.0; } /* Same thing for alpha channel. */ - if (out_col.a > 0.99) { + if (out_col.a > 0.993) { out_col.a = 1.0; } - else if (out_col.a < 0.01) { + else if (out_col.a < 0.003) { out_col.a = 0.0; } } @@ -573,71 +574,67 @@ void dof_slight_focus_gather(sampler2D depth_tx, sampler2D bkh_lut_tx, /* Renamed because of ugly macro job. */ float radius, out vec4 out_color, - out float out_weight) + out float out_weight, + out float out_center_coc) { vec2 frag_coord = vec2(gl_GlobalInvocationID.xy) + 0.5; - float noise_offset = sampling_rng_1D_get(SAMPLING_LENS_U); - float noise = no_gather_random ? 0.0 : interlieved_gradient_noise(frag_coord, 3, noise_offset); + vec2 noise_offset = sampling_rng_2D_get(SAMPLING_LENS_U); + vec2 noise = no_gather_random ? vec2(0.0) : + vec2(interlieved_gradient_noise(frag_coord, 3, noise_offset.x), + interlieved_gradient_noise(frag_coord, 5, noise_offset.y)); DofGatherData fg_accum = GATHER_DATA_INIT; DofGatherData bg_accum = GATHER_DATA_INIT; int i_radius = clamp(int(radius), 0, int(dof_layer_threshold)); - const int resolve_ring_density = DOF_SLIGHT_FOCUS_DENSITY; - ivec2 texel = ivec2(gl_GlobalInvocationID.xy); - - bool first_ring = true; - - for (int ring = i_radius; ring > 0; ring--) { - DofGatherData fg_ring = GATHER_DATA_INIT; - DofGatherData bg_ring = GATHER_DATA_INIT; - - int ring_distance = ring; - int ring_sample_count = resolve_ring_density * ring_distance; - for (int sample_id = 0; sample_id < ring_sample_count; sample_id++) { - int s = sample_id * (4 / resolve_ring_density) + - int(noise * float((4 - resolve_ring_density) * ring_distance)); - ivec2 offset = dof_square_ring_sample_offset(ring_distance, s); - float ring_dist = length(vec2(offset)); + const float sample_count_max = float(DOF_SLIGHT_FOCUS_SAMPLE_MAX); + /* Scale by search area. */ + float sample_count = sample_count_max * saturate(sqr(radius) / sqr(dof_layer_threshold)); - DofGatherData pair_data[2]; - for (int i = 0; i < 2; i++) { - ivec2 sample_offset = ((i == 0) ? offset : -offset); - ivec2 sample_texel = texel + sample_offset; - /* OPTI: could precompute the factor. */ - vec2 sample_uv = (vec2(sample_texel) + 0.5) / vec2(textureSize(depth_tx, 0)); - float depth = textureLod(depth_tx, sample_uv, 0.0).r; - pair_data[i].coc = dof_coc_from_depth(dof_buf, sample_uv, depth); - pair_data[i].color = safe_color(textureLod(color_tx, sample_uv, 0.0)); - pair_data[i].dist = ring_dist; - if (DOF_BOKEH_TEXTURE) { - /* Contains subpixel distance to bokeh shape. */ - sample_offset += dof_max_slight_focus_radius; - pair_data[i].dist = texelFetch(bkh_lut_tx, sample_offset, 0).r; - } - pair_data[i].coc = clamp(pair_data[i].coc, -dof_buf.coc_abs_max, dof_buf.coc_abs_max); - } + bool first_ring = true; - float bordering_radius = ring_dist + 0.5; - const float isect_mul = 1.0; - dof_gather_accumulate_sample_pair( - pair_data, bordering_radius, isect_mul, first_ring, false, false, bg_ring, bg_accum); + for (float s = 0.0; s < sample_count; s++) { + vec2 rand2 = fract(hammersley_2d(s, sample_count) + noise); + vec2 offset = sample_disk(rand2); + float ring_dist = sqrt(rand2.y); + DofGatherData pair_data[2]; + for (int i = 0; i < 2; i++) { + vec2 sample_offset = ((i == 0) ? offset : -offset); + /* OPTI: could precompute the factor. */ + vec2 sample_uv = (frag_coord + sample_offset) / vec2(textureSize(depth_tx, 0)); + float depth = textureLod(depth_tx, sample_uv, 0.0).r; + pair_data[i].coc = dof_coc_from_depth(dof_buf, sample_uv, depth); + pair_data[i].color = safe_color(textureLod(color_tx, sample_uv, 0.0)); + pair_data[i].dist = ring_dist; if (DOF_BOKEH_TEXTURE) { - /* Swap distances in order to flip bokeh shape for foreground. */ - float tmp = pair_data[0].dist; - pair_data[0].dist = pair_data[1].dist; - pair_data[1].dist = tmp; + /* Contains subpixel distance to bokeh shape. */ + ivec2 lut_texel = ivec2(round(sample_offset)) + dof_max_slight_focus_radius; + pair_data[i].dist = texelFetch(bkh_lut_tx, lut_texel, 0).r; } - dof_gather_accumulate_sample_pair( - pair_data, bordering_radius, isect_mul, first_ring, false, true, fg_ring, fg_accum); + pair_data[i].coc = clamp(pair_data[i].coc, -dof_buf.coc_abs_max, dof_buf.coc_abs_max); } - dof_gather_accumulate_sample_ring( - bg_ring, ring_sample_count * 2, first_ring, false, false, bg_accum); - dof_gather_accumulate_sample_ring( - fg_ring, ring_sample_count * 2, first_ring, false, true, fg_accum); + float bordering_radius = ring_dist + 0.5; + const float isect_mul = 1.0; + DofGatherData bg_ring = GATHER_DATA_INIT; + dof_gather_accumulate_sample_pair( + pair_data, bordering_radius, isect_mul, first_ring, false, false, bg_ring, bg_accum); + /* Treat each sample as a ring. */ + dof_gather_accumulate_sample_ring(bg_ring, 2, first_ring, false, false, bg_accum); + + if (DOF_BOKEH_TEXTURE) { + /* Swap distances in order to flip bokeh shape for foreground. */ + float tmp = pair_data[0].dist; + pair_data[0].dist = pair_data[1].dist; + pair_data[1].dist = tmp; + } + DofGatherData fg_ring = GATHER_DATA_INIT; + dof_gather_accumulate_sample_pair( + pair_data, bordering_radius, isect_mul, first_ring, false, true, fg_ring, fg_accum); + /* Treat each sample as a ring. */ + dof_gather_accumulate_sample_ring(fg_ring, 2, first_ring, false, true, fg_accum); first_ring = false; } @@ -650,6 +647,8 @@ void dof_slight_focus_gather(sampler2D depth_tx, center_data.coc = clamp(center_data.coc, -dof_buf.coc_abs_max, dof_buf.coc_abs_max); center_data.dist = 0.0; + out_center_coc = center_data.coc; + /* Slide 38. */ float bordering_radius = 0.5; @@ -662,11 +661,11 @@ void dof_slight_focus_gather(sampler2D depth_tx, float bg_weight, fg_weight; vec2 unused_occlusion; - int total_sample_count = dof_gather_total_sample_count(i_radius + 1, resolve_ring_density); + int total_sample_count = int(sample_count) * 2 + 1; dof_gather_accumulate_resolve(total_sample_count, bg_accum, bg_col, bg_weight, unused_occlusion); dof_gather_accumulate_resolve(total_sample_count, fg_accum, fg_col, fg_weight, unused_occlusion); - /* Fix weighting issues on perfectly focus > slight focus transitionning areas. */ + /* Fix weighting issues on perfectly focus to slight focus transitionning areas. */ if (abs(center_data.coc) < 0.5) { bg_col = center_data.color; bg_weight = 1.0; diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_filter_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_filter_comp.glsl index 88ecaab6a00..c5c0e210109 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_filter_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_filter_comp.glsl @@ -15,8 +15,9 @@ struct FilterSample { /** \name Pixel cache. * \{ */ -shared vec4 color_cache[10][10]; -shared float weight_cache[10][10]; +const uint cache_size = gl_WorkGroupSize.x + 2; +shared vec4 color_cache[cache_size][cache_size]; +shared float weight_cache[cache_size][cache_size]; void cache_init() { @@ -40,11 +41,12 @@ void cache_init() */ ivec2 texel = ivec2(gl_GlobalInvocationID.xy) - 1; - for (int y = 0; y < 2; y++) { - for (int x = 0; x < 2; x++) { - if (all(lessThan(gl_LocalInvocationID.xy, uvec2(5)))) { - ivec2 cache_texel = ivec2(gl_LocalInvocationID.xy) + ivec2(x, y) * 5; - ivec2 load_texel = clamp(texel + ivec2(x, y) * 5, ivec2(0), textureSize(color_tx, 0) - 1); + if (all(lessThan(gl_LocalInvocationID.xy, uvec2(cache_size / 2u)))) { + for (int y = 0; y < 2; y++) { + for (int x = 0; x < 2; x++) { + ivec2 offset = ivec2(x, y) * ivec2(cache_size / 2u); + ivec2 cache_texel = ivec2(gl_LocalInvocationID.xy) + offset; + ivec2 load_texel = clamp(texel + offset, ivec2(0), textureSize(color_tx, 0) - 1); color_cache[cache_texel.y][cache_texel.x] = texelFetch(color_tx, load_texel, 0); weight_cache[cache_texel.y][cache_texel.x] = texelFetch(weight_tx, load_texel, 0).r; @@ -130,7 +132,11 @@ FilterLmhResult filter_lmh(FilterSample s1, FilterSample s2, FilterSample s3) void main() { - /* OPTI(fclem) Could early return on some tiles. */ + /** + * NOTE: We can **NOT** optimize by discarding some tiles as the result is sampled using bilinear + * filtering in the resolve pass. Not outputing to a tile means that border texels have undefined + * value and tile border will be noticeable in the final image. + */ cache_init(); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_lib.glsl index 8a5d11ddc56..f89da641446 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_lib.glsl @@ -180,24 +180,12 @@ struct CocTile { float fg_min_coc; float fg_max_coc; float fg_max_intersectable_coc; - float fg_slight_focus_max_coc; float bg_min_coc; float bg_max_coc; float bg_min_intersectable_coc; }; -struct CocTilePrediction { - bool do_foreground; - bool do_slight_focus; - bool do_focus; - bool do_background; - bool do_hole_fill; -}; - /* WATCH: Might have to change depending on the texture format. */ -const float dof_tile_defocus = 0.25; -const float dof_tile_focus = 0.0; -const float dof_tile_mixed = 0.75; const float dof_tile_large_coc = 1024.0; /* Init a CoC tile for reduction algorithms. */ @@ -207,20 +195,18 @@ CocTile dof_coc_tile_init() tile.fg_min_coc = 0.0; tile.fg_max_coc = -dof_tile_large_coc; tile.fg_max_intersectable_coc = dof_tile_large_coc; - tile.fg_slight_focus_max_coc = -1.0; tile.bg_min_coc = dof_tile_large_coc; tile.bg_max_coc = 0.0; tile.bg_min_intersectable_coc = dof_tile_large_coc; return tile; } -CocTile dof_coc_tile_unpack(vec4 fg, vec3 bg) +CocTile dof_coc_tile_unpack(vec3 fg, vec3 bg) { CocTile tile; tile.fg_min_coc = -fg.x; tile.fg_max_coc = -fg.y; tile.fg_max_intersectable_coc = -fg.z; - tile.fg_slight_focus_max_coc = fg.w; tile.bg_min_coc = bg.x; tile.bg_max_coc = bg.y; tile.bg_min_intersectable_coc = bg.z; @@ -231,15 +217,14 @@ CocTile dof_coc_tile_unpack(vec4 fg, vec3 bg) * parameters. Workaround by using defines. */ #define dof_coc_tile_load(tiles_fg_img_, tiles_bg_img_, texel_) \ dof_coc_tile_unpack( \ - imageLoad(tiles_fg_img_, clamp(texel_, ivec2(0), imageSize(tiles_fg_img_) - 1)), \ + imageLoad(tiles_fg_img_, clamp(texel_, ivec2(0), imageSize(tiles_fg_img_) - 1)).xyz, \ imageLoad(tiles_bg_img_, clamp(texel_, ivec2(0), imageSize(tiles_bg_img_) - 1)).xyz) -void dof_coc_tile_pack(CocTile tile, out vec4 out_fg, out vec3 out_bg) +void dof_coc_tile_pack(CocTile tile, out vec3 out_fg, out vec3 out_bg) { out_fg.x = -tile.fg_min_coc; out_fg.y = -tile.fg_max_coc; out_fg.z = -tile.fg_max_intersectable_coc; - out_fg.w = tile.fg_slight_focus_max_coc; out_bg.x = tile.bg_min_coc; out_bg.y = tile.bg_max_coc; out_bg.z = tile.bg_min_intersectable_coc; @@ -247,10 +232,10 @@ void dof_coc_tile_pack(CocTile tile, out vec4 out_fg, out vec3 out_bg) #define dof_coc_tile_store(tiles_fg_img_, tiles_bg_img_, texel_out_, tile_data_) \ if (true) { \ - vec4 out_fg; \ + vec3 out_fg; \ vec3 out_bg; \ dof_coc_tile_pack(tile_data_, out_fg, out_bg); \ - imageStore(tiles_fg_img_, texel_out_, out_fg); \ + imageStore(tiles_fg_img_, texel_out_, out_fg.xyzz); \ imageStore(tiles_bg_img_, texel_out_, out_bg.xyzz); \ } @@ -269,6 +254,18 @@ bool dof_do_fast_gather(float max_absolute_coc, float min_absolute_coc, const bo return (max_absolute_coc - min_absolute_coc) < (DOF_FAST_GATHER_COC_ERROR * max_absolute_coc); } +struct CocTilePrediction { + bool do_foreground; + bool do_slight_focus; + bool do_focus; + bool do_background; + bool do_hole_fill; +}; + +/** + * Using the tile CoC infos, predict which convolutions are required and the ones that can be + * skipped. + */ CocTilePrediction dof_coc_tile_prediction_get(CocTile tile) { /* Based on tile value, predict what pass we need to load. */ @@ -277,15 +274,13 @@ CocTilePrediction dof_coc_tile_prediction_get(CocTile tile) predict.do_foreground = (-tile.fg_min_coc > dof_layer_threshold - dof_layer_offset_fg); bool fg_fully_opaque = predict.do_foreground && dof_do_fast_gather(-tile.fg_min_coc, -tile.fg_max_coc, true); - - predict.do_slight_focus = !fg_fully_opaque && (tile.fg_slight_focus_max_coc >= 0.5); - predict.do_focus = !fg_fully_opaque && (tile.fg_slight_focus_max_coc == dof_tile_focus); - - predict.do_background = !predict.do_focus && !fg_fully_opaque && + predict.do_background = !fg_fully_opaque && (tile.bg_max_coc > dof_layer_threshold - dof_layer_offset); bool bg_fully_opaque = predict.do_background && dof_do_fast_gather(-tile.bg_max_coc, tile.bg_min_coc, false); - predict.do_hole_fill = !predict.do_focus && !fg_fully_opaque && -tile.fg_min_coc > 0.0; + predict.do_hole_fill = !fg_fully_opaque && -tile.fg_min_coc > 0.0; + predict.do_focus = !fg_fully_opaque; + predict.do_slight_focus = !fg_fully_opaque; #if 0 /* Debug */ predict.do_foreground = predict.do_background = predict.do_hole_fill = true; @@ -293,20 +288,6 @@ CocTilePrediction dof_coc_tile_prediction_get(CocTile tile) return predict; } -/* Special function to return the correct max value of 2 slight focus coc. */ -float dof_coc_max_slight_focus(float coc1, float coc2) -{ - /* Do not consider values below 0.5 for expansion as they are "encoded". - * See setup pass shader for more infos. */ - if ((coc1 == dof_tile_defocus && coc2 == dof_tile_focus) || - (coc1 == dof_tile_focus && coc2 == dof_tile_defocus)) { - /* Tile where completely out of focus and in focus are both present. - * Consider as very slightly out of focus. */ - return dof_tile_mixed; - } - return max(coc1, coc2); -} - /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_reduce_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_reduce_comp.glsl index 88a577a1c3c..c757e8304ac 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_reduce_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_reduce_comp.glsl @@ -73,9 +73,10 @@ float fast_luma(vec3 color) return (2.0 * color.g) + color.r + color.b; } -shared vec4 color_cache[8][8]; -shared float coc_cache[8][8]; -shared float do_scatter[8][8]; +const uint cache_size = gl_WorkGroupSize.x; +shared vec4 color_cache[cache_size][cache_size]; +shared float coc_cache[cache_size][cache_size]; +shared float do_scatter[cache_size][cache_size]; void main() { @@ -83,7 +84,7 @@ void main() uvec2 texel_local = gl_LocalInvocationID.xy; /* Increase readablility. */ #define LOCAL_INDEX texel_local.y][texel_local.x -#define LOCAL_OFFSET(x_, y_) texel_local.y + y_][texel_local.x + x_ +#define LOCAL_OFFSET(x_, y_) texel_local.y + (y_)][texel_local.x + (x_) /* Load level 0 into cache. */ color_cache[LOCAL_INDEX] = imageLoad(inout_color_lod0_img, texel); @@ -200,22 +201,23 @@ void main() imageStore(inout_color_lod0_img, texel, color_cache[LOCAL_INDEX]); /* Recursive downsample. */ - for (uint i = 1u; i < DOF_MIP_MAX; i++) { + for (uint i = 1u; i < DOF_MIP_COUNT; i++) { barrier(); - if (all(lessThan(gl_LocalInvocationID.xy, uvec2(1u << (DOF_MIP_MAX - 1u - i))))) { - uvec2 texel_local = gl_LocalInvocationID.xy << i; + uint mask = ~(~0u << i); + if (all(equal(gl_LocalInvocationID.xy & mask, uvec2(0)))) { + uint ofs = 1u << (i - 1u); /* TODO(fclem): Could use wave shuffle intrinsics to avoid LDS as suggested by the paper. */ vec4 coc4; - coc4.x = coc_cache[LOCAL_OFFSET(0, 1)]; - coc4.y = coc_cache[LOCAL_OFFSET(1, 1)]; - coc4.z = coc_cache[LOCAL_OFFSET(1, 0)]; + coc4.x = coc_cache[LOCAL_OFFSET(0, ofs)]; + coc4.y = coc_cache[LOCAL_OFFSET(ofs, ofs)]; + coc4.z = coc_cache[LOCAL_OFFSET(ofs, 0)]; coc4.w = coc_cache[LOCAL_OFFSET(0, 0)]; vec4 colors[4]; - colors[0] = color_cache[LOCAL_OFFSET(0, 1)]; - colors[1] = color_cache[LOCAL_OFFSET(1, 1)]; - colors[2] = color_cache[LOCAL_OFFSET(1, 0)]; + colors[0] = color_cache[LOCAL_OFFSET(0, ofs)]; + colors[1] = color_cache[LOCAL_OFFSET(ofs, ofs)]; + colors[2] = color_cache[LOCAL_OFFSET(ofs, 0)]; colors[3] = color_cache[LOCAL_OFFSET(0, 0)]; vec4 weights = dof_bilateral_coc_weights(coc4); @@ -226,8 +228,7 @@ void main() color_cache[LOCAL_INDEX] = weighted_sum_array(colors, weights); coc_cache[LOCAL_INDEX] = dot(coc4, weights); - ivec2 texel = ivec2(gl_WorkGroupID.xy * (gl_WorkGroupSize.xy >> i) + - gl_LocalInvocationID.xy); + ivec2 texel = ivec2(gl_GlobalInvocationID.xy >> i); if (i == 1) { imageStore(out_color_lod1_img, texel, color_cache[LOCAL_INDEX]); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_resolve_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_resolve_comp.glsl index 3bb89bd7e0f..d21f6d69541 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_resolve_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_resolve_comp.glsl @@ -10,22 +10,108 @@ #pragma BLENDER_REQUIRE(eevee_depth_of_field_accumulator_lib.glsl) +shared uint shared_max_slight_focus_abs_coc; + +/** + * Returns The max CoC in the Slight Focus range inside this compute tile. + */ +float dof_slight_focus_coc_tile_get(vec2 frag_coord) +{ + if (all(equal(gl_LocalInvocationID, uvec3(0)))) { + shared_max_slight_focus_abs_coc = floatBitsToUint(0.0); + } + barrier(); + + float local_abs_max = 0.0; + /* Sample in a cross (X) pattern. This covers all pixels over the whole tile, as long as + * dof_max_slight_focus_radius is less than the group size. */ + for (int i = 0; i < 4; i++) { + vec2 sample_uv = (frag_coord + quad_offsets[i] * 2.0 * dof_max_slight_focus_radius) / + vec2(textureSize(color_tx, 0)); + float coc = dof_coc_from_depth(dof_buf, sample_uv, textureLod(depth_tx, sample_uv, 0.0).r); + coc = clamp(coc, -dof_buf.coc_abs_max, dof_buf.coc_abs_max); + if (abs(coc) < dof_max_slight_focus_radius) { + local_abs_max = max(local_abs_max, abs(coc)); + } + } + /* Use atomic reduce operation. */ + atomicMax(shared_max_slight_focus_abs_coc, floatBitsToUint(local_abs_max)); + /* "Broadcast" result accross all threads. */ + barrier(); + + return uintBitsToFloat(shared_max_slight_focus_abs_coc); +} + +vec3 dof_neighborhood_clamp(vec2 frag_coord, vec3 color, float center_coc, float weight) +{ + /* Stabilize color by clamping with the stable half res neighboorhood. */ + vec3 neighbor_min, neighbor_max; + const vec2 corners[4] = vec2[4](vec2(-1, -1), vec2(1, -1), vec2(-1, 1), vec2(1, 1)); + for (int i = 0; i < 4; i++) { + /** + * Visit the 4 half-res texels around (and containing) the fullres texel. + * Here a diagram of a fullscreen texel (f) in the bottom left corner of a half res texel. + * We sample the stable half-resolution texture at the 4 location denoted by (h). + * ┌───────┬───────┐ + * │ h │ h │ + * │ │ │ + * │ │ f │ + * ├───────┼───────┤ + * │ h │ h │ + * │ │ │ + * │ │ │ + * └───────┴───────┘ + */ + vec2 uv_sample = ((frag_coord + corners[i]) * 0.5) / vec2(textureSize(stable_color_tx, 0)); + /* Reminder: The content of this buffer is YCoCg + CoC. */ + vec3 ycocg_sample = textureLod(stable_color_tx, uv_sample, 0.0).rgb; + neighbor_min = (i == 0) ? ycocg_sample : min(neighbor_min, ycocg_sample); + neighbor_max = (i == 0) ? ycocg_sample : max(neighbor_max, ycocg_sample); + } + /* Pad the bounds in the near in focus region to get back a bit of detail. */ + float padding = 0.125 * saturate(1.0 - sqr(center_coc) / sqr(8.0)); + neighbor_max += abs(neighbor_min) * padding; + neighbor_min -= abs(neighbor_min) * padding; + /* Progressively apply the clamp to avoid harsh transition. Also mask by weight. */ + float fac = saturate(sqr(center_coc) * 4.0) * weight; + /* Clamp in YCoCg space to avoid too much color drift. */ + color = colorspace_YCoCg_from_scene_linear(color); + color = mix(color, clamp(color, neighbor_min, neighbor_max), fac); + color = colorspace_scene_linear_from_YCoCg(color); + return color; +} + void main() { vec2 frag_coord = vec2(gl_GlobalInvocationID.xy) + 0.5; ivec2 tile_co = ivec2(frag_coord / float(DOF_TILES_SIZE * 2)); + CocTile coc_tile = dof_coc_tile_load(in_tiles_fg_img, in_tiles_bg_img, tile_co); CocTilePrediction prediction = dof_coc_tile_prediction_get(coc_tile); + vec2 uv = frag_coord / vec2(textureSize(color_tx, 0)); + vec2 uv_halfres = (frag_coord * 0.5) / vec2(textureSize(color_bg_tx, 0)); + + float slight_focus_max_coc = 0.0; + if (prediction.do_slight_focus) { + slight_focus_max_coc = dof_slight_focus_coc_tile_get(frag_coord); + prediction.do_slight_focus = slight_focus_max_coc >= 0.5; + if (prediction.do_slight_focus) { + prediction.do_focus = false; + } + } + + if (prediction.do_focus) { + float center_coc = (dof_coc_from_depth(dof_buf, uv, textureLod(depth_tx, uv, 0.0).r)); + prediction.do_focus = abs(center_coc) <= 0.5; + } + vec4 out_color = vec4(0.0); float weight = 0.0; vec4 layer_color; float layer_weight; - vec2 uv = frag_coord / vec2(textureSize(color_tx, 0)); - vec2 uv_halfres = (frag_coord * 0.5) / vec2(textureSize(color_bg_tx, 0)); - if (!no_hole_fill_pass && prediction.do_hole_fill) { layer_color = textureLod(color_hole_fill_tx, uv_halfres, 0.0); layer_weight = textureLod(weight_hole_fill_tx, uv_halfres, 0.0).r; @@ -48,15 +134,20 @@ void main() } if (!no_slight_focus_pass && prediction.do_slight_focus) { + float center_coc; dof_slight_focus_gather(depth_tx, color_tx, bokeh_lut_tx, - coc_tile.fg_slight_focus_max_coc, + slight_focus_max_coc, layer_color, - layer_weight); + layer_weight, + center_coc); + /* Composite slight defocus. */ out_color = out_color * (1.0 - layer_weight) + layer_color; weight = weight * (1.0 - layer_weight) + layer_weight; + + out_color.rgb = dof_neighborhood_clamp(frag_coord, out_color.rgb, center_coc, layer_weight); } if (!no_focus_pass && prediction.do_focus) { @@ -79,7 +170,7 @@ void main() out_color.a = 1.0; } - if (debug_resolve_perf && coc_tile.fg_slight_focus_max_coc >= 0.5) { + if (debug_resolve_perf && prediction.do_slight_focus) { out_color.rgb *= vec3(1.0, 0.1, 0.1); } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_setup_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_setup_comp.glsl index 99fc42f5d98..c017a5aa965 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_setup_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_setup_comp.glsl @@ -15,26 +15,6 @@ #pragma BLENDER_REQUIRE(common_view_lib.glsl) #pragma BLENDER_REQUIRE(eevee_depth_of_field_lib.glsl) -float dof_abs_max_slight_of_focus_coc(vec4 cocs) -{ - /* Clamp to 0.5 if full in defocus to differentiate full focus tiles with coc == 0.0. - * This enables an optimization in the resolve pass. */ - const vec4 threshold = vec4(dof_layer_threshold + dof_layer_offset); - cocs = abs(cocs); - bvec4 defocus = greaterThan(cocs, threshold); - bvec4 focus = lessThanEqual(cocs, vec4(0.5)); - if (any(defocus) && any(focus)) { - /* For the same reason as in the flatten pass. This is a case we cannot optimize for. */ - cocs = mix(cocs, vec4(dof_tile_mixed), focus); - cocs = mix(cocs, vec4(dof_tile_mixed), defocus); - } - else { - cocs = mix(cocs, vec4(dof_tile_focus), focus); - cocs = mix(cocs, vec4(dof_tile_defocus), defocus); - } - return max_v4(cocs); -} - void main() { vec2 fullres_texel_size = 1.0 / vec2(textureSize(color_tx, 0).xy); @@ -61,8 +41,6 @@ void main() vec4 out_color = weighted_sum_array(colors, weights); imageStore(out_color_img, out_texel, out_color); - vec2 out_coc; - out_coc.x = dot(cocs, weights); - out_coc.y = dof_abs_max_slight_of_focus_coc(cocs); - imageStore(out_coc_img, out_texel, out_coc.xyxy); + float out_coc = dot(cocs, weights); + imageStore(out_coc_img, out_texel, vec4(out_coc)); } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_stabilize_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_stabilize_comp.glsl index ac371f76395..b22af0e88f0 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_stabilize_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_stabilize_comp.glsl @@ -2,8 +2,11 @@ /** * Temporal Stabilization of the Depth of field input. * Corresponds to the TAA pass in the paper. + * We actually duplicate the TAA logic but with a few changes: + * - We run this pass at half resolution. + * - We store CoC instead of Opacity in the alpha channel of the history. * - * TODO: This pass needs a cleanup / improvement using much better TAA. + * This is and adaption of the code found in eevee_film_lib.glsl * * Inputs: * - Output of setup pass (halfres). @@ -11,54 +14,354 @@ * - Stabilized Color and CoC (halfres). **/ +#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_colorspace_lib.glsl) #pragma BLENDER_REQUIRE(eevee_depth_of_field_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_velocity_lib.glsl) -float fast_luma(vec3 color) +struct DofSample { + vec4 color; + float coc; +}; + +/* -------------------------------------------------------------------- */ +/** \name LDS Cache + * \{ */ + +const uint cache_size = gl_WorkGroupSize.x + 2; +shared vec4 color_cache[cache_size][cache_size]; +shared float coc_cache[cache_size][cache_size]; +/* Need 2 pixel border for depth. */ +const uint cache_depth_size = gl_WorkGroupSize.x + 4; +shared float depth_cache[cache_depth_size][cache_depth_size]; + +void dof_cache_init() +{ + /** + * Load enough values into LDS to perform the filter. + * + * ┌──────────────────────────────┐ + * │ │ < Border texels that needs to be loaded. + * │ x x x x x x x x │ ─┐ + * │ x x x x x x x x │ │ + * │ x x x x x x x x │ │ + * │ x x x x x x x x │ │ Thread Group Size 8x8. + * │ L L L L L x x x x │ │ + * │ L L L L L x x x x │ │ + * │ L L L L L x x x x │ │ + * │ L L L L L x x x x │ ─┘ + * │ L L L L L │ < Border texels that needs to be loaded. + * └──────────────────────────────┘ + * └───────────┘ + * Load using 5x5 threads. + */ + + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); + for (int y = 0; y < 2; y++) { + for (int x = 0; x < 2; x++) { + /* 1 Pixel border. */ + if (all(lessThan(gl_LocalInvocationID.xy, uvec2(cache_size / 2u)))) { + ivec2 offset = ivec2(x, y) * ivec2(cache_size / 2u); + ivec2 cache_texel = ivec2(gl_LocalInvocationID.xy) + offset; + ivec2 load_texel = clamp(texel + offset - 1, ivec2(0), textureSize(color_tx, 0) - 1); + + vec4 color = texelFetch(color_tx, load_texel, 0); + color_cache[cache_texel.y][cache_texel.x] = colorspace_YCoCg_from_scene_linear(color); + coc_cache[cache_texel.y][cache_texel.x] = texelFetch(coc_tx, load_texel, 0).x; + } + /* 2 Pixels border. */ + if (all(lessThan(gl_LocalInvocationID.xy, uvec2(cache_depth_size / 2u)))) { + ivec2 offset = ivec2(x, y) * ivec2(cache_depth_size / 2u); + ivec2 cache_texel = ivec2(gl_LocalInvocationID.xy) + offset; + /* Depth is fullres. Load every 2 pixels. */ + ivec2 load_texel = clamp((texel + offset - 2) * 2, ivec2(0), textureSize(depth_tx, 0) - 1); + + depth_cache[cache_texel.y][cache_texel.x] = texelFetch(depth_tx, load_texel, 0).x; + } + } + } + barrier(); +} + +/* Note: Sample color space is already in YCoCg space. */ +DofSample dof_fetch_input_sample(ivec2 offset) +{ + ivec2 coord = offset + 1 + ivec2(gl_LocalInvocationID.xy); + return DofSample(color_cache[coord.y][coord.x], coc_cache[coord.y][coord.x]); +} + +float dof_fetch_half_depth(ivec2 offset) +{ + ivec2 coord = offset + 2 + ivec2(gl_LocalInvocationID.xy); + return depth_cache[coord.y][coord.x]; +} + +/** \} */ + +float dof_luma_weight(float luma) { - return (2.0 * color.g) + color.r + color.b; + /* Slide 20 of "High Quality Temporal Supersampling" by Brian Karis at Siggraph 2014. */ + /* To preserve more details in dark areas, we use a bigger bias. */ + const float exposure_scale = 1.0; /* TODO. */ + return 1.0 / (4.0 + luma * exposure_scale); } -/* Lightweight version of neighborhood clamping found in TAA. */ -vec3 dof_neighborhood_clamping(vec3 color) +float dof_bilateral_weight(float reference_coc, float sample_coc) { - vec2 texel_size = 1.0 / vec2(textureSize(color_tx, 0)); - vec2 uv = (vec2(gl_GlobalInvocationID.xy) + 0.5) * texel_size; - vec4 ofs = vec4(-1, 1, -1, 1) * texel_size.xxyy; + /* NOTE: The difference between the cocs should be inside a abs() function, + * but we follow UE4 implementation to improve how dithered transparency looks (see slide 19). + * Effectively bleed background into foreground. + * Compared to dof_bilateral_coc_weights() this saturates as 2x the reference CoC. */ + return saturate(1.0 - (sample_coc - reference_coc) / max(1.0, abs(reference_coc))); +} - /* Luma clamping. 3x3 square neighborhood. */ - float c00 = fast_luma(textureLod(color_tx, uv + ofs.xz, 0.0).rgb); - float c01 = fast_luma(textureLod(color_tx, uv + ofs.xz * vec2(1.0, 0.0), 0.0).rgb); - float c02 = fast_luma(textureLod(color_tx, uv + ofs.xw, 0.0).rgb); +DofSample dof_spatial_filtering() +{ + /* Plus (+) shape offsets. */ + const ivec2 plus_offsets[4] = ivec2[4](ivec2(-1, 0), ivec2(0, -1), ivec2(1, 0), ivec2(0, 1)); + DofSample center = dof_fetch_input_sample(ivec2(0)); + DofSample accum = DofSample(vec4(0.0), 0.0); + float accum_weight = 0.0; + for (int i = 0; i < 4; i++) { + DofSample samp = dof_fetch_input_sample(plus_offsets[i]); + float weight = dof_buf.filter_samples_weight[i] * dof_luma_weight(samp.color.x) * + dof_bilateral_weight(center.coc, samp.coc); - float c10 = fast_luma(textureLod(color_tx, uv + ofs.xz * vec2(0.0, 1.0), 0.0).rgb); - float c11 = fast_luma(color); - float c12 = fast_luma(textureLod(color_tx, uv + ofs.xw * vec2(0.0, 1.0), 0.0).rgb); + accum.color += samp.color * weight; + accum.coc += samp.coc * weight; + accum_weight += weight; + } + /* Accumulate center sample last as it does not need bilateral_weights. */ + float weight = dof_buf.filter_center_weight * dof_luma_weight(center.color.x); + accum.color += center.color * weight; + accum.coc += center.coc * weight; + accum_weight += weight; - float c20 = fast_luma(textureLod(color_tx, uv + ofs.yz, 0.0).rgb); - float c21 = fast_luma(textureLod(color_tx, uv + ofs.yz * vec2(1.0, 0.0), 0.0).rgb); - float c22 = fast_luma(textureLod(color_tx, uv + ofs.yw, 0.0).rgb); + float rcp_weight = 1.0 / accum_weight; + accum.color *= rcp_weight; + accum.coc *= rcp_weight; + return accum; +} - float avg_luma = avg8(c00, c01, c02, c10, c12, c20, c21, c22); - float max_luma = max8(c00, c01, c02, c10, c12, c20, c21, c22); +struct DofNeighborhoodMinMax { + DofSample min; + DofSample max; +}; - float upper_bound = mix(max_luma, avg_luma, dof_buf.denoise_factor); - upper_bound = mix(c11, upper_bound, dof_buf.denoise_factor); +/* Return history clipping bounding box in YCoCg color space. */ +DofNeighborhoodMinMax dof_neighbor_boundbox() +{ + /* Plus (+) shape offsets. */ + const ivec2 plus_offsets[4] = ivec2[4](ivec2(-1, 0), ivec2(0, -1), ivec2(1, 0), ivec2(0, 1)); + /** + * Simple bounding box calculation in YCoCg as described in: + * "High Quality Temporal Supersampling" by Brian Karis at Siggraph 2014 + */ + DofSample min_c = dof_fetch_input_sample(ivec2(0)); + DofSample max_c = min_c; + for (int i = 0; i < 4; i++) { + DofSample samp = dof_fetch_input_sample(plus_offsets[i]); + min_c.color = min(min_c.color, samp.color); + max_c.color = max(max_c.color, samp.color); + min_c.coc = min(min_c.coc, samp.coc); + max_c.coc = max(max_c.coc, samp.coc); + } + /* (Slide 32) Simple clamp to min/max of 8 neighbors results in 3x3 box artifacts. + * Round bbox shape by averaging 2 different min/max from 2 different neighborhood. */ + DofSample min_c_3x3 = min_c; + DofSample max_c_3x3 = max_c; + const ivec2 corners[4] = ivec2[4](ivec2(-1, -1), ivec2(1, -1), ivec2(-1, 1), ivec2(1, 1)); + for (int i = 0; i < 4; i++) { + DofSample samp = dof_fetch_input_sample(corners[i]); + min_c_3x3.color = min(min_c_3x3.color, samp.color); + max_c_3x3.color = max(max_c_3x3.color, samp.color); + min_c_3x3.coc = min(min_c_3x3.coc, samp.coc); + max_c_3x3.coc = max(max_c_3x3.coc, samp.coc); + } + min_c.color = (min_c.color + min_c_3x3.color) * 0.5; + max_c.color = (max_c.color + max_c_3x3.color) * 0.5; + min_c.coc = (min_c.coc + min_c_3x3.coc) * 0.5; + max_c.coc = (max_c.coc + max_c_3x3.coc) * 0.5; - float clamped_luma = min(upper_bound, c11); + return DofNeighborhoodMinMax(min_c, max_c); +} - return color * clamped_luma * safe_rcp(c11); +/* Returns motion in pixel space to retrieve the pixel history. */ +vec2 dof_pixel_history_motion_vector(ivec2 texel_sample) +{ + /** + * Dilate velocity by using the nearest pixel in a cross pattern. + * "High Quality Temporal Supersampling" by Brian Karis at Siggraph 2014 (Slide 27) + */ + const ivec2 corners[4] = ivec2[4](ivec2(-2, -2), ivec2(2, -2), ivec2(-2, 2), ivec2(2, 2)); + float min_depth = dof_fetch_half_depth(ivec2(0)); + ivec2 nearest_texel = ivec2(0); + for (int i = 0; i < 4; i++) { + float depth = dof_fetch_half_depth(corners[i]); + if (min_depth > depth) { + min_depth = depth; + nearest_texel = corners[i]; + } + } + /* Convert to full resolution buffer pixel. */ + ivec2 velocity_texel = (texel_sample + nearest_texel) * 2; + velocity_texel = clamp(velocity_texel, ivec2(0), textureSize(velocity_tx, 0).xy - 1); + vec4 vector = velocity_resolve(velocity_tx, velocity_texel, min_depth); + /* Transform to **half** pixel space. */ + return vector.xy * vec2(textureSize(color_tx, 0)); +} + +/* Load color using a special filter to avoid loosing detail. + * \a texel is sample position with subpixel accuracy. */ +DofSample dof_sample_history(vec2 input_texel) +{ +#if 1 /* Bilinar. */ + vec2 uv = vec2(input_texel + 0.5) / textureSize(in_history_tx, 0); + vec4 color = textureLod(in_history_tx, uv, 0.0); + +#else /* Catmull Rom interpolation. 5 Bilinear Taps. */ + vec2 center_texel; + vec2 inter_texel = modf(input_texel, center_texel); + vec2 weights[4]; + film_get_catmull_rom_weights(inter_texel, weights); + + /** + * Use optimized version by leveraging bilinear filtering from hardware sampler and by removing + * corner taps. + * From "Filmic SMAA" by Jorge Jimenez at Siggraph 2016 + * http://advances.realtimerendering.com/s2016/Filmic%20SMAA%20v7.pptx + */ + center_texel += 0.5; + + /* Slide 92. */ + vec2 weight_12 = weights[1] + weights[2]; + vec2 uv_12 = (center_texel + weights[2] / weight_12) * film_buf.extent_inv; + vec2 uv_0 = (center_texel - 1.0) * film_buf.extent_inv; + vec2 uv_3 = (center_texel + 2.0) * film_buf.extent_inv; + + vec4 color; + vec4 weight_cross = weight_12.xyyx * vec4(weights[0].yx, weights[3].xy); + float weight_center = weight_12.x * weight_12.y; + + color = textureLod(in_history_tx, uv_12, 0.0) * weight_center; + color += textureLod(in_history_tx, vec2(uv_12.x, uv_0.y), 0.0) * weight_cross.x; + color += textureLod(in_history_tx, vec2(uv_0.x, uv_12.y), 0.0) * weight_cross.y; + color += textureLod(in_history_tx, vec2(uv_3.x, uv_12.y), 0.0) * weight_cross.z; + color += textureLod(in_history_tx, vec2(uv_12.x, uv_3.y), 0.0) * weight_cross.w; + /* Re-normalize for the removed corners. */ + color /= (weight_center + sum(weight_cross)); +#endif + /* NOTE(fclem): Opacity is wrong on purpose. Final Opacity does not rely on history. */ + return DofSample(color.xyzz, color.w); +} + +/* Modulate the history color to avoid ghosting artifact. */ +DofSample dof_amend_history(DofNeighborhoodMinMax bbox, DofSample history, DofSample src) +{ +#if 0 + /* Clip instead of clamping to avoid color accumulating in the AABB corners. */ + vec3 clip_dir = src.color.rgb - history.color.rgb; + + float t = line_aabb_clipping_dist( + history.color.rgb, clip_dir, bbox.min.color.rgb, bbox.max.color.rgb); + history.color.rgb += clip_dir * saturate(t); +#else + /* More responsive. */ + history.color = clamp(history.color, bbox.min.color, bbox.max.color); +#endif + /* Clamp CoC to reduce convergence time. Otherwise the result is laggy. */ + history.coc = clamp(history.coc, bbox.min.coc, bbox.max.coc); + + return history; +} + +float dof_history_blend_factor( + float velocity, vec2 texel, DofNeighborhoodMinMax bbox, DofSample src, DofSample dst) +{ + float luma_min = bbox.min.color.x; + float luma_max = bbox.max.color.x; + float luma_incoming = src.color.x; + float luma_history = dst.color.x; + + /* 5% of incoming color by default. */ + float blend = 0.05; + /* Blend less history if the pixel has substential velocity. */ + /* NOTE(fclem): velocity threshold multiplied by 2 because of half resolution. */ + blend = mix(blend, 0.20, saturate(velocity * 0.02 * 2.0)); + /** + * "High Quality Temporal Supersampling" by Brian Karis at Siggraph 2014 (Slide 43) + * Bias towards history if incomming pixel is near clamping. Reduces flicker. + */ + float distance_to_luma_clip = min_v2(vec2(luma_history - luma_min, luma_max - luma_history)); + /* Divide by bbox size to get a factor. 2 factor to compensate the line above. */ + distance_to_luma_clip *= 2.0 * safe_rcp(luma_max - luma_min); + /* Linearly blend when history gets bellow to 25% of the bbox size. */ + blend *= saturate(distance_to_luma_clip * 4.0 + 0.1); + /* Progressively discard history until history CoC is twice as big as the filtered CoC. + * Note we use absolute diff here because we are not comparing neighbors and thus do not risk to + * dilate thin features like hair (slide 19). */ + float coc_diff_ratio = saturate(abs(src.coc - dst.coc) / max(1.0, abs(src.coc))); + blend = mix(blend, 1.0, coc_diff_ratio); + /* Discard out of view history. */ + if (any(lessThan(texel, vec2(0))) || + any(greaterThanEqual(texel, vec2(imageSize(out_history_img))))) { + blend = 1.0; + } + /* Discard history if invalid. */ + if (use_history == false) { + blend = 1.0; + } + return blend; } void main() { - vec2 uv = (vec2(gl_GlobalInvocationID.xy) + 0.5) / vec2(textureSize(color_tx, 0).xy); - vec4 out_color = textureLod(color_tx, uv, 0.0); - float out_coc = textureLod(coc_tx, uv, 0.0).r; + dof_cache_init(); + + ivec2 src_texel = ivec2(gl_GlobalInvocationID.xy); + + /** + * Naming convention is taken from the film implementation. + * SRC is incoming new data. + * DST is history data. + */ + DofSample src = dof_spatial_filtering(); + + /* Reproject by finding where this pixel was in the previous frame. */ + vec2 motion = dof_pixel_history_motion_vector(src_texel); + vec2 history_texel = vec2(src_texel) + motion; + + float velocity = length(motion); + + DofSample dst = dof_sample_history(history_texel); + + /* Get local color bounding box of source neighboorhood. */ + DofNeighborhoodMinMax bbox = dof_neighbor_boundbox(); + + float blend = dof_history_blend_factor(velocity, history_texel, bbox, src, dst); + + dst = dof_amend_history(bbox, dst, src); + + /* Luma weighted blend to reduce flickering. */ + float weight_dst = dof_luma_weight(dst.color.x) * (1.0 - blend); + float weight_src = dof_luma_weight(src.color.x) * (blend); + + DofSample result; + /* Weighted blend. */ + result.color = vec4(dst.color.rgb, dst.coc) * weight_dst + + vec4(src.color.rgb, src.coc) * weight_src; + result.color /= weight_src + weight_dst; + + /* Save history for next iteration. Still in YCoCg space with CoC in alpha. */ + imageStore(out_history_img, src_texel, result.color); + + /* Un-swizzle. */ + result.coc = result.color.a; + /* Clamp opacity since we don't store it in history. */ + result.color.a = clamp(src.color.a, bbox.min.color.a, bbox.max.color.a); - out_color.rgb = dof_neighborhood_clamping(out_color.rgb); - /* TODO(fclem): Stabilize CoC. */ + result.color = colorspace_scene_linear_from_YCoCg(result.color); - ivec2 out_texel = ivec2(gl_GlobalInvocationID.xy); - imageStore(out_color_img, out_texel, out_color); - imageStore(out_coc_img, out_texel, vec4(out_coc)); + imageStore(out_color_img, src_texel, result.color); + imageStore(out_coc_img, src_texel, vec4(result.coc)); } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_tiles_dilate_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_tiles_dilate_comp.glsl index db99e9e4fba..dba8b5fd79d 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_tiles_dilate_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_tiles_dilate_comp.glsl @@ -37,11 +37,6 @@ void main() /* Actually gather the "absolute" biggest coc but keeping the sign. */ ring_buckets[ring].fg_min_coc = min(ring_buckets[ring].fg_min_coc, adj_tile.fg_min_coc); ring_buckets[ring].bg_max_coc = max(ring_buckets[ring].bg_max_coc, adj_tile.bg_max_coc); - - if (dilate_slight_focus) { - ring_buckets[ring].fg_slight_focus_max_coc = dof_coc_max_slight_focus( - ring_buckets[ring].fg_slight_focus_max_coc, adj_tile.fg_slight_focus_max_coc); - } } else { /* DILATE_MODE_MIN_ABS */ ring_buckets[ring].fg_max_coc = max(ring_buckets[ring].fg_max_coc, adj_tile.fg_max_coc); @@ -65,12 +60,6 @@ void main() /* Load center tile. */ CocTile out_tile = dof_coc_tile_load(in_tiles_fg_img, in_tiles_bg_img, center_tile_pos); - /* Dilate once. */ - if (dilate_slight_focus) { - out_tile.fg_slight_focus_max_coc = dof_coc_max_slight_focus( - out_tile.fg_slight_focus_max_coc, ring_buckets[0].fg_slight_focus_max_coc); - } - for (int ring = 0; ring < ring_count && ring < DOF_DILATE_RING_COUNT; ring++) { float ring_distance = float(ring + 1); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_tiles_flatten_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_tiles_flatten_comp.glsl index 2a0787ec69f..88737ade386 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_tiles_flatten_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_tiles_flatten_comp.glsl @@ -26,11 +26,6 @@ shared uint bg_min_coc; shared uint bg_max_coc; shared uint bg_min_intersectable_coc; -shared uint fg_slight_focus_max_coc; -shared uint fg_slight_focus_flag; - -const uint slight_focus_flag_defocus = 1u; -const uint slight_focus_flag_focus = 2u; const uint dof_tile_large_coc_uint = floatBitsToUint(dof_tile_large_coc); void main() @@ -43,9 +38,6 @@ void main() bg_min_coc = dof_tile_large_coc_uint; bg_max_coc = floatBitsToUint(0.0); bg_min_intersectable_coc = dof_tile_large_coc_uint; - /* Should be -1.0 but we want to avoid the sign bit in float representation. */ - fg_slight_focus_max_coc = floatBitsToUint(0.0); - fg_slight_focus_flag = 0u; } barrier(); @@ -64,17 +56,6 @@ void main() atomicMax(bg_max_coc, bg_coc); atomicMin(bg_min_intersectable_coc, (sample_coc > 0.0) ? bg_coc : dof_tile_large_coc_uint); - /* Mimics logic of dof_coc_max_slight_focus(). */ - float sample_slight_focus_coc = sample_data.y; - if (sample_slight_focus_coc == dof_tile_defocus) { - atomicOr(fg_slight_focus_flag, slight_focus_flag_defocus); - } - else if (sample_slight_focus_coc == dof_tile_focus) { - atomicOr(fg_slight_focus_flag, slight_focus_flag_focus); - } - /* Add 1 in order to compare signed floats in [-1..1] range. */ - atomicMax(fg_slight_focus_max_coc, floatBitsToUint(sample_slight_focus_coc + 1.0)); - barrier(); if (all(equal(gl_LocalInvocationID.xy, uvec2(0)))) { @@ -91,15 +72,6 @@ void main() tile.bg_max_coc = uintBitsToFloat(bg_max_coc); tile.bg_min_intersectable_coc = uintBitsToFloat(bg_min_intersectable_coc); - /* Mimics logic of dof_coc_max_slight_focus(). */ - if (fg_slight_focus_flag == (slight_focus_flag_defocus | slight_focus_flag_focus)) { - tile.fg_slight_focus_max_coc = dof_tile_mixed; - } - else { - /* Remove the 1 bias. */ - tile.fg_slight_focus_max_coc = uintBitsToFloat(fg_slight_focus_max_coc) - 1.0; - } - ivec2 tile_co = ivec2(gl_WorkGroupID.xy); dof_coc_tile_store(out_tiles_fg_img, out_tiles_bg_img, tile_co, tile); } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_film_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_film_lib.glsl index 135507d956c..bf6293d5561 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_film_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_film_lib.glsl @@ -7,6 +7,7 @@ #pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) #pragma BLENDER_REQUIRE(eevee_camera_lib.glsl) #pragma BLENDER_REQUIRE(eevee_velocity_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_colorspace_lib.glsl) /* Return scene linear Z depth from the camera or radial depth for panoramic cameras. */ float film_depth_convert_to_scene(float depth) @@ -18,32 +19,6 @@ float film_depth_convert_to_scene(float depth) return abs(get_view_z_from_depth(depth)); } -vec3 film_YCoCg_from_scene_linear(vec3 rgb_color) -{ - const mat3 colorspace_tx = transpose(mat3(vec3(1, 2, 1), /* Y */ - vec3(2, 0, -2), /* Co */ - vec3(-1, 2, -1))); /* Cg */ - return colorspace_tx * rgb_color; -} - -vec4 film_YCoCg_from_scene_linear(vec4 rgba_color) -{ - return vec4(film_YCoCg_from_scene_linear(rgba_color.rgb), rgba_color.a); -} - -vec3 film_scene_linear_from_YCoCg(vec3 ycocg_color) -{ - float Y = ycocg_color.x; - float Co = ycocg_color.y; - float Cg = ycocg_color.z; - - vec3 rgb_color; - rgb_color.r = Y + Co - Cg; - rgb_color.g = Y + Cg; - rgb_color.b = Y - Co - Cg; - return rgb_color * 0.25; -} - /* Load a texture sample in a specific format. Combined pass needs to use this. */ vec4 film_texelfetch_as_YCoCg_opacity(sampler2D tx, ivec2 texel) { @@ -51,7 +26,7 @@ vec4 film_texelfetch_as_YCoCg_opacity(sampler2D tx, ivec2 texel) /* Convert transmittance to opacity. */ color.a = saturate(1.0 - color.a); /* Transform to YCoCg for accumulation. */ - color.rgb = film_YCoCg_from_scene_linear(color.rgb); + color.rgb = colorspace_YCoCg_from_scene_linear(color.rgb); return color; } @@ -220,7 +195,7 @@ vec2 film_pixel_history_motion_vector(ivec2 texel_sample) float min_depth = texelFetch(depth_tx, texel_sample, 0).x; ivec2 nearest_texel = texel_sample; for (int i = 0; i < 4; i++) { - ivec2 texel = clamp(texel_sample + corners[i], ivec2(0), textureSize(depth_tx, 0).xy); + ivec2 texel = clamp(texel_sample + corners[i], ivec2(0), textureSize(depth_tx, 0).xy - 1); float depth = texelFetch(depth_tx, texel, 0).x; if (min_depth > depth) { min_depth = depth; @@ -254,7 +229,7 @@ void film_get_catmull_rom_weights(vec2 t, out vec2 weights[4]) weights[3] = fct3 - fct2; } -/* Load color using a special filter to avoid loosing detail. +/* Load color using a special filter to avoid losing detail. * \a texel is sample position with subpixel accuracy. */ vec4 film_sample_catmull_rom(sampler2D color_tx, vec2 input_texel) { @@ -390,7 +365,7 @@ vec4 film_amend_combined_history( float t = line_aabb_clipping_dist(color_history.rgb, clip_dir.rgb, min_color.rgb, max_color.rgb); color_history.rgb += clip_dir.rgb * saturate(t); - /* Clip alpha on its own to avoid interference with other chanels. */ + /* Clip alpha on its own to avoid interference with other channels. */ float t_a = film_aabb_clipping_dist_alpha(color_history.a, clip_dir.a, min_color.a, max_color.a); color_history.a += clip_dir.a * saturate(t_a); @@ -406,16 +381,16 @@ float film_history_blend_factor(float velocity, { /* 5% of incoming color by default. */ float blend = 0.05; - /* Blend less history if the pixel has substential velocity. */ + /* Blend less history if the pixel has substantial velocity. */ blend = mix(blend, 0.20, saturate(velocity * 0.02)); /** * "High Quality Temporal Supersampling" by Brian Karis at Siggraph 2014 (Slide 43) - * Bias towards history if incomming pixel is near clamping. Reduces flicker. + * Bias towards history if incoming pixel is near clamping. Reduces flicker. */ float distance_to_luma_clip = min_v2(vec2(luma_history - luma_min, luma_max - luma_history)); /* Divide by bbox size to get a factor. 2 factor to compensate the line above. */ distance_to_luma_clip *= 2.0 * safe_rcp(luma_max - luma_min); - /* Linearly blend when history gets bellow to 25% of the bbox size. */ + /* Linearly blend when history gets below to 25% of the bbox size. */ blend *= saturate(distance_to_luma_clip * 4.0 + 0.1); /* Discard out of view history. */ if (any(lessThan(texel, vec2(0))) || any(greaterThanEqual(texel, film_buf.extent))) { @@ -451,13 +426,13 @@ void film_store_combined( float velocity = length(motion); - /* Load weight if it is not uniform accross the whole buffer (i.e: upsampling, panoramic). */ + /* Load weight if it is not uniform across the whole buffer (i.e: upsampling, panoramic). */ // dst.weight = film_weight_load(texel_combined); color_dst = film_sample_catmull_rom(in_combined_tx, history_texel); - color_dst.rgb = film_YCoCg_from_scene_linear(color_dst.rgb); + color_dst.rgb = colorspace_YCoCg_from_scene_linear(color_dst.rgb); - /* Get local color bounding box of source neighboorhood. */ + /* Get local color bounding box of source neighborhood. */ vec4 min_color, max_color; film_combined_neighbor_boundbox(src_texel, min_color, max_color); @@ -473,7 +448,7 @@ void film_store_combined( else { /* Everything is static. Use render accumulation. */ color_dst = texelFetch(in_combined_tx, dst.texel, 0); - color_dst.rgb = film_YCoCg_from_scene_linear(color_dst.rgb); + color_dst.rgb = colorspace_YCoCg_from_scene_linear(color_dst.rgb); /* Luma weighted blend to avoid flickering. */ weight_dst = film_luma_weight(color_dst.x) * dst.weight; @@ -483,7 +458,7 @@ void film_store_combined( color = color_dst * weight_dst + color_src * weight_src; color /= weight_src + weight_dst; - color.rgb = film_scene_linear_from_YCoCg(color.rgb); + color.rgb = colorspace_scene_linear_from_YCoCg(color.rgb); /* Fix alpha not accumulating to 1 because of float imprecision. */ if (color.a > 0.995) { @@ -622,7 +597,7 @@ void film_process_data(ivec2 texel_film, out vec4 out_color, out float out_depth src = film_sample_get(i, texel_film); film_sample_accum_combined(src, combined_accum, weight_accum); } - /* NOTE: src.texel is center texel in incomming data buffer. */ + /* NOTE: src.texel is center texel in incoming data buffer. */ film_store_combined(dst, src.texel, combined_accum, weight_accum, out_color); } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_motion_blur_dilate_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_motion_blur_dilate_comp.glsl index c59b7d7f4df..99186ab6f67 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_motion_blur_dilate_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_motion_blur_dilate_comp.glsl @@ -1,7 +1,7 @@ /** * Dilate motion vector tiles until we covered maximum velocity. - * Outputs the largest intersecting motion vector in the neighboorhod. + * Outputs the largest intersecting motion vector in the neighborhood. * */ @@ -62,7 +62,7 @@ bool is_inside_motion_line(ivec2 tile, MotionLine motion_line) /* NOTE: Everything in is tile unit. */ float dist = point_line_projection_dist(vec2(tile), motion_line.origin, motion_line.normal); /* In order to be conservative and for simplicity, we use the tiles bounding circles. - * Consider that both the tile and the line have bouding radius of M_SQRT1_2. */ + * Consider that both the tile and the line have bounding radius of M_SQRT1_2. */ return abs(dist) < M_SQRT2; } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_motion_blur_gather_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_motion_blur_gather_comp.glsl index a7329f77181..5249e6637b6 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_motion_blur_gather_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_motion_blur_gather_comp.glsl @@ -22,7 +22,7 @@ const int gather_sample_count = 8; * target post-fx framebuffer. */ vec4 motion_blur_sample_velocity(sampler2D velocity_tx, vec2 uv) { - /* We can load velocity without velocity_resolve() since we resovled during the flatten pass. */ + /* We can load velocity without velocity_resolve() since we resolved during the flatten pass. */ vec4 velocity = velocity_unpack(texture(velocity_tx, uv)); return velocity * vec2(textureSize(velocity_tx, 0)).xyxy * motion_blur_buf.motion_scale.xxyy; } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_sampling_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_sampling_lib.glsl index 0c7bbaa9dc2..0eea4a5ff33 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_sampling_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_sampling_lib.glsl @@ -9,7 +9,7 @@ /* -------------------------------------------------------------------- */ /** \name Sampling data. * - * Return a random values from Low Discrepency Sequence in [0..1) range. + * Return a random values from Low Discrepancy Sequence in [0..1) range. * This value is uniform (constant) for the whole scene sample. * You might want to couple it with a noise function. * \{ */ diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_velocity_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_velocity_lib.glsl index c0a5b976810..8d02609fedc 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_velocity_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_velocity_lib.glsl @@ -47,7 +47,7 @@ vec4 velocity_surface(vec3 P_prv, vec3 P, vec3 P_nxt) */ vec4 velocity_background(vec3 vV) { - /* Only transform direction to avoid loosing precision. */ + /* Only transform direction to avoid losing precision. */ vec3 V = transform_direction(camera_curr.viewinv, vV); /* NOTE: We don't use the drw_view.winmat to avoid adding the TAA jitter to the velocity. */ vec2 prev_uv = project_point(camera_prev.winmat, V).xy; diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_depth_of_field_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_depth_of_field_info.hh index 42a8c78a51d..94ff694b147 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_depth_of_field_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_depth_of_field_info.hh @@ -24,19 +24,23 @@ GPU_SHADER_CREATE_INFO(eevee_depth_of_field_setup) .sampler(0, ImageType::FLOAT_2D, "color_tx") .sampler(1, ImageType::DEPTH_2D, "depth_tx") .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_color_img") - .image(1, GPU_RG16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_coc_img") + .image(1, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_coc_img") .compute_source("eevee_depth_of_field_setup_comp.glsl"); GPU_SHADER_CREATE_INFO(eevee_depth_of_field_stabilize) .do_static_compilation(true) - .local_group_size(DOF_DEFAULT_GROUP_SIZE, DOF_DEFAULT_GROUP_SIZE) - .additional_info("eevee_shared", "draw_view") - .uniform_buf(1, "DepthOfFieldData", "dof_buf") - .sampler(0, ImageType::DEPTH_2D, "coc_tx") + .local_group_size(DOF_STABILIZE_GROUP_SIZE, DOF_STABILIZE_GROUP_SIZE) + .additional_info("eevee_shared", "draw_view", "eevee_velocity_camera") + .uniform_buf(4, "DepthOfFieldData", "dof_buf") + .sampler(0, ImageType::FLOAT_2D, "coc_tx") .sampler(1, ImageType::FLOAT_2D, "color_tx") - // .sampler(2, ImageType::FLOAT_2D, "velocity_tx") /* TODO: TAA with reprojection. */ + .sampler(2, ImageType::FLOAT_2D, "velocity_tx") + .sampler(3, ImageType::FLOAT_2D, "in_history_tx") + .sampler(4, ImageType::DEPTH_2D, "depth_tx") + .push_constant(Type::BOOL, "use_history") .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_color_img") .image(1, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_coc_img") + .image(2, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_history_img") .compute_source("eevee_depth_of_field_stabilize_comp.glsl"); GPU_SHADER_CREATE_INFO(eevee_depth_of_field_downsample) @@ -79,18 +83,17 @@ GPU_SHADER_CREATE_INFO(eevee_depth_of_field_tiles_flatten) .local_group_size(DOF_TILES_FLATTEN_GROUP_SIZE, DOF_TILES_FLATTEN_GROUP_SIZE) .additional_info("eevee_shared", "draw_view") .sampler(0, ImageType::FLOAT_2D, "coc_tx") - .image(2, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_tiles_fg_img") + .image(2, GPU_R11F_G11F_B10F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_tiles_fg_img") .image(3, GPU_R11F_G11F_B10F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_tiles_bg_img") .compute_source("eevee_depth_of_field_tiles_flatten_comp.glsl"); GPU_SHADER_CREATE_INFO(eevee_depth_of_field_tiles_dilate) .additional_info("eevee_shared", "draw_view", "eevee_depth_of_field_tiles_common") .local_group_size(DOF_TILES_DILATE_GROUP_SIZE, DOF_TILES_DILATE_GROUP_SIZE) - .image(2, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_tiles_fg_img") + .image(2, GPU_R11F_G11F_B10F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_tiles_fg_img") .image(3, GPU_R11F_G11F_B10F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_tiles_bg_img") .push_constant(Type::INT, "ring_count") .push_constant(Type::INT, "ring_width_multiplier") - .push_constant(Type::BOOL, "dilate_slight_focus") .compute_source("eevee_depth_of_field_tiles_dilate_comp.glsl"); GPU_SHADER_CREATE_INFO(eevee_depth_of_field_tiles_dilate_minabs) @@ -104,7 +107,7 @@ GPU_SHADER_CREATE_INFO(eevee_depth_of_field_tiles_dilate_minmax) .additional_info("eevee_depth_of_field_tiles_dilate"); GPU_SHADER_CREATE_INFO(eevee_depth_of_field_tiles_common) - .image(0, GPU_RGBA16F, Qualifier::READ, ImageType::FLOAT_2D, "in_tiles_fg_img") + .image(0, GPU_R11F_G11F_B10F, Qualifier::READ, ImageType::FLOAT_2D, "in_tiles_fg_img") .image(1, GPU_R11F_G11F_B10F, Qualifier::READ, ImageType::FLOAT_2D, "in_tiles_bg_img"); /** \} */ @@ -117,7 +120,7 @@ GPU_SHADER_CREATE_INFO(eevee_depth_of_field_no_lut) .define("DOF_BOKEH_TEXTURE", "false") /** * WORKAROUND(@fclem): This is to keep the code as is for now. The bokeh_lut_tx is referenced - * even if not used after optimisation. But we don't want to include it in the create infos. + * even if not used after optimization. But we don't want to include it in the create infos. */ .define("bokeh_lut_tx", "color_tx"); @@ -127,24 +130,18 @@ GPU_SHADER_CREATE_INFO(eevee_depth_of_field_lut) GPU_SHADER_CREATE_INFO(eevee_depth_of_field_background).define("DOF_FOREGROUND_PASS", "false"); GPU_SHADER_CREATE_INFO(eevee_depth_of_field_foreground).define("DOF_FOREGROUND_PASS", "true"); -GPU_SHADER_CREATE_INFO(eevee_depth_of_field_hq).define("DOF_SLIGHT_FOCUS_DENSITY", "4"); -GPU_SHADER_CREATE_INFO(eevee_depth_of_field_lq).define("DOF_SLIGHT_FOCUS_DENSITY", "2"); #define EEVEE_DOF_FINAL_VARIATION(name, ...) \ GPU_SHADER_CREATE_INFO(name).additional_info(__VA_ARGS__).do_static_compilation(true); #define EEVEE_DOF_LUT_VARIATIONS(prefix, ...) \ EEVEE_DOF_FINAL_VARIATION(prefix##_lut, "eevee_depth_of_field_lut", __VA_ARGS__) \ - EEVEE_DOF_FINAL_VARIATION(prefix, "eevee_depth_of_field_no_lut", __VA_ARGS__) + EEVEE_DOF_FINAL_VARIATION(prefix##_no_lut, "eevee_depth_of_field_no_lut", __VA_ARGS__) #define EEVEE_DOF_GROUND_VARIATIONS(name, ...) \ EEVEE_DOF_LUT_VARIATIONS(name##_background, "eevee_depth_of_field_background", __VA_ARGS__) \ EEVEE_DOF_LUT_VARIATIONS(name##_foreground, "eevee_depth_of_field_foreground", __VA_ARGS__) -#define EEVEE_DOF_HQ_VARIATIONS(name, ...) \ - EEVEE_DOF_LUT_VARIATIONS(name##_hq, "eevee_depth_of_field_hq", __VA_ARGS__) \ - EEVEE_DOF_LUT_VARIATIONS(name##_lq, "eevee_depth_of_field_lq", __VA_ARGS__) - /** \} */ /* -------------------------------------------------------------------- */ @@ -240,9 +237,10 @@ GPU_SHADER_CREATE_INFO(eevee_depth_of_field_resolve) .sampler(7, ImageType::FLOAT_2D, "weight_bg_tx") .sampler(8, ImageType::FLOAT_2D, "weight_fg_tx") .sampler(9, ImageType::FLOAT_2D, "weight_hole_fill_tx") + .sampler(10, ImageType::FLOAT_2D, "stable_color_tx") .image(2, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_color_img") .compute_source("eevee_depth_of_field_resolve_comp.glsl"); -EEVEE_DOF_HQ_VARIATIONS(eevee_depth_of_field_resolve, "eevee_depth_of_field_resolve") +EEVEE_DOF_LUT_VARIATIONS(eevee_depth_of_field_resolve, "eevee_depth_of_field_resolve") /** \} */ diff --git a/source/blender/draw/engines/overlay/shaders/overlay_antialiasing_frag.glsl b/source/blender/draw/engines/overlay/shaders/overlay_antialiasing_frag.glsl index f28a809fdab..606292bbe83 100644 --- a/source/blender/draw/engines/overlay/shaders/overlay_antialiasing_frag.glsl +++ b/source/blender/draw/engines/overlay/shaders/overlay_antialiasing_frag.glsl @@ -96,7 +96,7 @@ void main() float dist_raw = texelFetch(lineTex, center_texel, 0).b; float dist = decode_line_dist(dist_raw); - /* TODO: Opti: use textureGather. */ + /* TODO: Optimization: use textureGather. */ vec4 neightbor_col0 = texelFetchOffset(colorTex, center_texel, 0, ivec2(1, 0)); vec4 neightbor_col1 = texelFetchOffset(colorTex, center_texel, 0, ivec2(-1, 0)); vec4 neightbor_col2 = texelFetchOffset(colorTex, center_texel, 0, ivec2(0, 1)); diff --git a/source/blender/draw/engines/select/select_engine.c b/source/blender/draw/engines/select/select_engine.c index 88ae5ac707e..026a1f52ac1 100644 --- a/source/blender/draw/engines/select/select_engine.c +++ b/source/blender/draw/engines/select/select_engine.c @@ -201,7 +201,7 @@ static void select_cache_populate(void *vedata, Object *ob) if (!e_data.context.is_dirty && sel_data && sel_data->is_drawn) { /* The object indices have already been drawn. Fill depth pass. - * Opti: Most of the time this depth pass is not used. */ + * Optimization: Most of the time this depth pass is not used. */ struct Mesh *me = ob->data; if (e_data.context.select_mode & SCE_SELECT_FACE) { struct GPUBatch *geom_faces = DRW_mesh_batch_cache_get_triangles_with_select_id(me); diff --git a/source/blender/draw/intern/DRW_gpu_wrapper.hh b/source/blender/draw/intern/DRW_gpu_wrapper.hh index ed00d24ce2a..5405afd2a90 100644 --- a/source/blender/draw/intern/DRW_gpu_wrapper.hh +++ b/source/blender/draw/intern/DRW_gpu_wrapper.hh @@ -602,17 +602,17 @@ class Texture : NonCopyable { /** * Returns true if the texture has been allocated or acquired from the pool. */ - bool is_valid(void) const + bool is_valid() const { return tx_ != nullptr; } - int width(void) const + int width() const { return GPU_texture_width(tx_); } - int height(void) const + int height() const { return GPU_texture_height(tx_); } @@ -622,27 +622,27 @@ class Texture : NonCopyable { return GPU_texture_width(tx_) * GPU_texture_height(tx_); } - bool depth(void) const + bool depth() const { return GPU_texture_depth(tx_); } - bool is_stencil(void) const + bool is_stencil() const { return GPU_texture_stencil(tx_); } - bool is_integer(void) const + bool is_integer() const { return GPU_texture_integer(tx_); } - bool is_cube(void) const + bool is_cube() const { return GPU_texture_cube(tx_); } - bool is_array(void) const + bool is_array() const { return GPU_texture_array(tx_); } @@ -796,7 +796,7 @@ class TextureFromPool : public Texture, NonMovable { DST.vmempool->texture_pool, UNPACK2(extent), format); } - void release(void) + void release() { /* Allows multiple release. */ if (this->tx_ == nullptr) { @@ -807,12 +807,22 @@ class TextureFromPool : public Texture, NonMovable { } /** - * Swap the GPUTexture pointers of the two texture. + * Swap the content of the two textures. + * Also change ownership accordingly if needed. */ + static void swap(TextureFromPool &a, Texture &b) + { + Texture::swap(a, b); + DRW_texture_pool_give_texture_ownership(DST.vmempool->texture_pool, a); + DRW_texture_pool_take_texture_ownership(DST.vmempool->texture_pool, b); + } + static void swap(Texture &a, TextureFromPool &b) + { + swap(b, a); + } static void swap(TextureFromPool &a, TextureFromPool &b) { - SWAP(GPUTexture *, a.tx_, b.tx_); - SWAP(const char *, a.name_, b.name_); + Texture::swap(a, b); } /** Remove methods that are forbidden with this type of textures. */ diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h index 2b2288f23cd..a3097251d35 100644 --- a/source/blender/draw/intern/DRW_render.h +++ b/source/blender/draw/intern/DRW_render.h @@ -795,7 +795,7 @@ bool DRW_culling_box_test(const DRWView *view, const BoundBox *bbox); bool DRW_culling_plane_test(const DRWView *view, const float plane[4]); /** * Return True if the given box intersect the current view frustum. - * This function will have to be replaced when world space bb per objects is implemented. + * This function will have to be replaced when world space bounding-box per objects is implemented. */ bool DRW_culling_min_max_test(const DRWView *view, float obmat[4][4], float min[3], float max[3]); diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.cc b/source/blender/draw/intern/draw_cache_impl_mesh.cc index d1eb937d711..5de9f1b44c8 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.cc +++ b/source/blender/draw/intern/draw_cache_impl_mesh.cc @@ -293,26 +293,28 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Object *object, for (int i = 0; i < gpumat_array_len; i++) { GPUMaterial *gpumat = gpumat_array[i]; - if (gpumat) { - ListBase gpu_attrs = GPU_material_attributes(gpumat); - LISTBASE_FOREACH (GPUMaterialAttribute *, gpu_attr, &gpu_attrs) { - const char *name = gpu_attr->name; - eCustomDataType type = static_cast<eCustomDataType>(gpu_attr->type); - int layer = -1; - std::optional<eAttrDomain> domain; - - if (gpu_attr->is_default_color) { - name = default_color_name.c_str(); - } + if (gpumat == nullptr) { + continue; + } + ListBase gpu_attrs = GPU_material_attributes(gpumat); + LISTBASE_FOREACH (GPUMaterialAttribute *, gpu_attr, &gpu_attrs) { + const char *name = gpu_attr->name; + eCustomDataType type = static_cast<eCustomDataType>(gpu_attr->type); + int layer = -1; + std::optional<eAttrDomain> domain; + + if (gpu_attr->is_default_color) { + name = default_color_name.c_str(); + } - if (type == CD_AUTO_FROM_NAME) { - /* We need to deduce what exact layer is used. - * - * We do it based on the specified name. - */ - if (name[0] != '\0') { - layer = CustomData_get_named_layer(cd_ldata, CD_MLOOPUV, name); - type = CD_MTFACE; + if (type == CD_AUTO_FROM_NAME) { + /* We need to deduce what exact layer is used. + * + * We do it based on the specified name. + */ + if (name[0] != '\0') { + layer = CustomData_get_named_layer(cd_ldata, CD_MLOOPUV, name); + type = CD_MTFACE; #if 0 /* Tangents are always from UV's - this will never happen. */ if (layer == -1) { @@ -320,88 +322,87 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Object *object, type = CD_TANGENT; } #endif - if (layer == -1) { - /* Try to match a generic attribute, we use the first attribute domain with a - * matching name. */ - if (drw_custom_data_match_attribute(cd_vdata, name, &layer, &type)) { - domain = ATTR_DOMAIN_POINT; - } - else if (drw_custom_data_match_attribute(cd_ldata, name, &layer, &type)) { - domain = ATTR_DOMAIN_CORNER; - } - else if (drw_custom_data_match_attribute(cd_pdata, name, &layer, &type)) { - domain = ATTR_DOMAIN_FACE; - } - else if (drw_custom_data_match_attribute(cd_edata, name, &layer, &type)) { - domain = ATTR_DOMAIN_EDGE; - } - else { - layer = -1; - } + if (layer == -1) { + /* Try to match a generic attribute, we use the first attribute domain with a + * matching name. */ + if (drw_custom_data_match_attribute(cd_vdata, name, &layer, &type)) { + domain = ATTR_DOMAIN_POINT; } - - if (layer == -1) { - continue; + else if (drw_custom_data_match_attribute(cd_ldata, name, &layer, &type)) { + domain = ATTR_DOMAIN_CORNER; + } + else if (drw_custom_data_match_attribute(cd_pdata, name, &layer, &type)) { + domain = ATTR_DOMAIN_FACE; + } + else if (drw_custom_data_match_attribute(cd_edata, name, &layer, &type)) { + domain = ATTR_DOMAIN_EDGE; + } + else { + layer = -1; } } - else { - /* Fall back to the UV layer, which matches old behavior. */ - type = CD_MTFACE; + + if (layer == -1) { + continue; } } + else { + /* Fall back to the UV layer, which matches old behavior. */ + type = CD_MTFACE; + } + } - switch (type) { - case CD_MTFACE: { - if (layer == -1) { - layer = (name[0] != '\0') ? CustomData_get_named_layer(cd_ldata, CD_MLOOPUV, name) : - CustomData_get_render_layer(cd_ldata, CD_MLOOPUV); - } - if (layer != -1) { - cd_used.uv |= (1 << layer); - } - break; + switch (type) { + case CD_MTFACE: { + if (layer == -1) { + layer = (name[0] != '\0') ? CustomData_get_named_layer(cd_ldata, CD_MLOOPUV, name) : + CustomData_get_render_layer(cd_ldata, CD_MLOOPUV); } - case CD_TANGENT: { - if (layer == -1) { - layer = (name[0] != '\0') ? CustomData_get_named_layer(cd_ldata, CD_MLOOPUV, name) : - CustomData_get_render_layer(cd_ldata, CD_MLOOPUV); - - /* Only fallback to orco (below) when we have no UV layers, see: T56545 */ - if (layer == -1 && name[0] != '\0') { - layer = CustomData_get_render_layer(cd_ldata, CD_MLOOPUV); - } - } - if (layer != -1) { - cd_used.tan |= (1 << layer); - } - else { - /* no UV layers at all => requesting orco */ - cd_used.tan_orco = 1; - cd_used.orco = 1; + if (layer != -1) { + cd_used.uv |= (1 << layer); + } + break; + } + case CD_TANGENT: { + if (layer == -1) { + layer = (name[0] != '\0') ? CustomData_get_named_layer(cd_ldata, CD_MLOOPUV, name) : + CustomData_get_render_layer(cd_ldata, CD_MLOOPUV); + + /* Only fallback to orco (below) when we have no UV layers, see: T56545 */ + if (layer == -1 && name[0] != '\0') { + layer = CustomData_get_render_layer(cd_ldata, CD_MLOOPUV); } - break; } - - case CD_ORCO: { + if (layer != -1) { + cd_used.tan |= (1 << layer); + } + else { + /* no UV layers at all => requesting orco */ + cd_used.tan_orco = 1; cd_used.orco = 1; - break; } - case CD_PROP_BYTE_COLOR: - case CD_PROP_COLOR: - case CD_PROP_FLOAT3: - case CD_PROP_BOOL: - case CD_PROP_INT8: - case CD_PROP_INT32: - case CD_PROP_FLOAT: - case CD_PROP_FLOAT2: { - if (layer != -1 && domain.has_value()) { - drw_attributes_add_request(attributes, name, type, layer, *domain); - } - break; + break; + } + + case CD_ORCO: { + cd_used.orco = 1; + break; + } + case CD_PROP_BYTE_COLOR: + case CD_PROP_COLOR: + case CD_PROP_FLOAT3: + case CD_PROP_BOOL: + case CD_PROP_INT8: + case CD_PROP_INT32: + case CD_PROP_FLOAT: + case CD_PROP_FLOAT2: { + if (layer != -1 && domain.has_value()) { + drw_attributes_add_request(attributes, name, type, layer, *domain); } - default: - break; + break; } + default: + break; } } } diff --git a/source/blender/draw/intern/draw_texture_pool.cc b/source/blender/draw/intern/draw_texture_pool.cc index b36cb5c809e..017ecec7be2 100644 --- a/source/blender/draw/intern/draw_texture_pool.cc +++ b/source/blender/draw/intern/draw_texture_pool.cc @@ -160,6 +160,19 @@ void DRW_texture_pool_texture_release(DRWTexturePool *pool, GPUTexture *tmp_tex) pool->tmp_tex_released.append(tmp_tex); } +void DRW_texture_pool_take_texture_ownership(DRWTexturePool *pool, GPUTexture *tex) +{ + pool->tmp_tex_acquired.remove_first_occurrence_and_reorder(tex); +} + +void DRW_texture_pool_give_texture_ownership(DRWTexturePool *pool, GPUTexture *tex) +{ + BLI_assert(pool->tmp_tex_acquired.first_index_of_try(tex) == -1 && + pool->tmp_tex_released.first_index_of_try(tex) == -1 && + pool->tmp_tex_pruned.first_index_of_try(tex) == -1); + pool->tmp_tex_acquired.append(tex); +} + void DRW_texture_pool_reset(DRWTexturePool *pool) { pool->last_user_id = -1; diff --git a/source/blender/draw/intern/draw_texture_pool.h b/source/blender/draw/intern/draw_texture_pool.h index 7396b55d53a..9fbbf630833 100644 --- a/source/blender/draw/intern/draw_texture_pool.h +++ b/source/blender/draw/intern/draw_texture_pool.h @@ -41,6 +41,22 @@ GPUTexture *DRW_texture_pool_texture_acquire(DRWTexturePool *pool, * Releases a previously acquired texture. */ void DRW_texture_pool_texture_release(DRWTexturePool *pool, GPUTexture *tmp_tex); + +/** + * This effectively remove a texture from the texture pool, giving full ownership to the caller. + * The given texture needs to be been acquired through DRW_texture_pool_texture_acquire(). + * IMPORTANT: This removes the need for a DRW_texture_pool_texture_release() call on this texture. + */ +void DRW_texture_pool_take_texture_ownership(DRWTexturePool *pool, GPUTexture *tex); +/** + * This Inserts a texture into the texture pool, giving full ownership to the texture pool. + * The texture needs not to be in the pool already. + * The texture may be reused in a latter call to DRW_texture_pool_texture_acquire(); + * IMPORTANT: DRW_texture_pool_texture_release() still needs to be called on this texture + * after usage. + */ +void DRW_texture_pool_give_texture_ownership(DRWTexturePool *pool, GPUTexture *tex); + /** * Resets the user bits for each texture in the pool and delete unused ones. */ diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc index 7f16837022c..64ade020418 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc @@ -58,7 +58,6 @@ template<typename AttributeType, typename VBOType> struct AttributeTypeConverter } }; -/* Similar to the one in #extract_mesh_vcol_vbo.cc */ struct gpuMeshCol { ushort r, g, b, a; }; diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c index 1c7b3496723..06a62b7a9de 100644 --- a/source/blender/editors/animation/anim_channels_edit.c +++ b/source/blender/editors/animation/anim_channels_edit.c @@ -1695,7 +1695,7 @@ static int animchannels_group_exec(bContext *C, wmOperator *op) /* Handle each animdata block separately, so that the regrouping doesn't flow into blocks. */ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_ANIMDATA | - ANIMFILTER_NODUPLIS); + ANIMFILTER_NODUPLIS | ANIMFILTER_FCURVESONLY); ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); for (ale = anim_data.first; ale; ale = ale->next) { @@ -3260,10 +3260,14 @@ static int mouse_anim_channels(bContext *C, bAnimListElem *ale; int filter; int notifierFlags = 0; + ScrArea *area = CTX_wm_area(C); /* get the channel that was clicked on */ /* filter channels */ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS); + if (ELEM(area->spacetype, SPACE_NLA, SPACE_GRAPH)) { + filter |= ANIMFILTER_FCURVESONLY; + } ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* get channel from index */ diff --git a/source/blender/editors/animation/anim_markers.c b/source/blender/editors/animation/anim_markers.c index 3608140a29d..e7c7f679b16 100644 --- a/source/blender/editors/animation/anim_markers.c +++ b/source/blender/editors/animation/anim_markers.c @@ -402,6 +402,7 @@ static void draw_marker_name(const uchar *text_color, const uiFontStyle *fstyle, TimeMarker *marker, float marker_x, + float xmax, float text_y) { const char *name = marker->name; @@ -419,8 +420,16 @@ static void draw_marker_name(const uchar *text_color, } #endif - int name_x = marker_x + UI_DPI_ICON_SIZE * 0.6; - UI_fontstyle_draw_simple(fstyle, name_x, text_y, name, final_text_color); + const int icon_half_width = UI_DPI_ICON_SIZE * 0.6; + const struct uiFontStyleDraw_Params fs_params = {.align = UI_STYLE_TEXT_LEFT, .word_wrap = 0}; + const struct rcti rect = { + .xmin = marker_x + icon_half_width, + .xmax = xmax - icon_half_width, + .ymin = text_y, + .ymax = text_y, + }; + + UI_fontstyle_draw(fstyle, &rect, name, strlen(name), final_text_color, &fs_params); } static void draw_marker_line(const uchar *color, int xpos, int ymin, int ymax) @@ -462,8 +471,13 @@ static int marker_get_icon_id(TimeMarker *marker, int flag) return (marker->flag & SELECT) ? ICON_MARKER_HLT : ICON_MARKER; } -static void draw_marker( - const uiFontStyle *fstyle, TimeMarker *marker, int cfra, int xpos, int flag, int region_height) +static void draw_marker(const uiFontStyle *fstyle, + TimeMarker *marker, + int xpos, + int xmax, + int flag, + int region_height, + bool is_elevated) { uchar line_color[4], text_color[4]; @@ -479,12 +493,11 @@ static void draw_marker( GPU_blend(GPU_BLEND_NONE); float name_y = UI_DPI_FAC * 18; - /* Give an offset to the marker name when selected, - * or when near the current frame (5 frames range, starting from the current one). */ - if ((marker->flag & SELECT) || (cfra - 4 <= marker->frame && marker->frame <= cfra)) { + /* Give an offset to the marker that is elevated. */ + if (is_elevated) { name_y += UI_DPI_FAC * 10; } - draw_marker_name(text_color, fstyle, marker, xpos, name_y); + draw_marker_name(text_color, fstyle, marker, xpos, xmax, name_y); } static void draw_markers_background(rctf *rect) @@ -532,6 +545,14 @@ static void get_marker_clip_frame_range(View2D *v2d, float xscale, int r_range[2 r_range[1] = v2d->cur.xmax + font_width_max; } +static int markers_frame_sort(const void *a, const void *b) +{ + const TimeMarker *marker_a = a; + const TimeMarker *marker_b = b; + + return marker_a->frame > marker_b->frame; +} + void ED_markers_draw(const bContext *C, int flag) { ListBase *markers = ED_context_get_markers(C); @@ -561,22 +582,69 @@ void ED_markers_draw(const bContext *C, int flag) const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; - /* Separate loops in order to draw selected markers on top */ - LISTBASE_FOREACH (TimeMarker *, marker, markers) { - if ((marker->flag & SELECT) == 0) { - if (marker_is_in_frame_range(marker, clip_frame_range)) { - draw_marker(fstyle, marker, cfra, marker->frame * xscale, flag, region->winy); - } + /* Markers are not stored by frame order, so we need to sort it here. */ + ListBase sorted_markers; + + BLI_duplicatelist(&sorted_markers, markers); + BLI_listbase_sort(&sorted_markers, markers_frame_sort); + + /** + * Set a temporary bit in the marker's flag to indicate that it should be elevated. + * This bit will be flipped back at the end of this function. + */ + const int ELEVATED = 0x10; + LISTBASE_FOREACH (TimeMarker *, marker, &sorted_markers) { + const bool is_elevated = (marker->flag & SELECT) || + (cfra >= marker->frame && + (marker->next == NULL || cfra < marker->next->frame)); + SET_FLAG_FROM_TEST(marker->flag, is_elevated, ELEVATED); + } + + /* Separate loops in order to draw selected markers on top. */ + + /** + * Draw non-elevated markers first. + * Note that unlike the elevated markers, these marker names will always be clipped by the + * proceeding marker. This is done because otherwise, the text overlaps with the icon of the + * marker itself. + */ + LISTBASE_FOREACH (TimeMarker *, marker, &sorted_markers) { + if ((marker->flag & ELEVATED) == 0 && marker_is_in_frame_range(marker, clip_frame_range)) { + const int xmax = marker->next ? marker->next->frame : clip_frame_range[1] + 1; + draw_marker( + fstyle, marker, marker->frame * xscale, xmax * xscale, flag, region->winy, false); } } - LISTBASE_FOREACH (TimeMarker *, marker, markers) { - if (marker->flag & SELECT) { - if (marker_is_in_frame_range(marker, clip_frame_range)) { - draw_marker(fstyle, marker, cfra, marker->frame * xscale, flag, region->winy); - } + + /* Now draw the elevated markers */ + for (TimeMarker *marker = sorted_markers.first; marker != NULL;) { + + /* Skip this marker if it is elevated or out of the frame range. */ + if ((marker->flag & ELEVATED) == 0 || !marker_is_in_frame_range(marker, clip_frame_range)) { + marker = marker->next; + continue; } + + /* Find the next elevated marker. */ + /* We use the next marker to determine how wide our text should be */ + TimeMarker *next_marker = marker->next; + while (next_marker != NULL && (next_marker->flag & ELEVATED) == 0) { + next_marker = next_marker->next; + } + + const int xmax = next_marker ? next_marker->frame : clip_frame_range[1] + 1; + draw_marker(fstyle, marker, marker->frame * xscale, xmax * xscale, flag, region->winy, true); + + marker = next_marker; } + /* Reset the elevated flag. */ + LISTBASE_FOREACH (TimeMarker *, marker, &sorted_markers) { + marker->flag &= ~ELEVATED; + } + + BLI_freelistN(&sorted_markers); + GPU_matrix_pop(); } diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c index 24302aca59b..852bfb00ea6 100644 --- a/source/blender/editors/curve/editcurve.c +++ b/source/blender/editors/curve/editcurve.c @@ -1541,67 +1541,6 @@ void CURVE_OT_split(wmOperatorType *ot) /** \name Flag Utility Functions * \{ */ -static bool isNurbselUV(const Nurb *nu, uint8_t flag, int *r_u, int *r_v) -{ - /* return (u != -1): 1 row in u-direction selected. U has value between 0-pntsv - * return (v != -1): 1 column in v-direction selected. V has value between 0-pntsu - */ - BPoint *bp; - int a, b, sel; - - *r_u = *r_v = -1; - - bp = nu->bp; - for (b = 0; b < nu->pntsv; b++) { - sel = 0; - for (a = 0; a < nu->pntsu; a++, bp++) { - if (bp->f1 & flag) { - sel++; - } - } - if (sel == nu->pntsu) { - if (*r_u == -1) { - *r_u = b; - } - else { - return 0; - } - } - else if (sel > 1) { - return 0; /* because sel == 1 is still ok */ - } - } - - for (a = 0; a < nu->pntsu; a++) { - sel = 0; - bp = &nu->bp[a]; - for (b = 0; b < nu->pntsv; b++, bp += nu->pntsu) { - if (bp->f1 & flag) { - sel++; - } - } - if (sel == nu->pntsv) { - if (*r_v == -1) { - *r_v = a; - } - else { - return 0; - } - } - else if (sel > 1) { - return 0; - } - } - - if (*r_u == -1 && *r_v > -1) { - return 1; - } - if (*r_v == -1 && *r_u > -1) { - return 1; - } - return 0; -} - /* return true if U direction is selected and number of selected columns v */ static bool isNurbselU(Nurb *nu, int *v, int flag) { @@ -1976,119 +1915,201 @@ static void ed_curve_delete_selected(Object *obedit, View3D *v3d) } } -bool ed_editnurb_extrude_flag(EditNurb *editnurb, const uint8_t flag) +static void select_bpoints(BPoint *bp, + const int stride, + const int count, + const bool selstatus, + const uint8_t flag, + const bool hidden) { - BPoint *bp, *bpn, *newbp; - int a, u, v, len; - bool ok = false; + for (int i = 0; i < count; i++) { + select_bpoint(bp, selstatus, flag, hidden); + bp += stride; + } +} - LISTBASE_FOREACH (Nurb *, nu, &editnurb->nurbs) { - if (nu->pntsv == 1) { - bp = nu->bp; - a = nu->pntsu; - while (a) { - if (bp->f1 & flag) { - /* pass */ - } - else { - break; - } - bp++; - a--; - } - if (a == 0) { - ok = true; - newbp = (BPoint *)MEM_mallocN(2 * nu->pntsu * sizeof(BPoint), "extrudeNurb1"); - ED_curve_bpcpy(editnurb, newbp, nu->bp, nu->pntsu); - bp = newbp + nu->pntsu; - ED_curve_bpcpy(editnurb, bp, nu->bp, nu->pntsu); - MEM_freeN(nu->bp); - nu->bp = newbp; - a = nu->pntsu; - while (a--) { - select_bpoint(bp, SELECT, flag, HIDDEN); - select_bpoint(newbp, DESELECT, flag, HIDDEN); - bp++; - newbp++; - } +/** + * Calculate and return fully selected legs along i dimension. + * Calculates intervals to create extrusion by duplicating existing points while copied to + * destination NURBS. For ex. for curve of 3 points indexed by 0..2 to extrude first and last + * point copy intervals would be [0, 0][0, 2][2, 2]. Representation in copy_intervals array would + * be [0, 0, 2, 2]. Returns -1 if selection is not valid. + */ +static int sel_to_copy_ints(const BPoint *bp, + const int next_j, + const int max_j, + const int next_i, + const int max_i, + const uint8_t flag, + int copy_intervals[], + int *interval_count, + bool *out_is_first_sel) +{ + const BPoint *bp_j = bp; - nu->pntsv = 2; - nu->orderv = 2; - BKE_nurb_knot_calc_v(nu); - } - } - else { - /* which row or column is selected */ + int selected_leg_count = 0; + int ins = 0; + int selected_in_prev_leg = -1; + int not_full = -1; - if (isNurbselUV(nu, flag, &u, &v)) { + bool is_first_sel = false; + bool is_last_sel = false; - /* deselect all */ - bp = nu->bp; - a = nu->pntsu * nu->pntsv; - while (a--) { - select_bpoint(bp, DESELECT, flag, HIDDEN); - bp++; - } + for (int j = 0; j < max_j; j++, bp_j += next_j) { + const BPoint *bp_j_i = bp_j; + int selected_in_curr_leg = 0; + for (int i = 0; i < max_i; i++, bp_j_i += next_i) { + if (bp_j_i->f1 & flag) { + selected_in_curr_leg++; + } + } + if (selected_in_curr_leg == max_i) { + selected_leg_count++; + if (j == 0) { + is_first_sel = true; + } + else if (j + 1 == max_j) { + is_last_sel = true; + } + } + else if (not_full == -1) { + not_full = selected_in_curr_leg; + } + /* We have partialy selected leg in opposite dimension if condition is met. */ + else if (not_full != selected_in_curr_leg) { + return -1; + } + /* Extrusion area starts/ends if met. */ + if (selected_in_prev_leg != selected_in_curr_leg) { + copy_intervals[ins] = selected_in_curr_leg == max_i || j == 0 ? j : j - 1; + ins++; + selected_in_prev_leg = selected_in_curr_leg; + } + copy_intervals[ins] = j; + } + if (selected_leg_count && + /* Prevents leading and trailing unselected legs if all selected. + * Unless it is extrusion from point or curve.*/ + (selected_leg_count < max_j || max_j == 1)) { + /* Prepend unselected leg if more than one leg selected at the starting edge. + * max_j == 1 handles extrusion from point to curve and from curve to surface cases. */ + if (is_first_sel && (copy_intervals[0] < copy_intervals[1] || max_j == 1)) { + memmove(copy_intervals + 1, copy_intervals, (ins + 1) * sizeof(copy_intervals[0])); + copy_intervals[0] = 0; + ins++; + is_first_sel = false; + } + /* Append unselected leg if more than one leg selected at the end. */ + if (is_last_sel && copy_intervals[ins - 1] < copy_intervals[ins]) { + copy_intervals[ins + 1] = copy_intervals[ins]; + ins++; + } + } + *interval_count = ins; + *out_is_first_sel = ins > 1 ? is_first_sel : false; + return selected_leg_count; +} - if (ELEM(u, 0, nu->pntsv - 1)) { /* row in u-direction selected */ - ok = true; - newbp = (BPoint *)MEM_mallocN(nu->pntsu * (nu->pntsv + 1) * sizeof(BPoint), - "extrudeNurb1"); - if (u == 0) { - len = nu->pntsv * nu->pntsu; - ED_curve_bpcpy(editnurb, newbp + nu->pntsu, nu->bp, len); - ED_curve_bpcpy(editnurb, newbp, nu->bp, nu->pntsu); - bp = newbp; - } - else { - len = nu->pntsv * nu->pntsu; - ED_curve_bpcpy(editnurb, newbp, nu->bp, len); - ED_curve_bpcpy(editnurb, newbp + len, &nu->bp[len - nu->pntsu], nu->pntsu); - bp = newbp + len; - } +typedef struct NurbDim { + int pntsu; + int pntsv; +} NurbDim; - a = nu->pntsu; - while (a--) { - select_bpoint(bp, SELECT, flag, HIDDEN); - bp++; - } +static NurbDim editnurb_find_max_points_num(const EditNurb *editnurb) +{ + NurbDim ret = {0, 0}; + LISTBASE_FOREACH (Nurb *, nu, &editnurb->nurbs) { + if (nu->pntsu > ret.pntsu) { + ret.pntsu = nu->pntsu; + } + if (nu->pntsv > ret.pntsv) { + ret.pntsv = nu->pntsv; + } + } + return ret; +} - MEM_freeN(nu->bp); - nu->bp = newbp; - nu->pntsv++; - BKE_nurb_knot_calc_v(nu); - } - else if (ELEM(v, 0, nu->pntsu - 1)) { /* column in v-direction selected */ - ok = true; - bpn = newbp = (BPoint *)MEM_mallocN((nu->pntsu + 1) * nu->pntsv * sizeof(BPoint), - "extrudeNurb1"); - bp = nu->bp; +bool ed_editnurb_extrude_flag(EditNurb *editnurb, const uint8_t flag) +{ + const NurbDim max = editnurb_find_max_points_num(editnurb); + /* One point induces at most one interval. Except single point case, it can give + 1. + * Another +1 is for first element of the first interval. */ + int *const intvls_u = MEM_malloc_arrayN(max.pntsu + 2, sizeof(int), "extrudeNurb0"); + int *const intvls_v = MEM_malloc_arrayN(max.pntsv + 2, sizeof(int), "extrudeNurb1"); + bool ok = false; - for (a = 0; a < nu->pntsv; a++) { - if (v == 0) { - *bpn = *bp; - bpn->f1 |= flag; - bpn++; - } - ED_curve_bpcpy(editnurb, bpn, bp, nu->pntsu); - bp += nu->pntsu; - bpn += nu->pntsu; - if (v == nu->pntsu - 1) { - *bpn = *(bp - 1); - bpn->f1 |= flag; - bpn++; - } - } + LISTBASE_FOREACH (Nurb *, nu, &editnurb->nurbs) { + int intvl_cnt_u; + bool is_first_sel_u; - MEM_freeN(nu->bp); - nu->bp = newbp; - nu->pntsu++; - BKE_nurb_knot_calc_u(nu); - } - } + /* Calculate selected U legs and intervals for their extrusion. */ + const int selected_us = sel_to_copy_ints( + nu->bp, 1, nu->pntsu, nu->pntsu, nu->pntsv, flag, intvls_u, &intvl_cnt_u, &is_first_sel_u); + if (selected_us == -1) { + continue; } - } + int intvl_cnt_v; + bool is_first_sel_v; + const bool is_point = nu->pntsu == 1; + const bool is_curve = nu->pntsv == 1; + const bool extrude_every_u_point = selected_us == nu->pntsu; + if (is_point || (is_curve && !extrude_every_u_point)) { + intvls_v[0] = intvls_v[1] = 0; + intvl_cnt_v = 1; + is_first_sel_v = false; + } + else { + sel_to_copy_ints(nu->bp, + nu->pntsu, + nu->pntsv, + 1, + nu->pntsu, + flag, + intvls_v, + &intvl_cnt_v, + &is_first_sel_v); + } + + const int new_pntsu = nu->pntsu + intvl_cnt_u - 1; + const int new_pntsv = nu->pntsv + intvl_cnt_v - 1; + BPoint *const new_bp = (BPoint *)MEM_malloc_arrayN( + new_pntsu * new_pntsv, sizeof(BPoint), "extrudeNurb2"); + BPoint *new_bp_v = new_bp; + + bool selected_v = is_first_sel_v; + for (int j = 1; j <= intvl_cnt_v; j++, selected_v = !selected_v) { + BPoint *old_bp_v = nu->bp + intvls_v[j - 1] * nu->pntsu; + for (int v_j = intvls_v[j - 1]; v_j <= intvls_v[j]; + v_j++, new_bp_v += new_pntsu, old_bp_v += nu->pntsu) { + BPoint *new_bp_u_v = new_bp_v; + bool selected_u = is_first_sel_u; + for (int i = 1; i <= intvl_cnt_u; i++, selected_u = !selected_u) { + const int copy_from = intvls_u[i - 1]; + const int copy_to = intvls_u[i]; + const int copy_count = copy_to - copy_from + 1; + const bool sel_status = selected_u || selected_v ? SELECT : DESELECT; + ED_curve_bpcpy(editnurb, new_bp_u_v, old_bp_v + copy_from, copy_count); + select_bpoints(new_bp_u_v, 1, copy_count, sel_status, flag, HIDDEN); + new_bp_u_v += copy_count; + } + } + } + + MEM_freeN(nu->bp); + nu->bp = new_bp; + nu->pntsu = new_pntsu; + if (nu->pntsv == 1 && new_pntsv > 1) { + nu->orderv = 2; + } + nu->pntsv = new_pntsv; + BKE_nurb_knot_calc_u(nu); + BKE_nurb_knot_calc_v(nu); + + ok = true; + } + MEM_freeN(intvls_u); + MEM_freeN(intvls_v); return ok; } @@ -5695,23 +5716,12 @@ static int curve_extrude_exec(bContext *C, wmOperator *UNUSED(op)) Curve *cu = obedit->data; EditNurb *editnurb = cu->editnurb; bool changed = false; - bool as_curve = false; if (!ED_curve_select_check(v3d, cu->editnurb)) { continue; } - /* First test: curve? */ - if (obedit->type != OB_CURVES_LEGACY) { - LISTBASE_FOREACH (Nurb *, nu, &editnurb->nurbs) { - if ((nu->pntsv == 1) && (ED_curve_nurb_select_count(v3d, nu) < nu->pntsu)) { - as_curve = true; - break; - } - } - } - - if (obedit->type == OB_CURVES_LEGACY || as_curve) { + if (obedit->type == OB_CURVES_LEGACY) { changed = ed_editcurve_extrude(cu, editnurb, v3d); } else { diff --git a/source/blender/editors/gpencil/gpencil_add_blank.c b/source/blender/editors/gpencil/gpencil_add_blank.c index e8e6d328804..b88b33913ac 100644 --- a/source/blender/editors/gpencil/gpencil_add_blank.c +++ b/source/blender/editors/gpencil/gpencil_add_blank.c @@ -19,6 +19,8 @@ #include "BKE_main.h" #include "BKE_material.h" +#include "BLT_translation.h" + #include "DEG_depsgraph.h" #include "ED_gpencil.h" @@ -34,7 +36,7 @@ typedef struct ColorTemplate { static int gpencil_stroke_material(Main *bmain, Object *ob, const ColorTemplate *pct) { int index; - Material *ma = BKE_gpencil_object_material_ensure_by_name(bmain, ob, pct->name, &index); + Material *ma = BKE_gpencil_object_material_ensure_by_name(bmain, ob, DATA_(pct->name), &index); copy_v4_v4(ma->gp_style->stroke_rgba, pct->line); srgb_to_linearrgb_v4(ma->gp_style->stroke_rgba, ma->gp_style->stroke_rgba); @@ -52,7 +54,7 @@ static int gpencil_stroke_material(Main *bmain, Object *ob, const ColorTemplate /* Color Data */ static const ColorTemplate gp_stroke_material_black = { - "Black", + N_("Black"), {0.0f, 0.0f, 0.0f, 1.0f}, {0.0f, 0.0f, 0.0f, 0.0f}, }; diff --git a/source/blender/editors/gpencil/gpencil_add_lineart.c b/source/blender/editors/gpencil/gpencil_add_lineart.c index 964a57a8ced..2ac26c927f4 100644 --- a/source/blender/editors/gpencil/gpencil_add_lineart.c +++ b/source/blender/editors/gpencil/gpencil_add_lineart.c @@ -21,6 +21,8 @@ #include "BKE_main.h" #include "BKE_material.h" +#include "BLT_translation.h" + #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" @@ -40,7 +42,7 @@ static int gpencil_lineart_material(Main *bmain, const bool fill) { int index; - Material *ma = BKE_gpencil_object_material_ensure_by_name(bmain, ob, pct->name, &index); + Material *ma = BKE_gpencil_object_material_ensure_by_name(bmain, ob, DATA_(pct->name), &index); copy_v4_v4(ma->gp_style->stroke_rgba, pct->line); srgb_to_linearrgb_v4(ma->gp_style->stroke_rgba, ma->gp_style->stroke_rgba); @@ -59,7 +61,7 @@ static int gpencil_lineart_material(Main *bmain, /* Color Data */ static const ColorTemplate gp_stroke_material_black = { - "Black", + N_("Black"), {0.0f, 0.0f, 0.0f, 1.0f}, {0.0f, 0.0f, 0.0f, 0.0f}, }; diff --git a/source/blender/editors/gpencil/gpencil_add_monkey.c b/source/blender/editors/gpencil/gpencil_add_monkey.c index bc046e89d21..ce38c261c1f 100644 --- a/source/blender/editors/gpencil/gpencil_add_monkey.c +++ b/source/blender/editors/gpencil/gpencil_add_monkey.c @@ -19,6 +19,8 @@ #include "BKE_main.h" #include "BKE_material.h" +#include "BLT_translation.h" + #include "DEG_depsgraph.h" #include "ED_gpencil.h" @@ -54,7 +56,7 @@ static int gpencil_monkey_color( Main *bmain, Object *ob, const ColorTemplate *pct, bool stroke, bool fill) { int index; - Material *ma = BKE_gpencil_object_material_ensure_by_name(bmain, ob, pct->name, &index); + Material *ma = BKE_gpencil_object_material_ensure_by_name(bmain, ob, DATA_(pct->name), &index); copy_v4_v4(ma->gp_style->stroke_rgba, pct->line); srgb_to_linearrgb_v4(ma->gp_style->stroke_rgba, ma->gp_style->stroke_rgba); @@ -781,37 +783,37 @@ static const float data27[33 * GP_PRIM_DATABUF_SIZE] = { /* Monkey Color Data */ static const ColorTemplate gp_monkey_pct_black = { - "Black", + N_("Black"), {0.0f, 0.0f, 0.0f, 1.0f}, {0.0f, 0.0f, 0.0f, 0.0f}, }; static const ColorTemplate gp_monkey_pct_skin = { - "Skin", + N_("Skin"), {0.733f, 0.569f, 0.361f, 1.0f}, {0.745f, 0.502f, 0.278f, 1.0f}, }; static const ColorTemplate gp_monkey_pct_skin_light = { - "Skin_Light", + N_("Skin_Light"), {0.914f, 0.827f, 0.635f, 1.0f}, {0.913f, 0.828f, 0.637f, 0.0f}, }; static const ColorTemplate gp_monkey_pct_skin_shadow = { - "Skin_Shadow", + N_("Skin_Shadow"), {0.322f, 0.29f, 0.224f, 0.5f}, {0.32f, 0.29f, 0.223f, 0.3f}, }; static const ColorTemplate gp_monkey_pct_eyes = { - "Eyes", + N_("Eyes"), {0.553f, 0.39f, 0.266f, 0.0f}, {0.847f, 0.723f, 0.599f, 1.0f}, }; static const ColorTemplate gp_monkey_pct_pupils = { - "Pupils", + N_("Pupils"), {0.0f, 0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f, 1.0f}, }; diff --git a/source/blender/editors/gpencil/gpencil_add_stroke.c b/source/blender/editors/gpencil/gpencil_add_stroke.c index e24964c4832..4687f9188fd 100644 --- a/source/blender/editors/gpencil/gpencil_add_stroke.c +++ b/source/blender/editors/gpencil/gpencil_add_stroke.c @@ -19,6 +19,8 @@ #include "BKE_main.h" #include "BKE_material.h" +#include "BLT_translation.h" + #include "DEG_depsgraph.h" #include "ED_gpencil.h" @@ -37,7 +39,7 @@ static int gpencil_stroke_material(Main *bmain, const bool fill) { int index; - Material *ma = BKE_gpencil_object_material_ensure_by_name(bmain, ob, pct->name, &index); + Material *ma = BKE_gpencil_object_material_ensure_by_name(bmain, ob, DATA_(pct->name), &index); copy_v4_v4(ma->gp_style->stroke_rgba, pct->line); srgb_to_linearrgb_v4(ma->gp_style->stroke_rgba, ma->gp_style->stroke_rgba); @@ -150,37 +152,37 @@ static const float data0[175 * GP_PRIM_DATABUF_SIZE] = { /* Color Data */ static const ColorTemplate gp_stroke_material_black = { - "Black", + N_("Black"), {0.0f, 0.0f, 0.0f, 1.0f}, {0.0f, 0.0f, 0.0f, 0.0f}, }; static const ColorTemplate gp_stroke_material_white = { - "White", + N_("White"), {1.0f, 1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f, 0.0f}, }; static const ColorTemplate gp_stroke_material_red = { - "Red", + N_("Red"), {1.0f, 0.0f, 0.0f, 1.0f}, {0.0f, 0.0f, 0.0f, 0.0f}, }; static const ColorTemplate gp_stroke_material_green = { - "Green", + N_("Green"), {0.0f, 1.0f, 0.0f, 1.0f}, {0.0f, 0.0f, 0.0f, 0.0f}, }; static const ColorTemplate gp_stroke_material_blue = { - "Blue", + N_("Blue"), {0.0f, 0.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f, 0.0f}, }; static const ColorTemplate gp_stroke_material_grey = { - "Grey", + N_("Grey"), {0.358f, 0.358f, 0.358f, 1.0f}, {0.5f, 0.5f, 0.5f, 1.0f}, }; diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index 69a74c36aee..c05ab8c6b28 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -3395,6 +3395,7 @@ static int gpencil_stroke_caps_set_exec(bContext *C, wmOperator *op) bGPdata *gpd = ED_gpencil_data_get_active(C); Object *ob = CTX_data_active_object(C); const int type = RNA_enum_get(op->ptr, "type"); + const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); /* sanity checks */ if (ELEM(NULL, gpd)) { @@ -3404,46 +3405,57 @@ static int gpencil_stroke_caps_set_exec(bContext *C, wmOperator *op) bool changed = false; /* loop all selected strokes */ CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { - if (gpl->actframe == NULL) { - continue; - } + bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe; - for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) { - MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1); + for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { + if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { + if (gpf == NULL) { + continue; + } - /* skip strokes that are not selected or invalid for current view */ - if (((gps->flag & GP_STROKE_SELECT) == 0) || (ED_gpencil_stroke_can_use(C, gps) == false)) { - continue; - } - /* skip hidden or locked colors */ - if (!gp_style || (gp_style->flag & GP_MATERIAL_HIDE) || - (gp_style->flag & GP_MATERIAL_LOCKED)) { - continue; - } + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1); - short prev_first = gps->caps[0]; - short prev_last = gps->caps[1]; + /* skip strokes that are not selected or invalid for current view */ + if (((gps->flag & GP_STROKE_SELECT) == 0) || + (ED_gpencil_stroke_can_use(C, gps) == false)) { + continue; + } + /* skip hidden or locked colors */ + if (!gp_style || (gp_style->flag & GP_MATERIAL_HIDE) || + (gp_style->flag & GP_MATERIAL_LOCKED)) { + continue; + } + + short prev_first = gps->caps[0]; + short prev_last = gps->caps[1]; + + if (ELEM(type, GP_STROKE_CAPS_TOGGLE_BOTH, GP_STROKE_CAPS_TOGGLE_START)) { + ++gps->caps[0]; + if (gps->caps[0] >= GP_STROKE_CAP_MAX) { + gps->caps[0] = GP_STROKE_CAP_ROUND; + } + } + if (ELEM(type, GP_STROKE_CAPS_TOGGLE_BOTH, GP_STROKE_CAPS_TOGGLE_END)) { + ++gps->caps[1]; + if (gps->caps[1] >= GP_STROKE_CAP_MAX) { + gps->caps[1] = GP_STROKE_CAP_ROUND; + } + } + if (type == GP_STROKE_CAPS_TOGGLE_DEFAULT) { + gps->caps[0] = GP_STROKE_CAP_ROUND; + gps->caps[1] = GP_STROKE_CAP_ROUND; + } - if (ELEM(type, GP_STROKE_CAPS_TOGGLE_BOTH, GP_STROKE_CAPS_TOGGLE_START)) { - ++gps->caps[0]; - if (gps->caps[0] >= GP_STROKE_CAP_MAX) { - gps->caps[0] = GP_STROKE_CAP_ROUND; + if (prev_first != gps->caps[0] || prev_last != gps->caps[1]) { + changed = true; + } } - } - if (ELEM(type, GP_STROKE_CAPS_TOGGLE_BOTH, GP_STROKE_CAPS_TOGGLE_END)) { - ++gps->caps[1]; - if (gps->caps[1] >= GP_STROKE_CAP_MAX) { - gps->caps[1] = GP_STROKE_CAP_ROUND; + /* If not multi-edit, exit loop. */ + if (!is_multiedit) { + break; } } - if (type == GP_STROKE_CAPS_TOGGLE_DEFAULT) { - gps->caps[0] = GP_STROKE_CAP_ROUND; - gps->caps[1] = GP_STROKE_CAP_ROUND; - } - - if (prev_first != gps->caps[0] || prev_last != gps->caps[1]) { - changed = true; - } } } CTX_DATA_END; @@ -3550,9 +3562,9 @@ static int gpencil_stroke_join_exec(bContext *C, wmOperator *op) bGPdata *gpd = ED_gpencil_data_get_active(C); bGPDlayer *activegpl = BKE_gpencil_layer_active_get(gpd); Object *ob = CTX_data_active_object(C); - /* Limit the number of strokes to join. It makes no sense to allow an very high number of strokes - * for CPU time and because to have a stroke with thousands of points is unpractical, so limit - * this number avoid to joining a full frame scene in one single stroke. */ + /* Limit the number of strokes to join. It makes no sense to allow an very high number of + * strokes for CPU time and because to have a stroke with thousands of points is unpractical, + * so limit this number avoid to joining a full frame scene in one single stroke. */ const int max_join_strokes = 128; const int type = RNA_enum_get(op->ptr, "type"); diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h index 8ecd1d850d1..71ddffca8a9 100644 --- a/source/blender/editors/include/ED_mesh.h +++ b/source/blender/editors/include/ED_mesh.h @@ -144,6 +144,7 @@ void BM_uv_element_map_free(struct UvElementMap *element_map); struct UvElement *BM_uv_element_get(struct UvElementMap *map, struct BMFace *efa, struct BMLoop *l); +struct UvElement *BM_uv_element_get_head(struct UvElementMap *map, struct UvElement *child); /** * Can we edit UV's for this mesh? diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 0298983ed26..931bb7be8bf 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -1196,6 +1196,28 @@ bool ED_view3d_camera_lock_autokey(struct View3D *v3d, void ED_view3d_lock_clear(struct View3D *v3d); +/** + * Create an undo step when the camera is locked to the view. + * \param str: The name of the undo step (typically #wmOperatorType.name should be used). + * + * \return true when the call to push an undo step was made. + */ +bool ED_view3d_camera_lock_undo_push(const char *str, + View3D *v3d, + struct RegionView3D *rv3d, + struct bContext *C); + +/** + * A version of #ED_view3d_camera_lock_undo_push that performs a grouped undo push. + * + * \note use for actions that are likely to be repeated such as mouse wheel to zoom, + * where adding a separate undo step each time isn't desirable. + */ +bool ED_view3d_camera_lock_undo_grouped_push(const char *str, + View3D *v3d, + struct RegionView3D *rv3d, + struct bContext *C); + #define VIEW3D_MARGIN 1.4f #define VIEW3D_DIST_FALLBACK 1.0f diff --git a/source/blender/editors/interface/interface_panel.cc b/source/blender/editors/interface/interface_panel.cc index 53f1265ee74..dc6a0fecb73 100644 --- a/source/blender/editors/interface/interface_panel.cc +++ b/source/blender/editors/interface/interface_panel.cc @@ -1090,7 +1090,7 @@ static void panel_draw_aligned_widgets(const uiStyle *style, const int header_height = BLI_rcti_size_y(header_rect); const int scaled_unit = round_fl_to_int(UI_UNIT_X / aspect); - /* Offset triangle and text to the right for subpanels. */ + /* Offset triangle and text to the right for sub-panels. */ rcti widget_rect; widget_rect.xmin = header_rect->xmin + (is_subpanel ? scaled_unit * 0.7f : 0); widget_rect.xmax = header_rect->xmax; @@ -1285,7 +1285,7 @@ bool UI_panel_should_show_background(const ARegion *region, const PanelType *pan void UI_panel_category_draw_all(ARegion *region, const char *category_id_active) { // #define USE_FLAT_INACTIVE - const bool is_left = (bool)RGN_ALIGN_ENUM_FROM_MASK(region->alignment != RGN_ALIGN_RIGHT); + const bool is_left = RGN_ALIGN_ENUM_FROM_MASK(region->alignment) != RGN_ALIGN_RIGHT; View2D *v2d = ®ion->v2d; const uiStyle *style = UI_style_get(); const uiFontStyle *fstyle = &style->widget; @@ -2092,7 +2092,7 @@ static void ui_handle_panel_header(const bContext *C, ui_panel_drag_collapse_handler_add(C, UI_panel_is_closed(panel)); } - /* Set panel custom data (modifier) active when expanding subpanels, but not top-level + /* Set panel custom data (modifier) active when expanding sub-panels, but not top-level * panels to allow collapsing and expanding without setting the active element. */ if (is_subpanel) { panel_custom_data_active_set(panel); diff --git a/source/blender/editors/interface/interface_region_search.cc b/source/blender/editors/interface/interface_region_search.cc index f04229609f9..6bb47666afd 100644 --- a/source/blender/editors/interface/interface_region_search.cc +++ b/source/blender/editors/interface/interface_region_search.cc @@ -710,18 +710,18 @@ static ARegion *ui_searchbox_create_generic_ex(bContext *C, type.regionid = RGN_TYPE_TEMPORARY; region->type = &type; - /* create searchbox data */ + /* Create search-box data. */ uiSearchboxData *data = MEM_cnew<uiSearchboxData>(__func__); - /* set font, get bb */ + /* Set font, get the bounding-box. */ data->fstyle = style->widget; /* copy struct */ ui_fontscale(&data->fstyle.points, aspect); UI_fontstyle_set(&data->fstyle); region->regiondata = data; - /* special case, hardcoded feature, not draw backdrop when called from menus, - * assume for design that popup already added it */ + /* Special case, hard-coded feature, not draw backdrop when called from menus, + * assume for design that popup already added it. */ if (but->block->flag & UI_BLOCK_SEARCH_MENU) { data->noback = true; } diff --git a/source/blender/editors/interface/interface_region_tooltip.cc b/source/blender/editors/interface/interface_region_tooltip.cc index e18d23c8a1c..6a39b761983 100644 --- a/source/blender/editors/interface/interface_region_tooltip.cc +++ b/source/blender/editors/interface/interface_region_tooltip.cc @@ -90,8 +90,10 @@ struct uiTooltipField { char *text; char *text_suffix; struct { - uint x_pos; /* x cursor position at the end of the last line */ - uint lines; /* number of lines, 1 or more with word-wrap */ + /** X cursor position at the end of the last line. */ + uint x_pos; + /** Number of lines, 1 or more with word-wrap. */ + uint lines; } geom; uiTooltipFormat format; }; @@ -165,7 +167,7 @@ static void ui_tooltip_region_draw_cb(const bContext *UNUSED(C), ARegion *region float tip_colors[UI_TIP_LC_MAX][3]; uchar drawcol[4] = {0, 0, 0, 255}; /* to store color in while drawing (alpha is always 255) */ - /* the color from the theme */ + /* The color from the theme. */ float *main_color = tip_colors[static_cast<int>(uiTooltipFormat::ColorID::Main)]; float *value_color = tip_colors[static_cast<int>(uiTooltipFormat::ColorID::Value)]; float *active_color = tip_colors[static_cast<int>(uiTooltipFormat::ColorID::Active)]; @@ -177,13 +179,13 @@ static void ui_tooltip_region_draw_cb(const bContext *UNUSED(C), ARegion *region wmOrtho2_region_pixelspace(region); - /* draw background */ + /* Draw background. */ ui_draw_tooltip_background(UI_style_get(), nullptr, &bbox); /* set background_color */ rgb_uchar_to_float(background_color, theme->inner); - /* calculate normal_color */ + /* Calculate `normal_color`. */ rgb_uchar_to_float(main_color, theme->text); copy_v3_v3(active_color, main_color); copy_v3_v3(normal_color, main_color); @@ -191,19 +193,19 @@ static void ui_tooltip_region_draw_cb(const bContext *UNUSED(C), ARegion *region copy_v3_v3(alert_color, main_color); copy_v3_v3(value_color, main_color); - /* find the brightness difference between background and text colors */ + /* Find the brightness difference between background and text colors. */ const float tone_bg = rgb_to_grayscale(background_color); - /* tone_fg = rgb_to_grayscale(main_color); */ + // tone_fg = rgb_to_grayscale(main_color); - /* mix the colors */ + /* Mix the colors. */ rgb_tint(value_color, 0.0f, 0.0f, tone_bg, 0.2f); /* Light gray. */ rgb_tint(active_color, 0.6f, 0.2f, tone_bg, 0.2f); /* Light blue. */ rgb_tint(normal_color, 0.0f, 0.0f, tone_bg, 0.4f); /* Gray. */ rgb_tint(python_color, 0.0f, 0.0f, tone_bg, 0.5f); /* Dark gray. */ rgb_tint(alert_color, 0.0f, 0.8f, tone_bg, 0.1f); /* Red. */ - /* draw text */ + /* Draw text. */ BLF_wordwrap(data->fstyle.uifont_id, data->wrap_width); BLF_wordwrap(blf_mono_font, data->wrap_width); @@ -221,12 +223,12 @@ static void ui_tooltip_region_draw_cb(const bContext *UNUSED(C), ARegion *region fs_params.align = UI_STYLE_TEXT_LEFT; fs_params.word_wrap = true; - /* draw header and active data (is done here to be able to change color) */ + /* Draw header and active data (is done here to be able to change color). */ rgb_float_to_uchar(drawcol, tip_colors[static_cast<int>(uiTooltipFormat::ColorID::Main)]); UI_fontstyle_set(&data->fstyle); UI_fontstyle_draw(&data->fstyle, &bbox, field->text, UI_TIP_STR_MAX, drawcol, &fs_params); - /* offset to the end of the last line */ + /* Offset to the end of the last line. */ if (field->text_suffix) { const float xofs = field->geom.x_pos; const float yofs = data->lineh * (field->geom.lines - 1); @@ -238,7 +240,7 @@ static void ui_tooltip_region_draw_cb(const bContext *UNUSED(C), ARegion *region UI_fontstyle_draw( &data->fstyle, &bbox, field->text_suffix, UI_TIP_STR_MAX, drawcol, &fs_params); - /* undo offset */ + /* Undo offset. */ bbox.xmin -= xofs; bbox.ymax += yofs; } @@ -251,7 +253,7 @@ static void ui_tooltip_region_draw_cb(const bContext *UNUSED(C), ARegion *region fstyle_mono.uifont_id = blf_mono_font; UI_fontstyle_set(&fstyle_mono); - /* XXX, needed because we don't have mono in 'U.uifonts' */ + /* XXX: needed because we don't have mono in 'U.uifonts'. */ BLF_size(fstyle_mono.uifont_id, fstyle_mono.points * U.pixelsize, U.dpi); rgb_float_to_uchar(drawcol, tip_colors[static_cast<int>(field->format.color_id)]); UI_fontstyle_draw(&fstyle_mono, &bbox, field->text, UI_TIP_STR_MAX, drawcol, &fs_params); @@ -262,7 +264,7 @@ static void ui_tooltip_region_draw_cb(const bContext *UNUSED(C), ARegion *region fs_params.align = UI_STYLE_TEXT_LEFT; fs_params.word_wrap = true; - /* draw remaining data */ + /* Draw remaining data. */ rgb_float_to_uchar(drawcol, tip_colors[static_cast<int>(field->format.color_id)]); UI_fontstyle_set(&data->fstyle); UI_fontstyle_draw(&data->fstyle, &bbox, field->text, UI_TIP_STR_MAX, drawcol, &fs_params); @@ -327,13 +329,13 @@ static bool ui_tooltip_data_append_from_keymap(bContext *C, uiTooltipData *data, LISTBASE_FOREACH (wmKeyMapItem *, kmi, &keymap->items) { wmOperatorType *ot = WM_operatortype_find(kmi->idname, true); if (ot != nullptr) { - /* Tip */ + /* Tip. */ { uiTooltipField *field = text_field_add( data, uiTooltipFormat::Style::Normal, uiTooltipFormat::ColorID::Main, true); field->text = BLI_strdup(ot->description ? ot->description : ot->name); } - /* Shortcut */ + /* Shortcut. */ { uiTooltipField *field = text_field_add( data, uiTooltipFormat::Style::Normal, uiTooltipFormat::ColorID::Normal); @@ -344,7 +346,7 @@ static bool ui_tooltip_data_append_from_keymap(bContext *C, uiTooltipData *data, field->text = BLI_sprintfN(TIP_("Shortcut: %s"), found ? buf : "None"); } - /* Python */ + /* Python. */ if (U.flag & USER_TOOLTIPS_PYTHON) { uiTooltipField *field = text_field_add( data, uiTooltipFormat::Style::Normal, uiTooltipFormat::ColorID::Python); @@ -408,9 +410,8 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is uiTooltipData *data = MEM_cnew<uiTooltipData>(__func__); #ifdef WITH_PYTHON - /* it turns out to be most simple to do this via Python since C - * doesn't have access to information about non-active tools. - */ + /* It turns out to be most simple to do this via Python since C + * doesn't have access to information about non-active tools. */ /* Title (when icon-only). */ if (but->drawstr[0] == '\0') { @@ -781,8 +782,8 @@ static uiTooltipData *ui_tooltip_data_from_button_or_extra_icon(bContext *C, /* Tip Label (only for buttons not already showing the label). * Check prefix instead of comparing because the button may include the shortcut. - * Buttons with dynamic tooltips also don't get their default label here since they - * can already provide more accurate and specific tooltip content. */ + * Buttons with dynamic tool-tips also don't get their default label here since they + * can already provide more accurate and specific tool-tip content. */ if (but_label.strinfo && !STRPREFIX(but->drawstr, but_label.strinfo) && !but->tip_func) { uiTooltipField *field = text_field_add( data, uiTooltipFormat::Style::Header, uiTooltipFormat::ColorID::Normal); @@ -811,21 +812,21 @@ static uiTooltipData *ui_tooltip_data_from_button_or_extra_icon(bContext *C, field->text = BLI_strdup(TIP_("(Shift-Click/Drag to select multiple)")); } } - /* Enum field label & tip */ + /* Enum field label & tip. */ if (enum_tip.strinfo) { uiTooltipField *field = text_field_add( data, uiTooltipFormat::Style::Normal, uiTooltipFormat::ColorID::Value); field->text = BLI_strdup(enum_tip.strinfo); } - /* Op shortcut */ + /* Operator shortcut. */ if (op_keymap.strinfo) { uiTooltipField *field = text_field_add( data, uiTooltipFormat::Style::Normal, uiTooltipFormat::ColorID::Value, true); field->text = BLI_sprintfN(TIP_("Shortcut: %s"), op_keymap.strinfo); } - /* Property context-toggle shortcut */ + /* Property context-toggle shortcut. */ if (prop_keymap.strinfo) { uiTooltipField *field = text_field_add( data, uiTooltipFormat::Style::Normal, uiTooltipFormat::ColorID::Value, true); @@ -833,9 +834,9 @@ static uiTooltipData *ui_tooltip_data_from_button_or_extra_icon(bContext *C, } if (ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) { - /* better not show the value of a password */ + /* Better not show the value of a password. */ if ((rnaprop && (RNA_property_subtype(rnaprop) == PROP_PASSWORD)) == 0) { - /* full string */ + /* Full string. */ ui_but_string_get(but, buf, sizeof(buf)); if (buf[0]) { uiTooltipField *field = text_field_add( @@ -879,15 +880,15 @@ static uiTooltipData *ui_tooltip_data_from_button_or_extra_icon(bContext *C, } else if (optype) { PointerRNA *opptr = extra_icon ? UI_but_extra_operator_icon_opptr_get(extra_icon) : - /* allocated when needed, the button owns it */ + /* Allocated when needed, the button owns it. */ UI_but_operator_ptr_get(but); - /* so the context is passed to fieldf functions (some py fieldf functions use it) */ + /* So the context is passed to field functions (some Python field functions use it). */ WM_operator_properties_sanitize(opptr, false); char *str = ui_tooltip_text_python_from_op(C, optype, opptr); - /* operator info */ + /* Operator info. */ if (U.flag & USER_TOOLTIPS_PYTHON) { uiTooltipField *field = text_field_add( data, uiTooltipFormat::Style::Mono, uiTooltipFormat::ColorID::Python, true); @@ -897,12 +898,12 @@ static uiTooltipData *ui_tooltip_data_from_button_or_extra_icon(bContext *C, MEM_freeN(str); } - /* button is disabled, we may be able to tell user why */ + /* Button is disabled, we may be able to tell user why. */ if ((but->flag & UI_BUT_DISABLED) || extra_icon) { const char *disabled_msg = nullptr; bool disabled_msg_free = false; - /* if operator poll check failed, it can give pretty precise info why */ + /* If operator poll check failed, it can give pretty precise info why. */ if (optype) { const wmOperatorCallContext opcontext = extra_icon ? extra_icon->optype_params->opcontext : but->opcontext; @@ -913,7 +914,7 @@ static uiTooltipData *ui_tooltip_data_from_button_or_extra_icon(bContext *C, ui_but_context_poll_operator_ex(C, but, &call_params); disabled_msg = CTX_wm_operator_poll_msg_get(C, &disabled_msg_free); } - /* alternatively, buttons can store some reasoning too */ + /* Alternatively, buttons can store some reasoning too. */ else if (!extra_icon && but->disabled_info) { disabled_msg = TIP_(but->disabled_info); } @@ -937,7 +938,7 @@ static uiTooltipData *ui_tooltip_data_from_button_or_extra_icon(bContext *C, field->text = BLI_sprintfN(TIP_("Python: %s.%s"), rna_struct.strinfo, rna_prop.strinfo); } else { - /* Only struct (e.g. menus) */ + /* Only struct (e.g. menus). */ field->text = BLI_sprintfN(TIP_("Python: %s"), rna_struct.strinfo); } } @@ -946,7 +947,7 @@ static uiTooltipData *ui_tooltip_data_from_button_or_extra_icon(bContext *C, uiTooltipField *field = text_field_add( data, uiTooltipFormat::Style::Mono, uiTooltipFormat::ColorID::Python); - /* this could get its own 'BUT_GET_...' type */ + /* This could get its own `BUT_GET_...` type. */ /* never fails */ /* Move ownership (no need for re-allocation). */ @@ -997,7 +998,7 @@ static uiTooltipData *ui_tooltip_data_from_gizmo(bContext *C, wmGizmo *gz) { uiTooltipData *data = MEM_cnew<uiTooltipData>(__func__); - /* TODO(campbell): a way for gizmos to have their own descriptions (low priority). */ + /* TODO(@campbellbarton): a way for gizmos to have their own descriptions (low priority). */ /* Operator Actions */ { @@ -1059,7 +1060,7 @@ static uiTooltipData *ui_tooltip_data_from_gizmo(bContext *C, wmGizmo *gz) if (gz->type->target_property_defs_len) { wmGizmoProperty *gz_prop_array = WM_gizmo_target_property_array(gz); for (int i = 0; i < gz->type->target_property_defs_len; i++) { - /* TODO(campbell): function callback descriptions. */ + /* TODO(@campbellbarton): function callback descriptions. */ wmGizmoProperty *gz_prop = &gz_prop_array[i]; if (gz_prop->prop != nullptr) { const char *info = RNA_property_ui_description(gz_prop->prop); @@ -1093,7 +1094,7 @@ static ARegion *ui_tooltip_create_with_data(bContext *C, rcti rect_i; int font_flag = 0; - /* create area region */ + /* Create area region. */ ARegion *region = ui_region_temp_add(CTX_wm_screen(C)); static ARegionType type; @@ -1103,7 +1104,7 @@ static ARegion *ui_tooltip_create_with_data(bContext *C, type.regionid = RGN_TYPE_TEMPORARY; region->type = &type; - /* set font, get bb */ + /* Set font, get bounding-box. */ data->fstyle = style->widget; /* copy struct */ ui_fontscale(&data->fstyle.points, aspect); @@ -1117,7 +1118,7 @@ static ARegion *ui_tooltip_create_with_data(bContext *C, BLF_wordwrap(data->fstyle.uifont_id, data->wrap_width); BLF_wordwrap(blf_mono_font, data->wrap_width); - /* these defines tweaked depending on font */ + /* These defines tweaked depending on font. */ #define TIP_BORDER_X (16.0f / aspect) #define TIP_BORDER_Y (6.0f / aspect) @@ -1186,8 +1187,8 @@ static ARegion *ui_tooltip_create_with_data(bContext *C, /* Clamp to window bounds. */ { - /* Ensure at least 5 px above screen bounds - * UI_UNIT_Y is just a guess to be above the menu item */ + /* Ensure at least 5 px above screen bounds. + * #UI_UNIT_Y is just a guess to be above the menu item. */ if (init_rect_overlap != nullptr) { const int pad = max_ff(1.0f, U.pixelsize) * 5; rcti init_rect; @@ -1333,7 +1334,7 @@ ARegion *UI_tooltip_create_from_button_or_extra_icon( bContext *C, ARegion *butregion, uiBut *but, uiButExtraOpIcon *extra_icon, bool is_label) { wmWindow *win = CTX_wm_window(C); - /* aspect values that shrink text are likely unreadable */ + /* Aspect values that shrink text are likely unreadable. */ const float aspect = min_ff(1.0f, but->block->aspect); float init_position[2]; @@ -1406,10 +1407,8 @@ ARegion *UI_tooltip_create_from_gizmo(bContext *C, wmGizmo *gz) return nullptr; } - /* TODO(harley): - * Julian preferred that the gizmo callback return the 3D bounding box - * which we then project to 2D here. Would make a nice improvement. - */ + /* TODO(@harley): Julian preferred that the gizmo callback return the 3D bounding box + * which we then project to 2D here. Would make a nice improvement. */ if (gz->type->screen_bounds_get) { rcti bounds; if (gz->type->screen_bounds_get(C, gz, &bounds)) { diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c index ac5530c8ea9..195a3686b3b 100644 --- a/source/blender/editors/mesh/editmesh_utils.c +++ b/source/blender/editors/mesh/editmesh_utils.c @@ -603,7 +603,7 @@ static void bm_uv_assign_island(UvElementMap *element_map, int islandbufsize) { element->island = nisland; - map[element - element_map->buf] = islandbufsize; + map[element - element_map->storage] = islandbufsize; /* Copy *element to islandbuf[islandbufsize]. */ islandbuf[islandbufsize].l = element->l; @@ -620,16 +620,16 @@ static int bm_uv_edge_select_build_islands(UvElementMap *element_map, bool uv_selected, int cd_loop_uv_offset) { - int totuv = element_map->totalUVs; + int total_uvs = element_map->total_uvs; /* For each UvElement, locate the "separate" UvElement that precedes it in the linked list. */ - UvElement **head_table = MEM_mallocN(sizeof(*head_table) * totuv, "uv_island_head_table"); - for (int i = 0; i < totuv; i++) { - UvElement *head = element_map->buf + i; + UvElement **head_table = MEM_mallocN(sizeof(*head_table) * total_uvs, "uv_island_head_table"); + for (int i = 0; i < total_uvs; i++) { + UvElement *head = element_map->storage + i; if (head->separate) { UvElement *element = head; while (element) { - head_table[element - element_map->buf] = head; + head_table[element - element_map->storage] = head; element = element->next; if (element && element->separate) { break; @@ -641,12 +641,12 @@ static int bm_uv_edge_select_build_islands(UvElementMap *element_map, /* Depth first search the graph, building islands as we go. */ int nislands = 0; int islandbufsize = 0; - int stack_upper_bound = totuv; + int stack_upper_bound = total_uvs; UvElement **stack_uv = MEM_mallocN(sizeof(*stack_uv) * stack_upper_bound, "uv_island_element_stack"); int stacksize_uv = 0; - for (int i = 0; i < totuv; i++) { - UvElement *element = element_map->buf + i; + for (int i = 0; i < total_uvs; i++) { + UvElement *element = element_map->storage + i; if (element->island != INVALID_ISLAND) { /* Unique UV (element and all it's children) are already part of an island. */ continue; @@ -676,7 +676,7 @@ static int bm_uv_edge_select_build_islands(UvElementMap *element_map, if (!uv_selected || uvedit_edge_select_test(scene, element->l, cd_loop_uv_offset)) { UvElement *next = BM_uv_element_get(element_map, element->l->next->f, element->l->next); if (next->island == INVALID_ISLAND) { - UvElement *tail = head_table[next - element_map->buf]; + UvElement *tail = head_table[next - element_map->storage]; stack_uv[stacksize_uv++] = tail; while (tail) { bm_uv_assign_island(element_map, tail, nislands, map, islandbuf, islandbufsize++); @@ -692,7 +692,7 @@ static int bm_uv_edge_select_build_islands(UvElementMap *element_map, if (!uv_selected || uvedit_edge_select_test(scene, element->l->prev, cd_loop_uv_offset)) { UvElement *prev = BM_uv_element_get(element_map, element->l->prev->f, element->l->prev); if (prev->island == INVALID_ISLAND) { - UvElement *tail = head_table[prev - element_map->buf]; + UvElement *tail = head_table[prev - element_map->storage]; stack_uv[stacksize_uv++] = tail; while (tail) { bm_uv_assign_island(element_map, tail, nislands, map, islandbuf, islandbufsize++); @@ -713,7 +713,7 @@ static int bm_uv_edge_select_build_islands(UvElementMap *element_map, } nislands++; } - BLI_assert(islandbufsize == totuv); + BLI_assert(islandbufsize == total_uvs); MEM_SAFE_FREE(stack_uv); MEM_SAFE_FREE(head_table); @@ -732,26 +732,18 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm, BMVert *ev; BMFace *efa; - BMLoop *l; BMIter iter, liter; - /* vars from original func */ - UvElementMap *element_map; - UvElement *buf; - bool *winding = NULL; BLI_buffer_declare_static(vec2f, tf_uv_buf, BLI_BUFFER_NOP, BM_DEFAULT_NGON_STACK_SIZE); - MLoopUV *luv; - int totverts, totfaces, i, totuv, j; - const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); + if (cd_loop_uv_offset < 0) { + return NULL; + } BM_mesh_elem_index_ensure(bm, BM_VERT | BM_FACE); - totfaces = bm->totface; - totverts = bm->totvert; - totuv = 0; - - /* generate UvElement array */ + /* Count total uvs. */ + int totuv = 0; BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { continue; @@ -765,6 +757,7 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm, totuv += efa->len; } else { + BMLoop *l; BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) { totuv++; @@ -777,17 +770,17 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm, return NULL; } - element_map = (UvElementMap *)MEM_callocN(sizeof(*element_map), "UvElementMap"); - element_map->totalUVs = totuv; - element_map->vert = (UvElement **)MEM_callocN(sizeof(*element_map->vert) * totverts, - "UvElementVerts"); - buf = element_map->buf = (UvElement *)MEM_callocN(sizeof(*element_map->buf) * totuv, - "UvElement"); + UvElementMap *element_map = (UvElementMap *)MEM_callocN(sizeof(*element_map), "UvElementMap"); + element_map->total_uvs = totuv; + element_map->vertex = (UvElement **)MEM_callocN(sizeof(*element_map->vertex) * bm->totvert, + "UvElementVerts"); + element_map->storage = (UvElement *)MEM_callocN(sizeof(*element_map->storage) * totuv, + "UvElement"); - if (use_winding) { - winding = MEM_callocN(sizeof(*winding) * totfaces, "winding"); - } + bool *winding = use_winding ? MEM_callocN(sizeof(*winding) * bm->totface, "winding") : NULL; + UvElement *buf = element_map->storage; + int j; BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, j) { if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { @@ -804,18 +797,20 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm, tf_uv = (float(*)[2])BLI_buffer_reinit_data(&tf_uv_buf, vec2f, efa->len); } + int i; + BMLoop *l; BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) { if (uv_selected && !uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) { continue; } buf->l = l; - buf->separate = 0; buf->island = INVALID_ISLAND; buf->loop_of_poly_index = i; - buf->next = element_map->vert[BM_elem_index_get(l->v)]; - element_map->vert[BM_elem_index_get(l->v)] = buf; + /* Insert to head of linked list associated with BMVert. */ + buf->next = element_map->vertex[BM_elem_index_get(l->v)]; + element_map->vertex[BM_elem_index_get(l->v)] = buf; if (use_winding) { luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); @@ -830,34 +825,33 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm, } } - /* sort individual uvs for each vert */ + /* For each BMVert, sort associated linked list into unique uvs. */ + int i; BM_ITER_MESH_INDEX (ev, &iter, bm, BM_VERTS_OF_MESH, i) { - UvElement *newvlist = NULL, *vlist = element_map->vert[i]; - UvElement *iterv, *v, *lastv, *next; - const float *uv, *uv2; - bool uv_vert_sel, uv2_vert_sel; - + UvElement *newvlist = NULL; + UvElement *vlist = element_map->vertex[i]; while (vlist) { - v = vlist; + + /* Detach head from unsorted list. */ + UvElement *v = vlist; vlist = vlist->next; v->next = newvlist; newvlist = v; - l = v->l; - luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - uv = luv->uv; - uv_vert_sel = uvedit_uv_select_test(scene, l, cd_loop_uv_offset); + luv = BM_ELEM_CD_GET_VOID_P(v->l, cd_loop_uv_offset); + const float *uv = luv->uv; + bool uv_vert_sel = uvedit_uv_select_test(scene, v->l, cd_loop_uv_offset); - lastv = NULL; - iterv = vlist; + UvElement *lastv = NULL; + UvElement *iterv = vlist; + /* Scan through unsorted list, finding UvElements which match `v`. */ while (iterv) { - next = iterv->next; + UvElement *next = iterv->next; - l = iterv->l; - luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - uv2 = luv->uv; - uv2_vert_sel = uvedit_uv_select_test(scene, l, cd_loop_uv_offset); + luv = BM_ELEM_CD_GET_VOID_P(iterv->l, cd_loop_uv_offset); + const float *uv2 = luv->uv; + const bool uv2_vert_sel = uvedit_uv_select_test(scene, iterv->l, cd_loop_uv_offset); /* Check if the uv loops share the same selection state (if not, they are not connected as * they have been ripped or other edit commands have separated them). */ @@ -882,32 +876,30 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm, iterv = next; } - newvlist->separate = 1; + element_map->total_unique_uvs++; + newvlist->separate = true; } - element_map->vert[i] = newvlist; + /* Write back sorted list. */ + element_map->vertex[i] = newvlist; } - if (use_winding) { - MEM_freeN(winding); - } + MEM_SAFE_FREE(winding); if (do_islands) { uint *map; - BMFace **stack; - int stacksize = 0; UvElement *islandbuf; - /* island number for faces */ - int *island_number = NULL; int nislands = 0, islandbufsize = 0; /* map holds the map from current vmap->buf to the new, sorted map */ map = MEM_mallocN(sizeof(*map) * totuv, "uvelement_remap"); - stack = MEM_mallocN(sizeof(*stack) * bm->totface, "uv_island_face_stack"); + BMFace **stack = MEM_mallocN(sizeof(*stack) * bm->totface, "uv_island_face_stack"); islandbuf = MEM_callocN(sizeof(*islandbuf) * totuv, "uvelement_island_buffer"); - island_number = MEM_mallocN(sizeof(*island_number) * totfaces, "uv_island_number_face"); - copy_vn_i(island_number, totfaces, INVALID_ISLAND); + /* Island number for BMFaces. */ + int *island_number = MEM_callocN(sizeof(*island_number) * bm->totface, + "uv_island_number_face"); + copy_vn_i(island_number, bm->totface, INVALID_ISLAND); const bool use_uv_edge_connectivity = scene->toolsettings->uv_flag & UV_SYNC_SELECTION ? scene->toolsettings->selectmode & SCE_SELECT_EDGE : @@ -921,23 +913,25 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm, /* at this point, every UvElement in vert points to a UvElement sharing the same vertex. * Now we should sort uv's in islands. */ for (i = 0; i < totuv; i++) { - if (element_map->buf[i].island == INVALID_ISLAND) { - element_map->buf[i].island = nislands; - stack[0] = element_map->buf[i].l->f; + if (element_map->storage[i].island == INVALID_ISLAND) { + int stacksize = 0; + element_map->storage[i].island = nislands; + stack[0] = element_map->storage[i].l->f; island_number[BM_elem_index_get(stack[0])] = nislands; stacksize = 1; while (stacksize > 0) { efa = stack[--stacksize]; + BMLoop *l; BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { if (uv_selected && !uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) { continue; } - UvElement *element, *initelement = element_map->vert[BM_elem_index_get(l->v)]; + UvElement *initelement = element_map->vertex[BM_elem_index_get(l->v)]; - for (element = initelement; element; element = element->next) { + for (UvElement *element = initelement; element; element = element->next) { if (element->separate) { initelement = element; } @@ -968,13 +962,13 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm, } } - MEM_freeN(island_number); + MEM_SAFE_FREE(island_number); /* remap */ for (i = 0; i < bm->totvert; i++) { /* important since we may do selection only. Some of these may be NULL */ - if (element_map->vert[i]) { - element_map->vert[i] = &islandbuf[map[element_map->vert[i] - element_map->buf]]; + if (element_map->vertex[i]) { + element_map->vertex[i] = &islandbuf[map[element_map->vertex[i] - element_map->storage]]; } } @@ -982,12 +976,12 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm, "UvElementMap_island_indices"); j = 0; for (i = 0; i < totuv; i++) { - UvElement *element = element_map->buf[i].next; + UvElement *element = element_map->storage[i].next; if (element == NULL) { islandbuf[map[i]].next = NULL; } else { - islandbuf[map[i]].next = &islandbuf[map[element - element_map->buf]]; + islandbuf[map[i]].next = &islandbuf[map[element - element_map->storage]]; } if (islandbuf[i].island != j) { @@ -996,16 +990,23 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm, } } - MEM_freeN(element_map->buf); - - element_map->buf = islandbuf; + MEM_SAFE_FREE(element_map->storage); + element_map->storage = islandbuf; + islandbuf = NULL; element_map->totalIslands = nislands; - MEM_freeN(stack); - MEM_freeN(map); + MEM_SAFE_FREE(stack); + MEM_SAFE_FREE(map); } BLI_buffer_free(&tf_uv_buf); + element_map->total_unique_uvs = 0; + for (int i = 0; i < element_map->total_uvs; i++) { + if (element_map->storage[i].separate) { + element_map->total_unique_uvs++; + } + } + return element_map; } @@ -1025,30 +1026,35 @@ void BM_uv_vert_map_free(UvVertMap *vmap) void BM_uv_element_map_free(UvElementMap *element_map) { if (element_map) { - if (element_map->vert) { - MEM_freeN(element_map->vert); - } - if (element_map->buf) { - MEM_freeN(element_map->buf); - } - if (element_map->islandIndices) { - MEM_freeN(element_map->islandIndices); - } - MEM_freeN(element_map); + MEM_SAFE_FREE(element_map->storage); + MEM_SAFE_FREE(element_map->vertex); + MEM_SAFE_FREE(element_map->islandIndices); + MEM_SAFE_FREE(element_map); } } -UvElement *BM_uv_element_get(UvElementMap *map, BMFace *efa, BMLoop *l) +UvElement *BM_uv_element_get(UvElementMap *element_map, BMFace *efa, BMLoop *l) { - for (UvElement *element = map->vert[BM_elem_index_get(l->v)]; element; element = element->next) { + UvElement *element = element_map->vertex[BM_elem_index_get(l->v)]; + while (element) { if (element->l->f == efa) { return element; } + element = element->next; } return NULL; } +UvElement *BM_uv_element_get_head(UvElementMap *element_map, UvElement *child) +{ + if (!child) { + return NULL; + } + + return element_map->vertex[BM_elem_index_get(child->l->v)]; +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c index a664d93bb2e..708f1d02656 100644 --- a/source/blender/editors/object/object_bake_api.c +++ b/source/blender/editors/object/object_bake_api.c @@ -24,6 +24,7 @@ #include "BKE_attribute.h" #include "BKE_callbacks.h" #include "BKE_context.h" +#include "BKE_editmesh.h" #include "BKE_global.h" #include "BKE_image.h" #include "BKE_image_format.h" @@ -933,7 +934,10 @@ static bool bake_targets_output_external(const BakeAPIRender *bkr, /* Vertex Color Bake Targets */ -static bool bake_targets_init_vertex_colors(BakeTargets *targets, Object *ob, ReportList *reports) +static bool bake_targets_init_vertex_colors(Main *bmain, + BakeTargets *targets, + Object *ob, + ReportList *reports) { if (ob->type != OB_MESH) { BKE_report(reports, RPT_ERROR, "Color attribute baking is only supported for mesh objects"); @@ -946,6 +950,9 @@ static bool bake_targets_init_vertex_colors(BakeTargets *targets, Object *ob, Re return false; } + /* Ensure mesh and editmesh topology are in sync. */ + ED_object_editmode_load(bmain, ob); + targets->images = MEM_callocN(sizeof(BakeImage), "BakeTargets.images"); targets->images_num = 1; @@ -1109,6 +1116,7 @@ static void convert_float_color_to_byte_color(const MPropCol *float_colors, static bool bake_targets_output_vertex_colors(BakeTargets *targets, Object *ob) { Mesh *me = ob->data; + BMEditMesh *em = me->edit_mesh; CustomDataLayer *active_color_layer = BKE_id_attributes_active_color_get(&me->id); BLI_assert(active_color_layer != NULL); const eAttrDomain domain = BKE_id_attribute_domain(&me->id, active_color_layer); @@ -1121,9 +1129,7 @@ static bool bake_targets_output_vertex_colors(BakeTargets *targets, Object *ob) const int totvert = me->totvert; const int totloop = me->totloop; - MPropCol *mcol = active_color_layer->type == CD_PROP_COLOR ? - active_color_layer->data : - MEM_malloc_arrayN(totvert, sizeof(MPropCol), __func__); + MPropCol *mcol = MEM_malloc_arrayN(totvert, sizeof(MPropCol), __func__); /* Accumulate float vertex colors in scene linear color space. */ int *num_loops_for_vertex = MEM_callocN(sizeof(int) * me->totvert, "num_loops_for_vertex"); @@ -1143,24 +1149,75 @@ static bool bake_targets_output_vertex_colors(BakeTargets *targets, Object *ob) } } - if (mcol != active_color_layer->data) { - convert_float_color_to_byte_color(mcol, totvert, is_noncolor, active_color_layer->data); - MEM_freeN(mcol); + if (em) { + /* Copy to bmesh. */ + const int active_color_offset = CustomData_get_offset_named( + &em->bm->vdata, active_color_layer->type, active_color_layer->name); + BMVert *v; + BMIter viter; + int i = 0; + BM_ITER_MESH (v, &viter, em->bm, BM_VERTS_OF_MESH) { + void *data = BM_ELEM_CD_GET_VOID_P(v, active_color_offset); + if (active_color_layer->type == CD_PROP_COLOR) { + memcpy(data, &mcol[i], sizeof(MPropCol)); + } + else { + convert_float_color_to_byte_color(&mcol[i], 1, is_noncolor, data); + } + i++; + } + } + else { + /* Copy to mesh. */ + if (active_color_layer->type == CD_PROP_COLOR) { + memcpy(active_color_layer->data, mcol, sizeof(MPropCol) * me->totvert); + } + else { + convert_float_color_to_byte_color(mcol, totvert, is_noncolor, active_color_layer->data); + } } + MEM_freeN(mcol); + MEM_SAFE_FREE(num_loops_for_vertex); } else if (domain == ATTR_DOMAIN_CORNER) { - switch (active_color_layer->type) { - case CD_PROP_COLOR: { + if (em) { + /* Copy to bmesh. */ + const int active_color_offset = CustomData_get_offset_named( + &em->bm->ldata, active_color_layer->type, active_color_layer->name); + BMFace *f; + BMIter fiter; + int i = 0; + BM_ITER_MESH (f, &fiter, em->bm, BM_FACES_OF_MESH) { + BMLoop *l; + BMIter liter; + BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) { + MPropCol color; + zero_v4(color.color); + bake_result_add_to_rgba(color.color, &result[i * channels_num], channels_num); + i++; + + void *data = BM_ELEM_CD_GET_VOID_P(l, active_color_offset); + if (active_color_layer->type == CD_PROP_COLOR) { + memcpy(data, &color, sizeof(MPropCol)); + } + else { + convert_float_color_to_byte_color(&color, 1, is_noncolor, data); + } + } + } + } + else { + /* Copy to mesh. */ + if (active_color_layer->type == CD_PROP_COLOR) { MPropCol *colors = active_color_layer->data; for (int i = 0; i < me->totloop; i++) { zero_v4(colors[i].color); bake_result_add_to_rgba(colors[i].color, &result[i * channels_num], channels_num); } - break; } - case CD_PROP_BYTE_COLOR: { + else { MLoopCol *colors = active_color_layer->data; for (int i = 0; i < me->totloop; i++) { MPropCol color; @@ -1168,10 +1225,7 @@ static bool bake_targets_output_vertex_colors(BakeTargets *targets, Object *ob) bake_result_add_to_rgba(color.color, &result[i * channels_num], channels_num); convert_float_color_to_byte_color(&color, 1, is_noncolor, &colors[i]); } - break; } - default: - BLI_assert_unreachable(); } } @@ -1201,7 +1255,7 @@ static bool bake_targets_init(const BakeAPIRender *bkr, } } else if (bkr->target == R_BAKE_TARGET_VERTEX_COLORS) { - if (!bake_targets_init_vertex_colors(targets, ob, reports)) { + if (!bake_targets_init_vertex_colors(bkr->main, targets, ob, reports)) { return false; } } diff --git a/source/blender/editors/object/object_remesh.cc b/source/blender/editors/object/object_remesh.cc index 8a7138b25ac..ac4fb40d832 100644 --- a/source/blender/editors/object/object_remesh.cc +++ b/source/blender/editors/object/object_remesh.cc @@ -577,10 +577,18 @@ static int voxel_size_edit_invoke(bContext *C, wmOperator *op, const wmEvent *ev /* Use the Bounding Box face normal as the basis Z. */ normal_tri_v3(cd->text_mat[2], cd->preview_plane[0], cd->preview_plane[1], cd->preview_plane[2]); + /* Invert object scale. */ + float scale[3]; + mat4_to_size(scale, active_object->obmat); + invert_v3(scale); + size_to_mat4(scale_mat, scale); + + mul_m4_m4_pre(cd->text_mat, scale_mat); + /* Write the text position into the matrix. */ copy_v3_v3(cd->text_mat[3], text_pos); - /* Scale the text. */ + /* Scale the text to constant viewport size. */ float text_pos_word_space[3]; mul_v3_m4v3(text_pos_word_space, active_object->obmat, text_pos); const float pixelsize = ED_view3d_pixel_size(rv3d, text_pos_word_space); diff --git a/source/blender/editors/render/render_opengl.cc b/source/blender/editors/render/render_opengl.cc index 77ad23f1e3f..e91bffce2c2 100644 --- a/source/blender/editors/render/render_opengl.cc +++ b/source/blender/editors/render/render_opengl.cc @@ -278,19 +278,10 @@ static void screen_opengl_views_setup(OGLRender *oglrender) static void screen_opengl_render_doit(const bContext *C, OGLRender *oglrender, RenderResult *rr) { - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Scene *scene = oglrender->scene; - ARegion *region = oglrender->region; - View3D *v3d = oglrender->v3d; - RegionView3D *rv3d = oglrender->rv3d; Object *camera = nullptr; int sizex = oglrender->sizex; int sizey = oglrender->sizey; - const short view_context = (v3d != nullptr); - bool draw_sky = (scene->r.alphamode == R_ADDSKY); - float *rectf = nullptr; - uchar *rect = nullptr; - const char *viewname = RE_GetActiveRenderView(oglrender->re); ImBuf *ibuf_result = nullptr; if (oglrender->is_sequencer) { @@ -301,7 +292,7 @@ static void screen_opengl_render_doit(const bContext *C, OGLRender *oglrender, R ImBuf *ibuf = oglrender->seq_data.ibufs_arr[oglrender->view_id]; if (ibuf) { - ImBuf *out = IMB_dupImBuf(ibuf); + ibuf_result = IMB_dupImBuf(ibuf); IMB_freeImBuf(ibuf); /* OpenGL render is considered to be preview and should be * as fast as possible. So currently we're making sure sequencer @@ -310,25 +301,21 @@ static void screen_opengl_render_doit(const bContext *C, OGLRender *oglrender, R * TODO(sergey): In the case of output to float container (EXR) * it actually makes sense to keep float buffer instead. */ - if (out->rect_float != nullptr) { - IMB_rect_from_float(out); - imb_freerectfloatImBuf(out); + if (ibuf_result->rect_float != nullptr) { + IMB_rect_from_float(ibuf_result); + imb_freerectfloatImBuf(ibuf_result); } - BLI_assert((oglrender->sizex == ibuf->x) && (oglrender->sizey == ibuf->y)); - RE_render_result_rect_from_ibuf(rr, out, oglrender->view_id); - IMB_freeImBuf(out); + BLI_assert((sizex == ibuf->x) && (sizey == ibuf->y)); } else if (gpd) { /* If there are no strips, Grease Pencil still needs a buffer to draw on */ - ImBuf *out = IMB_allocImBuf(oglrender->sizex, oglrender->sizey, 32, IB_rect); - RE_render_result_rect_from_ibuf(rr, out, oglrender->view_id); - IMB_freeImBuf(out); + ibuf_result = IMB_allocImBuf(sizex, sizey, 32, IB_rect); } if (gpd) { int i; uchar *gp_rect; - uchar *render_rect = (uchar *)RE_RenderViewGetById(rr, oglrender->view_id)->rect32; + uchar *render_rect = (uchar *)ibuf_result->rect; DRW_opengl_context_enable(); GPU_offscreen_bind(oglrender->ofs, true); @@ -359,10 +346,16 @@ static void screen_opengl_render_doit(const bContext *C, OGLRender *oglrender, R } else { /* shouldn't suddenly give errors mid-render but possible */ + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); char err_out[256] = "unknown"; ImBuf *ibuf_view; + bool draw_sky = (scene->r.alphamode == R_ADDSKY); const int alpha_mode = (draw_sky) ? R_ADDSKY : R_ALPHAPREMUL; - if (view_context) { + const char *viewname = RE_GetActiveRenderView(oglrender->re); + View3D *v3d = oglrender->v3d; + + if (v3d != nullptr) { + ARegion *region = oglrender->region; ibuf_view = ED_view3d_draw_offscreen_imbuf(depsgraph, scene, static_cast<eDrawType>(v3d->shading.type), @@ -378,7 +371,7 @@ static void screen_opengl_render_doit(const bContext *C, OGLRender *oglrender, R err_out); /* for stamp only */ - if (rv3d->persp == RV3D_CAMOB && v3d->camera) { + if (oglrender->rv3d->persp == RV3D_CAMOB && v3d->camera) { camera = BKE_camera_multiview_render(oglrender->scene, v3d->camera, viewname); } } @@ -388,8 +381,8 @@ static void screen_opengl_render_doit(const bContext *C, OGLRender *oglrender, R nullptr, OB_SOLID, scene->camera, - oglrender->sizex, - oglrender->sizey, + sizex, + sizey, IB_rectfloat, V3D_OFSDRAW_SHOW_ANNOTATION, alpha_mode, @@ -401,12 +394,6 @@ static void screen_opengl_render_doit(const bContext *C, OGLRender *oglrender, R if (ibuf_view) { ibuf_result = ibuf_view; - if (ibuf_view->rect_float) { - rectf = ibuf_view->rect_float; - } - else { - rect = (uchar *)ibuf_view->rect; - } } else { fprintf(stderr, "%s: failed to get buffer, %s\n", __func__, err_out); @@ -415,6 +402,14 @@ static void screen_opengl_render_doit(const bContext *C, OGLRender *oglrender, R if (ibuf_result != nullptr) { if ((scene->r.stamp & R_STAMP_ALL) && (scene->r.stamp & R_STAMP_DRAW)) { + float *rectf = nullptr; + uchar *rect = nullptr; + if (ibuf_result->rect_float) { + rectf = ibuf_result->rect_float; + } + else { + rect = (uchar *)ibuf_result->rect; + } BKE_image_stamp_buf(scene, camera, nullptr, rect, rectf, rr->rectx, rr->recty, 4); } RE_render_result_rect_from_ibuf(rr, ibuf_result, oglrender->view_id); diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 85cc1b526ab..020b0954458 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -5392,7 +5392,7 @@ static bool sculpt_stroke_test_start(bContext *C, struct wmOperator *op, const f static void sculpt_stroke_update_step(bContext *C, wmOperator *UNUSED(op), - struct PaintStroke *UNUSED(stroke), + struct PaintStroke *stroke, PointerRNA *itemptr) { UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings; @@ -5401,6 +5401,8 @@ static void sculpt_stroke_update_step(bContext *C, SculptSession *ss = ob->sculpt; const Brush *brush = BKE_paint_brush(&sd->paint); ToolSettings *tool_settings = CTX_data_tool_settings(C); + StrokeCache *cache = ss->cache; + cache->stroke_distance = paint_stroke_distance_get(stroke); SCULPT_stroke_modifiers_check(C, ob, brush); sculpt_update_cache_variants(C, sd, ob, itemptr); diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index 86f89c3c2fa..6a10f7cad18 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -489,6 +489,7 @@ typedef struct StrokeCache { float true_last_location[3]; float location[3]; float last_location[3]; + float stroke_distance; /* Used for alternating between deformation in brushes that need to apply different ones to * achieve certain effects. */ diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_color.c b/source/blender/editors/sculpt_paint/sculpt_paint_color.c index cb178b23008..c494c71f1eb 100644 --- a/source/blender/editors/sculpt_paint/sculpt_paint_color.c +++ b/source/blender/editors/sculpt_paint/sculpt_paint_color.c @@ -17,6 +17,7 @@ #include "DNA_meshdata_types.h" #include "BKE_brush.h" +#include "BKE_colorband.h" #include "BKE_colortools.h" #include "BKE_context.h" #include "BKE_mesh.h" @@ -117,11 +118,31 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata, const int thread_id = BLI_task_parallel_thread_id(tls); float brush_color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; + copy_v3_v3(brush_color, ss->cache->invert ? BKE_brush_secondary_color_get(ss->scene, brush) : BKE_brush_color_get(ss->scene, brush)); + IMB_colormanagement_srgb_to_scene_linear_v3(brush_color, brush_color); + if (brush->flag & BRUSH_USE_GRADIENT) { + switch (brush->gradient_stroke_mode) { + case BRUSH_GRADIENT_PRESSURE: + BKE_colorband_evaluate(brush->gradient, ss->cache->pressure, brush_color); + break; + case BRUSH_GRADIENT_SPACING_REPEAT: { + float coord = fmod(ss->cache->stroke_distance / brush->gradient_spacing, 1.0); + BKE_colorband_evaluate(brush->gradient, coord, brush_color); + break; + } + case BRUSH_GRADIENT_SPACING_CLAMP: { + BKE_colorband_evaluate( + brush->gradient, ss->cache->stroke_distance / brush->gradient_spacing, brush_color); + break; + } + } + } + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index 99f6a45ce1a..04b2b2f04bf 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.c +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -171,7 +171,7 @@ static void update_cb_partial(PBVHNode *node, void *userdata) } else { if (BKE_pbvh_node_has_vert_with_normal_update_tag(data->pbvh, node)) { - BKE_pbvh_node_mark_normals_update(node); + BKE_pbvh_node_mark_update(node); } int verts_num; const int *vert_indices; diff --git a/source/blender/editors/sculpt_paint/sculpt_uv.c b/source/blender/editors/sculpt_paint/sculpt_uv.c index dfa85e8e56d..f2017e68b4c 100644 --- a/source/blender/editors/sculpt_paint/sculpt_uv.c +++ b/source/blender/editors/sculpt_paint/sculpt_uv.c @@ -414,9 +414,8 @@ static void uv_sculpt_stroke_exit(bContext *C, wmOperator *op) if (data->timer) { WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), data->timer); } - if (data->elementMap) { - BM_uv_element_map_free(data->elementMap); - } + BM_uv_element_map_free(data->elementMap); + data->elementMap = NULL; MEM_SAFE_FREE(data->uv); MEM_SAFE_FREE(data->uvedges); if (data->initial_stroke) { @@ -435,7 +434,7 @@ static int uv_element_offset_from_face_get( if (!element || (doIslands && element->island != island_index)) { return -1; } - return element - map->buf; + return element - map->storage; } static uint uv_edge_hash(const void *key) @@ -469,7 +468,6 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm BKE_curvemapping_init(ts->uvsculpt->paint.brush->curve); if (data) { - int counter = 0, i; ARegion *region = CTX_wm_region(C); float co[2]; BMFace *efa; @@ -492,13 +490,12 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm data->uvsculpt = &ts->uvsculpt->paint; - if (do_island_optimization) { - /* We will need island information */ - data->elementMap = BM_uv_element_map_create(bm, scene, false, true, true); - } - else { - data->elementMap = BM_uv_element_map_create(bm, scene, false, true, false); - } + /* Winding was added to island detection in 5197aa04c6bd + * However the sculpt tools can flip faces, potentially creating orphaned islands. + * See T100132 */ + bool use_winding = false; + data->elementMap = BM_uv_element_map_create( + bm, scene, false, use_winding, do_island_optimization); if (!data->elementMap) { uv_sculpt_stroke_exit(C, op); @@ -519,20 +516,24 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm } /* Count 'unique' UV's */ - for (i = 0; i < data->elementMap->totalUVs; i++) { - if (data->elementMap->buf[i].separate && - (!do_island_optimization || data->elementMap->buf[i].island == island_index)) { - counter++; + int unique_uvs = data->elementMap->total_unique_uvs; + if (do_island_optimization) { + unique_uvs = 0; + for (int i = 0; i < data->elementMap->total_uvs; i++) { + if (data->elementMap->storage[i].separate && + (data->elementMap->storage[i].island == island_index)) { + unique_uvs++; + } } } /* Allocate the unique uv buffers */ - data->uv = MEM_mallocN(sizeof(*data->uv) * counter, "uv_brush_unique_uvs"); - uniqueUv = MEM_mallocN(sizeof(*uniqueUv) * data->elementMap->totalUVs, + data->uv = MEM_mallocN(sizeof(*data->uv) * unique_uvs, "uv_brush_unique_uvs"); + uniqueUv = MEM_mallocN(sizeof(*uniqueUv) * data->elementMap->total_uvs, "uv_brush_unique_uv_map"); edgeHash = BLI_ghash_new(uv_edge_hash, uv_edge_compare, "uv_brush_edge_hash"); /* we have at most totalUVs edges */ - edges = MEM_mallocN(sizeof(*edges) * data->elementMap->totalUVs, "uv_brush_all_edges"); + edges = MEM_mallocN(sizeof(*edges) * data->elementMap->total_uvs, "uv_brush_all_edges"); if (!data->uv || !uniqueUv || !edgeHash || !edges) { MEM_SAFE_FREE(edges); MEM_SAFE_FREE(uniqueUv); @@ -543,12 +544,12 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm return NULL; } - data->totalUniqueUvs = counter; - /* So that we can use this as index for the UvElements */ - counter = -1; + data->totalUniqueUvs = unique_uvs; + /* Index for the UvElements. */ + int counter = -1; /* initialize the unique UVs */ - for (i = 0; i < bm->totvert; i++) { - UvElement *element = data->elementMap->vert[i]; + for (int i = 0; i < bm->totvert; i++) { + UvElement *element = data->elementMap->vertex[i]; for (; element; element = element->next) { if (element->separate) { if (do_island_optimization && (element->island != island_index)) { @@ -568,7 +569,7 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm data->uv[counter].uv = luv->uv; } /* Pointer arithmetic to the rescue, as always :). */ - uniqueUv[element - data->elementMap->buf] = counter; + uniqueUv[element - data->elementMap->storage] = counter; } } @@ -628,7 +629,7 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm } /* fill the edges with data */ - i = 0; + int i = 0; GHASH_ITER (gh_iter, edgeHash) { data->uvedges[i++] = *((UvEdge *)BLI_ghashIterator_getKey(&gh_iter)); } @@ -640,7 +641,7 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm /* transfer boundary edge property to UV's */ if (ts->uv_sculpt_settings & UV_SCULPT_LOCK_BORDERS) { - for (i = 0; i < data->totalUvEdges; i++) { + for (int i = 0; i < data->totalUvEdges; i++) { if (!data->uvedges[i].flag) { data->uv[data->uvedges[i].uv1].flag |= MARK_BOUNDARY; data->uv[data->uvedges[i].uv2].flag |= MARK_BOUNDARY; @@ -687,7 +688,7 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm counter = 0; - for (i = 0; i < data->totalUniqueUvs; i++) { + for (int i = 0; i < data->totalUniqueUvs; i++) { float dist, diff[2]; if (data->uv[i].flag & MARK_BOUNDARY) { continue; diff --git a/source/blender/editors/space_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c index 0a774ee679c..2109d3f9701 100644 --- a/source/blender/editors/space_image/image_buttons.c +++ b/source/blender/editors/space_image/image_buttons.c @@ -869,7 +869,8 @@ void uiTemplateImage(uiLayout *layout, uiItemS(col); uiItemR(col, &imaptr, "generated_type", UI_ITEM_R_EXPAND, IFACE_("Type"), ICON_NONE); - if (ima->gen_type == IMA_GENTYPE_BLANK) { + ImageTile *base_tile = BKE_image_get_tile(ima, 0); + if (base_tile->gen_type == IMA_GENTYPE_BLANK) { uiItemR(col, &imaptr, "generated_color", 0, NULL, ICON_NONE); } } @@ -1211,6 +1212,11 @@ void uiTemplateImageInfo(uiLayout *layout, bContext *C, Image *ima, ImageUser *i ofs += BLI_strncpy_rlen(str + ofs, TIP_(" + Z"), len - ofs); } + eGPUTextureFormat texture_format = IMB_gpu_get_texture_format(ibuf, + ima->flag & IMA_HIGH_BITDEPTH); + const char *texture_format_description = GPU_texture_format_description(texture_format); + ofs += BLI_snprintf_rlen(str + ofs, len - ofs, TIP_(", %s"), texture_format_description); + uiItemL(col, str, ICON_NONE); } diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index 4036f859231..78aaf957a87 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -3847,15 +3847,16 @@ void IMAGE_OT_clear_render_border(wmOperatorType *ot) static bool do_fill_tile(PointerRNA *ptr, Image *ima, ImageTile *tile) { - float color[4]; - RNA_float_get_array(ptr, "color", color); - int gen_type = RNA_enum_get(ptr, "generated_type"); - int width = RNA_int_get(ptr, "width"); - int height = RNA_int_get(ptr, "height"); + RNA_float_get_array(ptr, "color", tile->gen_color); + tile->gen_type = RNA_enum_get(ptr, "generated_type"); + tile->gen_x = RNA_int_get(ptr, "width"); + tile->gen_y = RNA_int_get(ptr, "height"); bool is_float = RNA_boolean_get(ptr, "float"); - int planes = RNA_boolean_get(ptr, "alpha") ? 32 : 24; - return BKE_image_fill_tile(ima, tile, width, height, color, gen_type, planes, is_float); + tile->gen_flag = is_float ? IMA_GEN_FLOAT : 0; + tile->gen_depth = RNA_boolean_get(ptr, "alpha") ? 32 : 24; + + return BKE_image_fill_tile(ima, tile); } static void draw_fill_tile(PointerRNA *ptr, uiLayout *layout) diff --git a/source/blender/editors/space_image/image_undo.cc b/source/blender/editors/space_image/image_undo.cc index 9e8db58ccc2..065641c4051 100644 --- a/source/blender/editors/space_image/image_undo.cc +++ b/source/blender/editors/space_image/image_undo.cc @@ -454,7 +454,6 @@ struct UndoImageBuf { struct { short source; bool use_float; - char gen_type; } image_state; }; @@ -473,7 +472,6 @@ static UndoImageBuf *ubuf_from_image_no_tiles(Image *image, const ImBuf *ibuf) MEM_callocN(sizeof(*ubuf->tiles) * ubuf->tiles_len, __func__)); BLI_strncpy(ubuf->ibuf_name, ibuf->name, sizeof(ubuf->ibuf_name)); - ubuf->image_state.gen_type = image->gen_type; ubuf->image_state.source = image->source; ubuf->image_state.use_float = ibuf->rect_float != nullptr; diff --git a/source/blender/editors/space_outliner/CMakeLists.txt b/source/blender/editors/space_outliner/CMakeLists.txt index 97d2957eed2..78ec057f921 100644 --- a/source/blender/editors/space_outliner/CMakeLists.txt +++ b/source/blender/editors/space_outliner/CMakeLists.txt @@ -48,6 +48,7 @@ set(SRC tree/tree_element_anim_data.cc tree/tree_element_collection.cc tree/tree_element_driver.cc + tree/tree_element_label.cc tree/tree_element_gpencil_layer.cc tree/tree_element_id.cc tree/tree_element_id_library.cc @@ -67,6 +68,7 @@ set(SRC tree/tree_element_anim_data.hh tree/tree_element_collection.hh tree/tree_element_driver.hh + tree/tree_element_label.hh tree/tree_element_gpencil_layer.hh tree/tree_element_id.hh tree/tree_element_id_library.hh diff --git a/source/blender/editors/space_outliner/outliner_draw.cc b/source/blender/editors/space_outliner/outliner_draw.cc index 1828846811a..8bc1ed8c84e 100644 --- a/source/blender/editors/space_outliner/outliner_draw.cc +++ b/source/blender/editors/space_outliner/outliner_draw.cc @@ -1804,18 +1804,17 @@ static void outliner_draw_overrides_rna_buts(uiBlock *block, if (!outliner_is_element_in_view(te, ®ion->v2d)) { continue; } - if (tselem->type != TSE_LIBRARY_OVERRIDE) { + TreeElementOverridesProperty *override_elem = tree_element_cast<TreeElementOverridesProperty>( + te); + if (!override_elem) { continue; } - TreeElementOverridesProperty &override_elem = *tree_element_cast<TreeElementOverridesProperty>( - te); - - if (!override_elem.is_rna_path_valid) { + if (!override_elem->is_rna_path_valid) { uiBut *but = uiDefBut(block, UI_BTYPE_LABEL, 0, - override_elem.rna_path.c_str(), + override_elem->rna_path.c_str(), x + pad_x, te->ys + pad_y, item_max_width, @@ -1830,8 +1829,28 @@ static void outliner_draw_overrides_rna_buts(uiBlock *block, continue; } - PointerRNA *ptr = &override_elem.override_rna_ptr; - PropertyRNA *prop = &override_elem.override_rna_prop; + if (const TreeElementOverridesPropertyOperation *override_op_elem = + tree_element_cast<TreeElementOverridesPropertyOperation>(te)) { + StringRefNull op_label = override_op_elem->getOverrideOperationLabel(); + uiDefBut(block, + UI_BTYPE_LABEL, + 0, + op_label.c_str(), + x + pad_x, + te->ys + pad_y, + item_max_width, + item_height, + nullptr, + 0, + 0, + 0, + 0, + ""); + continue; + } + + PointerRNA *ptr = &override_elem->override_rna_ptr; + PropertyRNA *prop = &override_elem->override_rna_prop; const PropertyType prop_type = RNA_property_type(prop); uiBut *auto_but = uiDefAutoButR(block, @@ -2825,10 +2844,20 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te) data.icon = tree_element_get_icon_from_id(tselem->id); } + if (!te->abstract_element) { + /* Pass */ + } + else if (auto icon = te->abstract_element->getIcon()) { + data.icon = *icon; + } + return data; } -static void tselem_draw_icon(uiBlock *block, +/** + * \return Return true if the element has an icon that was drawn, false if it doesn't have an icon. + */ +static bool tselem_draw_icon(uiBlock *block, int xmax, float x, float y, @@ -2839,7 +2868,7 @@ static void tselem_draw_icon(uiBlock *block, { TreeElementIcon data = tree_element_get_icon(tselem, te); if (data.icon == 0) { - return; + return false; } const bool is_collection = outliner_is_collection_tree_element(te); @@ -2863,7 +2892,7 @@ static void tselem_draw_icon(uiBlock *block, 0.0f, btheme->collection_color[collection->color_tag].color, true); - return; + return true; } } @@ -2895,6 +2924,8 @@ static void tselem_draw_icon(uiBlock *block, alpha, (data.drag_id && ID_IS_LINKED(data.drag_id)) ? data.drag_id->lib->filepath : ""); } + + return true; } /** @@ -3089,6 +3120,7 @@ static void outliner_draw_iconrow(bContext *C, TSE_GP_LAYER, TSE_LIBRARY_OVERRIDE_BASE, TSE_LIBRARY_OVERRIDE, + TSE_LIBRARY_OVERRIDE_OPERATION, TSE_BONE, TSE_EBONE, TSE_POSE_CHANNEL, @@ -3314,15 +3346,15 @@ static void outliner_draw_tree_element(bContext *C, offsx += UI_UNIT_X; /* Data-type icon. */ - if (!(ELEM(tselem->type, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM, TSE_ID_BASE))) { - tselem_draw_icon(block, - xmax, - (float)startx + offsx, - (float)*starty, - tselem, - te, - (tselem->flag & TSE_HIGHLIGHTED_ICON) ? alpha_fac + 0.5f : alpha_fac, - true); + if (!(ELEM(tselem->type, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM, TSE_ID_BASE)) && + tselem_draw_icon(block, + xmax, + (float)startx + offsx, + (float)*starty, + tselem, + te, + (tselem->flag & TSE_HIGHLIGHTED_ICON) ? alpha_fac + 0.5f : alpha_fac, + true)) { offsx += UI_UNIT_X + 4 * ufac; } else { diff --git a/source/blender/editors/space_outliner/outliner_tree.cc b/source/blender/editors/space_outliner/outliner_tree.cc index 49220762b65..0906bbb5797 100644 --- a/source/blender/editors/space_outliner/outliner_tree.cc +++ b/source/blender/editors/space_outliner/outliner_tree.cc @@ -817,9 +817,12 @@ TreeElement *outliner_add_element(SpaceOutliner *space_outliner, /* idv is the layer itself */ id = TREESTORE(parent)->id; } + else if (ELEM(type, TSE_GENERIC_LABEL)) { + id = nullptr; + } /* exceptions */ - if (type == TSE_ID_BASE) { + if (ELEM(type, TSE_ID_BASE, TSE_GENERIC_LABEL)) { /* pass */ } else if (id == nullptr) { @@ -869,7 +872,7 @@ TreeElement *outliner_add_element(SpaceOutliner *space_outliner, else if (ELEM(type, TSE_LAYER_COLLECTION, TSE_SCENE_COLLECTION_BASE, TSE_VIEW_COLLECTION_BASE)) { /* pass */ } - else if (type == TSE_ID_BASE) { + else if (ELEM(type, TSE_ID_BASE, TSE_GENERIC_LABEL)) { /* pass */ } else if (type == TSE_SOME_ID) { @@ -877,7 +880,10 @@ TreeElement *outliner_add_element(SpaceOutliner *space_outliner, BLI_assert_msg(0, "Expected this ID type to be ported to new Outliner tree-element design"); } } - else if (ELEM(type, TSE_LIBRARY_OVERRIDE_BASE, TSE_LIBRARY_OVERRIDE)) { + else if (ELEM(type, + TSE_LIBRARY_OVERRIDE_BASE, + TSE_LIBRARY_OVERRIDE, + TSE_LIBRARY_OVERRIDE_OPERATION)) { if (!te->abstract_element) { BLI_assert_msg(0, "Expected override types to be ported to new Outliner tree-element design"); @@ -895,10 +901,13 @@ TreeElement *outliner_add_element(SpaceOutliner *space_outliner, te->idcode = GS(id->name); } - if (expand && te->abstract_element && te->abstract_element->isExpandValid()) { + if (!expand) { + /* Pass */ + } + else if (te->abstract_element && te->abstract_element->isExpandValid()) { tree_element_expand(*te->abstract_element, *space_outliner); } - else if (expand && (type == TSE_SOME_ID)) { + else if (type == TSE_SOME_ID) { /* ID types not (fully) ported to new design yet. */ if (te->abstract_element->expandPoll(*space_outliner)) { outliner_add_id_contents(space_outliner, te, tselem, id); @@ -916,7 +925,8 @@ TreeElement *outliner_add_element(SpaceOutliner *space_outliner, TSE_RNA_ARRAY_ELEM, TSE_SEQUENCE, TSE_SEQ_STRIP, - TSE_SEQUENCE_DUP)) { + TSE_SEQUENCE_DUP, + TSE_GENERIC_LABEL)) { BLI_assert_msg(false, "Element type should already use new AbstractTreeElement design"); } diff --git a/source/blender/editors/space_outliner/tree/tree_element.cc b/source/blender/editors/space_outliner/tree/tree_element.cc index 7808c4a3c0f..4a540c3ce87 100644 --- a/source/blender/editors/space_outliner/tree/tree_element.cc +++ b/source/blender/editors/space_outliner/tree/tree_element.cc @@ -4,6 +4,9 @@ * \ingroup spoutliner */ +#include <string> +#include <string_view> + #include "DNA_anim_types.h" #include "DNA_listBase.h" #include "DNA_space_types.h" @@ -17,6 +20,7 @@ #include "tree_element_driver.hh" #include "tree_element_gpencil_layer.hh" #include "tree_element_id.hh" +#include "tree_element_label.hh" #include "tree_element_nla.hh" #include "tree_element_overrides.hh" #include "tree_element_rna.hh" @@ -52,6 +56,8 @@ std::unique_ptr<AbstractTreeElement> AbstractTreeElement::createFromType(const i switch (type) { case TSE_SOME_ID: return TreeElementID::createFromID(legacy_te, *static_cast<ID *>(idv)); + case TSE_GENERIC_LABEL: + return std::make_unique<TreeElementLabel>(legacy_te, static_cast<const char *>(idv)); case TSE_ANIM_DATA: return std::make_unique<TreeElementAnimData>(legacy_te, *static_cast<IdAdtTemplate *>(idv)->adt); @@ -76,6 +82,9 @@ std::unique_ptr<AbstractTreeElement> AbstractTreeElement::createFromType(const i case TSE_LIBRARY_OVERRIDE: return std::make_unique<TreeElementOverridesProperty>( legacy_te, *static_cast<TreeElementOverridesData *>(idv)); + case TSE_LIBRARY_OVERRIDE_OPERATION: + return std::make_unique<TreeElementOverridesPropertyOperation>( + legacy_te, *static_cast<TreeElementOverridesData *>(idv)); case TSE_RNA_STRUCT: return std::make_unique<TreeElementRNAStruct>(legacy_te, *static_cast<PointerRNA *>(idv)); case TSE_RNA_PROPERTY: @@ -103,6 +112,22 @@ StringRefNull AbstractTreeElement::getWarning() const return ""; } +std::optional<BIFIconID> AbstractTreeElement::getIcon() const +{ + return {}; +} + +void AbstractTreeElement::print_path() +{ + std::string path = legacy_te_.name; + + for (TreeElement *parent = legacy_te_.parent; parent; parent = parent->parent) { + path = parent->name + std::string_view("/") + path; + } + + std::cout << path << std::endl; +} + void AbstractTreeElement::uncollapse_by_default(TreeElement *legacy_te) { if (!TREESTORE(legacy_te)->used) { diff --git a/source/blender/editors/space_outliner/tree/tree_element.hh b/source/blender/editors/space_outliner/tree/tree_element.hh index 1098068d628..fc6211f20ea 100644 --- a/source/blender/editors/space_outliner/tree/tree_element.hh +++ b/source/blender/editors/space_outliner/tree/tree_element.hh @@ -7,8 +7,10 @@ #pragma once #include <memory> +#include <optional> #include "BLI_string_ref.hh" +#include "UI_resources.h" struct ListBase; struct SpaceOutliner; @@ -64,6 +66,25 @@ class AbstractTreeElement { virtual StringRefNull getWarning() const; /** + * Define the icon to be displayed for this element. If this returns an icon, this will be + * displayed. Otherwise, #tree_element_get_icon() may still determine an icon. By default no + * value is returned (#std::nullopt). + * + * All elements should be ported to use this over #tree_element_get_icon(). + */ + virtual std::optional<BIFIconID> getIcon() const; + + /** + * Debugging helper: Print effective path of this tree element, constructed out of the + * #TreeElement.name of each element. E.g.: + * - Lorem + * - ipsum dolor sit + * - amet + * will print: Lorem/ipsum dolor sit/amet. + */ + void print_path(); + + /** * Expand this tree element if it is displayed for the first time (as identified by its * tree-store element). * diff --git a/source/blender/editors/space_outliner/tree/tree_element_label.cc b/source/blender/editors/space_outliner/tree/tree_element_label.cc new file mode 100644 index 00000000000..32fa62c5f5e --- /dev/null +++ b/source/blender/editors/space_outliner/tree/tree_element_label.cc @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup spoutliner + */ + +#include "DNA_listBase.h" + +#include "DNA_outliner_types.h" + +#include "../outliner_intern.hh" + +#include "tree_element_label.hh" + +namespace blender::ed::outliner { + +TreeElementLabel::TreeElementLabel(TreeElement &legacy_te, const char *label) + : AbstractTreeElement(legacy_te), label_(label) +{ + BLI_assert(legacy_te_.store_elem->type == TSE_GENERIC_LABEL); + /* The draw string is actually accessed via #TreeElement.name, so make sure this always points to + * our string. */ + legacy_te_.name = label_.c_str(); +} + +void TreeElementLabel::setIcon(const BIFIconID icon) +{ + icon_ = icon; +} + +std::optional<BIFIconID> TreeElementLabel::getIcon() const +{ + return icon_; +} + +} // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/tree/tree_element_label.hh b/source/blender/editors/space_outliner/tree/tree_element_label.hh new file mode 100644 index 00000000000..fc730c7b8f4 --- /dev/null +++ b/source/blender/editors/space_outliner/tree/tree_element_label.hh @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup spoutliner + */ + +#pragma once + +#include <string> + +#include "UI_resources.h" + +#include "tree_element.hh" + +namespace blender::ed::outliner { + +/** + * A basic, general purpose tree element to just display a label and an icon. Can be used to group + * together items underneath as well of course. + * + * Make sure to give this a unique index, so the element can be identified uniquely. Otherwise + * glitches like multiple highlighted elements happen, that share all state (e.g. collapsed, + * selected, etc.). + */ +class TreeElementLabel final : public AbstractTreeElement { + const std::string label_; + BIFIconID icon_ = ICON_NONE; + + public: + TreeElementLabel(TreeElement &legacy_te, const char *label); + + void setIcon(BIFIconID icon); + std::optional<BIFIconID> getIcon() const override; +}; + +} // namespace blender::ed::outliner 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 d1babda642e..e19459ced61 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_overrides.cc +++ b/source/blender/editors/space_outliner/tree/tree_element_overrides.cc @@ -7,30 +7,60 @@ #include "BKE_collection.h" #include "BKE_lib_override.h" -#include "BLI_utildefines.h" - +#include "BLI_function_ref.hh" #include "BLI_listbase_wrapper.hh" +#include "BLI_map.hh" +#include "BLI_utildefines.h" #include "BLT_translation.h" #include "DNA_space_types.h" #include "RNA_access.h" +#include "RNA_path.h" #include "../outliner_intern.hh" +#include "tree_element_label.hh" #include "tree_element_overrides.hh" namespace blender::ed::outliner { +class OverrideRNAPathTreeBuilder { + SpaceOutliner &space_outliner_; + Map<std::string, TreeElement *> path_te_map; + + public: + OverrideRNAPathTreeBuilder(SpaceOutliner &space_outliner); + void build_path(TreeElement &parent, TreeElementOverridesData &override_data, short &index); + + private: + TreeElement &ensure_label_element_for_prop( + TreeElement &parent, StringRef elem_path, PointerRNA &ptr, PropertyRNA &prop, short &index); + TreeElement &ensure_label_element_for_ptr(TreeElement &parent, + StringRef elem_path, + PointerRNA &ptr, + short &index); + void ensure_entire_collection(TreeElement &te_to_expand, + const TreeElementOverridesData &override_data, + const char *coll_prop_path, + short &index); +}; + +/* -------------------------------------------------------------------- */ +/** \name Base Element + * + * Represents an ID that has overridden properties. The expanding will invoke building of tree + * elements for the full RNA path of the property. + * + * \{ */ + TreeElementOverridesBase::TreeElementOverridesBase(TreeElement &legacy_te, ID &id) : AbstractTreeElement(legacy_te), id(id) { BLI_assert(legacy_te.store_elem->type == TSE_LIBRARY_OVERRIDE_BASE); if (legacy_te.parent != nullptr && - ELEM(legacy_te.parent->store_elem->type, TSE_SOME_ID, TSE_LAYER_COLLECTION)) - - { + ELEM(legacy_te.parent->store_elem->type, TSE_SOME_ID, TSE_LAYER_COLLECTION)) { legacy_te.name = IFACE_("Library Overrides"); } else { @@ -51,21 +81,17 @@ StringRefNull TreeElementOverridesBase::getWarning() const return {}; } -void TreeElementOverridesBase::expand(SpaceOutliner &space_outliner) const +static void iterate_properties_to_display(ID &id, + const bool show_system_overrides, + FunctionRef<void(TreeElementOverridesData &data)> fn) { - BLI_assert(id.override_library != nullptr); + PointerRNA override_rna_ptr; + PropertyRNA *override_rna_prop; - const bool show_system_overrides = (SUPPORT_FILTER_OUTLINER(&space_outliner) && - (space_outliner.filter & SO_FILTER_SHOW_SYSTEM_OVERRIDES) != - 0); PointerRNA idpoin; RNA_id_pointer_create(&id, &idpoin); - PointerRNA override_rna_ptr; - PropertyRNA *override_rna_prop; - short index = 0; - - for (auto *override_prop : + for (IDOverrideLibraryProperty *override_prop : ListBaseWrapper<IDOverrideLibraryProperty>(id.override_library->properties)) { int rnaprop_index = 0; const bool is_rna_path_valid = BKE_lib_override_rna_property_find( @@ -80,7 +106,7 @@ void TreeElementOverridesBase::expand(SpaceOutliner &space_outliner) const /* Matching ID pointers are considered as system overrides. */ if (ELEM(override_prop->rna_prop_type, PROP_POINTER, PROP_COLLECTION) && RNA_struct_is_ID(RNA_property_pointer_type(&override_rna_ptr, override_rna_prop))) { - for (auto *override_prop_op : + for (IDOverrideLibraryPropertyOperation *override_prop_op : ListBaseWrapper<IDOverrideLibraryPropertyOperation>(override_prop->operations)) { if ((override_prop_op->flag & IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE) == 0) { do_skip = false; @@ -103,11 +129,36 @@ void TreeElementOverridesBase::expand(SpaceOutliner &space_outliner) const TreeElementOverridesData data = { id, *override_prop, override_rna_ptr, *override_rna_prop, is_rna_path_valid}; - outliner_add_element( - &space_outliner, &legacy_te_.subtree, &data, &legacy_te_, TSE_LIBRARY_OVERRIDE, index++); + + fn(data); } } +void TreeElementOverridesBase::expand(SpaceOutliner &space_outliner) const +{ + BLI_assert(id.override_library != nullptr); + + const bool show_system_overrides = (SUPPORT_FILTER_OUTLINER(&space_outliner) && + (space_outliner.filter & SO_FILTER_SHOW_SYSTEM_OVERRIDES) != + 0); + + OverrideRNAPathTreeBuilder path_builder(space_outliner); + short index = 0; + + iterate_properties_to_display(id, show_system_overrides, [&](TreeElementOverridesData &data) { + path_builder.build_path(legacy_te_, data, index); + }); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Overridden Property + * + * Represents an RNA property that was overridden. + * + * \{ */ + TreeElementOverridesProperty::TreeElementOverridesProperty(TreeElement &legacy_te, TreeElementOverridesData &override_data) : AbstractTreeElement(legacy_te), @@ -116,9 +167,10 @@ TreeElementOverridesProperty::TreeElementOverridesProperty(TreeElement &legacy_t 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); + BLI_assert( + ELEM(legacy_te.store_elem->type, TSE_LIBRARY_OVERRIDE, TSE_LIBRARY_OVERRIDE_OPERATION)); - legacy_te.name = override_data.override_property.rna_path; + legacy_te.name = RNA_property_ui_name(&override_data.override_rna_prop); } StringRefNull TreeElementOverridesProperty::getWarning() const @@ -132,4 +184,306 @@ StringRefNull TreeElementOverridesProperty::getWarning() const return {}; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Overridden Property Operation + * + * See #TreeElementOverridesPropertyOperation. + * \{ */ + +TreeElementOverridesPropertyOperation::TreeElementOverridesPropertyOperation( + TreeElement &legacy_te, TreeElementOverridesData &override_data) + : TreeElementOverridesProperty(legacy_te, override_data) +{ + BLI_assert(legacy_te.store_elem->type == TSE_LIBRARY_OVERRIDE_OPERATION); + BLI_assert_msg(RNA_property_type(&override_rna_prop) == PROP_COLLECTION, + "Override operations are only supported for collections right now"); + /* Quiet Clang Static Analyzer warning by throwing instead of asserting (possible + * null-dereference). */ + if (!override_data.operation) { + throw std::invalid_argument("missing operation"); + } + + operation_ = std::make_unique<IDOverrideLibraryPropertyOperation>(*override_data.operation); + /* Just for extra sanity. */ + operation_->next = operation_->prev = nullptr; + + if (std::optional<PointerRNA> col_item_ptr = get_collection_ptr()) { + const char *dyn_name = RNA_struct_name_get_alloc(&*col_item_ptr, nullptr, 0, nullptr); + if (dyn_name) { + legacy_te.name = dyn_name; + legacy_te.flag |= TE_FREE_NAME; + } + else { + legacy_te.name = RNA_struct_ui_name(col_item_ptr->type); + } + } +} + +StringRefNull TreeElementOverridesPropertyOperation::getOverrideOperationLabel() const +{ + if (ELEM(operation_->operation, + IDOVERRIDE_LIBRARY_OP_INSERT_AFTER, + IDOVERRIDE_LIBRARY_OP_INSERT_BEFORE)) { + return TIP_("Added through override"); + } + + BLI_assert_unreachable(); + return {}; +} + +std::optional<BIFIconID> TreeElementOverridesPropertyOperation::getIcon() const +{ + if (const std::optional<PointerRNA> col_item_ptr = get_collection_ptr()) { + return (BIFIconID)RNA_struct_ui_icon(col_item_ptr->type); + } + + return {}; +} + +std::optional<PointerRNA> TreeElementOverridesPropertyOperation::get_collection_ptr() const +{ + PointerRNA col_item_ptr; + if (RNA_property_collection_lookup_int(const_cast<PointerRNA *>(&override_rna_ptr), + &override_rna_prop, + operation_->subitem_local_index, + &col_item_ptr)) { + return col_item_ptr; + } + + return {}; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Helper to build a hierarchy from an RNA path. + * + * Builds a nice hierarchy representing the nested structs of the override property's RNA path + * using UI names and icons. For example `animation_visualization_mothion_path.frame_end` becomes: + * - Animation Visualization + * - Motion Paths + * - End Frame + * + * Paths are merged so that each RNA sub-path is only represented once in the tree. So there is + * some finicky path building going on to create a path -> tree-element map. + * + * This is more complicated than you'd think it needs to be. Mostly because of RNA collection + * overrides: + * - A single override may add (and in future remove) multiple collection items. So all operations + * of the override have to be considered. + * - The order of collection items may matter (e.g. for modifiers), so if collection items are + * added/removed, we want to show all other collection items too, in the right order. + * + * - If the override is inside some collection item, the collection item has to be built, but the + * RNA path iterator doesn't + * \{ */ + +OverrideRNAPathTreeBuilder::OverrideRNAPathTreeBuilder(SpaceOutliner &space_outliner) + : space_outliner_(space_outliner) +{ +} + +void OverrideRNAPathTreeBuilder::build_path(TreeElement &parent, + TreeElementOverridesData &override_data, + short &index) +{ + PointerRNA idpoin; + RNA_id_pointer_create(&override_data.id, &idpoin); + + ListBase path_elems = {NULL}; + if (!RNA_path_resolve_elements(&idpoin, override_data.override_property.rna_path, &path_elems)) { + return; + } + + const char *elem_path = nullptr; + TreeElement *te_to_expand = &parent; + + LISTBASE_FOREACH (PropertyElemRNA *, elem, &path_elems) { + if (!elem->next) { + /* The last element is added as #TSE_LIBRARY_OVERRIDE below. */ + break; + } + const char *previous_path = elem_path; + const char *new_path = RNA_path_append(previous_path, &elem->ptr, elem->prop, -1, nullptr); + + te_to_expand = &ensure_label_element_for_prop( + *te_to_expand, new_path, elem->ptr, *elem->prop, index); + + /* Above the collection property was added (e.g. "Modifiers"), to get the actual collection + * item the path refers to, we have to peek at the following path element and add a tree + * element for its pointer (e.g. "My Subdiv Modifier"). */ + if (RNA_property_type(elem->prop) == PROP_COLLECTION) { + const int coll_item_idx = RNA_property_collection_lookup_index( + &elem->ptr, elem->prop, &elem->next->ptr); + const char *coll_item_path = RNA_path_append( + previous_path, &elem->ptr, elem->prop, coll_item_idx, nullptr); + + te_to_expand = &ensure_label_element_for_ptr( + *te_to_expand, coll_item_path, elem->next->ptr, index); + + MEM_delete(new_path); + new_path = coll_item_path; + } + + if (new_path) { + MEM_delete(elem_path); + elem_path = new_path; + } + } + BLI_freelistN(&path_elems); + + /* Special case: Overriding collections, e.g. adding or removing items. In this case we add + * elements for all collection items to show full context, and indicate which ones were + * added/removed (currently added only). Note that a single collection override may add/remove + * multiple items. */ + if (RNA_property_type(&override_data.override_rna_prop) == PROP_COLLECTION) { + /* Tree element for the actual collection item (e.g. "Modifiers"). Can just use the override + * ptr & prop here, since they point to the collection property (e.g. `modifiers`). */ + te_to_expand = &ensure_label_element_for_prop(*te_to_expand, + override_data.override_property.rna_path, + override_data.override_rna_ptr, + override_data.override_rna_prop, + index); + + ensure_entire_collection(*te_to_expand, override_data, elem_path, index); + } + /* Some properties have multiple operations (e.g. an array property with multiple changed + * values), so the element may already be present. At this point they are displayed as a single + * property in the tree, so don't add it multiple times here. */ + else if (!path_te_map.contains(override_data.override_property.rna_path)) { + outliner_add_element(&space_outliner_, + &te_to_expand->subtree, + &override_data, + te_to_expand, + TSE_LIBRARY_OVERRIDE, + index++); + } + + MEM_delete(elem_path); +} + +void OverrideRNAPathTreeBuilder::ensure_entire_collection( + TreeElement &te_to_expand, + const TreeElementOverridesData &override_data, + /* The path of the owning collection property. */ + const char *coll_prop_path, + short &index) +{ + AbstractTreeElement *abstract_parent = tree_element_cast<AbstractTreeElement>(&te_to_expand); + BLI_assert(abstract_parent != nullptr); + + TreeElement *previous_te = nullptr; + int item_idx = 0; + RNA_PROP_BEGIN (&override_data.override_rna_ptr, itemptr, &override_data.override_rna_prop) { + const char *coll_item_path = RNA_path_append(coll_prop_path, + &override_data.override_rna_ptr, + &override_data.override_rna_prop, + item_idx, + nullptr); + IDOverrideLibraryPropertyOperation *item_operation = + BKE_lib_override_library_property_operation_find( + &override_data.override_property, nullptr, nullptr, -1, item_idx, false, nullptr); + TreeElement *current_te = nullptr; + + TreeElement *existing_te = path_te_map.lookup_default(coll_item_path, nullptr); + + if (existing_te) { + /* Reinsert the element to make sure the order is right. It may have been inserted by a + * previous override. */ + BLI_remlink(&te_to_expand.subtree, existing_te); + BLI_insertlinkafter(&te_to_expand.subtree, previous_te, existing_te); + current_te = existing_te; + } + /* Is there an operation for this item (added or removed the item to/from the collection)? If + * so indicate it as override using #TSE_LIBRARY_OVERRIDE_OPERATION. Otherwise it's just a + * regular collection we display for context. */ + else if (item_operation) { + TreeElementOverridesData override_op_data = override_data; + override_op_data.operation = item_operation; + + current_te = outliner_add_element(&space_outliner_, + &te_to_expand.subtree, + /* Element will store a copy. */ + &override_op_data, + &te_to_expand, + TSE_LIBRARY_OVERRIDE_OPERATION, + index++); + } + else { + current_te = &ensure_label_element_for_ptr(te_to_expand, coll_item_path, itemptr, index); + } + + MEM_delete(coll_item_path); + item_idx++; + previous_te = current_te; + } + RNA_PROP_END; +} + +static BIFIconID get_property_icon(PointerRNA &ptr, PropertyRNA &prop) +{ + BIFIconID icon = (BIFIconID)RNA_property_ui_icon(&prop); + if (icon) { + return icon; + } + + /* Try if the collection item type has a dedicated icon (e.g. #ICON_MODIFIER for the + * #Object.modifiers property). */ + if (RNA_property_type(&prop) == PROP_COLLECTION) { + const StructRNA *coll_ptr_type = RNA_property_pointer_type(&ptr, &prop); + icon = (BIFIconID)RNA_struct_ui_icon(coll_ptr_type); + if (icon != ICON_DOT) { + return icon; + } + } + + return ICON_NONE; +} + +TreeElement &OverrideRNAPathTreeBuilder::ensure_label_element_for_prop( + TreeElement &parent, StringRef elem_path, PointerRNA &ptr, PropertyRNA &prop, short &index) +{ + return *path_te_map.lookup_or_add_cb(elem_path, [&]() { + TreeElement *new_te = outliner_add_element(&space_outliner_, + &parent.subtree, + (void *)RNA_property_ui_name(&prop), + &parent, + TSE_GENERIC_LABEL, + index++, + false); + TreeElementLabel *te_label = tree_element_cast<TreeElementLabel>(new_te); + + te_label->setIcon(get_property_icon(ptr, prop)); + return new_te; + }); +} + +TreeElement &OverrideRNAPathTreeBuilder::ensure_label_element_for_ptr(TreeElement &parent, + StringRef elem_path, + PointerRNA &ptr, + short &index) +{ + return *path_te_map.lookup_or_add_cb(elem_path, [&]() { + const char *dyn_name = RNA_struct_name_get_alloc(&ptr, nullptr, 0, nullptr); + + TreeElement *new_te = outliner_add_element( + &space_outliner_, + &parent.subtree, + (void *)(dyn_name ? dyn_name : RNA_struct_ui_name(ptr.type)), + &parent, + TSE_GENERIC_LABEL, + index++); + TreeElementLabel *te_label = tree_element_cast<TreeElementLabel>(new_te); + te_label->setIcon((BIFIconID)RNA_struct_ui_icon(ptr.type)); + + MEM_delete(dyn_name); + + return new_te; + }); +} + +/** \} */ + } // 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 1db46d9af1d..f8ca146a4ea 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_overrides.hh +++ b/source/blender/editors/space_outliner/tree/tree_element_overrides.hh @@ -14,6 +14,7 @@ struct ID; struct IDOverrideLibraryProperty; +struct IDOverrideLibraryPropertyOperation; namespace blender::ed::outliner { @@ -24,6 +25,11 @@ struct TreeElementOverridesData { PropertyRNA &override_rna_prop; bool is_rna_path_valid; + + /* In case the property references a specific operation. Only used for collection overrides + * currently, where a single override may add/remove multiple collection items (only add + * currently). */ + IDOverrideLibraryPropertyOperation *operation = nullptr; }; class TreeElementOverridesBase final : public AbstractTreeElement { @@ -38,7 +44,12 @@ class TreeElementOverridesBase final : public AbstractTreeElement { StringRefNull getWarning() const override; }; -class TreeElementOverridesProperty final : public AbstractTreeElement { +/** + * Represent a single overridden property. Collection properties may support multiple override + * operations, e.g. to insert/remove multiple collection items. For these multiple operation cases, + * use #TreeElementOverridesPropertyOperation. + */ +class TreeElementOverridesProperty : public AbstractTreeElement { public: PointerRNA override_rna_ptr; PropertyRNA &override_rna_prop; @@ -50,6 +61,33 @@ class TreeElementOverridesProperty final : public AbstractTreeElement { TreeElementOverridesProperty(TreeElement &legacy_te, TreeElementOverridesData &override_data); StringRefNull getWarning() const override; + + bool isCollectionOperation() const; +}; + +/** + * Represent a single operation within an overridden property. While usually a single override + * property represents a single operation (changing the value), a single overridden collection + * property may have multiple operations, e.g. to insert or remove collection items. + * + * Inherits from the override property class since it should look/behave mostly the same. + */ +class TreeElementOverridesPropertyOperation final : public TreeElementOverridesProperty { + /** See #TreeElementOverridesData::operation. Operations are recreated as part of the diffing + * (e.g. on undo pushes) so store a copy of the data here. */ + std::unique_ptr<IDOverrideLibraryPropertyOperation> operation_; + + public: + TreeElementOverridesPropertyOperation(TreeElement &legacy_te, + TreeElementOverridesData &override_data); + + /** Return a short string to display in the right column of the properties mode, indicating what + * the override operation did (e.g. added or removed a collection item). */ + StringRefNull getOverrideOperationLabel() const; + std::optional<BIFIconID> getIcon() const override; + + private: + std::optional<PointerRNA> get_collection_ptr() const; }; } // namespace blender::ed::outliner diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index eb2e4ef05e5..0bacbde8240 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -519,7 +519,7 @@ static void draw_seq_waveform_overlay( MEM_freeN(waveform_data); } -/* +#if 0 static size_t *waveform_append(WaveVizData *waveform_data, vec2f pos, const float value_min, @@ -529,7 +529,7 @@ static size_t *waveform_append(WaveVizData *waveform_data, const float rms, const bool is_clipping, const bool is_line_strip) -*/ +#endif static void drawmeta_contents(Scene *scene, Sequence *seqm, diff --git a/source/blender/editors/space_text/text_ops.c b/source/blender/editors/space_text/text_ops.c index 33219092d20..f0196bf8e00 100644 --- a/source/blender/editors/space_text/text_ops.c +++ b/source/blender/editors/space_text/text_ops.c @@ -273,7 +273,7 @@ static int text_new_exec(bContext *C, wmOperator *UNUSED(op)) PointerRNA ptr, idptr; PropertyRNA *prop; - text = BKE_text_add(bmain, "Text"); + text = BKE_text_add(bmain, DATA_("Text")); /* hook into UI */ UI_context_active_but_prop_get_templateID(C, &ptr, &prop); diff --git a/source/blender/editors/space_view3d/view3d_navigate.c b/source/blender/editors/space_view3d/view3d_navigate.c index 50d7626a57d..88e004aac48 100644 --- a/source/blender/editors/space_view3d/view3d_navigate.c +++ b/source/blender/editors/space_view3d/view3d_navigate.c @@ -1554,6 +1554,7 @@ static int viewpan_invoke(bContext *C, wmOperator *op, const wmEvent *event) viewmove_apply(vod, vod->prev.event_xy[0] + x, vod->prev.event_xy[1] + y); + ED_view3d_camera_lock_undo_push(op->type->name, vod->v3d, vod->rv3d, C); viewops_data_free(C, vod); return OPERATOR_FINISHED; diff --git a/source/blender/editors/space_view3d/view3d_navigate_dolly.c b/source/blender/editors/space_view3d/view3d_navigate_dolly.c index d45b0c436ac..376e8ba190b 100644 --- a/source/blender/editors/space_view3d/view3d_navigate_dolly.c +++ b/source/blender/editors/space_view3d/view3d_navigate_dolly.c @@ -181,6 +181,7 @@ static int viewdolly_modal(bContext *C, wmOperator *op, const wmEvent *event) } if (ret & OPERATOR_FINISHED) { + ED_view3d_camera_lock_undo_push(op->type->name, vod->v3d, vod->rv3d, C); viewops_data_free(C, vod); op->customdata = NULL; } diff --git a/source/blender/editors/space_view3d/view3d_navigate_fly.c b/source/blender/editors/space_view3d/view3d_navigate_fly.c index 399f422f411..95114941d66 100644 --- a/source/blender/editors/space_view3d/view3d_navigate_fly.c +++ b/source/blender/editors/space_view3d/view3d_navigate_fly.c @@ -1079,6 +1079,7 @@ static int fly_modal(bContext *C, wmOperator *op, const wmEvent *event) int exit_code; bool do_draw = false; FlyInfo *fly = op->customdata; + View3D *v3d = fly->v3d; RegionView3D *rv3d = fly->rv3d; Object *fly_object = ED_view3d_cameracontrol_object_get(fly->v3d_camera_control); @@ -1102,6 +1103,9 @@ static int fly_modal(bContext *C, wmOperator *op, const wmEvent *event) exit_code = flyEnd(C, fly); + if (exit_code == OPERATOR_FINISHED) { + ED_view3d_camera_lock_undo_push(op->type->name, v3d, rv3d, C); + } if (exit_code != OPERATOR_RUNNING_MODAL) { do_draw = true; } diff --git a/source/blender/editors/space_view3d/view3d_navigate_move.c b/source/blender/editors/space_view3d/view3d_navigate_move.c index e653b349a2f..e236b702fb8 100644 --- a/source/blender/editors/space_view3d/view3d_navigate_move.c +++ b/source/blender/editors/space_view3d/view3d_navigate_move.c @@ -140,6 +140,7 @@ static int viewmove_modal(bContext *C, wmOperator *op, const wmEvent *event) } if (ret & OPERATOR_FINISHED) { + ED_view3d_camera_lock_undo_push(op->type->name, vod->v3d, vod->rv3d, C); viewops_data_free(C, op->customdata); op->customdata = NULL; } diff --git a/source/blender/editors/space_view3d/view3d_navigate_rotate.c b/source/blender/editors/space_view3d/view3d_navigate_rotate.c index 989fa152acc..20385e15c48 100644 --- a/source/blender/editors/space_view3d/view3d_navigate_rotate.c +++ b/source/blender/editors/space_view3d/view3d_navigate_rotate.c @@ -375,6 +375,7 @@ static int viewrotate_modal(bContext *C, wmOperator *op, const wmEvent *event) } if (ret & OPERATOR_FINISHED) { + ED_view3d_camera_lock_undo_push(op->type->name, vod->v3d, vod->rv3d, C); viewops_data_free(C, op->customdata); op->customdata = NULL; } diff --git a/source/blender/editors/space_view3d/view3d_navigate_walk.c b/source/blender/editors/space_view3d/view3d_navigate_walk.c index f1e9ac22882..69deaab7ebe 100644 --- a/source/blender/editors/space_view3d/view3d_navigate_walk.c +++ b/source/blender/editors/space_view3d/view3d_navigate_walk.c @@ -1386,6 +1386,7 @@ static int walk_modal(bContext *C, wmOperator *op, const wmEvent *event) int exit_code; bool do_draw = false; WalkInfo *walk = op->customdata; + View3D *v3d = walk->v3d; RegionView3D *rv3d = walk->rv3d; Object *walk_object = ED_view3d_cameracontrol_object_get(walk->v3d_camera_control); @@ -1412,6 +1413,9 @@ static int walk_modal(bContext *C, wmOperator *op, const wmEvent *event) if (exit_code != OPERATOR_RUNNING_MODAL) { do_draw = true; } + if (exit_code == OPERATOR_FINISHED) { + ED_view3d_camera_lock_undo_push(op->type->name, v3d, rv3d, C); + } if (do_draw) { if (rv3d->persp == RV3D_CAMOB) { diff --git a/source/blender/editors/space_view3d/view3d_navigate_zoom.c b/source/blender/editors/space_view3d/view3d_navigate_zoom.c index a67c0850ad9..9230aa09b1a 100644 --- a/source/blender/editors/space_view3d/view3d_navigate_zoom.c +++ b/source/blender/editors/space_view3d/view3d_navigate_zoom.c @@ -425,6 +425,7 @@ static int viewzoom_modal(bContext *C, wmOperator *op, const wmEvent *event) } if (ret & OPERATOR_FINISHED) { + ED_view3d_camera_lock_undo_push(op->type->name, vod->v3d, vod->rv3d, C); viewops_data_free(C, op->customdata); op->customdata = NULL; } @@ -507,6 +508,7 @@ static int viewzoom_exec(bContext *C, wmOperator *op) ED_region_tag_redraw(region); + ED_view3d_camera_lock_undo_grouped_push(op->type->name, v3d, rv3d, C); viewops_data_free(C, op->customdata); op->customdata = NULL; diff --git a/source/blender/editors/space_view3d/view3d_utils.c b/source/blender/editors/space_view3d/view3d_utils.c index 85b1af8e55d..99f8cbc975b 100644 --- a/source/blender/editors/space_view3d/view3d_utils.c +++ b/source/blender/editors/space_view3d/view3d_utils.c @@ -44,6 +44,7 @@ #include "ED_keyframing.h" #include "ED_screen.h" +#include "ED_undo.h" #include "ED_view3d.h" #include "UI_resources.h" @@ -688,6 +689,43 @@ bool ED_view3d_camera_lock_autokey(View3D *v3d, return false; } +/** + * Create a MEMFILE undo-step for locked camera movement when transforming the view. + * Edit and texture paint mode don't use MEMFILE undo so undo push is skipped for them. + * NDOF and track-pad navigation would create an undo step on every gesture and we may end up with + * unnecessary undo steps so undo push for them is not supported for now. Also operators that uses + * smooth view for navigation are excluded too, but they can be supported, see: D15345. + */ +static bool view3d_camera_lock_undo_ex( + const char *str, View3D *v3d, RegionView3D *rv3d, struct bContext *C, bool undo_group) +{ + if (ED_view3d_camera_lock_check(v3d, rv3d)) { + if (ED_undo_is_memfile_compatible(C)) { + if (undo_group) { + ED_undo_grouped_push(C, str); + } + else { + ED_undo_push(C, str); + } + return true; + } + } + return false; +} + +bool ED_view3d_camera_lock_undo_push(const char *str, View3D *v3d, RegionView3D *rv3d, bContext *C) +{ + return view3d_camera_lock_undo_ex(str, v3d, rv3d, C, false); +} + +bool ED_view3d_camera_lock_undo_grouped_push(const char *str, + View3D *v3d, + RegionView3D *rv3d, + bContext *C) +{ + return view3d_camera_lock_undo_ex(str, v3d, rv3d, C, true); +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/editors/transform/transform_mode_resize.c b/source/blender/editors/transform/transform_mode_resize.c index 1ccda96fecb..70599c3577c 100644 --- a/source/blender/editors/transform/transform_mode_resize.c +++ b/source/blender/editors/transform/transform_mode_resize.c @@ -126,19 +126,14 @@ static void constrain_scale_to_boundary(const float numerator, static bool clip_uv_transform_resize(TransInfo *t, float vec[2]) { - /* Check if the current image in UV editor is a tiled image or not. */ - const SpaceImage *sima = t->area->spacedata.first; - const Image *image = sima->image; - const bool is_tiled_image = image && (image->source == IMA_SRC_TILED); /* Stores the coordinates of the closest UDIM tile. * Also acts as an offset to the tile from the origin of UV space. */ float base_offset[2] = {0.0f, 0.0f}; /* If tiled image then constrain to correct/closest UDIM tile, else 0-1 UV space. */ - if (is_tiled_image) { - BKE_image_find_nearest_tile_with_offset(image, t->center_global, base_offset); - } + const SpaceImage *sima = t->area->spacedata.first; + BKE_image_find_nearest_tile_with_offset(sima->image, t->center_global, base_offset); /* Assume no change is required. */ float scale = 1.0f; diff --git a/source/blender/editors/transform/transform_mode_translate.c b/source/blender/editors/transform/transform_mode_translate.c index 04a41814b53..8f6ec7bd98f 100644 --- a/source/blender/editors/transform/transform_mode_translate.c +++ b/source/blender/editors/transform/transform_mode_translate.c @@ -437,19 +437,13 @@ static void applyTranslationValue(TransInfo *t, const float vec[3]) static bool clip_uv_transform_translation(TransInfo *t, float vec[2]) { - /* Check if the current image in UV editor is a tiled image or not. */ - const SpaceImage *sima = t->area->spacedata.first; - const Image *image = sima->image; - const bool is_tiled_image = image && (image->source == IMA_SRC_TILED); - /* Stores the coordinates of the closest UDIM tile. * Also acts as an offset to the tile from the origin of UV space. */ float base_offset[2] = {0.0f, 0.0f}; /* If tiled image then constrain to correct/closest UDIM tile, else 0-1 UV space. */ - if (is_tiled_image) { - BKE_image_find_nearest_tile_with_offset(image, t->center_global, base_offset); - } + const SpaceImage *sima = t->area->spacedata.first; + BKE_image_find_nearest_tile_with_offset(sima->image, t->center_global, base_offset); float min[2], max[2]; min[0] = min[1] = FLT_MAX; diff --git a/source/blender/editors/uvedit/uvedit_islands.c b/source/blender/editors/uvedit/uvedit_islands.c index 2afc60a4b5c..3877a9bb63b 100644 --- a/source/blender/editors/uvedit/uvedit_islands.c +++ b/source/blender/editors/uvedit/uvedit_islands.c @@ -259,9 +259,8 @@ static float uv_nearest_image_tile_distance(const Image *image, const float coords[2], float nearest_tile_co[2]) { - if (BKE_image_find_nearest_tile_with_offset(image, coords, nearest_tile_co) == -1) { - zero_v2(nearest_tile_co); - } + BKE_image_find_nearest_tile_with_offset(image, coords, nearest_tile_co); + /* Add 0.5 to get tile center coordinates. */ float nearest_tile_center_co[2] = {nearest_tile_co[0], nearest_tile_co[1]}; add_v2_fl(nearest_tile_center_co, 0.5f); diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c index 5ebdfcec294..092f0c49d8a 100644 --- a/source/blender/editors/uvedit/uvedit_ops.c +++ b/source/blender/editors/uvedit/uvedit_ops.c @@ -543,11 +543,11 @@ static bool uvedit_uv_straighten(Scene *scene, BMesh *bm, eUVWeldAlign tool) bool changed = false; /* Loop backwards to simplify logic. */ - int j1 = element_map->totalUVs; + int j1 = element_map->total_uvs; for (int i = element_map->totalIslands - 1; i >= 0; --i) { int j0 = element_map->islandIndices[i]; changed |= uvedit_uv_straighten_elements( - element_map->buf + j0, j1 - j0, cd_loop_uv_offset, tool); + element_map->storage + j0, j1 - j0, cd_loop_uv_offset, tool); j1 = j0; } diff --git a/source/blender/editors/uvedit/uvedit_smart_stitch.c b/source/blender/editors/uvedit/uvedit_smart_stitch.c index 579674930a6..26ed98ba236 100644 --- a/source/blender/editors/uvedit/uvedit_smart_stitch.c +++ b/source/blender/editors/uvedit/uvedit_smart_stitch.c @@ -290,7 +290,7 @@ static void stitch_update_header(StitchStateContainer *ssc, bContext *C) static int getNumOfIslandUvs(UvElementMap *elementMap, int island) { if (island == elementMap->totalIslands - 1) { - return elementMap->totalUVs - elementMap->islandIndices[island]; + return elementMap->total_uvs - elementMap->islandIndices[island]; } return elementMap->islandIndices[island + 1] - elementMap->islandIndices[island]; } @@ -465,7 +465,7 @@ static void stitch_calculate_island_snapping(StitchState *state, angle_to_mat2(rotation_mat, rotation); numOfIslandUVs = getNumOfIslandUvs(state->element_map, i); - element = &state->element_map->buf[state->element_map->islandIndices[i]]; + element = &state->element_map->storage[state->element_map->islandIndices[i]]; for (j = 0; j < numOfIslandUVs; j++, element++) { /* stitchable uvs have already been processed, don't process */ if (!(element->flag & STITCH_PROCESSED)) { @@ -527,8 +527,8 @@ static void stitch_island_calculate_edge_rotation(UvEdge *edge, luv2 = CustomData_bmesh_get(&bm->ldata, element2->l->head.data, CD_MLOOPUV); if (ssc->mode == STITCH_VERT) { - index1 = uvfinal_map[element1 - state->element_map->buf]; - index2 = uvfinal_map[element2 - state->element_map->buf]; + index1 = uvfinal_map[element1 - state->element_map->storage]; + index2 = uvfinal_map[element2 - state->element_map->storage]; } else { index1 = edge->uv1; @@ -569,27 +569,17 @@ static void stitch_island_calculate_vert_rotation(UvElement *element, StitchState *state, IslandStitchData *island_stitch_data) { - float edgecos = 1.0f, edgesin = 0.0f; - int index; - UvElement *element_iter; float rotation = 0, rotation_neg = 0; int rot_elem = 0, rot_elem_neg = 0; - BMLoop *l; if (element->island == ssc->static_island && !ssc->midpoints) { return; } - l = element->l; - - index = BM_elem_index_get(l->v); - - element_iter = state->element_map->vert[index]; - + UvElement *element_iter = BM_uv_element_get_head(state->element_map, element); for (; element_iter; element_iter = element_iter->next) { if (element_iter->separate && stitch_check_uvs_state_stitchable(element, element_iter, ssc, state)) { - int index_tmp1, index_tmp2; float normal[2]; /* only calculate rotation against static island uv verts */ @@ -597,14 +587,14 @@ static void stitch_island_calculate_vert_rotation(UvElement *element, continue; } - index_tmp1 = element_iter - state->element_map->buf; + int index_tmp1 = element_iter - state->element_map->storage; index_tmp1 = state->map[index_tmp1]; - index_tmp2 = element - state->element_map->buf; + int index_tmp2 = element - state->element_map->storage; index_tmp2 = state->map[index_tmp2]; negate_v2_v2(normal, state->normals + index_tmp2 * 2); - edgecos = dot_v2v2(normal, state->normals + index_tmp1 * 2); - edgesin = cross_v2v2(normal, state->normals + index_tmp1 * 2); + float edgecos = dot_v2v2(normal, state->normals + index_tmp1 * 2); + float edgesin = cross_v2v2(normal, state->normals + index_tmp1 * 2); if (edgesin > 0.0f) { rotation += acosf(max_ff(-1.0f, min_ff(1.0f, edgecos))); rot_elem++; @@ -653,9 +643,8 @@ static void state_delete(StitchState *state) if (state->edges) { MEM_freeN(state->edges); } - if (state->stitch_preview) { - stitch_preview_delete(state->stitch_preview); - } + stitch_preview_delete(state->stitch_preview); + state->stitch_preview = NULL; if (state->edge_hash) { BLI_ghash_free(state->edge_hash, NULL, NULL); } @@ -680,10 +669,7 @@ static void stitch_uv_edge_generate_linked_edges(GHash *edge_hash, StitchState * UvEdge *edges = state->edges; const int *map = state->map; UvElementMap *element_map = state->element_map; - UvElement *first_element = element_map->buf; - int i; - - for (i = 0; i < state->total_separate_edges; i++) { + for (int i = 0; i < state->total_separate_edges; i++) { UvEdge *edge = edges + i; if (edge->first) { @@ -696,7 +682,7 @@ static void stitch_uv_edge_generate_linked_edges(GHash *edge_hash, StitchState * UvElement *element2 = state->uvs[edge->uv2]; /* Now iterate through all faces and try to find edges sharing the same vertices */ - UvElement *iter1 = element_map->vert[BM_elem_index_get(element1->l->v)]; + UvElement *iter1 = BM_uv_element_get_head(state->element_map, element1); UvEdge *last_set = edge; int elemindex2 = BM_elem_index_get(element2->l->v); @@ -714,8 +700,8 @@ static void stitch_uv_edge_generate_linked_edges(GHash *edge_hash, StitchState * } if (iter2) { - int index1 = map[iter1 - first_element]; - int index2 = map[iter2 - first_element]; + int index1 = map[iter1 - element_map->storage]; + int index2 = map[iter2 - element_map->storage]; UvEdge edgetmp; UvEdge *edge2, *eiter; bool valid = true; @@ -764,15 +750,7 @@ static void determine_uv_stitchability(UvElement *element, StitchState *state, IslandStitchData *island_stitch_data) { - int vert_index; - UvElement *element_iter; - BMLoop *l; - - l = element->l; - - vert_index = BM_elem_index_get(l->v); - element_iter = state->element_map->vert[vert_index]; - + UvElement *element_iter = BM_uv_element_get_head(state->element_map, element); for (; element_iter; element_iter = element_iter->next) { if (element_iter->separate) { if (stitch_check_uvs_stitchable(element, element_iter, ssc, state)) { @@ -853,16 +831,7 @@ static void stitch_validate_uv_stitchability(UvElement *element, return; } - UvElement *element_iter; - int vert_index; - BMLoop *l; - - l = element->l; - - vert_index = BM_elem_index_get(l->v); - - element_iter = state->element_map->vert[vert_index]; - + UvElement *element_iter = BM_uv_element_get_head(state->element_map, element); for (; element_iter; element_iter = element_iter->next) { if (element_iter->separate) { if (element_iter == element) { @@ -1177,7 +1146,7 @@ static int stitch_process_data(StitchStateContainer *ssc, int numOfIslandUVs = 0, j; UvElement *element; numOfIslandUVs = getNumOfIslandUvs(state->element_map, i); - element = &state->element_map->buf[state->element_map->islandIndices[i]]; + element = &state->element_map->storage[state->element_map->islandIndices[i]]; for (j = 0; j < numOfIslandUVs; j++, element++) { stitch_set_face_preview_buffer_position(element->l->f, preview, preview_position); } @@ -1263,7 +1232,7 @@ static int stitch_process_data(StitchStateContainer *ssc, if (ssc->mode == STITCH_VERT) { final_position = MEM_callocN(state->selection_size * sizeof(*final_position), "stitch_uv_average"); - uvfinal_map = MEM_mallocN(state->element_map->totalUVs * sizeof(*uvfinal_map), + uvfinal_map = MEM_mallocN(state->element_map->total_uvs * sizeof(*uvfinal_map), "stitch_uv_final_map"); } else { @@ -1279,12 +1248,11 @@ static int stitch_process_data(StitchStateContainer *ssc, if (element->flag & STITCH_STITCHABLE) { BMLoop *l; MLoopUV *luv; - UvElement *element_iter; l = element->l; luv = CustomData_bmesh_get(&bm->ldata, l->head.data, CD_MLOOPUV); - uvfinal_map[element - state->element_map->buf] = i; + uvfinal_map[element - state->element_map->storage] = i; copy_v2_v2(final_position[i].uv, luv->uv); final_position[i].count = 1; @@ -1293,8 +1261,7 @@ static int stitch_process_data(StitchStateContainer *ssc, continue; } - element_iter = state->element_map->vert[BM_elem_index_get(l->v)]; - + UvElement *element_iter = state->element_map->vertex[BM_elem_index_get(l->v)]; for (; element_iter; element_iter = element_iter->next) { if (element_iter->separate) { if (stitch_check_uvs_state_stitchable(element, element_iter, ssc, state)) { @@ -1542,6 +1509,7 @@ static int stitch_process_data_all(StitchStateContainer *ssc, Scene *scene, int static uint uv_edge_hash(const void *key) { const UvEdge *edge = key; + BLI_assert(edge->uv1 < edge->uv2); return (BLI_ghashutil_uinthash(edge->uv2) + BLI_ghashutil_uinthash(edge->uv1)); } @@ -1549,6 +1517,8 @@ static bool uv_edge_compare(const void *a, const void *b) { const UvEdge *edge1 = a; const UvEdge *edge2 = b; + BLI_assert(edge1->uv1 < edge1->uv2); + BLI_assert(edge2->uv1 < edge2->uv2); if ((edge1->uv1 == edge2->uv1) && (edge1->uv2 == edge2->uv2)) { return 0; @@ -1588,13 +1558,8 @@ static void stitch_select_edge(UvEdge *edge, StitchState *state, int always_sele /* Select all common uvs */ static void stitch_select_uv(UvElement *element, StitchState *state, int always_select) { - BMLoop *l; - UvElement *element_iter; UvElement **selection_stack = (UvElement **)state->selection_stack; - - l = element->l; - - element_iter = state->element_map->vert[BM_elem_index_get(l->v)]; + UvElement *element_iter = BM_uv_element_get_head(state->element_map, element); /* first deselect all common uvs */ for (; element_iter; element_iter = element_iter->next) { if (element_iter->separate) { @@ -1850,8 +1815,8 @@ static UvEdge *uv_edge_get(BMLoop *l, StitchState *state) UvElement *element1 = BM_uv_element_get(state->element_map, l->f, l); UvElement *element2 = BM_uv_element_get(state->element_map, l->f, l->next); - int uv1 = state->map[element1 - state->element_map->buf]; - int uv2 = state->map[element2 - state->element_map->buf]; + int uv1 = state->map[element1 - state->element_map->storage]; + int uv2 = state->map[element2 - state->element_map->storage]; if (uv1 < uv2) { tmp_edge.uv1 = uv1; @@ -1878,7 +1843,6 @@ static StitchState *stitch_init(bContext *C, int total_edges; /* maps uvelements to their first coincident uv */ int *map; - int counter = 0, i; BMFace *efa; BMLoop *l; BMIter iter, liter; @@ -1913,45 +1877,39 @@ static StitchState *stitch_init(bContext *C, ED_uvedit_get_aspect(obedit, &aspx, &aspy); state->aspect = aspx / aspy; - /* Count 'unique' uvs */ - for (i = 0; i < state->element_map->totalUVs; i++) { - if (state->element_map->buf[i].separate) { - counter++; - } - } + int unique_uvs = state->element_map->total_unique_uvs; + state->total_separate_uvs = unique_uvs; - /* explicitly set preview to NULL, - * to avoid deleting an invalid pointer on stitch_process_data */ - state->stitch_preview = NULL; /* Allocate the unique uv buffers */ - state->uvs = MEM_mallocN(sizeof(*state->uvs) * counter, "uv_stitch_unique_uvs"); + state->uvs = MEM_mallocN(sizeof(*state->uvs) * unique_uvs, "uv_stitch_unique_uvs"); /* internal uvs need no normals but it is hard and slow to keep a map of - * normals only for boundary uvs, so allocating for all uvs */ - state->normals = MEM_callocN(sizeof(*state->normals) * counter * 2, "uv_stitch_normals"); - state->total_separate_uvs = counter; - state->map = map = MEM_mallocN(sizeof(*map) * state->element_map->totalUVs, + * normals only for boundary uvs, so allocating for all uvs. + * Times 2 because each `float[2]` is stored as `{n[2 * i], n[2*i + 1]}`. */ + state->normals = MEM_callocN(sizeof(*state->normals) * 2 * unique_uvs, "uv_stitch_normals"); + state->map = map = MEM_mallocN(sizeof(*map) * state->element_map->total_uvs, "uv_stitch_unique_map"); /* Allocate the edge stack */ edge_hash = BLI_ghash_new(uv_edge_hash, uv_edge_compare, "stitch_edge_hash"); - all_edges = MEM_mallocN(sizeof(*all_edges) * state->element_map->totalUVs, "ssc_edges"); + all_edges = MEM_mallocN(sizeof(*all_edges) * state->element_map->total_uvs, "ssc_edges"); + BLI_assert(!state->stitch_preview); /* Paranoia. */ if (!state->uvs || !map || !edge_hash || !all_edges) { state_delete(state); return NULL; } - /* So that we can use this as index for the UvElements */ - counter = -1; + /* Index for the UvElements. */ + int counter = -1; /* initialize the unique UVs and map */ - for (i = 0; i < em->bm->totvert; i++) { - UvElement *element = state->element_map->vert[i]; + for (int i = 0; i < em->bm->totvert; i++) { + UvElement *element = state->element_map->vertex[i]; for (; element; element = element->next) { if (element->separate) { counter++; state->uvs[counter] = element; } /* Pointer arithmetic to the rescue, as always :). */ - map[element - state->element_map->buf] = counter; + map[element - state->element_map->storage] = counter; } } @@ -1965,13 +1923,13 @@ static StitchState *stitch_init(bContext *C, BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { UvElement *element = BM_uv_element_get(state->element_map, efa, l); - int offset1, itmp1 = element - state->element_map->buf; - int offset2, - itmp2 = BM_uv_element_get(state->element_map, efa, l->next) - state->element_map->buf; + int itmp1 = element - state->element_map->storage; + int itmp2 = BM_uv_element_get(state->element_map, efa, l->next) - + state->element_map->storage; UvEdge *edge; - offset1 = map[itmp1]; - offset2 = map[itmp2]; + int offset1 = map[itmp1]; + int offset2 = map[itmp2]; all_edges[counter].next = NULL; all_edges[counter].first = NULL; @@ -2012,7 +1970,7 @@ static StitchState *stitch_init(bContext *C, state->total_separate_edges = total_edges; /* fill the edges with data */ - i = 0; + int i = 0; GHASH_ITER (gh_iter, edge_hash) { edges[i++] = *((UvEdge *)BLI_ghashIterator_getKey(&gh_iter)); } @@ -2091,13 +2049,13 @@ static StitchState *stitch_init(bContext *C, efa = BM_face_at_index(em->bm, faceIndex); element = BM_uv_element_get( state->element_map, efa, BM_iter_at_index(NULL, BM_LOOPS_OF_FACE, efa, elementIndex)); - uv1 = map[element - state->element_map->buf]; + uv1 = map[element - state->element_map->storage]; element = BM_uv_element_get( state->element_map, efa, BM_iter_at_index(NULL, BM_LOOPS_OF_FACE, efa, (elementIndex + 1) % efa->len)); - uv2 = map[element - state->element_map->buf]; + uv2 = map[element - state->element_map->storage]; if (uv1 < uv2) { tmp_edge.uv1 = uv1; diff --git a/source/blender/freestyle/intern/view_map/ViewMap.cpp b/source/blender/freestyle/intern/view_map/ViewMap.cpp index b26a833b32e..d918cfec2ae 100644 --- a/source/blender/freestyle/intern/view_map/ViewMap.cpp +++ b/source/blender/freestyle/intern/view_map/ViewMap.cpp @@ -398,7 +398,7 @@ void TVertex::setBackEdgeB(ViewEdge *iBackEdgeB, bool incoming) void TVertex::Replace(ViewEdge *iOld, ViewEdge *iNew) { - // theoritically, we only replace edges for which this + // theoretically, we only replace edges for which this // view vertex is the B vertex if ((iOld == _FrontEdgeA.first) && (_FrontEdgeA.first->B() == this)) { _FrontEdgeA.first = iNew; diff --git a/source/blender/geometry/CMakeLists.txt b/source/blender/geometry/CMakeLists.txt index da83d9e8957..0f06890cbfa 100644 --- a/source/blender/geometry/CMakeLists.txt +++ b/source/blender/geometry/CMakeLists.txt @@ -27,7 +27,7 @@ set(SRC intern/reverse_uv_sampler.cc intern/set_curve_type.cc intern/subdivide_curves.cc - intern/uv_parametrizer.c + intern/uv_parametrizer.cc GEO_add_curves_on_mesh.hh GEO_fillet_curves.hh diff --git a/source/blender/geometry/intern/add_curves_on_mesh.cc b/source/blender/geometry/intern/add_curves_on_mesh.cc index 7184d774a22..299040d4d32 100644 --- a/source/blender/geometry/intern/add_curves_on_mesh.cc +++ b/source/blender/geometry/intern/add_curves_on_mesh.cc @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ #include "BLI_length_parameterize.hh" +#include "BLI_task.hh" #include "BKE_attribute_math.hh" #include "BKE_mesh_sample.hh" @@ -102,8 +103,8 @@ void interpolate_from_neighbors(const Span<NeighborCurves> neighbors_per_curve, } } } + mixer.finalize(range); }); - mixer.finalize(); } static void interpolate_position_without_interpolation( diff --git a/source/blender/geometry/intern/set_curve_type.cc b/source/blender/geometry/intern/set_curve_type.cc index 40ee2488a4b..92609a45bdc 100644 --- a/source/blender/geometry/intern/set_curve_type.cc +++ b/source/blender/geometry/intern/set_curve_type.cc @@ -286,42 +286,6 @@ static void retrieve_curve_sizes(const bke::CurvesGeometry &curves, MutableSpan< }); } -struct GenericAttributes : NonCopyable, NonMovable { - Vector<GSpan> src; - Vector<GMutableSpan> dst; - - Vector<bke::GSpanAttributeWriter> attributes; -}; - -static void retrieve_generic_point_attributes(const bke::AttributeAccessor &src_attributes, - bke::MutableAttributeAccessor &dst_attributes, - GenericAttributes &attributes) -{ - src_attributes.for_all( - [&](const bke::AttributeIDRef &id, const bke::AttributeMetaData meta_data) { - if (meta_data.domain != ATTR_DOMAIN_POINT) { - /* Curve domain attributes are all copied directly to the result in one step. */ - return true; - } - if (src_attributes.is_builtin(id)) { - if (!(id.is_named() && ELEM(id, "tilt", "radius"))) { - return true; - } - } - - GVArray src_attribute = src_attributes.lookup(id, ATTR_DOMAIN_POINT); - BLI_assert(src_attribute); - attributes.src.append(src_attribute.get_internal_span()); - - bke::GSpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_span( - id, ATTR_DOMAIN_POINT, meta_data.data_type); - attributes.dst.append(dst_attribute.span); - attributes.attributes.append(std::move(dst_attribute)); - - return true; - }); -} - static bke::CurvesGeometry convert_curves_to_bezier(const bke::CurvesGeometry &src_curves, const IndexMask selection) { @@ -347,8 +311,16 @@ static bke::CurvesGeometry convert_curves_to_bezier(const bke::CurvesGeometry &s const bke::AttributeAccessor src_attributes = src_curves.attributes(); bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write(); - GenericAttributes attributes; - retrieve_generic_point_attributes(src_attributes, dst_attributes, attributes); + Vector<bke::AttributeTransferData> generic_attributes = bke::retrieve_attributes_for_transfer( + src_attributes, + dst_attributes, + ATTR_DOMAIN_MASK_POINT, + {"position", + "handle_type_left", + "handle_type_right", + "handle_right", + "handle_left", + "nurbs_weight"}); MutableSpan<float3> dst_positions = dst_curves.positions_for_write(); MutableSpan<float3> dst_handles_l = dst_curves.handle_positions_left_for_write(); @@ -373,9 +345,9 @@ static bke::CurvesGeometry convert_curves_to_bezier(const bke::CurvesGeometry &s } }); - for (const int i : attributes.src.index_range()) { + for (bke::AttributeTransferData &attribute : generic_attributes) { bke::curves::copy_point_data( - src_curves, dst_curves, selection, attributes.src[i], attributes.dst[i]); + src_curves, dst_curves, selection, attribute.src, attribute.dst.span); } }; @@ -384,9 +356,9 @@ static bke::CurvesGeometry convert_curves_to_bezier(const bke::CurvesGeometry &s bke::curves::fill_points<int8_t>(dst_curves, selection, BEZIER_HANDLE_VECTOR, dst_types_l); bke::curves::fill_points<int8_t>(dst_curves, selection, BEZIER_HANDLE_VECTOR, dst_types_r); dst_curves.calculate_bezier_auto_handles(); - for (const int i : attributes.src.index_range()) { + for (bke::AttributeTransferData &attribute : generic_attributes) { bke::curves::copy_point_data( - src_curves, dst_curves, selection, attributes.src[i], attributes.dst[i]); + src_curves, dst_curves, selection, attribute.src, attribute.dst.span); } }; @@ -404,9 +376,9 @@ static bke::CurvesGeometry convert_curves_to_bezier(const bke::CurvesGeometry &s dst_curves.calculate_bezier_auto_handles(); - for (const int i : attributes.src.index_range()) { + for (bke::AttributeTransferData &attribute : generic_attributes) { bke::curves::copy_point_data( - src_curves, dst_curves, selection, attributes.src[i], attributes.dst[i]); + src_curves, dst_curves, selection, attribute.src, attribute.dst.span); } }; @@ -445,14 +417,14 @@ static bke::CurvesGeometry convert_curves_to_bezier(const bke::CurvesGeometry &s } }); - for (const int i_attribute : attributes.src.index_range()) { + for (bke::AttributeTransferData &attribute : generic_attributes) { threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) { for (const int i : selection.slice(range)) { const IndexRange src_points = src_curves.points_for_curve(i); const IndexRange dst_points = dst_curves.points_for_curve(i); - nurbs_to_bezier_assign(attributes.src[i_attribute].slice(src_points), + nurbs_to_bezier_assign(attribute.src.slice(src_points), KnotsMode(src_knot_modes[i]), - attributes.dst[i_attribute].slice(dst_points)); + attribute.dst.span.slice(dst_points)); } }); } @@ -469,13 +441,13 @@ static bke::CurvesGeometry convert_curves_to_bezier(const bke::CurvesGeometry &s const Vector<IndexRange> unselected_ranges = selection.extract_ranges_invert( src_curves.curves_range()); - for (const int i : attributes.src.index_range()) { + for (bke::AttributeTransferData &attribute : generic_attributes) { bke::curves::copy_point_data( - src_curves, dst_curves, unselected_ranges, attributes.src[i], attributes.dst[i]); + src_curves, dst_curves, unselected_ranges, attribute.src, attribute.dst.span); } - for (bke::GSpanAttributeWriter &attribute : attributes.attributes) { - attribute.finish(); + for (bke::AttributeTransferData &attribute : generic_attributes) { + attribute.dst.finish(); } return dst_curves; @@ -504,8 +476,16 @@ static bke::CurvesGeometry convert_curves_to_nurbs(const bke::CurvesGeometry &sr const bke::AttributeAccessor src_attributes = src_curves.attributes(); bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write(); - GenericAttributes attributes; - retrieve_generic_point_attributes(src_attributes, dst_attributes, attributes); + Vector<bke::AttributeTransferData> generic_attributes = bke::retrieve_attributes_for_transfer( + src_attributes, + dst_attributes, + ATTR_DOMAIN_MASK_POINT, + {"position", + "handle_type_left", + "handle_type_right", + "handle_right", + "handle_left", + "nurbs_weight"}); MutableSpan<float3> dst_positions = dst_curves.positions_for_write(); @@ -529,13 +509,13 @@ static bke::CurvesGeometry convert_curves_to_nurbs(const bke::CurvesGeometry &sr } }); - for (const int i_attribute : attributes.src.index_range()) { + for (bke::AttributeTransferData &attribute : generic_attributes) { threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) { for (const int i : selection.slice(range)) { const IndexRange src_points = src_curves.points_for_curve(i); const IndexRange dst_points = dst_curves.points_for_curve(i); - bezier_generic_to_nurbs(attributes.src[i_attribute].slice(src_points), - attributes.dst[i_attribute].slice(dst_points)); + bezier_generic_to_nurbs(attribute.src.slice(src_points), + attribute.dst.span.slice(dst_points)); } }); } @@ -563,12 +543,9 @@ static bke::CurvesGeometry convert_curves_to_nurbs(const bke::CurvesGeometry &sr }); } - for (const int i_attribute : attributes.src.index_range()) { - bke::curves::copy_point_data(src_curves, - dst_curves, - selection, - attributes.src[i_attribute], - attributes.dst[i_attribute]); + for (bke::AttributeTransferData &attribute : generic_attributes) { + bke::curves::copy_point_data( + src_curves, dst_curves, selection, attribute.src, attribute.dst.span); } }; @@ -591,13 +568,13 @@ static bke::CurvesGeometry convert_curves_to_nurbs(const bke::CurvesGeometry &sr } }); - for (const int i_attribute : attributes.src.index_range()) { + for (bke::AttributeTransferData &attribute : generic_attributes) { threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) { for (const int i : selection.slice(range)) { const IndexRange src_points = src_curves.points_for_curve(i); const IndexRange dst_points = dst_curves.points_for_curve(i); - bezier_generic_to_nurbs(attributes.src[i_attribute].slice(src_points), - attributes.dst[i_attribute].slice(dst_points)); + bezier_generic_to_nurbs(attribute.src.slice(src_points), + attribute.dst.span.slice(dst_points)); } }); } @@ -614,12 +591,9 @@ static bke::CurvesGeometry convert_curves_to_nurbs(const bke::CurvesGeometry &sr dst_curves.nurbs_weights_for_write()); } - for (const int i_attribute : attributes.src.index_range()) { - bke::curves::copy_point_data(src_curves, - dst_curves, - selection, - attributes.src[i_attribute], - attributes.dst[i_attribute]); + for (bke::AttributeTransferData &attribute : generic_attributes) { + bke::curves::copy_point_data( + src_curves, dst_curves, selection, attribute.src, attribute.dst.span); } }; @@ -634,13 +608,13 @@ static bke::CurvesGeometry convert_curves_to_nurbs(const bke::CurvesGeometry &sr const Vector<IndexRange> unselected_ranges = selection.extract_ranges_invert( src_curves.curves_range()); - for (const int i : attributes.src.index_range()) { + for (bke::AttributeTransferData &attribute : generic_attributes) { bke::curves::copy_point_data( - src_curves, dst_curves, unselected_ranges, attributes.src[i], attributes.dst[i]); + src_curves, dst_curves, unselected_ranges, attribute.src, attribute.dst.span); } - for (bke::GSpanAttributeWriter &attribute : attributes.attributes) { - attribute.finish(); + for (bke::AttributeTransferData &attribute : generic_attributes) { + attribute.dst.finish(); } return dst_curves; diff --git a/source/blender/geometry/intern/uv_parametrizer.c b/source/blender/geometry/intern/uv_parametrizer.cc index 4fea9ecebc5..ae2794bf9bc 100644 --- a/source/blender/geometry/intern/uv_parametrizer.c +++ b/source/blender/geometry/intern/uv_parametrizer.cc @@ -937,17 +937,16 @@ static void p_split_vert(ParamHandle *handle, PChart *chart, PEdge *e) static PChart **p_split_charts(ParamHandle *handle, PChart *chart, int ncharts) { - PChart **charts = MEM_mallocN(sizeof(*charts) * ncharts, "PCharts"); - PFace *f, *nextf; + PChart **charts = (PChart **)MEM_callocN(sizeof(*charts) * ncharts, "PCharts"); for (int i = 0; i < ncharts; i++) { charts[i] = (PChart *)MEM_callocN(sizeof(*chart), "PChart"); } - f = chart->faces; + PFace *f = chart->faces; while (f) { PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next; - nextf = f->nextlink; + PFace *nextf = f->nextlink; PChart *nchart = charts[f->u.chart]; @@ -2288,7 +2287,7 @@ static void p_abf_setup_system(PAbfSystem *sys) sys->lambdaPlanar = (float *)MEM_callocN(sizeof(float) * sys->ninterior, "ABFlamdaplane"); sys->lambdaLength = (float *)MEM_mallocN(sizeof(float) * sys->ninterior, "ABFlambdalen"); - sys->J2dt = MEM_mallocN(sizeof(float) * sys->nangles * 3, "ABFj2dt"); + sys->J2dt = static_cast<float(*)[3]>(MEM_mallocN(sizeof(float) * sys->nangles * 3, "ABFj2dt")); sys->bstar = (float *)MEM_mallocN(sizeof(float) * sys->nfaces, "ABFbstar"); sys->dstar = (float *)MEM_mallocN(sizeof(float) * sys->nfaces, "ABFdstar"); @@ -3667,7 +3666,8 @@ static void p_chart_rotate_minimum_area(PChart *chart) static void p_chart_rotate_fit_aabb(PChart *chart) { - float(*points)[2] = MEM_mallocN(sizeof(*points) * chart->nverts, __func__); + float(*points)[2] = static_cast<float(*)[2]>( + MEM_mallocN(sizeof(*points) * chart->nverts, __func__)); p_chart_uv_to_array(chart, points); @@ -3759,7 +3759,8 @@ ParamKey GEO_uv_find_pin_index(ParamHandle *handle, const int bmvertindex, const return bmvertindex; /* No verts pinned. */ } - GeoUVPinIndex *pinuvlist = BLI_ghash_lookup(handle->pin_hash, POINTER_FROM_INT(bmvertindex)); + const GeoUVPinIndex *pinuvlist = (const GeoUVPinIndex *)BLI_ghash_lookup( + handle->pin_hash, POINTER_FROM_INT(bmvertindex)); if (!pinuvlist) { return bmvertindex; /* Vert not pinned. */ } @@ -3781,7 +3782,7 @@ ParamKey GEO_uv_find_pin_index(ParamHandle *handle, const int bmvertindex, const static GeoUVPinIndex *new_geo_uv_pinindex(ParamHandle *handle, const float uv[2]) { - GeoUVPinIndex *pinuv = BLI_memarena_alloc(handle->arena, sizeof(*pinuv)); + GeoUVPinIndex *pinuv = (GeoUVPinIndex *)BLI_memarena_alloc(handle->arena, sizeof(*pinuv)); pinuv->next = NULL; copy_v2_v2(pinuv->uv, uv); pinuv->reindex = PARAM_KEY_MAX - (handle->unique_pin_count++); @@ -3794,7 +3795,8 @@ void GEO_uv_prepare_pin_index(ParamHandle *handle, const int bmvertindex, const handle->pin_hash = BLI_ghash_int_new("uv pin reindex"); } - GeoUVPinIndex *pinuvlist = BLI_ghash_lookup(handle->pin_hash, POINTER_FROM_INT(bmvertindex)); + GeoUVPinIndex *pinuvlist = (GeoUVPinIndex *)BLI_ghash_lookup(handle->pin_hash, + POINTER_FROM_INT(bmvertindex)); if (!pinuvlist) { BLI_ghash_insert( handle->pin_hash, POINTER_FROM_INT(bmvertindex), new_geo_uv_pinindex(handle, uv)); @@ -3826,8 +3828,10 @@ static void p_add_ngon(ParamHandle *handle, MemArena *arena = handle->polyfill_arena; Heap *heap = handle->polyfill_heap; uint nfilltri = nverts - 2; - uint(*tris)[3] = BLI_memarena_alloc(arena, sizeof(*tris) * (size_t)nfilltri); - float(*projverts)[2] = BLI_memarena_alloc(arena, sizeof(*projverts) * (size_t)nverts); + uint(*tris)[3] = static_cast<uint(*)[3]>( + BLI_memarena_alloc(arena, sizeof(*tris) * (size_t)nfilltri)); + float(*projverts)[2] = static_cast<float(*)[2]>( + BLI_memarena_alloc(arena, sizeof(*projverts) * (size_t)nverts)); /* Calc normal, flipped: to get a positive 2d cross product. */ float normal[3]; diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c index 77616ae13b6..6bb59f29b98 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c @@ -373,19 +373,16 @@ static void edge_types_panel_draw(const bContext *UNUSED(C), Panel *panel) } sub = uiLayoutRow(col, false); - uiItemR(sub, ptr, "use_crease", 0, "", ICON_NONE); - entry = uiLayoutColumn(sub, false); - uiItemL(entry, IFACE_("Crease"), ICON_NONE); - uiLayoutSetActive(entry, RNA_boolean_get(ptr, "use_crease") || is_first); if (use_cache && !is_first) { - uiItemL(entry, IFACE_("Crease Angle Cached"), ICON_INFO); + uiItemR(sub, ptr, "use_crease", 0, IFACE_("Crease (Angle Cached)"), ICON_NONE); } else { - uiItemR(entry, + uiItemR(sub, ptr, "use_crease", 0, "", ICON_NONE); + uiItemR(sub, ptr, "crease_threshold", UI_ITEM_R_SLIDER | UI_ITEM_R_FORCE_BLANK_DECORATE, - IFACE_("Default Angle"), + NULL, ICON_NONE); } @@ -447,8 +444,6 @@ static void options_light_reference_draw(const bContext *UNUSED(C), Panel *panel uiLayout *col = uiLayoutColumn(remaining, true); uiItemR(col, ptr, "shadow_camera_near", 0, "Near", ICON_NONE); uiItemR(col, ptr, "shadow_camera_far", 0, "Far", ICON_NONE); - - uiItemR(layout, ptr, "use_shadow_enclosed_shapes", 0, IFACE_("Enclosed Shapes"), ICON_NONE); } static void options_panel_draw(const bContext *UNUSED(C), Panel *panel) diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilshrinkwrap.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilshrinkwrap.c index f492e9ee044..74b7efb1d04 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilshrinkwrap.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilshrinkwrap.c @@ -202,7 +202,6 @@ static void updateDepsgraph(GpencilModifierData *md, CustomData_MeshMasks mask = {0}; if (BKE_shrinkwrap_needs_normals(mmd->shrink_type, mmd->shrink_mode)) { - mask.vmask |= CD_MASK_NORMAL; mask.lmask |= CD_MASK_NORMAL | CD_MASK_CUSTOMLOOPNORMAL; } @@ -225,7 +224,7 @@ static void updateDepsgraph(GpencilModifierData *md, ctx->node, &mmd->aux_target->id, DAG_EVAL_NEED_SHRINKWRAP_BOUNDARY); } } - DEG_add_modifier_to_transform_relation(ctx->node, "Shrinkwrap Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "Shrinkwrap Modifier"); } static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk, void *userData) diff --git a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h index 5dd833fb12b..63433113822 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h +++ b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h @@ -366,8 +366,9 @@ typedef struct LineartData { /* Keep an copy of these data so when line art is running it's self-contained. */ bool cam_is_persp; - bool cam_is_persp_secondary; /* "Secondary" ones are from viewing camera (as opposed to shadow - camera), during shadow calculation. */ + /* "Secondary" ones are from viewing camera + * (as opposed to shadow camera), during shadow calculation. */ + bool cam_is_persp_secondary; float cam_obmat[4][4]; float cam_obmat_secondary[4][4]; double camera_pos[3]; @@ -439,12 +440,17 @@ typedef enum eLineartTriangleFlags { } eLineartTriangleFlags; #define LRT_SHADOW_MASK_UNDEFINED 0 -#define LRT_SHADOW_MASK_LIT (1 << 0) +#define LRT_SHADOW_MASK_ILLUMINATED (1 << 0) #define LRT_SHADOW_MASK_SHADED (1 << 1) #define LRT_SHADOW_MASK_ENCLOSED_SHAPE (1 << 2) #define LRT_SHADOW_MASK_INHIBITED (1 << 3) #define LRT_SHADOW_SILHOUETTE_ERASED_GROUP (1 << 4) #define LRT_SHADOW_SILHOUETTE_ERASED_OBJECT (1 << 5) +#define LRT_SHADOW_MASK_ILLUMINATED_SHAPE (1 << 6) + +#define LRT_SHADOW_TEST_SHAPE_BITS \ + (LRT_SHADOW_MASK_ILLUMINATED | LRT_SHADOW_MASK_SHADED | LRT_SHADOW_MASK_INHIBITED | \ + LRT_SHADOW_MASK_ILLUMINATED_SHAPE) /** * Controls how many edges a worker thread is processing at one request. diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c index 874da3b88c9..011c79025c4 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c @@ -304,13 +304,12 @@ void lineart_edge_cut(LineartData *ld, /* The enclosed shape flag will override regular lit/shaded * flags. See LineartEdgeSegment::shadow_mask_bits for details. */ if (shadow_bits == LRT_SHADOW_MASK_ENCLOSED_SHAPE) { - if (seg->shadow_mask_bits & LRT_SHADOW_MASK_LIT || e->flags & LRT_EDGE_FLAG_LIGHT_CONTOUR) { - seg->shadow_mask_bits &= ~LRT_SHADOW_MASK_LIT; + if (seg->shadow_mask_bits & LRT_SHADOW_MASK_ILLUMINATED || + e->flags & LRT_EDGE_FLAG_LIGHT_CONTOUR) { seg->shadow_mask_bits |= LRT_SHADOW_MASK_INHIBITED; } else if (seg->shadow_mask_bits & LRT_SHADOW_MASK_SHADED) { - seg->shadow_mask_bits &= ~LRT_SHADOW_MASK_SHADED; - seg->shadow_mask_bits |= LRT_SHADOW_MASK_LIT; + seg->shadow_mask_bits |= LRT_SHADOW_MASK_ILLUMINATED_SHAPE; } } else { @@ -3643,7 +3642,8 @@ static LineartData *lineart_create_render_buffer(Scene *scene, (lmd->light_contour_object != NULL)); ld->conf.shadow_selection = lmd->shadow_selection_override; - ld->conf.shadow_enclose_shapes = (lmd->calculation_flags & LRT_SHADOW_ENCLOSED_SHAPES) != 0; + ld->conf.shadow_enclose_shapes = lmd->shadow_selection_override == + LRT_SHADOW_FILTER_ILLUMINATED_ENCLOSED_SHAPES; ld->conf.shadow_use_silhouette = lmd->shadow_use_silhouette_override != 0; ld->conf.use_back_face_culling = (lmd->calculation_flags & LRT_USE_BACK_FACE_CULLING) != 0; @@ -5227,13 +5227,22 @@ static void lineart_gpencil_generate(LineartCache *cache, } if (shaodow_selection) { if (ec->shadow_mask_bits != LRT_SHADOW_MASK_UNDEFINED) { - /* TODO(@Yiming): Give a behavior option for how to display undefined shadow info. */ - if ((shaodow_selection == LRT_SHADOW_FILTER_LIT && - (!(ec->shadow_mask_bits & LRT_SHADOW_MASK_LIT))) || - (shaodow_selection == LRT_SHADOW_FILTER_SHADED && - (!(ec->shadow_mask_bits & LRT_SHADOW_MASK_SHADED)))) { + /* TODO(Yiming): Give a behaviour option for how to display undefined shadow info. */ + if ((shaodow_selection == LRT_SHADOW_FILTER_ILLUMINATED && + (!(ec->shadow_mask_bits & LRT_SHADOW_MASK_ILLUMINATED)))) { continue; } + else if ((shaodow_selection == LRT_SHADOW_FILTER_SHADED && + (!(ec->shadow_mask_bits & LRT_SHADOW_MASK_SHADED)))) { + continue; + } + else if (shaodow_selection == LRT_SHADOW_FILTER_ILLUMINATED_ENCLOSED_SHAPES) { + uint32_t test_bits = ec->shadow_mask_bits & LRT_SHADOW_TEST_SHAPE_BITS; + if ((test_bits != LRT_SHADOW_MASK_ILLUMINATED) && + (test_bits != (LRT_SHADOW_MASK_SHADED | LRT_SHADOW_MASK_ILLUMINATED_SHAPE))) { + continue; + } + } } } if (silhouette_mode && (ec->type & (LRT_EDGE_FLAG_CONTOUR))) { diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_shadow.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_shadow.c index ad0137fe0f0..bf42677d79c 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_shadow.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_shadow.c @@ -109,9 +109,10 @@ void lineart_register_shadow_cuts(LineartData *ld, LineartEdge *e, LineartEdge * la2 = la2 * e->v2->fbcoord[3] / (e->v1->fbcoord[3] - la2 * (e->v1->fbcoord[3] - e->v2->fbcoord[3])); unsigned char shadow_bits = (es->occlusion != 0) ? LRT_SHADOW_MASK_SHADED : - LRT_SHADOW_MASK_LIT; + LRT_SHADOW_MASK_ILLUMINATED; - if (lineart_contour_viewed_from_dark_side(ld, e) && shadow_bits == LRT_SHADOW_MASK_LIT) { + if (lineart_contour_viewed_from_dark_side(ld, e) && + shadow_bits == LRT_SHADOW_MASK_ILLUMINATED) { shadow_bits = LRT_SHADOW_MASK_SHADED; } @@ -481,7 +482,7 @@ static void lineart_shadow_create_shadow_edge_array(LineartData *ld, * This process is repeated on each existing segments of the shadow edge (#e), which ensures they * all have been tested for closest segments after cutting. And in the diagram it's clear that the * left/right side of cuts are likely to be discontinuous, each cut's left side designates the - * right side of the last segment, and vise versa. */ + * right side of the last segment, and vice-versa. */ static void lineart_shadow_edge_cut(LineartData *ld, LineartShadowEdge *e, double start, diff --git a/source/blender/gpu/GPU_material.h b/source/blender/gpu/GPU_material.h index 12a7cf49f9d..1ab06f3369d 100644 --- a/source/blender/gpu/GPU_material.h +++ b/source/blender/gpu/GPU_material.h @@ -167,10 +167,6 @@ bool GPU_stack_link(GPUMaterial *mat, GPUNodeStack *in, GPUNodeStack *out, ...); -GPUNodeLink *GPU_uniformbuf_link_out(struct GPUMaterial *mat, - struct bNode *node, - struct GPUNodeStack *stack, - int index); void GPU_material_output_surface(GPUMaterial *material, GPUNodeLink *link); void GPU_material_output_volume(GPUMaterial *material, GPUNodeLink *link); diff --git a/source/blender/gpu/GPU_texture.h b/source/blender/gpu/GPU_texture.h index 5bd20b7be98..6e31b1b69f6 100644 --- a/source/blender/gpu/GPU_texture.h +++ b/source/blender/gpu/GPU_texture.h @@ -331,6 +331,7 @@ int GPU_texture_orig_width(const GPUTexture *tex); int GPU_texture_orig_height(const GPUTexture *tex); void GPU_texture_orig_size_set(GPUTexture *tex, int w, int h); eGPUTextureFormat GPU_texture_format(const GPUTexture *tex); +const char *GPU_texture_format_description(eGPUTextureFormat texture_format); bool GPU_texture_array(const GPUTexture *tex); bool GPU_texture_cube(const GPUTexture *tex); bool GPU_texture_depth(const GPUTexture *tex); diff --git a/source/blender/gpu/intern/gpu_buffers.c b/source/blender/gpu/intern/gpu_buffers.c index bfa30f74f11..d4823abab22 100644 --- a/source/blender/gpu/intern/gpu_buffers.c +++ b/source/blender/gpu/intern/gpu_buffers.c @@ -1249,9 +1249,7 @@ static int gpu_pbvh_make_attr_offs(eAttrDomainMask domain_mask, } } - /* ensure render layer is last - draw cache code seems to need this - */ + /* Ensure render layer is last, draw cache code seems to need this. */ for (int i = 0; i < count; i++) { GPUAttrRef *ref = r_cd_attrs + i; diff --git a/source/blender/gpu/intern/gpu_node_graph.c b/source/blender/gpu/intern/gpu_node_graph.c index c7b2fde492f..377cbc53893 100644 --- a/source/blender/gpu/intern/gpu_node_graph.c +++ b/source/blender/gpu/intern/gpu_node_graph.c @@ -738,14 +738,6 @@ bool GPU_stack_link(GPUMaterial *material, return valid; } -GPUNodeLink *GPU_uniformbuf_link_out(GPUMaterial *mat, - bNode *node, - GPUNodeStack *stack, - const int index) -{ - return gpu_uniformbuffer_link(mat, node, stack, index, SOCK_OUT); -} - /* Node Graph */ static void gpu_inputs_free(ListBase *inputs) diff --git a/source/blender/gpu/intern/gpu_texture.cc b/source/blender/gpu/intern/gpu_texture.cc index 218d22ddf53..e52311b3bf0 100644 --- a/source/blender/gpu/intern/gpu_texture.cc +++ b/source/blender/gpu/intern/gpu_texture.cc @@ -641,6 +641,112 @@ eGPUTextureFormat GPU_texture_format(const GPUTexture *tex) return reinterpret_cast<const Texture *>(tex)->format_get(); } +const char *GPU_texture_format_description(eGPUTextureFormat texture_format) +{ + switch (texture_format) { + case GPU_RGBA8UI: + return "RGBA8UI"; + case GPU_RGBA8I: + return "RGBA8I"; + case GPU_RGBA8: + return "RGBA8"; + case GPU_RGBA32UI: + return "RGBA32UI"; + case GPU_RGBA32I: + return "RGBA32I"; + case GPU_RGBA32F: + return "RGBA32F"; + case GPU_RGBA16UI: + return "RGBA16UI"; + case GPU_RGBA16I: + return "RGBA16I"; + case GPU_RGBA16F: + return "RGBA16F"; + case GPU_RGBA16: + return "RGBA16"; + case GPU_RG8UI: + return "RG8UI"; + case GPU_RG8I: + return "RG8I"; + case GPU_RG8: + return "RG8"; + case GPU_RG32UI: + return "RG32UI"; + case GPU_RG32I: + return "RG32I"; + case GPU_RG32F: + return "RG32F"; + case GPU_RG16UI: + return "RG16UI"; + case GPU_RG16I: + return "RG16I"; + case GPU_RG16F: + return "RG16F"; + case GPU_RG16: + return "RG16"; + case GPU_R8UI: + return "R8UI"; + case GPU_R8I: + return "R8I"; + case GPU_R8: + return "R8"; + case GPU_R32UI: + return "R32UI"; + case GPU_R32I: + return "R32I"; + case GPU_R32F: + return "R32F"; + case GPU_R16UI: + return "R16UI"; + case GPU_R16I: + return "R16I"; + case GPU_R16F: + return "R16F"; + case GPU_R16: + return "R16"; + + /* Special formats texture & render-buffer. */ + case GPU_RGB10_A2: + return "RGB10A2"; + case GPU_R11F_G11F_B10F: + return "R11FG11FB10F"; + case GPU_DEPTH32F_STENCIL8: + return "DEPTH32FSTENCIL8"; + case GPU_DEPTH24_STENCIL8: + return "DEPTH24STENCIL8"; + case GPU_SRGB8_A8: + return "SRGB8A8"; + + /* Texture only format */ + case (GPU_RGB16F): + return "RGB16F"; + + /* Special formats texture only */ + case GPU_SRGB8_A8_DXT1: + return "SRGB8_A8_DXT1"; + case GPU_SRGB8_A8_DXT3: + return "SRGB8_A8_DXT3"; + case GPU_SRGB8_A8_DXT5: + return "SRGB8_A8_DXT5"; + case GPU_RGBA8_DXT1: + return "RGBA8_DXT1"; + case GPU_RGBA8_DXT3: + return "RGBA8_DXT3"; + case GPU_RGBA8_DXT5: + return "RGBA8_DXT5"; + + /* Depth Formats */ + case GPU_DEPTH_COMPONENT32F: + return "DEPTH32F"; + case GPU_DEPTH_COMPONENT24: + return "DEPTH24"; + case GPU_DEPTH_COMPONENT16: + return "DEPTH16"; + } + BLI_assert_unreachable(); + return ""; +} + bool GPU_texture_depth(const GPUTexture *tex) { return (reinterpret_cast<const Texture *>(tex)->format_flag_get() & GPU_FORMAT_DEPTH) != 0; diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h index 20c414bb1ad..28125c006eb 100644 --- a/source/blender/imbuf/IMB_imbuf.h +++ b/source/blender/imbuf/IMB_imbuf.h @@ -41,6 +41,7 @@ /* for bool */ #include "../blenlib/BLI_sys_types.h" +#include "../gpu/GPU_texture.h" #ifdef __cplusplus extern "C" { @@ -74,12 +75,6 @@ struct Stereo3dFormat; /** * - * \attention defined in GPU_texture.h - */ -struct GPUTexture; - -/** - * * \attention Defined in allocimbuf.c */ void IMB_init(void); @@ -933,22 +928,25 @@ const char *IMB_ffmpeg_last_error(void); * * \attention defined in util_gpu.c */ -struct GPUTexture *IMB_create_gpu_texture(const char *name, - struct ImBuf *ibuf, - bool use_high_bitdepth, - bool use_premult); +GPUTexture *IMB_create_gpu_texture(const char *name, + struct ImBuf *ibuf, + bool use_high_bitdepth, + bool use_premult); + +eGPUTextureFormat IMB_gpu_get_texture_format(const struct ImBuf *ibuf, bool high_bitdepth); + /** * The `ibuf` is only here to detect the storage type. The produced texture will have undefined * content. It will need to be populated by using #IMB_update_gpu_texture_sub(). */ -struct GPUTexture *IMB_touch_gpu_texture( +GPUTexture *IMB_touch_gpu_texture( const char *name, struct ImBuf *ibuf, int w, int h, int layers, bool use_high_bitdepth); /** * Will update a #GPUTexture using the content of the #ImBuf. Only one layer will be updated. * Will resize the ibuf if needed. * Z is the layer to update. Unused if the texture is 2D. */ -void IMB_update_gpu_texture_sub(struct GPUTexture *tex, +void IMB_update_gpu_texture_sub(GPUTexture *tex, struct ImBuf *ibuf, int x, int y, diff --git a/source/blender/imbuf/intern/util_gpu.c b/source/blender/imbuf/intern/util_gpu.c index 5feb0ceb515..727704e27e8 100644 --- a/source/blender/imbuf/intern/util_gpu.c +++ b/source/blender/imbuf/intern/util_gpu.c @@ -290,3 +290,13 @@ GPUTexture *IMB_create_gpu_texture(const char *name, return tex; } + +eGPUTextureFormat IMB_gpu_get_texture_format(const ImBuf *ibuf, bool high_bitdepth) +{ + eGPUTextureFormat gpu_texture_format; + eGPUDataFormat gpu_data_format; + + imb_gpu_get_format(ibuf, high_bitdepth, &gpu_data_format, &gpu_texture_format); + + return gpu_texture_format; +} diff --git a/source/blender/io/usd/intern/usd_reader_material.cc b/source/blender/io/usd/intern/usd_reader_material.cc index 8feceee55ed..f59b8be147e 100644 --- a/source/blender/io/usd/intern/usd_reader_material.cc +++ b/source/blender/io/usd/intern/usd_reader_material.cc @@ -9,8 +9,11 @@ #include "BKE_node.h" #include "BKE_node_tree_update.h" +#include "BLI_fileops.h" #include "BLI_math_vector.h" +#include "BLI_path_util.h" #include "BLI_string.h" +#include "BLI_vector.hh" #include "DNA_material_types.h" @@ -94,6 +97,60 @@ static void link_nodes( nodeAddLink(ntree, source, source_socket, dest, dest_socket); } +/* Returns a layer handle retrieved from the given attribute's property specs. + * Note that the returned handle may be invalid if no layer could be found. */ +static pxr::SdfLayerHandle get_layer_handle(const pxr::UsdAttribute &attribute) +{ + for (auto PropertySpec : attribute.GetPropertyStack(pxr::UsdTimeCode::EarliestTime())) { + if (PropertySpec->HasDefaultValue() || + PropertySpec->GetLayer()->GetNumTimeSamplesForPath(PropertySpec->GetPath()) > 0) { + return PropertySpec->GetLayer(); + } + } + + return pxr::SdfLayerHandle(); +} + +static bool is_udim_path(const std::string &path) +{ + return path.find("<UDIM>") != std::string::npos; +} + +/* For the given UDIM path (assumed to contain the UDIM token), returns an array + * containing valid tile indices. */ +static blender::Vector<int> get_udim_tiles(const std::string &file_path) +{ + char base_udim_path[FILE_MAX]; + BLI_strncpy(base_udim_path, file_path.c_str(), sizeof(base_udim_path)); + + blender::Vector<int> udim_tiles; + + /* Extract the tile numbers from all files on disk. */ + ListBase tiles = {nullptr, nullptr}; + int tile_start, tile_range; + bool result = BKE_image_get_tile_info(base_udim_path, &tiles, &tile_start, &tile_range); + if (result) { + LISTBASE_FOREACH (LinkData *, tile, &tiles) { + int tile_number = POINTER_AS_INT(tile->data); + udim_tiles.append(tile_number); + } + } + + BLI_freelistN(&tiles); + + return udim_tiles; +} + +/* Add tiles with the given indices to the given image. */ +static void add_udim_tiles(Image *image, const blender::Vector<int> &indices) +{ + image->source = IMA_SRC_TILED; + + for (int tile_number : indices) { + BKE_image_add_tile(image, tile_number, nullptr); + } +} + /* Returns true if the given shader may have opacity < 1.0, based * on heuristics. */ static bool needs_blend(const pxr::UsdShadeShader &usd_shader) @@ -601,11 +658,31 @@ void USDMaterialReader::load_tex_image(const pxr::UsdShadeShader &usd_shader, const pxr::SdfAssetPath &asset_path = file_val.Get<pxr::SdfAssetPath>(); std::string file_path = asset_path.GetResolvedPath(); if (file_path.empty()) { + /* No resolved path, so use the asset path (usually + * necessary for UDIM paths). */ + file_path = asset_path.GetAssetPath(); + + /* Texture paths are frequently relative to the USD, so get + * the absolute path. */ + if (pxr::SdfLayerHandle layer_handle = get_layer_handle(file_input.GetAttr())) { + file_path = layer_handle->ComputeAbsolutePath(file_path); + } + } + + if (file_path.empty()) { std::cerr << "WARNING: Couldn't resolve image asset '" << asset_path << "' for Texture Image node." << std::endl; return; } + /* If this is a UDIM texture, this will store the + * UDIM tile indices. */ + blender::Vector<int> udim_tiles; + + if (is_udim_path(file_path)) { + udim_tiles = get_udim_tiles(file_path); + } + const char *im_file = file_path.c_str(); Image *image = BKE_image_load_exists(bmain_, im_file); if (!image) { @@ -614,6 +691,10 @@ void USDMaterialReader::load_tex_image(const pxr::UsdShadeShader &usd_shader, return; } + if (udim_tiles.size() > 0) { + add_udim_tiles(image, udim_tiles); + } + tex_image->id = &image->id; /* Set texture color space. 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 731587bfcea..36a9cf1b9ae 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 @@ -179,13 +179,19 @@ void OBJWriter::write_mtllib_name(const StringRefNull mtl_filepath) const fh.write_to_file(outfile_); } +static void spaces_to_underscores(std::string &r_name) +{ + std::replace(r_name.begin(), r_name.end(), ' ', '_'); +} + void OBJWriter::write_object_name(FormatHandler<eFileType::OBJ> &fh, const OBJMesh &obj_mesh_data) const { - const char *object_name = obj_mesh_data.get_object_name(); + std::string object_name = obj_mesh_data.get_object_name(); + spaces_to_underscores(object_name); if (export_params_.export_object_groups) { - const std::string object_name = obj_mesh_data.get_object_name(); - const char *mesh_name = obj_mesh_data.get_object_mesh_name(); + std::string mesh_name = obj_mesh_data.get_object_mesh_name(); + spaces_to_underscores(mesh_name); fh.write<eOBJSyntaxElement::object_group>(object_name + "_" + mesh_name); return; } @@ -389,7 +395,8 @@ void OBJWriter::write_poly_elements(FormatHandler<eFileType::OBJ> &fh, mat_name = MATERIAL_GROUP_DISABLED; } if (export_params_.export_material_groups) { - const std::string object_name = obj_mesh_data.get_object_name(); + std::string object_name = obj_mesh_data.get_object_name(); + spaces_to_underscores(object_name); fh.write<eOBJSyntaxElement::object_group>(object_name + "_" + mat_name); } buf.write<eOBJSyntaxElement::poly_usemtl>(mat_name); 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 6f66ce5a6bd..d7b2bc2e67c 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc @@ -323,6 +323,10 @@ void MeshFromGeometry::create_normals(Mesh *mesh) if (global_vertices_.vertex_normals.is_empty()) { return; } + /* Custom normals can only be stored on face corners. */ + if (mesh_geometry_.total_loops_ == 0) { + return; + } float(*loop_normals)[3] = static_cast<float(*)[3]>( MEM_malloc_arrayN(mesh_geometry_.total_loops_, sizeof(float[3]), __func__)); diff --git a/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc b/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc index 4055d892332..339f672c3e0 100644 --- a/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc +++ b/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc @@ -290,7 +290,7 @@ TEST_F(obj_importer_test, import_nurbs_mesh) { Expectation expect[] = { {"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)}, - {"OBTorus Knot", + {"OBTorus_Knot", OB_MESH, 108, 108, diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index 0dc7d75e744..b3a07f7ff37 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -256,6 +256,7 @@ typedef struct IDOverrideLibraryProperty { /** * List of overriding operations (IDOverrideLibraryPropertyOperation) applied to this property. + * Recreated as part of the diffing, so do not store any of these elsewhere. */ ListBase operations; @@ -873,7 +874,7 @@ typedef enum IDRecalcFlag { /* Provisioned flags. * * Not for actual use. The idea of them is to have all bits of the `IDRecalcFlag` defined to a - * known value, silencing sanitizer warnings when checkign bits of the ID_RECALC_ALL. */ + * known value, silencing sanitizer warnings when checking bits of the ID_RECALC_ALL. */ ID_RECALC_PROVISION_26 = (1 << 26), ID_RECALC_PROVISION_27 = (1 << 27), ID_RECALC_PROVISION_28 = (1 << 28), diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h index 2bb95caddfb..7f8e436f007 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_types.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h @@ -999,9 +999,14 @@ typedef enum eLineartGpencilModifierSource { } eLineartGpencilModifierSource; typedef enum eLineartGpencilModifierShadowFilter { + /* These options need to be ordered in this way because those latter options requires line art to + run a few extra stages. Having those values set up this way will allow + #BKE_gpencil_get_lineart_modifier_limits() to find out maximum stages needed in multiple + cached line art modifiers. */ LRT_SHADOW_FILTER_NONE = 0, - LRT_SHADOW_FILTER_LIT = 1, + LRT_SHADOW_FILTER_ILLUMINATED = 1, LRT_SHADOW_FILTER_SHADED = 2, + LRT_SHADOW_FILTER_ILLUMINATED_ENCLOSED_SHAPES = 3, } eLineartGpencilModifierShadowFilter; typedef enum eLineartGpencilModifierSilhouetteFilter { diff --git a/source/blender/makesdna/DNA_image_types.h b/source/blender/makesdna/DNA_image_types.h index 6e4e515a0fe..f35c77f663b 100644 --- a/source/blender/makesdna/DNA_image_types.h +++ b/source/blender/makesdna/DNA_image_types.h @@ -92,8 +92,14 @@ typedef struct ImageTile { struct ImageTile_Runtime runtime; - char _pad[4]; int tile_number; + + /* for generated images */ + int gen_x, gen_y; + char gen_type, gen_flag; + short gen_depth; + float gen_color[4]; + char label[64]; } ImageTile; @@ -167,10 +173,10 @@ typedef struct Image { int lastused; /* for generated images */ - int gen_x, gen_y; - char gen_type, gen_flag; - short gen_depth; - float gen_color[4]; + int gen_x DNA_DEPRECATED, gen_y DNA_DEPRECATED; + char gen_type DNA_DEPRECATED, gen_flag DNA_DEPRECATED; + short gen_depth DNA_DEPRECATED; + float gen_color[4] DNA_DEPRECATED; /* display aspect - for UV editing images resized for faster openGL display */ float aspx, aspy; @@ -262,7 +268,8 @@ enum { /** #Image.gen_flag */ enum { - IMA_GEN_FLOAT = 1, + IMA_GEN_FLOAT = (1 << 0), + IMA_GEN_TILE = (1 << 1), }; /** #Image.alpha_mode */ diff --git a/source/blender/makesdna/DNA_lineart_types.h b/source/blender/makesdna/DNA_lineart_types.h index 1ff656f85ed..05380325852 100644 --- a/source/blender/makesdna/DNA_lineart_types.h +++ b/source/blender/makesdna/DNA_lineart_types.h @@ -38,7 +38,6 @@ typedef enum eLineartMainFlags { LRT_USE_BACK_FACE_CULLING = (1 << 19), LRT_USE_IMAGE_BOUNDARY_TRIMMING = (1 << 20), LRT_CHAIN_PRESERVE_DETAILS = (1 << 22), - LRT_SHADOW_ENCLOSED_SHAPES = (1 << 23), LRT_SHADOW_USE_SILHOUETTE = (1 << 24), } eLineartMainFlags; diff --git a/source/blender/makesdna/DNA_outliner_types.h b/source/blender/makesdna/DNA_outliner_types.h index 4a2e3e1ab72..489fb6917e8 100644 --- a/source/blender/makesdna/DNA_outliner_types.h +++ b/source/blender/makesdna/DNA_outliner_types.h @@ -113,7 +113,9 @@ typedef enum eTreeStoreElemType { TSE_GPENCIL_EFFECT_BASE = 42, TSE_GPENCIL_EFFECT = 43, TSE_LIBRARY_OVERRIDE_BASE = 44, - TSE_LIBRARY_OVERRIDE = 45, + TSE_LIBRARY_OVERRIDE = 45, /* No ID */ + TSE_LIBRARY_OVERRIDE_OPERATION = 46, /* No ID */ + TSE_GENERIC_LABEL = 47, /* No ID */ } eTreeStoreElemType; /** Check whether given #TreeStoreElem should have a real ID in #TreeStoreElem.id member. */ @@ -129,7 +131,8 @@ typedef enum eTreeStoreElemType { TSE_RNA_PROPERTY, \ TSE_RNA_ARRAY_ELEM, \ TSE_ID_BASE, \ - TSE_GP_LAYER)) + TSE_GP_LAYER, \ + TSE_GENERIC_LABEL)) #ifdef __cplusplus } diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index b4fa7088d38..4b8f8ca7b4b 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -650,7 +650,7 @@ static ID *rna_ID_evaluated_get(ID *id, struct Depsgraph *depsgraph) static ID *rna_ID_copy(ID *id, Main *bmain) { - ID *newid = BKE_id_copy(bmain, id); + ID *newid = BKE_id_copy_for_use_in_bmain(bmain, id); if (newid != NULL) { id_us_min(newid); @@ -2045,7 +2045,10 @@ static void rna_def_ID(BlenderRNA *brna) func = RNA_def_function(srna, "copy", "rna_ID_copy"); RNA_def_function_ui_description( - func, "Create a copy of this data-block (not supported for all data-blocks)"); + func, + "Create a copy of this data-block (not supported for all data-blocks). " + "The result is added to the Blend-File Data (Main database), with all references to other " + "data-blocks ensured to be from within the same Blend-File Data"); RNA_def_function_flag(func, FUNC_USE_MAIN); parm = RNA_def_pointer(func, "id", "ID", "", "New copy of the ID"); RNA_def_function_return(func, parm); diff --git a/source/blender/makesrna/intern/rna_constraint.c b/source/blender/makesrna/intern/rna_constraint.c index 986de0930ed..719c7441174 100644 --- a/source/blender/makesrna/intern/rna_constraint.c +++ b/source/blender/makesrna/intern/rna_constraint.c @@ -3450,6 +3450,7 @@ void RNA_def_constraint(BlenderRNA *brna) RNA_def_struct_refine_func(srna, "rna_ConstraintType_refine"); RNA_def_struct_path_func(srna, "rna_Constraint_path"); RNA_def_struct_sdna(srna, "bConstraint"); + RNA_def_struct_ui_icon(srna, ICON_CONSTRAINT); /* strings */ prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index 0647bc62081..2dfd9d46665 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -3197,9 +3197,27 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna) }; static const EnumPropertyItem modifier_lineart_shadow_region_filtering[] = { - {LRT_SHADOW_FILTER_NONE, "NONE", 0, "None", ""}, - {LRT_SHADOW_FILTER_LIT, "LIT", 0, "Lit", ""}, - {LRT_SHADOW_FILTER_SHADED, "SHADED", 0, "Shaded", ""}, + {LRT_SHADOW_FILTER_NONE, + "NONE", + 0, + "None", + "Not filtering any lines based on illumination region"}, + {LRT_SHADOW_FILTER_ILLUMINATED, + "ILLUMINATED", + 0, + "Illuminated", + "Only selecting lines from illuminated regions"}, + {LRT_SHADOW_FILTER_SHADED, + "SHADED", + 0, + "Shaded", + "Only selecting lines from shaded regions"}, + {LRT_SHADOW_FILTER_ILLUMINATED_ENCLOSED_SHAPES, + "ILLUMINATED_ENCLOSED", + 0, + "Illuminated (Enclosed Shapes)", + "Selecting lines from lit regions, and make the combination of contour, light contour and " + "shadow lines into enclosed shapes"}, {0, NULL, 0, NULL, NULL}, }; @@ -3464,13 +3482,6 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna) "affect cast shadow and light contour since they are at the border"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update"); - prop = RNA_def_property(srna, "use_shadow_enclosed_shapes", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "calculation_flags", LRT_SHADOW_ENCLOSED_SHAPES); - RNA_def_property_ui_text(prop, - "Shadow Enclosed Shapes", - "Reproject visible lines again to get enclosed shadow shapes"); - RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - prop = RNA_def_property(srna, "silhouette_filtering", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "silhouette_selection"); RNA_def_property_enum_items(prop, modifier_lineart_silhouette_filtering); diff --git a/source/blender/makesrna/intern/rna_image.c b/source/blender/makesrna/intern/rna_image.c index 7f134c5055f..b7ab7689dd7 100644 --- a/source/blender/makesrna/intern/rna_image.c +++ b/source/blender/makesrna/intern/rna_image.c @@ -52,6 +52,7 @@ static const EnumPropertyItem image_source_items[] = { #ifdef RNA_RUNTIME # include "BLI_math_base.h" +# include "BLI_math_vector.h" # include "BKE_global.h" @@ -85,6 +86,10 @@ static void rna_Image_source_set(PointerRNA *ptr, int value) ima->source = value; BLI_assert(BKE_id_is_in_global_main(&ima->id)); BKE_image_signal(G_MAIN, ima, NULL, IMA_SIGNAL_SRC_CHANGE); + if (ima->source == IMA_SRC_TILED) { + BKE_image_signal(G_MAIN, ima, NULL, IMA_SIGNAL_RELOAD); + } + DEG_id_tag_update(&ima->id, 0); DEG_id_tag_update(&ima->id, ID_RECALC_EDITORS); DEG_relations_tag_update(G_MAIN); @@ -100,6 +105,83 @@ static void rna_Image_reload_update(Main *bmain, Scene *UNUSED(scene), PointerRN DEG_id_tag_update(&ima->id, ID_RECALC_EDITORS); } +static int rna_Image_generated_type_get(PointerRNA *ptr) +{ + Image *ima = (Image *)ptr->data; + ImageTile *base_tile = BKE_image_get_tile(ima, 0); + return base_tile->gen_type; +} + +static void rna_Image_generated_type_set(PointerRNA *ptr, int value) +{ + Image *ima = (Image *)ptr->data; + ImageTile *base_tile = BKE_image_get_tile(ima, 0); + base_tile->gen_type = value; +} + +static int rna_Image_generated_width_get(PointerRNA *ptr) +{ + Image *ima = (Image *)ptr->data; + ImageTile *base_tile = BKE_image_get_tile(ima, 0); + return base_tile->gen_x; +} + +static void rna_Image_generated_width_set(PointerRNA *ptr, int value) +{ + Image *ima = (Image *)ptr->data; + ImageTile *base_tile = BKE_image_get_tile(ima, 0); + base_tile->gen_x = CLAMPIS(value, 1, 65536); +} + +static int rna_Image_generated_height_get(PointerRNA *ptr) +{ + Image *ima = (Image *)ptr->data; + ImageTile *base_tile = BKE_image_get_tile(ima, 0); + return base_tile->gen_y; +} + +static void rna_Image_generated_height_set(PointerRNA *ptr, int value) +{ + Image *ima = (Image *)ptr->data; + ImageTile *base_tile = BKE_image_get_tile(ima, 0); + base_tile->gen_y = CLAMPIS(value, 1, 65536); +} + +static bool rna_Image_generated_float_get(PointerRNA *ptr) +{ + Image *ima = (Image *)ptr->data; + ImageTile *base_tile = BKE_image_get_tile(ima, 0); + return (base_tile->gen_flag & IMA_GEN_FLOAT) != 0; +} + +static void rna_Image_generated_float_set(PointerRNA *ptr, bool value) +{ + Image *ima = (Image *)ptr->data; + ImageTile *base_tile = BKE_image_get_tile(ima, 0); + if (value) { + base_tile->gen_flag |= IMA_GEN_FLOAT; + } + else { + base_tile->gen_flag &= ~IMA_GEN_FLOAT; + } +} + +void rna_Image_generated_color_get(PointerRNA *ptr, float values[4]) +{ + Image *ima = (Image *)(ptr->data); + ImageTile *base_tile = BKE_image_get_tile(ima, 0); + copy_v4_v4(values, base_tile->gen_color); +} + +void rna_Image_generated_color_set(PointerRNA *ptr, const float values[4]) +{ + Image *ima = (Image *)(ptr->data); + ImageTile *base_tile = BKE_image_get_tile(ima, 0); + for (unsigned int i = 0; i < 4; i++) { + base_tile->gen_color[i] = CLAMPIS(values[i], 0.0f, FLT_MAX); + } +} + static void rna_Image_generated_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr) { Image *ima = (Image *)ptr->owner_id; @@ -335,6 +417,20 @@ static void rna_UDIMTile_tile_number_set(PointerRNA *ptr, int value) } } +static void rna_UDIMTile_generated_update(Main *UNUSED(bmain), + Scene *UNUSED(scene), + PointerRNA *ptr) +{ + Image *ima = (Image *)ptr->owner_id; + ImageTile *tile = (ImageTile *)ptr->data; + + /* If the tile is still marked as generated, then update the tile as requested. */ + if ((tile->gen_flag & IMA_GEN_TILE) != 0) { + BKE_image_fill_tile(ima, tile); + BKE_image_partial_update_mark_full_update(ima); + } +} + static int rna_Image_active_tile_index_get(PointerRNA *ptr) { Image *image = (Image *)ptr->data; @@ -896,6 +992,43 @@ static void rna_def_udim_tile(BlenderRNA *brna) RNA_def_property_int_funcs(prop, "rna_UDIMTile_channels_get", NULL, NULL); RNA_def_property_ui_text(prop, "Channels", "Number of channels in the tile pixels buffer"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); + + /* Generated tile information. */ + prop = RNA_def_property(srna, "generated_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "gen_type"); + RNA_def_property_enum_items(prop, rna_enum_image_generated_type_items); + RNA_def_property_ui_text(prop, "Generated Type", "Generated image type"); + RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_UDIMTile_generated_update"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + + prop = RNA_def_property(srna, "generated_width", PROP_INT, PROP_PIXEL); + RNA_def_property_int_sdna(prop, NULL, "gen_x"); + RNA_def_property_flag(prop, PROP_PROPORTIONAL); + RNA_def_property_range(prop, 1, 65536); + RNA_def_property_ui_text(prop, "Generated Width", "Generated image width"); + RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_UDIMTile_generated_update"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + + prop = RNA_def_property(srna, "generated_height", PROP_INT, PROP_PIXEL); + RNA_def_property_int_sdna(prop, NULL, "gen_y"); + RNA_def_property_flag(prop, PROP_PROPORTIONAL); + RNA_def_property_range(prop, 1, 65536); + RNA_def_property_ui_text(prop, "Generated Height", "Generated image height"); + RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_UDIMTile_generated_update"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + + prop = RNA_def_property(srna, "use_generated_float", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "gen_flag", IMA_GEN_FLOAT); + RNA_def_property_ui_text(prop, "Float Buffer", "Generate floating-point buffer"); + RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_UDIMTile_generated_update"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + + prop = RNA_def_property(srna, "generated_color", PROP_FLOAT, PROP_COLOR_GAMMA); + RNA_def_property_float_sdna(prop, NULL, "gen_color"); + RNA_def_property_array(prop, 4); + RNA_def_property_ui_text(prop, "Color", "Fill color for the generated image"); + RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_UDIMTile_generated_update"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); } static void rna_def_udim_tiles(BlenderRNA *brna, PropertyRNA *cprop) @@ -1079,6 +1212,8 @@ static void rna_def_image(BlenderRNA *brna) RNA_def_property_enum_sdna(prop, NULL, "gen_type"); RNA_def_property_enum_items(prop, rna_enum_image_generated_type_items); RNA_def_property_ui_text(prop, "Generated Type", "Generated image type"); + RNA_def_property_enum_funcs( + prop, "rna_Image_generated_type_get", "rna_Image_generated_type_set", NULL); RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Image_generated_update"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); @@ -1087,6 +1222,8 @@ static void rna_def_image(BlenderRNA *brna) RNA_def_property_flag(prop, PROP_PROPORTIONAL); RNA_def_property_range(prop, 1, 65536); RNA_def_property_ui_text(prop, "Generated Width", "Generated image width"); + RNA_def_property_int_funcs( + prop, "rna_Image_generated_width_get", "rna_Image_generated_width_set", NULL); RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Image_generated_update"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); @@ -1095,12 +1232,16 @@ static void rna_def_image(BlenderRNA *brna) RNA_def_property_flag(prop, PROP_PROPORTIONAL); RNA_def_property_range(prop, 1, 65536); RNA_def_property_ui_text(prop, "Generated Height", "Generated image height"); + RNA_def_property_int_funcs( + prop, "rna_Image_generated_height_get", "rna_Image_generated_height_set", NULL); RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Image_generated_update"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); prop = RNA_def_property(srna, "use_generated_float", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "gen_flag", IMA_GEN_FLOAT); RNA_def_property_ui_text(prop, "Float Buffer", "Generate floating-point buffer"); + RNA_def_property_boolean_funcs( + prop, "rna_Image_generated_float_get", "rna_Image_generated_float_set"); RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Image_generated_update"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); @@ -1108,6 +1249,8 @@ static void rna_def_image(BlenderRNA *brna) RNA_def_property_float_sdna(prop, NULL, "gen_color"); RNA_def_property_array(prop, 4); RNA_def_property_ui_text(prop, "Color", "Fill color for the generated image"); + RNA_def_property_float_funcs( + prop, "rna_Image_generated_color_get", "rna_Image_generated_color_set", NULL); RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Image_generated_update"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index 4810784b3f7..0e420f7556f 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -7243,6 +7243,7 @@ void RNA_def_modifier(BlenderRNA *brna) RNA_def_struct_refine_func(srna, "rna_Modifier_refine"); RNA_def_struct_path_func(srna, "rna_Modifier_path"); RNA_def_struct_sdna(srna, "ModifierData"); + RNA_def_struct_ui_icon(srna, ICON_MODIFIER); /* strings */ prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_path.cc b/source/blender/makesrna/intern/rna_path.cc index fe1f4c0101f..5d570657b53 100644 --- a/source/blender/makesrna/intern/rna_path.cc +++ b/source/blender/makesrna/intern/rna_path.cc @@ -380,9 +380,7 @@ static bool rna_path_parse(const PointerRNA *ptr, } const bool use_id_prop = (*path == '['); - /* custom property lookup ? - * C.object["someprop"] - */ + /* Custom property lookup: e.g. `C.object["someprop"]`. */ if (!curptr.data) { return false; @@ -604,7 +602,8 @@ char *RNA_path_append(const char *path, BLI_dynstr_append(dynstr, RNA_property_identifier(prop)); - if (RNA_property_type(prop) == PROP_COLLECTION) { + const bool has_key = (intkey > -1) || (strkey != nullptr); + if (has_key && (RNA_property_type(prop) == PROP_COLLECTION)) { /* add ["strkey"] or [intkey] */ BLI_dynstr_append(dynstr, "["); diff --git a/source/blender/modifiers/intern/MOD_armature.c b/source/blender/modifiers/intern/MOD_armature.c index 15ad361a262..3fce556ce77 100644 --- a/source/blender/modifiers/intern/MOD_armature.c +++ b/source/blender/modifiers/intern/MOD_armature.c @@ -125,7 +125,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte DEG_add_object_relation(ctx->node, amd->object, DEG_OB_COMP_TRANSFORM, "Armature Modifier"); } - DEG_add_modifier_to_transform_relation(ctx->node, "Armature Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "Armature Modifier"); } static void deformVerts(ModifierData *md, diff --git a/source/blender/modifiers/intern/MOD_array.c b/source/blender/modifiers/intern/MOD_array.c index 569b0fd0fda..b29b34436ca 100644 --- a/source/blender/modifiers/intern/MOD_array.c +++ b/source/blender/modifiers/intern/MOD_array.c @@ -93,7 +93,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte } if (need_transform_dependency) { - DEG_add_modifier_to_transform_relation(ctx->node, "Array Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "Array Modifier"); } } diff --git a/source/blender/modifiers/intern/MOD_boolean.cc b/source/blender/modifiers/intern/MOD_boolean.cc index aa64c1f83bc..685338cf351 100644 --- a/source/blender/modifiers/intern/MOD_boolean.cc +++ b/source/blender/modifiers/intern/MOD_boolean.cc @@ -117,7 +117,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte DEG_add_collection_geometry_relation(ctx->node, col, "Boolean Modifier"); } /* We need own transformation as well. */ - DEG_add_modifier_to_transform_relation(ctx->node, "Boolean Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "Boolean Modifier"); } static Mesh *get_quick_mesh( diff --git a/source/blender/modifiers/intern/MOD_cast.c b/source/blender/modifiers/intern/MOD_cast.c index 9aaf7fead36..874dd20691f 100644 --- a/source/blender/modifiers/intern/MOD_cast.c +++ b/source/blender/modifiers/intern/MOD_cast.c @@ -87,7 +87,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte CastModifierData *cmd = (CastModifierData *)md; if (cmd->object != NULL) { DEG_add_object_relation(ctx->node, cmd->object, DEG_OB_COMP_TRANSFORM, "Cast Modifier"); - DEG_add_modifier_to_transform_relation(ctx->node, "Cast Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "Cast Modifier"); } } diff --git a/source/blender/modifiers/intern/MOD_cloth.c b/source/blender/modifiers/intern/MOD_cloth.c index cc0bd87d614..4fe65bf28ac 100644 --- a/source/blender/modifiers/intern/MOD_cloth.c +++ b/source/blender/modifiers/intern/MOD_cloth.c @@ -144,7 +144,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte DEG_add_forcefield_relations( ctx->node, ctx->object, clmd->sim_parms->effector_weights, true, 0, "Cloth Field"); } - DEG_add_modifier_to_transform_relation(ctx->node, "Cloth Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "Cloth Modifier"); } static void requiredDataMask(Object *UNUSED(ob), diff --git a/source/blender/modifiers/intern/MOD_collision.c b/source/blender/modifiers/intern/MOD_collision.c index 74cb4ac700a..868e164e223 100644 --- a/source/blender/modifiers/intern/MOD_collision.c +++ b/source/blender/modifiers/intern/MOD_collision.c @@ -236,7 +236,7 @@ static void deformVerts(ModifierData *md, static void updateDepsgraph(ModifierData *UNUSED(md), const ModifierUpdateDepsgraphContext *ctx) { - DEG_add_modifier_to_transform_relation(ctx->node, "Collision Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "Collision Modifier"); } static void panel_draw(const bContext *UNUSED(C), Panel *panel) diff --git a/source/blender/modifiers/intern/MOD_curve.c b/source/blender/modifiers/intern/MOD_curve.c index 48a59f4d949..bd622fc1373 100644 --- a/source/blender/modifiers/intern/MOD_curve.c +++ b/source/blender/modifiers/intern/MOD_curve.c @@ -97,7 +97,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte DEG_add_special_eval_flag(ctx->node, &cmd->object->id, DAG_EVAL_NEED_CURVE_PATH); } - DEG_add_modifier_to_transform_relation(ctx->node, "Curve Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "Curve Modifier"); } static void deformVerts(ModifierData *md, diff --git a/source/blender/modifiers/intern/MOD_datatransfer.c b/source/blender/modifiers/intern/MOD_datatransfer.c index e9f1cf47e38..7cd6b829d37 100644 --- a/source/blender/modifiers/intern/MOD_datatransfer.c +++ b/source/blender/modifiers/intern/MOD_datatransfer.c @@ -129,7 +129,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte if (dtmd->flags & MOD_DATATRANSFER_OBSRC_TRANSFORM) { DEG_add_object_relation( ctx->node, dtmd->ob_source, DEG_OB_COMP_TRANSFORM, "DataTransfer Modifier"); - DEG_add_modifier_to_transform_relation(ctx->node, "DataTransfer Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "DataTransfer Modifier"); } } } diff --git a/source/blender/modifiers/intern/MOD_displace.c b/source/blender/modifiers/intern/MOD_displace.c index 5289fc42e21..cf1bb64a41d 100644 --- a/source/blender/modifiers/intern/MOD_displace.c +++ b/source/blender/modifiers/intern/MOD_displace.c @@ -142,7 +142,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte } if (need_transform_relation) { - DEG_add_modifier_to_transform_relation(ctx->node, "Displace Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "Displace Modifier"); } } diff --git a/source/blender/modifiers/intern/MOD_hook.c b/source/blender/modifiers/intern/MOD_hook.c index 3c4e6b0d90f..b9942ff8a4d 100644 --- a/source/blender/modifiers/intern/MOD_hook.c +++ b/source/blender/modifiers/intern/MOD_hook.c @@ -122,7 +122,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte DEG_add_object_relation(ctx->node, hmd->object, DEG_OB_COMP_TRANSFORM, "Hook Modifier"); } /* We need own transformation as well. */ - DEG_add_modifier_to_transform_relation(ctx->node, "Hook Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "Hook Modifier"); } struct HookData_cb { diff --git a/source/blender/modifiers/intern/MOD_lattice.c b/source/blender/modifiers/intern/MOD_lattice.c index 0e1994eed36..2edcbd8e59a 100644 --- a/source/blender/modifiers/intern/MOD_lattice.c +++ b/source/blender/modifiers/intern/MOD_lattice.c @@ -87,7 +87,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte DEG_add_object_relation(ctx->node, lmd->object, DEG_OB_COMP_GEOMETRY, "Lattice Modifier"); DEG_add_object_relation(ctx->node, lmd->object, DEG_OB_COMP_TRANSFORM, "Lattice Modifier"); } - DEG_add_modifier_to_transform_relation(ctx->node, "Lattice Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "Lattice Modifier"); } static void deformVerts(ModifierData *md, diff --git a/source/blender/modifiers/intern/MOD_mask.cc b/source/blender/modifiers/intern/MOD_mask.cc index fac3ea36537..e48a949baf4 100644 --- a/source/blender/modifiers/intern/MOD_mask.cc +++ b/source/blender/modifiers/intern/MOD_mask.cc @@ -86,7 +86,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte /* TODO(sergey): Is it a proper relation here? */ DEG_add_object_relation(ctx->node, mmd->ob_arm, DEG_OB_COMP_TRANSFORM, "Mask Modifier"); arm->flag |= ARM_HAS_VIZ_DEPS; - DEG_add_modifier_to_transform_relation(ctx->node, "Mask Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "Mask Modifier"); } } diff --git a/source/blender/modifiers/intern/MOD_mesh_to_volume.cc b/source/blender/modifiers/intern/MOD_mesh_to_volume.cc index 9ac410eb3de..0471beadcc1 100644 --- a/source/blender/modifiers/intern/MOD_mesh_to_volume.cc +++ b/source/blender/modifiers/intern/MOD_mesh_to_volume.cc @@ -60,7 +60,7 @@ static void initData(ModifierData *md) static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx) { MeshToVolumeModifierData *mvmd = reinterpret_cast<MeshToVolumeModifierData *>(md); - DEG_add_modifier_to_transform_relation(ctx->node, "Mesh to Volume Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "Mesh to Volume Modifier"); if (mvmd->object) { DEG_add_object_relation( ctx->node, mvmd->object, DEG_OB_COMP_GEOMETRY, "Mesh to Volume Modifier"); diff --git a/source/blender/modifiers/intern/MOD_meshdeform.c b/source/blender/modifiers/intern/MOD_meshdeform.c index 334f5d75279..3b7e62f99e1 100644 --- a/source/blender/modifiers/intern/MOD_meshdeform.c +++ b/source/blender/modifiers/intern/MOD_meshdeform.c @@ -160,7 +160,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte DEG_add_object_relation(ctx->node, mmd->object, DEG_OB_COMP_GEOMETRY, "Mesh Deform Modifier"); } /* We need own transformation as well. */ - DEG_add_modifier_to_transform_relation(ctx->node, "Mesh Deform Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "Mesh Deform Modifier"); } static float meshdeform_dynamic_bind(MeshDeformModifierData *mmd, float (*dco)[3], float vec[3]) diff --git a/source/blender/modifiers/intern/MOD_mirror.c b/source/blender/modifiers/intern/MOD_mirror.c index 5f095a72dca..f1a36c04453 100644 --- a/source/blender/modifiers/intern/MOD_mirror.c +++ b/source/blender/modifiers/intern/MOD_mirror.c @@ -62,7 +62,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte MirrorModifierData *mmd = (MirrorModifierData *)md; if (mmd->mirror_ob != NULL) { DEG_add_object_relation(ctx->node, mmd->mirror_ob, DEG_OB_COMP_TRANSFORM, "Mirror Modifier"); - DEG_add_modifier_to_transform_relation(ctx->node, "Mirror Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "Mirror Modifier"); } } diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 72978d6410a..9c95561904a 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -307,7 +307,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte } if (needs_own_transform_relation) { - DEG_add_modifier_to_transform_relation(ctx->node, "Nodes Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "Nodes Modifier"); } } diff --git a/source/blender/modifiers/intern/MOD_normal_edit.c b/source/blender/modifiers/intern/MOD_normal_edit.c index 09bc9546325..94b48f65a66 100644 --- a/source/blender/modifiers/intern/MOD_normal_edit.c +++ b/source/blender/modifiers/intern/MOD_normal_edit.c @@ -670,7 +670,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte NormalEditModifierData *enmd = (NormalEditModifierData *)md; if (enmd->target) { DEG_add_object_relation(ctx->node, enmd->target, DEG_OB_COMP_TRANSFORM, "NormalEdit Modifier"); - DEG_add_modifier_to_transform_relation(ctx->node, "NormalEdit Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "NormalEdit Modifier"); } } diff --git a/source/blender/modifiers/intern/MOD_screw.c b/source/blender/modifiers/intern/MOD_screw.c index 9588b9acd3b..109795df796 100644 --- a/source/blender/modifiers/intern/MOD_screw.c +++ b/source/blender/modifiers/intern/MOD_screw.c @@ -1148,7 +1148,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte ScrewModifierData *ltmd = (ScrewModifierData *)md; if (ltmd->ob_axis != NULL) { DEG_add_object_relation(ctx->node, ltmd->ob_axis, DEG_OB_COMP_TRANSFORM, "Screw Modifier"); - DEG_add_modifier_to_transform_relation(ctx->node, "Screw Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "Screw Modifier"); } } diff --git a/source/blender/modifiers/intern/MOD_shrinkwrap.c b/source/blender/modifiers/intern/MOD_shrinkwrap.c index be12dc6639b..1c4ef5698b4 100644 --- a/source/blender/modifiers/intern/MOD_shrinkwrap.c +++ b/source/blender/modifiers/intern/MOD_shrinkwrap.c @@ -186,7 +186,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte DEG_add_special_eval_flag(ctx->node, &smd->auxTarget->id, DAG_EVAL_NEED_SHRINKWRAP_BOUNDARY); } } - DEG_add_modifier_to_transform_relation(ctx->node, "Shrinkwrap Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "Shrinkwrap Modifier"); } static bool dependsOnNormals(ModifierData *md) diff --git a/source/blender/modifiers/intern/MOD_simpledeform.c b/source/blender/modifiers/intern/MOD_simpledeform.c index 9f1d0cd36c4..168575b6330 100644 --- a/source/blender/modifiers/intern/MOD_simpledeform.c +++ b/source/blender/modifiers/intern/MOD_simpledeform.c @@ -438,7 +438,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte if (smd->origin != NULL) { DEG_add_object_relation( ctx->node, smd->origin, DEG_OB_COMP_TRANSFORM, "SimpleDeform Modifier"); - DEG_add_modifier_to_transform_relation(ctx->node, "SimpleDeform Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "SimpleDeform Modifier"); } } diff --git a/source/blender/modifiers/intern/MOD_softbody.c b/source/blender/modifiers/intern/MOD_softbody.c index a49f2609641..ecff6d80893 100644 --- a/source/blender/modifiers/intern/MOD_softbody.c +++ b/source/blender/modifiers/intern/MOD_softbody.c @@ -66,7 +66,7 @@ static void updateDepsgraph(ModifierData *UNUSED(md), const ModifierUpdateDepsgr ctx->node, ctx->object, ctx->object->soft->effector_weights, true, 0, "Softbody Field"); } /* We need own transformation as well. */ - DEG_add_modifier_to_transform_relation(ctx->node, "SoftBody Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "SoftBody Modifier"); } static void panel_draw(const bContext *UNUSED(C), Panel *panel) diff --git a/source/blender/modifiers/intern/MOD_uvproject.c b/source/blender/modifiers/intern/MOD_uvproject.c index 0474d3e47e6..a318a82fe64 100644 --- a/source/blender/modifiers/intern/MOD_uvproject.c +++ b/source/blender/modifiers/intern/MOD_uvproject.c @@ -80,7 +80,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte } } if (do_add_own_transform) { - DEG_add_modifier_to_transform_relation(ctx->node, "UV Project Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "UV Project Modifier"); } } diff --git a/source/blender/modifiers/intern/MOD_uvwarp.c b/source/blender/modifiers/intern/MOD_uvwarp.c index c33b25c38e3..4178f1dd33e 100644 --- a/source/blender/modifiers/intern/MOD_uvwarp.c +++ b/source/blender/modifiers/intern/MOD_uvwarp.c @@ -242,7 +242,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte MOD_depsgraph_update_object_bone_relation( ctx->node, umd->object_dst, umd->bone_dst, "UVWarp Modifier"); - DEG_add_modifier_to_transform_relation(ctx->node, "UVWarp Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "UVWarp Modifier"); } static void panel_draw(const bContext *UNUSED(C), Panel *panel) diff --git a/source/blender/modifiers/intern/MOD_volume_to_mesh.cc b/source/blender/modifiers/intern/MOD_volume_to_mesh.cc index 3292f73137a..215436e4a8d 100644 --- a/source/blender/modifiers/intern/MOD_volume_to_mesh.cc +++ b/source/blender/modifiers/intern/MOD_volume_to_mesh.cc @@ -62,7 +62,7 @@ static void initData(ModifierData *md) static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx) { VolumeToMeshModifierData *vmmd = reinterpret_cast<VolumeToMeshModifierData *>(md); - DEG_add_modifier_to_transform_relation(ctx->node, "Volume to Mesh Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "Volume to Mesh Modifier"); if (vmmd->object) { DEG_add_object_relation( ctx->node, vmmd->object, DEG_OB_COMP_GEOMETRY, "Volume to Mesh Modifier"); diff --git a/source/blender/modifiers/intern/MOD_warp.c b/source/blender/modifiers/intern/MOD_warp.c index afdc230a877..5bef19da53a 100644 --- a/source/blender/modifiers/intern/MOD_warp.c +++ b/source/blender/modifiers/intern/MOD_warp.c @@ -171,7 +171,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte } if (need_transform_relation) { - DEG_add_modifier_to_transform_relation(ctx->node, "Warp Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "Warp Modifier"); } } diff --git a/source/blender/modifiers/intern/MOD_wave.c b/source/blender/modifiers/intern/MOD_wave.c index b92e3a0fa9d..136ff6b6d15 100644 --- a/source/blender/modifiers/intern/MOD_wave.c +++ b/source/blender/modifiers/intern/MOD_wave.c @@ -98,7 +98,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte } if (need_transform_relation) { - DEG_add_modifier_to_transform_relation(ctx->node, "Wave Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "Wave Modifier"); } } diff --git a/source/blender/modifiers/intern/MOD_weightvgedit.c b/source/blender/modifiers/intern/MOD_weightvgedit.c index f6e0cd9303d..7ffaa120ba2 100644 --- a/source/blender/modifiers/intern/MOD_weightvgedit.c +++ b/source/blender/modifiers/intern/MOD_weightvgedit.c @@ -139,7 +139,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte } if (need_transform_relation) { - DEG_add_modifier_to_transform_relation(ctx->node, "WeightVGEdit Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "WeightVGEdit Modifier"); } } diff --git a/source/blender/modifiers/intern/MOD_weightvgmix.c b/source/blender/modifiers/intern/MOD_weightvgmix.c index 49088d42a5e..701e30fbf57 100644 --- a/source/blender/modifiers/intern/MOD_weightvgmix.c +++ b/source/blender/modifiers/intern/MOD_weightvgmix.c @@ -187,7 +187,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte } if (need_transform_relation) { - DEG_add_modifier_to_transform_relation(ctx->node, "WeightVGMix Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "WeightVGMix Modifier"); } } diff --git a/source/blender/modifiers/intern/MOD_weightvgproximity.c b/source/blender/modifiers/intern/MOD_weightvgproximity.c index b68d36366fd..70838bc5c4f 100644 --- a/source/blender/modifiers/intern/MOD_weightvgproximity.c +++ b/source/blender/modifiers/intern/MOD_weightvgproximity.c @@ -401,7 +401,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte } if (need_transform_relation) { - DEG_add_modifier_to_transform_relation(ctx->node, "WeightVGProximity Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "WeightVGProximity Modifier"); } } diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 586d3e36177..786ce88152e 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -404,7 +404,7 @@ DefNode(GeometryNode, GEO_NODE_TRANSLATE_INSTANCES, 0, "TRANSLATE_INSTANCES",Tra DefNode(GeometryNode, GEO_NODE_TRIANGULATE, def_geo_triangulate, "TRIANGULATE", Triangulate, "Triangulate", "Convert all faces in a mesh to triangular faces") DefNode(GeometryNode, GEO_NODE_TRIM_CURVE, def_geo_curve_trim, "TRIM_CURVE", TrimCurve, "Trim Curve", "Shorten curves by removing portions at the start or end") DefNode(GeometryNode, GEO_NODE_UV_PACK_ISLANDS, 0, "UV_PACK_ISLANDS", UVPackIslands, "Pack UV Islands", "Scale islands of a UV map and move them so they fill the UV space as much as possible") -DefNode(GeometryNode, GEO_NODE_UV_UNWRAP, def_geo_uv_unwrap, "UV_UNWRAP", UVUnwrap, "UV Unwrap", "Generate a UV map islands based on seam edges") +DefNode(GeometryNode, GEO_NODE_UV_UNWRAP, def_geo_uv_unwrap, "UV_UNWRAP", UVUnwrap, "UV Unwrap", "Generate a UV map based on seam edges") DefNode(GeometryNode, GEO_NODE_VIEWER, def_geo_viewer, "VIEWER", Viewer, "Viewer", "Display the input data in the Spreadsheet Editor") DefNode(GeometryNode, GEO_NODE_VOLUME_CUBE, 0, "VOLUME_CUBE", VolumeCube, "Volume Cube", "Generate a dense volume with a field that controls the density at each grid voxel based on its position") DefNode(GeometryNode, GEO_NODE_VOLUME_TO_MESH, def_geo_volume_to_mesh, "VOLUME_TO_MESH", VolumeToMesh, "Volume to Mesh", "Generate a mesh on the \"surface\" of a volume") diff --git a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc index 5462441660c..2d362a39814 100644 --- a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc +++ b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc @@ -379,6 +379,8 @@ void register_node_type_cmp_cryptomatte_legacy() node_type_init(&ntype, file_ns::node_init_cryptomatte_legacy); node_type_storage( &ntype, "NodeCryptomatte", file_ns::node_free_cryptomatte, file_ns::node_copy_cryptomatte); + ntype.gather_link_search_ops = nullptr; + nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc index a0d2485ea5a..a169f7e0dd3 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc @@ -30,6 +30,8 @@ void register_node_type_cmp_sephsva() cmp_node_type_base(&ntype, CMP_NODE_SEPHSVA_LEGACY, "Separate HSVA", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_sephsva_declare; + ntype.gather_link_search_ops = nullptr; + nodeRegisterType(&ntype); } @@ -56,6 +58,7 @@ void register_node_type_cmp_combhsva() cmp_node_type_base(&ntype, CMP_NODE_COMBHSVA_LEGACY, "Combine HSVA", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_combhsva_declare; + ntype.gather_link_search_ops = nullptr; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc index ae46681b0f4..a243500b56d 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc @@ -29,6 +29,7 @@ void register_node_type_cmp_seprgba() cmp_node_type_base(&ntype, CMP_NODE_SEPRGBA_LEGACY, "Separate RGBA", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_seprgba_declare; + ntype.gather_link_search_ops = nullptr; nodeRegisterType(&ntype); } @@ -56,6 +57,7 @@ void register_node_type_cmp_combrgba() cmp_node_type_base(&ntype, CMP_NODE_COMBRGBA_LEGACY, "Combine RGBA", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_combrgba_declare; + ntype.gather_link_search_ops = nullptr; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc index a3c40b61e64..51d3c18d238 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc @@ -36,6 +36,7 @@ void register_node_type_cmp_sepycca() cmp_node_type_base(&ntype, CMP_NODE_SEPYCCA_LEGACY, "Separate YCbCrA", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_sepycca_declare; node_type_init(&ntype, file_ns::node_composit_init_mode_sepycca); + ntype.gather_link_search_ops = nullptr; nodeRegisterType(&ntype); } @@ -69,6 +70,7 @@ void register_node_type_cmp_combycca() cmp_node_type_base(&ntype, CMP_NODE_COMBYCCA_LEGACY, "Combine YCbCrA", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_combycca_declare; node_type_init(&ntype, file_ns::node_composit_init_mode_combycca); + ntype.gather_link_search_ops = nullptr; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc index 7fdece5904d..4acd2294114 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc @@ -30,6 +30,7 @@ void register_node_type_cmp_sepyuva() cmp_node_type_base(&ntype, CMP_NODE_SEPYUVA_LEGACY, "Separate YUVA", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_sepyuva_declare; + ntype.gather_link_search_ops = nullptr; nodeRegisterType(&ntype); } @@ -57,6 +58,7 @@ void register_node_type_cmp_combyuva() cmp_node_type_base(&ntype, CMP_NODE_COMBYUVA_LEGACY, "Combine YUVA", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_combyuva_declare; + ntype.gather_link_search_ops = nullptr; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc index e739b052b6b..5a40ededa96 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc @@ -222,13 +222,13 @@ static void node_geo_exec(GeoNodeExecParams params) const Object *self_ob_eval = params.self_object(); if (self_ob_eval == nullptr || self_ob_eval->type != OB_CURVES) { pass_through_input(); + params.error_message_add(NodeWarningType::Error, TIP_("Node only works for curves objects")); return; } const Curves *self_curves_eval = static_cast<const Curves *>(self_ob_eval->data); if (self_curves_eval->surface_uv_map == nullptr || self_curves_eval->surface_uv_map[0] == '\0') { pass_through_input(); - const char *message = TIP_("Surface UV map not defined"); - params.error_message_add(NodeWarningType::Error, message); + params.error_message_add(NodeWarningType::Error, TIP_("Surface UV map not defined")); return; } /* Take surface information from self-object. */ @@ -242,7 +242,7 @@ static void node_geo_exec(GeoNodeExecParams params) } if (surface_ob_eval == nullptr || surface_ob_eval->type != OB_MESH) { pass_through_input(); - params.error_message_add(NodeWarningType::Error, "Curves not attached to a surface"); + params.error_message_add(NodeWarningType::Error, TIP_("Curves not attached to a surface")); return; } Object *surface_ob_orig = DEG_get_original_object(surface_ob_eval); @@ -258,7 +258,7 @@ static void node_geo_exec(GeoNodeExecParams params) Mesh *surface_mesh_eval = BKE_modifier_get_evaluated_mesh_from_evaluated_object(surface_ob_eval); if (surface_mesh_eval == nullptr) { pass_through_input(); - params.error_message_add(NodeWarningType::Error, "Surface has no mesh"); + params.error_message_add(NodeWarningType::Error, TIP_("Surface has no mesh")); return; } @@ -272,7 +272,7 @@ static void node_geo_exec(GeoNodeExecParams params) if (!mesh_attributes_eval.contains(uv_map_name)) { pass_through_input(); - char *message = BLI_sprintfN(TIP_("Evaluated surface missing UV map: %s"), + char *message = BLI_sprintfN(TIP_("Evaluated surface missing UV map: \"%s\""), uv_map_name.c_str()); params.error_message_add(NodeWarningType::Error, message); MEM_freeN(message); @@ -280,7 +280,8 @@ static void node_geo_exec(GeoNodeExecParams params) } if (!mesh_attributes_orig.contains(uv_map_name)) { pass_through_input(); - char *message = BLI_sprintfN(TIP_("Original surface missing UV map: %s"), uv_map_name.c_str()); + char *message = BLI_sprintfN(TIP_("Original surface missing UV map: \"%s\""), + uv_map_name.c_str()); params.error_message_add(NodeWarningType::Error, message); MEM_freeN(message); return; @@ -288,7 +289,7 @@ static void node_geo_exec(GeoNodeExecParams params) if (!mesh_attributes_eval.contains(rest_position_name)) { pass_through_input(); params.error_message_add(NodeWarningType::Error, - TIP_("Evaluated surface missing attribute: rest_position")); + TIP_("Evaluated surface missing attribute: \"rest_position\"")); return; } if (curves.surface_uv_coords().is_empty() && curves.curves_num() > 0) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc index cfb9cbf7e24..cf29c752257 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc @@ -220,11 +220,11 @@ BLI_NOINLINE static void update_elimination_mask_based_on_density_factors( const float v1_density_factor = std::max(0.0f, density_factors[v1_loop]); const float v2_density_factor = std::max(0.0f, density_factors[v2_loop]); - const float probablity = v0_density_factor * bary_coord.x + v1_density_factor * bary_coord.y + + const float probability = v0_density_factor * bary_coord.x + v1_density_factor * bary_coord.y + v2_density_factor * bary_coord.z; const float hash = noise::hash_float_to_float(bary_coord); - if (hash > probablity) { + if (hash > probability) { elimination_mask[i] = true; } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_selection.cc index efdf0911ec9..53cbd691fdb 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_selection.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_selection.cc @@ -110,7 +110,12 @@ class PathToEdgeSelectionFieldInput final : public GeometryFieldInput { bool is_equal_to(const fn::FieldNode &other) const override { - return dynamic_cast<const PathToEdgeSelectionFieldInput *>(&other) != nullptr; + if (const PathToEdgeSelectionFieldInput *other_field = + dynamic_cast<const PathToEdgeSelectionFieldInput *>(&other)) { + return other_field->start_vertices_ == start_vertices_ && + other_field->next_vertex_ == next_vertex_; + } + return false; } }; diff --git a/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc index acf85e74353..024dbd1c852 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc @@ -225,7 +225,7 @@ template<typename T, typename GetMixIndicesFn> void copy_with_mixing(MutableSpan<T> dst, Span<T> src, GetMixIndicesFn get_mix_indices_fn) { threading::parallel_for(dst.index_range(), 512, [&](const IndexRange range) { - attribute_math::DefaultPropatationMixer<T> mixer{dst.slice(range)}; + attribute_math::DefaultPropagationMixer<T> mixer{dst.slice(range)}; for (const int i_dst : IndexRange(range.size())) { for (const int i_src : get_mix_indices_fn(range[i_dst])) { mixer.mix_in(i_dst, src[i_src]); @@ -437,7 +437,7 @@ static void extrude_mesh_edges(MeshComponent &component, Array<float3> vert_offsets; if (!edge_offsets.is_single()) { vert_offsets.reinitialize(orig_vert_size); - attribute_math::DefaultPropatationMixer<float3> mixer(vert_offsets); + attribute_math::DefaultPropagationMixer<float3> mixer(vert_offsets); for (const int i_edge : edge_selection) { const MEdge &edge = orig_edges[i_edge]; const float3 offset = edge_offsets[i_edge]; @@ -583,7 +583,7 @@ static void extrude_mesh_edges(MeshComponent &component, /* Both corners on each vertical edge of the side polygon get the same value, * so there are only two unique values to mix. */ Array<T> side_poly_corner_data(2); - attribute_math::DefaultPropatationMixer<T> mixer{side_poly_corner_data}; + attribute_math::DefaultPropagationMixer<T> mixer{side_poly_corner_data}; const MEdge &duplicate_edge = duplicate_edges[i_edge_selection]; const int new_vert_1 = duplicate_edge.v1; @@ -705,7 +705,7 @@ static void extrude_mesh_face_regions(MeshComponent &component, Array<float3> vert_offsets; if (!poly_offsets.is_single()) { vert_offsets.reinitialize(orig_vert_size); - attribute_math::DefaultPropatationMixer<float3> mixer(vert_offsets); + attribute_math::DefaultPropagationMixer<float3> mixer(vert_offsets); for (const int i_poly : poly_selection) { const MPoly &poly = orig_polys[i_poly]; const float3 offset = poly_offsets[i_poly]; diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_shortest_edge_paths.cc b/source/blender/nodes/geometry/nodes/node_geo_input_shortest_edge_paths.cc index 89abaca3c66..ca6406d2810 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_shortest_edge_paths.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_shortest_edge_paths.cc @@ -148,7 +148,11 @@ class ShortestEdgePathsNextVertFieldInput final : public GeometryFieldInput { bool is_equal_to(const fn::FieldNode &other) const override { - return dynamic_cast<const ShortestEdgePathsNextVertFieldInput *>(&other) != nullptr; + if (const ShortestEdgePathsNextVertFieldInput *other_field = + dynamic_cast<const ShortestEdgePathsNextVertFieldInput *>(&other)) { + return other_field->end_selection_ == end_selection_ && other_field->cost_ == cost_; + } + return false; } }; @@ -215,7 +219,11 @@ class ShortestEdgePathsCostFieldInput final : public GeometryFieldInput { bool is_equal_to(const fn::FieldNode &other) const override { - return dynamic_cast<const ShortestEdgePathsCostFieldInput *>(&other) != nullptr; + if (const ShortestEdgePathsCostFieldInput *other_field = + dynamic_cast<const ShortestEdgePathsCostFieldInput *>(&other)) { + return other_field->end_selection_ == end_selection_ && other_field->cost_ == cost_; + } + return false; } }; diff --git a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc index 119d895fead..37f9917f39d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc @@ -70,6 +70,9 @@ static void add_instances_from_component( evaluator.evaluate(); const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); + if (selection.is_empty()) { + return; + } /* The initial size of the component might be non-zero when this function is called for multiple * component types. */ diff --git a/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc index 1d3beb8be96..70c33ad6a96 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc @@ -72,7 +72,7 @@ static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) const NodeDeclaration &declaration = *params.node_type().fixed_declaration; search_link_ops_for_declarations(params, declaration.inputs().take_front(2)); - if (params.in_out() == SOCK_OUT) { + if (params.in_out() == SOCK_IN) { const std::optional<eCustomDataType> type = node_data_type_to_custom_data_type( static_cast<eNodeSocketDatatype>(params.other_socket().type)); if (type && *type != CD_PROP_STRING) { diff --git a/source/blender/nodes/shader/nodes/node_shader_geometry.cc b/source/blender/nodes/shader/nodes/node_shader_geometry.cc index 47df932f9d4..d23561de7ff 100644 --- a/source/blender/nodes/shader/nodes/node_shader_geometry.cc +++ b/source/blender/nodes/shader/nodes/node_shader_geometry.cc @@ -29,10 +29,9 @@ static int node_shader_gpu_geometry(GPUMaterial *mat, if (out[5].hasoutput) { GPU_material_flag_set(mat, GPU_MATFLAG_BARYCENTRIC); } - /* Opti: don't request orco if not needed. */ + /* Optimization: don't request orco if not needed. */ const float val[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - GPUNodeLink *orco_link = (!out[2].hasoutput) ? GPU_constant(val) : - GPU_attribute(mat, CD_ORCO, ""); + GPUNodeLink *orco_link = out[2].hasoutput ? GPU_attribute(mat, CD_ORCO, "") : GPU_constant(val); const bool success = GPU_stack_link(mat, node, "node_geometry", in, out, orco_link); diff --git a/source/blender/nodes/shader/nodes/node_shader_hair_info.cc b/source/blender/nodes/shader/nodes/node_shader_hair_info.cc index 11d23e47735..f46556291ce 100644 --- a/source/blender/nodes/shader/nodes/node_shader_hair_info.cc +++ b/source/blender/nodes/shader/nodes/node_shader_hair_info.cc @@ -23,8 +23,8 @@ static int node_shader_gpu_hair_info(GPUMaterial *mat, { /* Length: don't request length if not needed. */ static const float zero = 0; - GPUNodeLink *length_link = (!out[2].hasoutput) ? GPU_constant(&zero) : - GPU_attribute(mat, CD_HAIRLENGTH, ""); + GPUNodeLink *length_link = out[2].hasoutput ? GPU_attribute(mat, CD_HAIRLENGTH, "") : + GPU_constant(&zero); return GPU_stack_link(mat, node, "node_hair_info", in, out, length_link); } diff --git a/source/blender/nodes/shader/nodes/node_shader_rgb.cc b/source/blender/nodes/shader/nodes/node_shader_rgb.cc index 38acfab322f..c854bc733a3 100644 --- a/source/blender/nodes/shader/nodes/node_shader_rgb.cc +++ b/source/blender/nodes/shader/nodes/node_shader_rgb.cc @@ -20,8 +20,9 @@ static int gpu_shader_rgb(GPUMaterial *mat, GPUNodeStack *in, GPUNodeStack *out) { - GPUNodeLink *link = GPU_uniformbuf_link_out(mat, node, out, 0); - return GPU_stack_link(mat, node, "set_rgba", in, out, link); + const bNodeSocket *socket = static_cast<bNodeSocket *>(node->outputs.first); + float *value = static_cast<bNodeSocketValueRGBA *>(socket->default_value)->value; + return GPU_link(mat, "set_rgba", GPU_uniform(value), &out->link); } } // namespace blender::nodes::node_shader_rgb_cc diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcomb_hsv.cc b/source/blender/nodes/shader/nodes/node_shader_sepcomb_hsv.cc index 6dfabe48292..1c02c5ba074 100644 --- a/source/blender/nodes/shader/nodes/node_shader_sepcomb_hsv.cc +++ b/source/blender/nodes/shader/nodes/node_shader_sepcomb_hsv.cc @@ -39,6 +39,7 @@ void register_node_type_sh_sephsv() sh_node_type_base(&ntype, SH_NODE_SEPHSV_LEGACY, "Separate HSV", NODE_CLASS_CONVERTER); ntype.declare = file_ns::node_declare_sephsv; node_type_gpu(&ntype, file_ns::gpu_shader_sephsv); + ntype.gather_link_search_ops = nullptr; nodeRegisterType(&ntype); } @@ -75,6 +76,7 @@ void register_node_type_sh_combhsv() sh_node_type_base(&ntype, SH_NODE_COMBHSV_LEGACY, "Combine HSV", NODE_CLASS_CONVERTER); ntype.declare = file_ns::node_declare_combhsv; node_type_gpu(&ntype, file_ns::gpu_shader_combhsv); + ntype.gather_link_search_ops = nullptr; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcomb_rgb.cc b/source/blender/nodes/shader/nodes/node_shader_sepcomb_rgb.cc index 28b55047633..3fe76b05b5d 100644 --- a/source/blender/nodes/shader/nodes/node_shader_sepcomb_rgb.cc +++ b/source/blender/nodes/shader/nodes/node_shader_sepcomb_rgb.cc @@ -80,6 +80,7 @@ void register_node_type_sh_seprgb() ntype.declare = file_ns::sh_node_seprgb_declare; node_type_gpu(&ntype, file_ns::gpu_shader_seprgb); ntype.build_multi_function = file_ns::sh_node_seprgb_build_multi_function; + ntype.gather_link_search_ops = nullptr; nodeRegisterType(&ntype); } @@ -123,6 +124,7 @@ void register_node_type_sh_combrgb() ntype.declare = file_ns::sh_node_combrgb_declare; node_type_gpu(&ntype, file_ns::gpu_shader_combrgb); ntype.build_multi_function = file_ns::sh_node_combrgb_build_multi_function; + ntype.gather_link_search_ops = nullptr; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_coord.cc b/source/blender/nodes/shader/nodes/node_shader_tex_coord.cc index fb5971021fc..0a28b34902e 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_coord.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_coord.cc @@ -41,9 +41,9 @@ static int node_shader_gpu_tex_coord(GPUMaterial *mat, GPUNodeLink *inv_obmat = (ob != NULL) ? GPU_uniform(&ob->imat[0][0]) : GPU_uniform(&dummy_matrix[0][0]); - /* Opti: don't request orco if not needed. */ + /* Optimization: don't request orco if not needed. */ float4 zero(0.0f); - GPUNodeLink *orco = (!out[0].hasoutput) ? GPU_constant(zero) : GPU_attribute(mat, CD_ORCO, ""); + GPUNodeLink *orco = out[0].hasoutput ? GPU_attribute(mat, CD_ORCO, "") : GPU_constant(zero); GPUNodeLink *mtface = GPU_attribute(mat, CD_AUTO_FROM_NAME, ""); GPU_stack_link(mat, node, "node_tex_coord", in, out, inv_obmat, orco, mtface); diff --git a/source/blender/nodes/shader/nodes/node_shader_value.cc b/source/blender/nodes/shader/nodes/node_shader_value.cc index 362cdf58052..b6b7fe10cf9 100644 --- a/source/blender/nodes/shader/nodes/node_shader_value.cc +++ b/source/blender/nodes/shader/nodes/node_shader_value.cc @@ -20,8 +20,9 @@ static int gpu_shader_value(GPUMaterial *mat, GPUNodeStack *in, GPUNodeStack *out) { - GPUNodeLink *link = GPU_uniformbuf_link_out(mat, node, out, 0); - return GPU_stack_link(mat, node, "set_value", in, out, link); + const bNodeSocket *socket = static_cast<bNodeSocket *>(node->outputs.first); + float value = static_cast<bNodeSocketValueFloat *>(socket->default_value)->value; + return GPU_link(mat, "set_value", GPU_uniform(&value), &out->link); } static void sh_node_value_build_multi_function(NodeMultiFunctionBuilder &builder) diff --git a/source/blender/python/gpu/gpu_py_framebuffer.c b/source/blender/python/gpu/gpu_py_framebuffer.c index 33d9ff0b041..9bb2a9137f4 100644 --- a/source/blender/python/gpu/gpu_py_framebuffer.c +++ b/source/blender/python/gpu/gpu_py_framebuffer.c @@ -686,7 +686,7 @@ static struct PyMethodDef pygpu_framebuffer__tp_methods[] = { PyDoc_STRVAR(pygpu_framebuffer__tp_doc, ".. class:: GPUFrameBuffer(depth_slot=None, color_slots=None)\n" "\n" - " This object gives access to framebuffer functionallities.\n" + " This object gives access to framebuffer functionalities.\n" " When a 'layer' is specified in a argument, a single layer of a 3D or array " "texture is attached to the frame-buffer.\n" " For cube map textures, layer is translated into a cube map face.\n" diff --git a/source/blender/python/gpu/gpu_py_vertex_buffer.c b/source/blender/python/gpu/gpu_py_vertex_buffer.c index ac050128a1d..ab2ff59a689 100644 --- a/source/blender/python/gpu/gpu_py_vertex_buffer.c +++ b/source/blender/python/gpu/gpu_py_vertex_buffer.c @@ -292,7 +292,7 @@ static PyObject *pygpu_vertbuf_attr_fill(BPyGPUVertBuf *self, PyObject *args, Py const char *name = PyUnicode_AsUTF8(identifier); id = GPU_vertformat_attr_id_get(format, name); if (id == -1) { - PyErr_SetString(PyExc_ValueError, "Unknown attribute name"); + PyErr_Format(PyExc_ValueError, "Unknown attribute '%s'", name); return NULL; } } diff --git a/source/blender/render/intern/pipeline.c b/source/blender/render/intern/pipeline.c index 1c42467bc3d..30e8cfa5c17 100644 --- a/source/blender/render/intern/pipeline.c +++ b/source/blender/render/intern/pipeline.c @@ -199,14 +199,20 @@ static void stats_background(void *UNUSED(arg), RenderStats *rs) megs_used_memory = (mem_in_use) / (1024.0 * 1024.0); megs_peak_memory = (peak_memory) / (1024.0 * 1024.0); + BLI_timecode_string_from_time_simple( + info_time_str, sizeof(info_time_str), PIL_check_seconds_timer() - rs->starttime); + + /* Compositor calls this from multiple threads, mutex lock to ensure we don't + * get garbled output. */ + static ThreadMutex mutex = BLI_MUTEX_INITIALIZER; + BLI_mutex_lock(&mutex); + fprintf(stdout, TIP_("Fra:%d Mem:%.2fM (Peak %.2fM) "), rs->cfra, megs_used_memory, megs_peak_memory); - BLI_timecode_string_from_time_simple( - info_time_str, sizeof(info_time_str), PIL_check_seconds_timer() - rs->starttime); fprintf(stdout, TIP_("| Time:%s | "), info_time_str); fprintf(stdout, "%s", rs->infostr); @@ -220,6 +226,8 @@ static void stats_background(void *UNUSED(arg), RenderStats *rs) fputc('\n', stdout); fflush(stdout); + + BLI_mutex_unlock(&mutex); } void RE_FreeRenderResult(RenderResult *rr) diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index a1ebe1fc76f..a5690b52a5a 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -1367,6 +1367,10 @@ static bool ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_pt event.xy[0] = ddd->x; event.xy[1] = ddd->y; + /* The values from #wm_window_update_eventstate may not match (under WAYLAND they don't) + * Write this into the event state. */ + copy_v2_v2_int(win->eventstate->xy, event.xy); + event.flag = 0; /* No context change! C->wm->windrawable is drawable, or for area queues. */ diff --git a/tests/performance/api/graph.py b/tests/performance/api/graph.py index 6c9ba70141f..d95d386569e 100644 --- a/tests/performance/api/graph.py +++ b/tests/performance/api/graph.py @@ -74,7 +74,7 @@ class TestGraph: revisions[revision] = len(revisions) revision_dates[revision] = int(entry.date) - # Google Charts JSON data layout is like a spreadsheat table, with + # Google Charts JSON data layout is like a spreadsheet table, with # columns, rows, and cells. We create one column for revision labels, # and one column for each test. cols = [] diff --git a/tests/python/eevee_render_tests.py b/tests/python/eevee_render_tests.py index 34c15872a11..9ed850fcb52 100644 --- a/tests/python/eevee_render_tests.py +++ b/tests/python/eevee_render_tests.py @@ -99,6 +99,7 @@ if inside_blender: print(e) sys.exit(1) + def get_gpu_device_type(blender): command = [ blender, @@ -119,7 +120,6 @@ def get_gpu_device_type(blender): return None - def get_arguments(filepath, output_filepath): return [ "--background", diff --git a/tests/python/gpu_info.py b/tests/python/gpu_info.py index 1df23d68000..518fdc98bcc 100644 --- a/tests/python/gpu_info.py +++ b/tests/python/gpu_info.py @@ -7,7 +7,7 @@ import bpy import gpu import sys -# Render with workbench to initialize the GPU backend otherwise it would fail when running in +# Render with workbench to initialize the GPU backend otherwise it would fail when running in # background mode as the GPU backend won't be initialized. scene = bpy.context.scene scene.render.resolution_x = 1 @@ -21,4 +21,4 @@ print('GPU_RENDERER:' + gpu.platform.renderer_get()) print('GPU_VERSION:' + gpu.platform.version_get()) print('GPU_DEVICE_TYPE:' + gpu.platform.device_type_get()) -sys.exit(0)
\ No newline at end of file +sys.exit(0) diff --git a/tests/python/modules/render_report.py b/tests/python/modules/render_report.py index 52f388586d8..15d46d6d127 100755 --- a/tests/python/modules/render_report.py +++ b/tests/python/modules/render_report.py @@ -354,7 +354,8 @@ class Report: name = test_get_name(filepath) name = name.replace('_', ' ') - old_img, ref_img, new_img, diff_img = test_get_images(self.output_dir, filepath, self.reference_dir, self.reference_override_dir) + old_img, ref_img, new_img, diff_img = test_get_images( + self.output_dir, filepath, self.reference_dir, self.reference_override_dir) status = error if error else "" tr_style = """ class="table-danger" """ if error else "" @@ -401,7 +402,8 @@ class Report: self.compare_tests += test_html def _diff_output(self, filepath, tmp_filepath): - old_img, ref_img, new_img, diff_img = test_get_images(self.output_dir, filepath, self.reference_dir, self.reference_override_dir) + old_img, ref_img, new_img, diff_img = test_get_images( + self.output_dir, filepath, self.reference_dir, self.reference_override_dir) # Create reference render directory. old_dirpath = os.path.dirname(old_img) |