Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans Goudey <h.goudey@me.com>2022-07-22 02:05:48 +0300
committerHans Goudey <h.goudey@me.com>2022-07-22 02:05:48 +0300
commit03c4548e1493a66618c8c79bceab163d2845b965 (patch)
treedd29865fade7689a05473f3649c16d614bc73a53 /source/blender
parent0d81fdc91321c3b69b32307c40217a449b7b6c72 (diff)
parent7a4a6ccad74f9a2094e9f0928f5ac61ffd6346ff (diff)
Merge branch 'master' into refactor-mesh-hide-generic
Diffstat (limited to 'source/blender')
-rw-r--r--source/blender/blenkernel/BKE_attribute.hh9
-rw-r--r--source/blender/blenkernel/intern/armature_deform.c30
-rw-r--r--source/blender/blenkernel/intern/attribute_access.cc47
-rw-r--r--source/blender/blenkernel/intern/curve_legacy_convert.cc16
-rw-r--r--source/blender/blenkernel/intern/curves_geometry.cc136
-rw-r--r--source/blender/blenkernel/intern/customdata.cc60
-rw-r--r--source/blender/blenkernel/intern/fluid.c7
-rw-r--r--source/blender/blenkernel/intern/layer.c10
-rw-r--r--source/blender/blenkernel/intern/library.c12
-rw-r--r--source/blender/blenlib/BLI_length_parameterize.hh14
-rw-r--r--source/blender/blenlib/intern/generic_virtual_array.cc2
-rw-r--r--source/blender/blenlib/tests/BLI_length_parameterize_test.cc20
-rw-r--r--source/blender/blenloader/intern/writefile.c5
-rw-r--r--source/blender/depsgraph/CMakeLists.txt2
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder.cc85
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder.h2
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes.cc40
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc22
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations.cc21
-rw-r--r--source/blender/depsgraph/intern/depsgraph.cc2
-rw-r--r--source/blender/depsgraph/intern/depsgraph.h6
-rw-r--r--source/blender/depsgraph/intern/depsgraph_query_iter.cc9
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval.cc88
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_visibility.cc147
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_visibility.h26
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_component.cc7
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_component.h19
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_id.cc9
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_id.h17
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_operation.h3
-rw-r--r--source/blender/draw/CMakeLists.txt1
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_camera.cc18
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_camera.hh18
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_film.cc50
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_film.hh13
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_instance.cc2
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_renderbuffers.cc7
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_sampling.cc29
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_sampling.hh25
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_shader.cc2
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_shader.hh2
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_shader_shared.hh10
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_velocity.cc89
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_velocity.hh61
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_view.cc15
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_view.hh3
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_camera_lib.glsl20
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_film_frag.glsl2
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_film_lib.glsl386
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_surf_depth_frag.glsl11
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_velocity_lib.glsl87
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_velocity_resolve_comp.glsl58
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/infos/eevee_film_info.hh8
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/infos/eevee_velocity_info.hh21
-rw-r--r--source/blender/draw/intern/DRW_render.h1
-rw-r--r--source/blender/draw/intern/draw_manager.c11
-rw-r--r--source/blender/draw/intern/draw_volume.cc4
-rw-r--r--source/blender/draw/intern/shaders/common_math_geom_lib.glsl108
-rw-r--r--source/blender/editors/curve/editcurve_paint.c2
-rw-r--r--source/blender/editors/curve/editcurve_pen.c2
-rw-r--r--source/blender/editors/gpencil/annotate_paint.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_paint.c2
-rw-r--r--source/blender/editors/include/ED_curves_sculpt.h25
-rw-r--r--source/blender/editors/interface/eyedroppers/eyedropper_color.c2
-rw-r--r--source/blender/editors/interface/eyedroppers/eyedropper_driver.c4
-rw-r--r--source/blender/editors/interface/interface_handlers.c9
-rw-r--r--source/blender/editors/interface/interface_panel.c2
-rw-r--r--source/blender/editors/interface/views/tree_view.cc3
-rw-r--r--source/blender/editors/mesh/editmesh_tools.c2
-rw-r--r--source/blender/editors/object/object_add.cc3
-rw-r--r--source/blender/editors/object/object_modifier.cc18
-rw-r--r--source/blender/editors/object/object_transform.cc2
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_brush.cc2
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc2
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_intern.hh8
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_selection.cc51
-rw-r--r--source/blender/editors/sculpt_paint/paint_stroke.c7
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c2
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_cloth.c15
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_intern.h2
-rw-r--r--source/blender/editors/space_outliner/outliner_tools.cc411
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc147
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh4
-rw-r--r--source/blender/editors/space_view3d/view3d_navigate_walk.c2
-rw-r--r--source/blender/editors/transform/transform.c2
-rw-r--r--source/blender/editors/transform/transform_generics.c2
-rw-r--r--source/blender/geometry/intern/resample_curves.cc24
-rw-r--r--source/blender/gpu/GPU_context.h4
-rw-r--r--source/blender/gpu/intern/gpu_shader_builder.cc1
-rw-r--r--source/blender/gpu/metal/mtl_backend.mm2
-rw-r--r--source/blender/gpu/metal/mtl_memory.mm2
-rw-r--r--source/blender/gpu/metal/mtl_query.mm4
-rw-r--r--source/blender/gpu/metal/mtl_uniform_buffer.hh2
-rw-r--r--source/blender/gpu/metal/mtl_uniform_buffer.mm2
-rw-r--r--source/blender/makesrna/intern/rna_image.c13
-rw-r--r--source/blender/makesrna/intern/rna_sequencer.c4
-rw-r--r--source/blender/makesrna/intern/rna_wm_api.c2
-rw-r--r--source/blender/windowmanager/intern/wm_event_query.c4
-rw-r--r--source/blender/windowmanager/intern/wm_event_system.cc15
-rw-r--r--source/blender/windowmanager/intern/wm_operators.c2
-rw-r--r--source/blender/windowmanager/wm_event_types.h29
101 files changed, 1708 insertions, 1072 deletions
diff --git a/source/blender/blenkernel/BKE_attribute.hh b/source/blender/blenkernel/BKE_attribute.hh
index 108993d91c0..1e61e477759 100644
--- a/source/blender/blenkernel/BKE_attribute.hh
+++ b/source/blender/blenkernel/BKE_attribute.hh
@@ -537,10 +537,7 @@ class MutableAttributeAccessor : public AttributeAccessor {
* Get a writable attribute or none if it does not exist.
* Make sure to call #finish after changes are done.
*/
- GAttributeWriter lookup_for_write(const AttributeIDRef &attribute_id)
- {
- return fn_->lookup_for_write(owner_, attribute_id);
- }
+ GAttributeWriter lookup_for_write(const AttributeIDRef &attribute_id);
/**
* Get a writable attribute or non if it does not exist.
@@ -677,8 +674,8 @@ struct AttributeTransferData {
* data-blocks of the same type.
*/
Vector<AttributeTransferData> retrieve_attributes_for_transfer(
- const bke::AttributeAccessor &src_attributes,
- bke::MutableAttributeAccessor &dst_attributes,
+ const bke::AttributeAccessor src_attributes,
+ bke::MutableAttributeAccessor dst_attributes,
eAttrDomainMask domain_mask,
const Set<std::string> &skip = {});
diff --git a/source/blender/blenkernel/intern/armature_deform.c b/source/blender/blenkernel/intern/armature_deform.c
index 0769049e9a9..8532c7d1c15 100644
--- a/source/blender/blenkernel/intern/armature_deform.c
+++ b/source/blender/blenkernel/intern/armature_deform.c
@@ -159,9 +159,9 @@ float distfactor_to_bone(
}
static float dist_bone_deform(
- bPoseChannel *pchan, float vec[3], DualQuat *dq, float mat[3][3], const float co[3])
+ const bPoseChannel *pchan, float vec[3], DualQuat *dq, float mat[3][3], const float co[3])
{
- Bone *bone = pchan->bone;
+ const Bone *bone = pchan->bone;
float fac, contrib = 0.0;
if (bone == NULL) {
@@ -188,7 +188,7 @@ static float dist_bone_deform(
return contrib;
}
-static void pchan_bone_deform(bPoseChannel *pchan,
+static void pchan_bone_deform(const bPoseChannel *pchan,
float weight,
float vec[3],
DualQuat *dq,
@@ -196,7 +196,7 @@ static void pchan_bone_deform(bPoseChannel *pchan,
const float co[3],
float *contrib)
{
- Bone *bone = pchan->bone;
+ const Bone *bone = pchan->bone;
if (!weight) {
return;
@@ -223,7 +223,6 @@ static void pchan_bone_deform(bPoseChannel *pchan,
typedef struct ArmatureUserdata {
const Object *ob_arm;
- const Object *ob_target;
const Mesh *me_target;
float (*vert_coords)[3];
float (*vert_deform_mats)[3][3];
@@ -264,7 +263,7 @@ static void armature_vert_task_with_dvert(const ArmatureUserdata *data,
const int armature_def_nr = data->armature_def_nr;
DualQuat sumdq, *dq = NULL;
- bPoseChannel *pchan;
+ const bPoseChannel *pchan;
float *co, dco[3];
float sumvec[3], summat[3][3];
float *vec = NULL, (*smat)[3] = NULL;
@@ -319,7 +318,7 @@ static void armature_vert_task_with_dvert(const ArmatureUserdata *data,
const uint index = dw->def_nr;
if (index < data->defbase_len && (pchan = data->pchan_from_defbase[index])) {
float weight = dw->weight;
- Bone *bone = pchan->bone;
+ const Bone *bone = pchan->bone;
deformed = 1;
@@ -434,7 +433,7 @@ static void armature_vert_task_editmesh(void *__restrict userdata,
{
const ArmatureUserdata *data = userdata;
BMVert *v = (BMVert *)iter;
- MDeformVert *dvert = BM_ELEM_CD_GET_VOID_P(v, data->bmesh.cd_dvert_offset);
+ const MDeformVert *dvert = BM_ELEM_CD_GET_VOID_P(v, data->bmesh.cd_dvert_offset);
armature_vert_task_with_dvert(data, BM_elem_index_get(v), dvert);
}
@@ -459,15 +458,14 @@ static void armature_deform_coords_impl(const Object *ob_arm,
BMEditMesh *em_target,
bGPDstroke *gps_target)
{
- bArmature *arm = ob_arm->data;
+ const bArmature *arm = ob_arm->data;
bPoseChannel **pchan_from_defbase = NULL;
const MDeformVert *dverts = NULL;
- bDeformGroup *dg;
const bool use_envelope = (deformflag & ARM_DEF_ENVELOPE) != 0;
const bool use_quaternion = (deformflag & ARM_DEF_QUATERNION) != 0;
const bool invert_vgroup = (deformflag & ARM_DEF_INVERT_VGROUP) != 0;
- int defbase_len = 0; /* safety for vertexgroup index overflow */
- int i, dverts_len = 0; /* safety for vertexgroup overflow */
+ int defbase_len = 0; /* safety for vertexgroup index overflow */
+ int dverts_len = 0; /* safety for vertexgroup overflow */
bool use_dverts = false;
int armature_def_nr = -1;
int cd_dvert_offset = -1;
@@ -492,7 +490,7 @@ static void armature_deform_coords_impl(const Object *ob_arm,
if (ob_target->type == OB_MESH) {
if (em_target == NULL) {
- Mesh *me = ob_target->data;
+ const Mesh *me = ob_target->data;
dverts = me->dvert;
if (dverts) {
dverts_len = me->totvert;
@@ -500,7 +498,7 @@ static void armature_deform_coords_impl(const Object *ob_arm,
}
}
else if (ob_target->type == OB_LATTICE) {
- Lattice *lt = ob_target->data;
+ const Lattice *lt = ob_target->data;
dverts = lt->dvert;
if (dverts) {
dverts_len = lt->pntsu * lt->pntsv * lt->pntsw;
@@ -534,7 +532,8 @@ static void armature_deform_coords_impl(const Object *ob_arm,
* - Check whether keeping this consistent across frames gives speedup.
*/
const ListBase *defbase = BKE_object_defgroup_list(ob_target);
- for (i = 0, dg = defbase->first; dg; i++, dg = dg->next) {
+ int i;
+ LISTBASE_FOREACH_INDEX (bDeformGroup *, dg, defbase, i) {
pchan_from_defbase[i] = BKE_pose_channel_find_name(ob_arm->pose, dg->name);
/* exclude non-deforming bones */
if (pchan_from_defbase[i]) {
@@ -549,7 +548,6 @@ static void armature_deform_coords_impl(const Object *ob_arm,
ArmatureUserdata data = {
.ob_arm = ob_arm,
- .ob_target = ob_target,
.me_target = me_target,
.vert_coords = vert_coords,
.vert_deform_mats = vert_deform_mats,
diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc
index a834b77d65e..19faddc5727 100644
--- a/source/blender/blenkernel/intern/attribute_access.cc
+++ b/source/blender/blenkernel/intern/attribute_access.cc
@@ -968,6 +968,49 @@ void MutableAttributeAccessor::remove_anonymous()
}
}
+/**
+ * Debug utility that checks whether the #finish function of an #AttributeWriter has been called.
+ */
+#ifdef DEBUG
+struct FinishCallChecker {
+ std::string name;
+ bool finish_called = false;
+ std::function<void()> real_finish_fn;
+
+ ~FinishCallChecker()
+ {
+ if (!this->finish_called) {
+ std::cerr << "Forgot to call `finish()` for '" << this->name << "'.\n";
+ }
+ }
+};
+#endif
+
+GAttributeWriter MutableAttributeAccessor::lookup_for_write(const AttributeIDRef &attribute_id)
+{
+ GAttributeWriter attribute = fn_->lookup_for_write(owner_, attribute_id);
+ /* Check that the #finish method is called in debug builds. */
+#ifdef DEBUG
+ if (attribute) {
+ auto checker = std::make_shared<FinishCallChecker>();
+ if (attribute_id.is_named()) {
+ checker->name = attribute_id.name();
+ }
+ else {
+ checker->name = BKE_anonymous_attribute_id_debug_name(&attribute_id.anonymous_id());
+ }
+ checker->real_finish_fn = attribute.tag_modified_fn;
+ attribute.tag_modified_fn = [checker]() {
+ if (checker->real_finish_fn) {
+ checker->real_finish_fn();
+ }
+ checker->finish_called = true;
+ };
+ }
+#endif
+ return attribute;
+}
+
GAttributeWriter MutableAttributeAccessor::lookup_or_add_for_write(
const AttributeIDRef &attribute_id,
const eAttrDomain domain,
@@ -1012,8 +1055,8 @@ GSpanAttributeWriter MutableAttributeAccessor::lookup_or_add_for_write_only_span
}
Vector<AttributeTransferData> retrieve_attributes_for_transfer(
- const bke::AttributeAccessor &src_attributes,
- bke::MutableAttributeAccessor &dst_attributes,
+ const bke::AttributeAccessor src_attributes,
+ bke::MutableAttributeAccessor dst_attributes,
const eAttrDomainMask domain_mask,
const Set<std::string> &skip)
{
diff --git a/source/blender/blenkernel/intern/curve_legacy_convert.cc b/source/blender/blenkernel/intern/curve_legacy_convert.cc
index ff5bbc32afe..5c62f292832 100644
--- a/source/blender/blenkernel/intern/curve_legacy_convert.cc
+++ b/source/blender/blenkernel/intern/curve_legacy_convert.cc
@@ -115,7 +115,7 @@ Curves *curve_legacy_to_curves(const Curve &curve_legacy, const ListBase &nurbs_
MutableSpan<float> tilts = curves.tilt_for_write();
auto create_poly = [&](IndexMask selection) {
- threading::parallel_for(selection.index_range(), 246, [&](IndexRange range) {
+ threading::parallel_for(selection.index_range(), 256, [&](IndexRange range) {
for (const int curve_i : selection.slice(range)) {
const Nurb &src_curve = *src_curves[curve_i];
const Span<BPoint> src_points(src_curve.bp, src_curve.pntsu);
@@ -142,7 +142,7 @@ Curves *curve_legacy_to_curves(const Curve &curve_legacy, const ListBase &nurbs_
MutableSpan<int8_t> handle_types_l = curves.handle_types_left_for_write();
MutableSpan<int8_t> handle_types_r = curves.handle_types_right_for_write();
- threading::parallel_for(selection.index_range(), 246, [&](IndexRange range) {
+ threading::parallel_for(selection.index_range(), 256, [&](IndexRange range) {
for (const int curve_i : selection.slice(range)) {
const Nurb &src_curve = *src_curves[curve_i];
const Span<BezTriple> src_points(src_curve.bezt, src_curve.pntsu);
@@ -165,12 +165,12 @@ Curves *curve_legacy_to_curves(const Curve &curve_legacy, const ListBase &nurbs_
};
auto create_nurbs = [&](IndexMask selection) {
- threading::parallel_for(selection.index_range(), 246, [&](IndexRange range) {
- MutableSpan<int> resolutions = curves.resolution_for_write();
- MutableSpan<float> nurbs_weights = curves.nurbs_weights_for_write();
- MutableSpan<int8_t> nurbs_orders = curves.nurbs_orders_for_write();
- MutableSpan<int8_t> nurbs_knots_modes = curves.nurbs_knots_modes_for_write();
-
+ MutableSpan<int> resolutions = curves.resolution_for_write();
+ MutableSpan<float> nurbs_weights = curves.nurbs_weights_for_write();
+ MutableSpan<int8_t> nurbs_orders = curves.nurbs_orders_for_write();
+ MutableSpan<int8_t> nurbs_knots_modes = curves.nurbs_knots_modes_for_write();
+
+ threading::parallel_for(selection.index_range(), 256, [&](IndexRange range) {
for (const int curve_i : selection.slice(range)) {
const Nurb &src_curve = *src_curves[curve_i];
const Span src_points(src_curve.bp, src_curve.pntsu);
diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc
index 7fc660cfbfc..f5c040a6fee 100644
--- a/source/blender/blenkernel/intern/curves_geometry.cc
+++ b/source/blender/blenkernel/intern/curves_geometry.cc
@@ -1070,21 +1070,6 @@ bool CurvesGeometry::bounds_min_max(float3 &min, float3 &max) const
return true;
}
-static void *ensure_customdata_layer(CustomData &custom_data,
- const StringRefNull name,
- const eCustomDataType data_type,
- const int tot_elements)
-{
- for (const int other_layer_i : IndexRange(custom_data.totlayer)) {
- CustomDataLayer &new_layer = custom_data.layers[other_layer_i];
- if (name == StringRef(new_layer.name)) {
- return new_layer.data;
- }
- }
- return CustomData_add_layer_named(
- &custom_data, data_type, CD_DEFAULT, nullptr, tot_elements, name.c_str());
-}
-
static void copy_between_buffers(const CPPType &type,
const void *src_buffer,
void *dst_buffer,
@@ -1180,56 +1165,37 @@ static CurvesGeometry copy_with_removed_points(const CurvesGeometry &curves,
[&]() { new_curves.offsets_for_write().copy_from(new_curve_offsets); },
/* Copy over point attributes. */
[&]() {
- const CustomData &old_point_data = curves.point_data;
- CustomData &new_point_data = new_curves.point_data;
- for (const int layer_i : IndexRange(old_point_data.totlayer)) {
- const CustomDataLayer &old_layer = old_point_data.layers[layer_i];
- const eCustomDataType data_type = static_cast<eCustomDataType>(old_layer.type);
- const CPPType &type = *bke::custom_data_type_to_cpp_type(data_type);
-
- void *dst_buffer = ensure_customdata_layer(
- new_point_data, old_layer.name, data_type, new_point_count);
-
- threading::parallel_for(
- copy_point_ranges.index_range(), 128, [&](const IndexRange ranges_range) {
- for (const int range_i : ranges_range) {
- const IndexRange src_range = copy_point_ranges[range_i];
- copy_between_buffers(type,
- old_layer.data,
- dst_buffer,
- src_range,
- {copy_point_range_dst_offsets[range_i], src_range.size()});
- }
- });
+ for (auto &attribute : bke::retrieve_attributes_for_transfer(
+ curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_POINT)) {
+ threading::parallel_for(copy_point_ranges.index_range(), 128, [&](IndexRange range) {
+ for (const int range_i : range) {
+ const IndexRange src_range = copy_point_ranges[range_i];
+ copy_between_buffers(attribute.src.type(),
+ attribute.src.data(),
+ attribute.dst.span.data(),
+ src_range,
+ {copy_point_range_dst_offsets[range_i], src_range.size()});
+ }
+ });
+ attribute.dst.finish();
}
},
/* Copy over curve attributes.
* In some cases points are just dissolved, so the the number of
* curves will be the same. That could be optimized in the future. */
[&]() {
- const CustomData &old_curve_data = curves.curve_data;
- CustomData &new_curve_data = new_curves.curve_data;
- for (const int layer_i : IndexRange(old_curve_data.totlayer)) {
- const CustomDataLayer &old_layer = old_curve_data.layers[layer_i];
- const eCustomDataType data_type = static_cast<eCustomDataType>(old_layer.type);
- const CPPType &type = *bke::custom_data_type_to_cpp_type(data_type);
-
- void *dst_buffer = ensure_customdata_layer(
- new_curve_data, old_layer.name, data_type, new_curve_count);
-
+ for (auto &attribute : bke::retrieve_attributes_for_transfer(
+ curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_CURVE)) {
if (new_curves.curves_num() == curves.curves_num()) {
- type.copy_construct_n(old_layer.data, dst_buffer, new_curves.curves_num());
+ attribute.dst.span.copy_from(attribute.src);
}
else {
- copy_with_map({type, old_layer.data, curves.curves_num()},
- new_curve_orig_indices,
- {type, dst_buffer, new_curves.curves_num()});
+ copy_with_map(attribute.src, new_curve_orig_indices, attribute.dst.span);
}
+ attribute.dst.finish();
}
});
- new_curves.update_curve_types();
-
return new_curves;
}
@@ -1296,55 +1262,37 @@ static CurvesGeometry copy_with_removed_curves(const CurvesGeometry &curves,
},
/* Copy over point attributes. */
[&]() {
- const CustomData &old_point_data = curves.point_data;
- CustomData &new_point_data = new_curves.point_data;
- for (const int layer_i : IndexRange(old_point_data.totlayer)) {
- const CustomDataLayer &old_layer = old_point_data.layers[layer_i];
- const eCustomDataType data_type = static_cast<eCustomDataType>(old_layer.type);
- const CPPType &type = *bke::custom_data_type_to_cpp_type(data_type);
-
- void *dst_buffer = ensure_customdata_layer(
- new_point_data, old_layer.name, data_type, new_tot_points);
-
- threading::parallel_for(
- old_curve_ranges.index_range(), 128, [&](const IndexRange ranges_range) {
- for (const int range_i : ranges_range) {
- copy_between_buffers(type,
- old_layer.data,
- dst_buffer,
- old_point_ranges[range_i],
- new_point_ranges[range_i]);
- }
- });
+ for (auto &attribute : bke::retrieve_attributes_for_transfer(
+ curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_POINT)) {
+ threading::parallel_for(old_curve_ranges.index_range(), 128, [&](IndexRange range) {
+ for (const int range_i : range) {
+ copy_between_buffers(attribute.src.type(),
+ attribute.src.data(),
+ attribute.dst.span.data(),
+ old_point_ranges[range_i],
+ new_point_ranges[range_i]);
+ }
+ });
+ attribute.dst.finish();
}
},
/* Copy over curve attributes. */
[&]() {
- const CustomData &old_curve_data = curves.curve_data;
- CustomData &new_curve_data = new_curves.curve_data;
- for (const int layer_i : IndexRange(old_curve_data.totlayer)) {
- const CustomDataLayer &old_layer = old_curve_data.layers[layer_i];
- const eCustomDataType data_type = static_cast<eCustomDataType>(old_layer.type);
- const CPPType &type = *bke::custom_data_type_to_cpp_type(data_type);
-
- void *dst_buffer = ensure_customdata_layer(
- new_curve_data, old_layer.name, data_type, new_tot_curves);
-
- threading::parallel_for(
- old_curve_ranges.index_range(), 128, [&](const IndexRange ranges_range) {
- for (const int range_i : ranges_range) {
- copy_between_buffers(type,
- old_layer.data,
- dst_buffer,
- old_curve_ranges[range_i],
- new_curve_ranges[range_i]);
- }
- });
+ for (auto &attribute : bke::retrieve_attributes_for_transfer(
+ curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_CURVE)) {
+ threading::parallel_for(old_curve_ranges.index_range(), 128, [&](IndexRange range) {
+ for (const int range_i : range) {
+ copy_between_buffers(attribute.src.type(),
+ attribute.src.data(),
+ attribute.dst.span.data(),
+ old_curve_ranges[range_i],
+ new_curve_ranges[range_i]);
+ }
+ });
+ attribute.dst.finish();
}
});
- new_curves.update_curve_types();
-
return new_curves;
}
diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc
index 8f1a8669f8e..8deadaa3e56 100644
--- a/source/blender/blenkernel/intern/customdata.cc
+++ b/source/blender/blenkernel/intern/customdata.cc
@@ -4491,9 +4491,52 @@ bool CustomData_verify_versions(CustomData *data, int index)
return keeplayer;
}
+static bool CustomData_layer_ensure_data_exists(CustomDataLayer *layer, size_t count)
+{
+ BLI_assert(layer);
+ const LayerTypeInfo *typeInfo = layerType_getInfo(layer->type);
+ BLI_assert(typeInfo);
+
+ if (layer->data || count == 0) {
+ return false;
+ }
+
+ switch (layer->type) {
+ /* When more instances of corrupt files are found, add them here. */
+ case CD_PROP_BOOL: /* See T84935. */
+ case CD_MLOOPUV: /* See T90620. */
+ layer->data = MEM_calloc_arrayN(count, typeInfo->size, layerType_getName(layer->type));
+ BLI_assert(layer->data);
+ if (typeInfo->set_default) {
+ typeInfo->set_default(layer->data, count);
+ }
+ return true;
+ break;
+
+ case CD_MTEXPOLY:
+ /* TODO: Investigate multiple test failures on cycles, e.g. cycles_shadow_catcher_cpu. */
+ break;
+
+ default:
+ /* Log an error so we can collect instances of bad files. */
+ CLOG_WARN(&LOG, "CustomDataLayer->data is NULL for type %d.", layer->type);
+ break;
+ }
+ return false;
+}
+
bool CustomData_layer_validate(CustomDataLayer *layer, const uint totitems, const bool do_fixes)
{
+ BLI_assert(layer);
const LayerTypeInfo *typeInfo = layerType_getInfo(layer->type);
+ BLI_assert(typeInfo);
+
+ if (do_fixes) {
+ CustomData_layer_ensure_data_exists(layer, totitems);
+ }
+
+ BLI_assert((totitems == 0) || layer->data);
+ BLI_assert(MEM_allocN_len(layer->data) >= totitems * typeInfo->size);
if (typeInfo->validate != nullptr) {
return typeInfo->validate(layer->data, totitems, do_fixes);
@@ -5254,16 +5297,15 @@ void CustomData_blend_read(BlendDataReader *reader, CustomData *data, int count)
if (CustomData_verify_versions(data, i)) {
BLO_read_data_address(reader, &layer->data);
- if (layer->data == nullptr && count > 0 && layer->type == CD_PROP_BOOL) {
- /* Usually this should never happen, except when a custom data layer has not been written
- * to a file correctly. */
- CLOG_WARN(&LOG, "Reallocating custom data layer that was not saved correctly.");
- const LayerTypeInfo *info = layerType_getInfo(layer->type);
- layer->data = MEM_calloc_arrayN((size_t)count, info->size, layerType_getName(layer->type));
- if (info->set_default) {
- info->set_default(layer->data, count);
- }
+ if (CustomData_layer_ensure_data_exists(layer, count)) {
+ /* Under normal operations, this shouldn't happen, but...
+ * For a CD_PROP_BOOL example, see T84935.
+ * For a CD_MLOOPUV example, see T90620. */
+ CLOG_WARN(&LOG,
+ "Allocated custom data layer that was not saved correctly for layer->type = %d.",
+ layer->type);
}
+
if (layer->type == CD_MDISPS) {
blend_read_mdisps(
reader, count, static_cast<MDisps *>(layer->data), layer->flag & CD_FLAG_EXTERNAL);
diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c
index 8e95bf18c6b..0fc09803088 100644
--- a/source/blender/blenkernel/intern/fluid.c
+++ b/source/blender/blenkernel/intern/fluid.c
@@ -1038,8 +1038,6 @@ static void obstacles_from_mesh(Object *coll_ob,
/* Transform mesh vertices to domain grid space for fast lookups.
* This is valid because the mesh is copied above. */
- BKE_mesh_vertex_normals_ensure(me);
- float(*vert_normals)[3] = BKE_mesh_vertex_normals_for_write(me);
for (i = 0; i < numverts; i++) {
float co[3];
@@ -1047,11 +1045,6 @@ static void obstacles_from_mesh(Object *coll_ob,
mul_m4_v3(coll_ob->obmat, mvert[i].co);
manta_pos_to_cell(fds, mvert[i].co);
- /* Vertex normal. */
- mul_mat3_m4_v3(coll_ob->obmat, vert_normals[i]);
- mul_mat3_m4_v3(fds->imat, vert_normals[i]);
- normalize_v3(vert_normals[i]);
-
/* Vertex velocity. */
add_v3fl_v3fl_v3i(co, mvert[i].co, fds->shift);
if (has_velocity) {
diff --git a/source/blender/blenkernel/intern/layer.c b/source/blender/blenkernel/intern/layer.c
index 534ff7f1fbc..ac582ff69ca 100644
--- a/source/blender/blenkernel/intern/layer.c
+++ b/source/blender/blenkernel/intern/layer.c
@@ -1377,12 +1377,12 @@ void BKE_main_collection_sync_remap(const Main *bmain)
if (view_layer->object_bases_hash) {
BLI_ghash_free(view_layer->object_bases_hash, NULL, NULL);
view_layer->object_bases_hash = NULL;
-
- /* Directly re-create the mapping here, so that we can also deal with duplicates in
- * `view_layer->object_bases` list of bases properly. This is the only place where such
- * duplicates should be fixed, and not considered as a critical error. */
- view_layer_bases_hash_create(view_layer, true);
}
+
+ /* Directly re-create the mapping here, so that we can also deal with duplicates in
+ * `view_layer->object_bases` list of bases properly. This is the only place where such
+ * duplicates should be fixed, and not considered as a critical error. */
+ view_layer_bases_hash_create(view_layer, true);
}
BKE_collection_object_cache_free(scene->master_collection);
diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c
index fee4cae2701..dd58c9cc4fe 100644
--- a/source/blender/blenkernel/intern/library.c
+++ b/source/blender/blenkernel/intern/library.c
@@ -73,18 +73,10 @@ static void library_foreach_path(ID *id, BPathForeachPathData *bpath_data)
}
}
-static void library_blend_write(struct BlendWriter *UNUSED(writer),
- ID *id,
- const void *UNUSED(id_address))
-{
- Library *lib = (Library *)id;
- library_runtime_reset(lib);
-}
-
static void library_blend_read_data(struct BlendDataReader *UNUSED(reader), ID *id)
{
Library *lib = (Library *)id;
- library_runtime_reset(lib);
+ lib->runtime.name_map = NULL;
}
IDTypeInfo IDType_ID_LI = {
@@ -107,7 +99,7 @@ IDTypeInfo IDType_ID_LI = {
.foreach_path = library_foreach_path,
.owner_get = NULL,
- .blend_write = library_blend_write,
+ .blend_write = NULL,
.blend_read_data = library_blend_read_data,
.blend_read_lib = NULL,
.blend_read_expand = NULL,
diff --git a/source/blender/blenlib/BLI_length_parameterize.hh b/source/blender/blenlib/BLI_length_parameterize.hh
index c44bb94f65d..1b494c021a3 100644
--- a/source/blender/blenlib/BLI_length_parameterize.hh
+++ b/source/blender/blenlib/BLI_length_parameterize.hh
@@ -9,7 +9,6 @@
#include "BLI_math_base.hh"
#include "BLI_math_color.hh"
#include "BLI_math_vector.hh"
-#include "BLI_vector.hh"
namespace blender::length_parameterize {
@@ -42,10 +41,10 @@ void accumulate_lengths(const Span<T> values, const bool cyclic, MutableSpan<flo
}
template<typename T>
-inline void linear_interpolation(const Span<T> src,
- const Span<int> indices,
- const Span<float> factors,
- MutableSpan<T> dst)
+inline void interpolate(const Span<T> src,
+ const Span<int> indices,
+ const Span<float> factors,
+ MutableSpan<T> dst)
{
BLI_assert(indices.size() == factors.size());
BLI_assert(indices.size() == dst.size());
@@ -75,6 +74,7 @@ struct SampleSegmentHint {
/**
* \param accumulated_segment_lengths: Lengths of individual segments added up.
+ * Each value describes the total length at the end of the segment following a point.
* \param sample_length: The position to sample at.
* \param r_segment_index: Returns the index of the segment that #sample_length is in.
* \param r_factor: Returns the position within the segment.
@@ -82,7 +82,7 @@ struct SampleSegmentHint {
* \note #sample_length must not be outside of any segment.
*/
inline void sample_at_length(const Span<float> accumulated_segment_lengths,
- float sample_length,
+ const float sample_length,
int &r_segment_index,
float &r_factor,
SampleSegmentHint *hint = nullptr)
@@ -117,7 +117,7 @@ inline void sample_at_length(const Span<float> accumulated_segment_lengths,
const float segment_start = prev_point_index == 0 ? 0.0f : lengths[prev_point_index - 1];
const float segment_end = lengths[prev_point_index];
const float segment_length = segment_end - segment_start;
- const float segment_length_inv = safe_divide(1.0f, segment_length);
+ const float segment_length_inv = math::safe_divide(1.0f, segment_length);
const float length_in_segment = sample_length - segment_start;
const float factor = length_in_segment * segment_length_inv;
diff --git a/source/blender/blenlib/intern/generic_virtual_array.cc b/source/blender/blenlib/intern/generic_virtual_array.cc
index 1e548d006b2..f66b1e14fc6 100644
--- a/source/blender/blenlib/intern/generic_virtual_array.cc
+++ b/source/blender/blenlib/intern/generic_virtual_array.cc
@@ -404,7 +404,7 @@ GMutableVArraySpan::~GMutableVArraySpan()
if (varray_) {
if (show_not_saved_warning_) {
if (!save_has_been_called_) {
- std::cout << "Warning: Call `apply()` to make sure that changes persist in all cases.\n";
+ std::cout << "Warning: Call `save()` to make sure that changes persist in all cases.\n";
}
}
}
diff --git a/source/blender/blenlib/tests/BLI_length_parameterize_test.cc b/source/blender/blenlib/tests/BLI_length_parameterize_test.cc
index 11f4997f563..3b41a7aed0c 100644
--- a/source/blender/blenlib/tests/BLI_length_parameterize_test.cc
+++ b/source/blender/blenlib/tests/BLI_length_parameterize_test.cc
@@ -32,7 +32,7 @@ TEST(length_parameterize, FloatSimple)
Array<float> factors(4);
sample_uniform(lengths, true, indices, factors);
Array<float> results(4);
- linear_interpolation<float>(values, indices, factors, results);
+ interpolate<float>(values, indices, factors, results);
Array<float> expected({
0.0f,
1.33333f,
@@ -54,7 +54,7 @@ TEST(length_parameterize, Float)
Array<float> factors(20);
sample_uniform(lengths, true, indices, factors);
Array<float> results(20);
- linear_interpolation<float>(values, indices, factors, results);
+ interpolate<float>(values, indices, factors, results);
Array<float> expected({
1.0f, 1.47368f, 1.94737f, 2.42105f, 2.89474f, 3.36842f, 3.84211f,
4.31579f, 4.78947f, 5.26316f, 5.73684f, 6.21053f, 6.68421f, 7.1579f,
@@ -75,7 +75,7 @@ TEST(length_parameterize, Float2)
Array<float> factors(12);
sample_uniform(lengths, true, indices, factors);
Array<float2> results(12);
- linear_interpolation<float2>(values, indices, factors, results);
+ interpolate<float2>(values, indices, factors, results);
Array<float2> expected({
{0.0f, 0.0f},
{0.272727f, 0.0f},
@@ -105,7 +105,7 @@ TEST(length_parameterize, Float2Cyclic)
Array<float> factors(12);
sample_uniform(lengths, false, indices, factors);
Array<float2> results(12);
- linear_interpolation<float2>(values, indices, factors, results);
+ interpolate<float2>(values, indices, factors, results);
Array<float2> expected({
{0.0f, 0.0f},
{0.333333f, 0.0f},
@@ -135,7 +135,7 @@ TEST(length_parameterize, LineMany)
Array<float> factors(5007);
sample_uniform(lengths, true, indices, factors);
Array<float> results(5007);
- linear_interpolation<float>(values, indices, factors, results);
+ interpolate<float>(values, indices, factors, results);
Array<float> expected({
1.9962f, 1.9964f, 1.9966f, 1.9968f, 1.997f, 1.9972f, 1.9974f, 1.9976f, 1.9978f, 1.998f,
1.9982f, 1.9984f, 1.9986f, 1.9988f, 1.999f, 1.9992f, 1.9994f, 1.9996f, 1.9998f, 2.0f,
@@ -154,7 +154,7 @@ TEST(length_parameterize, CyclicMany)
Array<float> factors(5007);
sample_uniform(lengths, false, indices, factors);
Array<float2> results(5007);
- linear_interpolation<float2>(values, indices, factors, results);
+ interpolate<float2>(values, indices, factors, results);
Array<float2> expected({
{0, 0.0159776}, {0, 0.0151787}, {0, 0.0143797}, {0, 0.013581}, {0, 0.0127821},
{0, 0.0119832}, {0, 0.0111842}, {0, 0.0103855}, {0, 0.00958657}, {0, 0.00878763},
@@ -178,7 +178,7 @@ TEST(length_parameterize, InterpolateColor)
Array<float> factors(10);
sample_uniform(lengths, false, indices, factors);
Array<ColorGeometry4f> results(10);
- linear_interpolation<ColorGeometry4f>(colors, indices, factors, results);
+ interpolate<ColorGeometry4f>(colors, indices, factors, results);
Array<ColorGeometry4f> expected({
{0, 0, 0, 1},
{0.4, 0, 0, 1},
@@ -209,8 +209,7 @@ TEST(length_parameterize, ArbitraryFloatSimple)
Array<float> factors(4);
sample_at_lengths(lengths, sample_lengths, indices, factors);
Array<float> results(4);
- linear_interpolation<float>(values, indices, factors, results);
- results.as_span().print_as_lines("results");
+ interpolate<float>(values, indices, factors, results);
Array<float> expected({
0.5f,
1.5f,
@@ -233,8 +232,7 @@ TEST(length_parameterize, ArbitraryFloat2)
Array<float> factors(12);
sample_at_lengths(lengths, sample_lengths, indices, factors);
Array<float2> results(12);
- linear_interpolation<float2>(values, indices, factors, results);
- results.as_span().print_as_lines("results");
+ interpolate<float2>(values, indices, factors, results);
Array<float2> expected({
{0.5f, 0.0f},
{1.0f, 0.5f},
diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c
index 68171f26a66..1ec056a9f50 100644
--- a/source/blender/blenloader/intern/writefile.c
+++ b/source/blender/blenloader/intern/writefile.c
@@ -970,10 +970,15 @@ static void write_libraries(WriteData *wd, Main *main)
if (found_one) {
/* Not overridable. */
+ void *runtime_name_data = main->curlib->runtime.name_map;
+ main->curlib->runtime.name_map = NULL;
+
BlendWriter writer = {wd};
writestruct(wd, ID_LI, Library, 1, main->curlib);
BKE_id_blend_write(&writer, &main->curlib->id);
+ main->curlib->runtime.name_map = runtime_name_data;
+
if (main->curlib->packedfile) {
BKE_packedfile_blend_write(&writer, main->curlib->packedfile);
if (wd->use_memfile == false) {
diff --git a/source/blender/depsgraph/CMakeLists.txt b/source/blender/depsgraph/CMakeLists.txt
index 3d539018cef..e799c5ce32a 100644
--- a/source/blender/depsgraph/CMakeLists.txt
+++ b/source/blender/depsgraph/CMakeLists.txt
@@ -67,6 +67,8 @@ set(SRC
intern/eval/deg_eval_runtime_backup_sound.cc
intern/eval/deg_eval_runtime_backup_volume.cc
intern/eval/deg_eval_stats.cc
+ intern/eval/deg_eval_visibility.cc
+ intern/eval/deg_eval_visibility.h
intern/node/deg_node.cc
intern/node/deg_node_component.cc
intern/node/deg_node_factory.cc
diff --git a/source/blender/depsgraph/intern/builder/deg_builder.cc b/source/blender/depsgraph/intern/builder/deg_builder.cc
index 1fec1fb3219..5353f71685c 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder.cc
@@ -29,6 +29,7 @@
#include "intern/depsgraph_tag.h"
#include "intern/depsgraph_type.h"
#include "intern/eval/deg_eval_copy_on_write.h"
+#include "intern/eval/deg_eval_visibility.h"
#include "intern/node/deg_node.h"
#include "intern/node/deg_node_component.h"
#include "intern/node/deg_node_id.h"
@@ -71,10 +72,15 @@ bool DepsgraphBuilder::need_pull_base_into_graph(const Base *base)
if (base->flag & base_flag) {
return true;
}
+
/* More involved check: since we don't support dynamic changes in dependency graph topology and
* all visible objects are to be part of dependency graph, we pull all objects which has animated
* visibility. */
- const Object *object = base->object;
+ return is_object_visibility_animated(base->object);
+}
+
+bool DepsgraphBuilder::is_object_visibility_animated(const Object *object)
+{
AnimatedPropertyID property_id;
if (graph_->mode == DAG_EVAL_VIEWPORT) {
property_id = AnimatedPropertyID(&object->id, &RNA_Object, "hide_viewport");
@@ -127,84 +133,9 @@ bool DepsgraphBuilder::check_pchan_has_bbone_segments(const Object *object, cons
/** \name Builder Finalizer.
* \{ */
-namespace {
-
-void deg_graph_build_flush_visibility(Depsgraph *graph)
-{
- enum {
- DEG_NODE_VISITED = (1 << 0),
- };
-
- BLI_Stack *stack = BLI_stack_new(sizeof(OperationNode *), "DEG flush layers stack");
- for (IDNode *id_node : graph->id_nodes) {
- for (ComponentNode *comp_node : id_node->components.values()) {
- comp_node->affects_directly_visible |= id_node->is_directly_visible;
- }
- }
-
- for (OperationNode *op_node : graph->operations) {
- op_node->custom_flags = 0;
- op_node->num_links_pending = 0;
- for (Relation *rel : op_node->outlinks) {
- if ((rel->from->type == NodeType::OPERATION) && (rel->flag & RELATION_FLAG_CYCLIC) == 0) {
- ++op_node->num_links_pending;
- }
- }
- if (op_node->num_links_pending == 0) {
- BLI_stack_push(stack, &op_node);
- op_node->custom_flags |= DEG_NODE_VISITED;
- }
- }
-
- while (!BLI_stack_is_empty(stack)) {
- OperationNode *op_node;
- BLI_stack_pop(stack, &op_node);
- /* Flush layers to parents. */
- for (Relation *rel : op_node->inlinks) {
- if (rel->from->type == NodeType::OPERATION) {
- OperationNode *op_from = (OperationNode *)rel->from;
- ComponentNode *comp_from = op_from->owner;
- const bool target_directly_visible = op_node->owner->affects_directly_visible;
-
- /* Visibility component forces all components of the current ID to be considered as
- * affecting directly visible. */
- if (comp_from->type == NodeType::VISIBILITY) {
- if (target_directly_visible) {
- IDNode *id_node_from = comp_from->owner;
- for (ComponentNode *comp_node : id_node_from->components.values()) {
- comp_node->affects_directly_visible |= target_directly_visible;
- }
- }
- }
- else {
- comp_from->affects_directly_visible |= target_directly_visible;
- }
- }
- }
- /* Schedule parent nodes. */
- for (Relation *rel : op_node->inlinks) {
- if (rel->from->type == NodeType::OPERATION) {
- OperationNode *op_from = (OperationNode *)rel->from;
- if ((rel->flag & RELATION_FLAG_CYCLIC) == 0) {
- BLI_assert(op_from->num_links_pending > 0);
- --op_from->num_links_pending;
- }
- if ((op_from->num_links_pending == 0) && (op_from->custom_flags & DEG_NODE_VISITED) == 0) {
- BLI_stack_push(stack, &op_from);
- op_from->custom_flags |= DEG_NODE_VISITED;
- }
- }
- }
- }
- BLI_stack_free(stack);
-}
-
-} // namespace
-
void deg_graph_build_finalize(Main *bmain, Depsgraph *graph)
{
- /* Make sure dependencies of visible ID data-blocks are visible. */
- deg_graph_build_flush_visibility(graph);
+ deg_graph_flush_visibility_flags(graph);
deg_graph_remove_unused_noops(graph);
/* Re-tag IDs for update if it was tagged before the relations
diff --git a/source/blender/depsgraph/intern/builder/deg_builder.h b/source/blender/depsgraph/intern/builder/deg_builder.h
index 950ebfb35ba..c44e5fd5f4d 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder.h
@@ -24,6 +24,8 @@ class DepsgraphBuilder {
virtual bool need_pull_base_into_graph(const Base *base);
+ virtual bool is_object_visibility_animated(const Object *object);
+
virtual bool check_pchan_has_bbone(const Object *object, const bPoseChannel *pchan);
virtual bool check_pchan_has_bbone_segments(const Object *object, const bPoseChannel *pchan);
virtual bool check_pchan_has_bbone_segments(const Object *object, const char *bone_name);
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
index 473dda2290c..4cbb2ce7060 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
@@ -26,6 +26,7 @@
#include "DNA_collection_types.h"
#include "DNA_constraint_types.h"
#include "DNA_curve_types.h"
+#include "DNA_curves_types.h"
#include "DNA_effect_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_key_types.h"
@@ -107,6 +108,7 @@
#include "intern/depsgraph_tag.h"
#include "intern/depsgraph_type.h"
#include "intern/eval/deg_eval_copy_on_write.h"
+#include "intern/eval/deg_eval_visibility.h"
#include "intern/node/deg_node.h"
#include "intern/node/deg_node_component.h"
#include "intern/node/deg_node_id.h"
@@ -179,11 +181,27 @@ IDNode *DepsgraphNodeBuilder::add_id_node(ID *id)
}
ComponentNode *visibility_component = id_node->add_component(NodeType::VISIBILITY);
- OperationNode *visibility_operation = visibility_component->add_operation(
- nullptr, OperationCode::VISIBILITY);
+ OperationNode *visibility_operation;
+
+ /* Optimization: currently only objects need a special visibility evaluation. For the rest ID
+ * types keep the node as a NO-OP so that relations can still be routed, but without penalty
+ * during the graph evaluation. */
+ if (id_type == ID_OB) {
+ visibility_operation = visibility_component->add_operation(
+ [id_node](::Depsgraph *depsgraph) {
+ deg_evaluate_object_node_visibility(depsgraph, id_node);
+ },
+ OperationCode::VISIBILITY);
+ }
+ else {
+ visibility_operation = visibility_component->add_operation(nullptr,
+ OperationCode::VISIBILITY);
+ }
+
/* Pin the node so that it and its relations are preserved by the unused nodes/relations
* deletion. This is mainly to make it easier to debug visibility. */
- visibility_operation->flag |= OperationFlag::DEPSOP_FLAG_PINNED;
+ visibility_operation->flag |= (OperationFlag::DEPSOP_FLAG_PINNED |
+ OperationFlag::DEPSOP_FLAG_AFFECTS_VISIBILITY);
graph_->operations.append(visibility_operation);
}
return id_node;
@@ -652,7 +670,7 @@ void DepsgraphNodeBuilder::build_collection(LayerCollection *from_layer_collecti
IDNode *id_node;
if (built_map_.checkIsBuiltAndTag(collection)) {
id_node = find_id_node(&collection->id);
- if (is_collection_visible && id_node->is_directly_visible == false &&
+ if (is_collection_visible && id_node->is_visible_on_build == false &&
id_node->is_collection_fully_expanded == true) {
/* Collection became visible, make sure nested collections and
* objects are poked with the new visibility flag, since they
@@ -669,7 +687,7 @@ void DepsgraphNodeBuilder::build_collection(LayerCollection *from_layer_collecti
else {
/* Collection itself. */
id_node = add_id_node(&collection->id);
- id_node->is_directly_visible = is_collection_visible;
+ id_node->is_visible_on_build = is_collection_visible;
build_idproperties(collection->id.properties);
add_operation_node(&collection->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_DONE);
@@ -716,7 +734,7 @@ void DepsgraphNodeBuilder::build_object(int base_index,
build_object_flags(base_index, object, linked_state);
}
id_node->linked_state = max(id_node->linked_state, linked_state);
- id_node->is_directly_visible |= is_visible;
+ id_node->is_visible_on_build |= is_visible;
id_node->has_base |= (base_index != -1);
/* There is no relation path which will connect current object with all the ones which come
@@ -735,10 +753,10 @@ void DepsgraphNodeBuilder::build_object(int base_index,
* Probably need to assign that to something non-nullptr, but then the logic here will still be
* somewhat weird. */
if (scene_ != nullptr && object == scene_->camera) {
- id_node->is_directly_visible = true;
+ id_node->is_visible_on_build = true;
}
else {
- id_node->is_directly_visible = is_visible;
+ id_node->is_visible_on_build = is_visible;
}
id_node->has_base |= (base_index != -1);
/* Various flags, flushing from bases/collections. */
@@ -1546,8 +1564,14 @@ void DepsgraphNodeBuilder::build_object_data_geometry_datablock(ID *obdata)
break;
}
case ID_CV: {
+ Curves *curves_id = reinterpret_cast<Curves *>(obdata);
+
op_node = add_operation_node(obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL);
op_node->set_as_entry();
+
+ if (curves_id->surface != nullptr) {
+ build_object(-1, curves_id->surface, DEG_ID_LINKED_INDIRECTLY, false);
+ }
break;
}
case ID_PT: {
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc
index 32ec94211a0..5af9e7d4fe9 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc
@@ -92,14 +92,20 @@ void DepsgraphNodeBuilder::build_view_layer(Scene *scene,
int base_index = 0;
LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
/* object itself */
- if (need_pull_base_into_graph(base)) {
- /* NOTE: We consider object visible even if it's currently
- * restricted by the base/restriction flags. Otherwise its drivers
- * will never be evaluated.
- *
- * TODO(sergey): Need to go more granular on visibility checks. */
- build_object(base_index, base->object, linked_state, true);
- base_index++;
+ if (!need_pull_base_into_graph(base)) {
+ continue;
+ }
+
+ /* NOTE: We consider object visible even if it's currently
+ * restricted by the base/restriction flags. Otherwise its drivers
+ * will never be evaluated.
+ *
+ * TODO(sergey): Need to go more granular on visibility checks. */
+ build_object(base_index, base->object, linked_state, true);
+ base_index++;
+
+ if (!graph_->has_animated_visibility) {
+ graph_->has_animated_visibility |= is_object_visibility_animated(base->object);
}
}
build_layer_collections(&view_layer->layer_collections);
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
index 4fe8a626af5..f36d94c7563 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
@@ -27,6 +27,7 @@
#include "DNA_collection_types.h"
#include "DNA_constraint_types.h"
#include "DNA_curve_types.h"
+#include "DNA_curves_types.h"
#include "DNA_effect_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_key_types.h"
@@ -810,6 +811,13 @@ void DepsgraphRelationBuilder::build_object(Object *object)
/* Parameters. */
build_parameters(&object->id);
+
+ /* Visibility.
+ * Evaluate visibility node after the object's base_flags has been updated to the current state
+ * of collections restrict and object's restrict flags. */
+ const ComponentKey object_from_layer_entry_key(&object->id, NodeType::OBJECT_FROM_LAYER);
+ const ComponentKey visibility_key(&object->id, NodeType::VISIBILITY);
+ add_relation(object_from_layer_entry_key, visibility_key, "Object Visibility");
}
/* NOTE: Implies that the object has base in the current view layer. */
@@ -2190,8 +2198,7 @@ void DepsgraphRelationBuilder::build_object_data_geometry(Object *object)
* data mask to be used. We add relation here to ensure object is never
* evaluated prior to Scene's CoW is ready. */
OperationKey scene_key(&scene_->id, NodeType::PARAMETERS, OperationCode::SCENE_EVAL);
- Relation *rel = add_relation(scene_key, obdata_ubereval_key, "CoW Relation");
- rel->flag |= RELATION_FLAG_NO_FLUSH;
+ add_relation(scene_key, obdata_ubereval_key, "CoW Relation", RELATION_FLAG_NO_FLUSH);
/* Modifiers */
if (object->modifiers.first != nullptr) {
ModifierUpdateDepsgraphContext ctx = {};
@@ -2419,8 +2426,16 @@ void DepsgraphRelationBuilder::build_object_data_geometry_datablock(ID *obdata)
}
break;
}
- case ID_CV:
+ case ID_CV: {
+ Curves *curves_id = reinterpret_cast<Curves *>(obdata);
+ if (curves_id->surface != nullptr) {
+ build_object(curves_id->surface);
+
+ /* The relations between the surface and the curves are handled as part of the modifier
+ * stack building. */
+ }
break;
+ }
case ID_PT:
break;
case ID_VO: {
diff --git a/source/blender/depsgraph/intern/depsgraph.cc b/source/blender/depsgraph/intern/depsgraph.cc
index d460a68747d..316d0b615c6 100644
--- a/source/blender/depsgraph/intern/depsgraph.cc
+++ b/source/blender/depsgraph/intern/depsgraph.cc
@@ -45,7 +45,9 @@ namespace blender::deg {
Depsgraph::Depsgraph(Main *bmain, Scene *scene, ViewLayer *view_layer, eEvaluationMode mode)
: time_source(nullptr),
+ has_animated_visibility(false),
need_update_relations(true),
+ need_update_nodes_visibility(true),
need_tag_id_on_graph_visibility_update(true),
need_tag_id_on_graph_visibility_time_update(false),
bmain(bmain),
diff --git a/source/blender/depsgraph/intern/depsgraph.h b/source/blender/depsgraph/intern/depsgraph.h
index 33d97e4b8b2..2f88199384d 100644
--- a/source/blender/depsgraph/intern/depsgraph.h
+++ b/source/blender/depsgraph/intern/depsgraph.h
@@ -88,9 +88,15 @@ struct Depsgraph {
/* Top-level time source node. */
TimeSourceNode *time_source;
+ /* The graph contains data-blocks whose visibility depends on evaluation (driven or animated). */
+ bool has_animated_visibility;
+
/* Indicates whether relations needs to be updated. */
bool need_update_relations;
+ /* Indicates whether indirect effect of nodes on a directly visible ones needs to be updated. */
+ bool need_update_nodes_visibility;
+
/* Indicated whether IDs in this graph are to be tagged as if they first appear visible, with
* an optional tag for their animation (time) update. */
bool need_tag_id_on_graph_visibility_update;
diff --git a/source/blender/depsgraph/intern/depsgraph_query_iter.cc b/source/blender/depsgraph/intern/depsgraph_query_iter.cc
index bcde1839a0f..171c9875e2a 100644
--- a/source/blender/depsgraph/intern/depsgraph_query_iter.cc
+++ b/source/blender/depsgraph/intern/depsgraph_query_iter.cc
@@ -219,7 +219,9 @@ bool deg_iterator_objects_step(DEGObjectIterData *data)
for (; data->id_node_index < data->num_id_nodes; data->id_node_index++) {
deg::IDNode *id_node = deg_graph->id_nodes[data->id_node_index];
- if (!id_node->is_directly_visible) {
+ /* Use the build time visibility so that the ID is not appearing/disappearing throughout
+ * animation export. */
+ if (!id_node->is_visible_on_build) {
continue;
}
@@ -338,10 +340,13 @@ static void DEG_iterator_ids_step(BLI_Iterator *iter, deg::IDNode *id_node, bool
{
ID *id_cow = id_node->id_cow;
- if (!id_node->is_directly_visible) {
+ /* Use the build time visibility so that the ID is not appearing/disappearing throughout
+ * animation export. */
+ if (!id_node->is_visible_on_build) {
iter->skip = true;
return;
}
+
if (only_updated && !(id_cow->recalc & ID_RECALC_ALL)) {
/* Node-tree is considered part of the data-block. */
bNodeTree *ntree = ntreeFromID(id_cow);
diff --git a/source/blender/depsgraph/intern/eval/deg_eval.cc b/source/blender/depsgraph/intern/eval/deg_eval.cc
index 9ac1f5275ac..426e04a5cf9 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval.cc
@@ -37,6 +37,7 @@
#include "intern/eval/deg_eval_copy_on_write.h"
#include "intern/eval/deg_eval_flush.h"
#include "intern/eval/deg_eval_stats.h"
+#include "intern/eval/deg_eval_visibility.h"
#include "intern/node/deg_node.h"
#include "intern/node/deg_node_component.h"
#include "intern/node/deg_node_id.h"
@@ -69,6 +70,9 @@ enum class EvaluationStage {
* involved. */
COPY_ON_WRITE,
+ /* Evaluate actual ID nodes visiblity based on the current state of animation and drivers. */
+ DYNAMIC_VISIBILITY,
+
/* Threaded evaluation of all possible operations. */
THREADED_EVALUATION,
@@ -83,7 +87,8 @@ struct DepsgraphEvalState {
Depsgraph *graph;
bool do_stats;
EvaluationStage stage;
- bool need_single_thread_pass;
+ bool need_update_pending_parents = true;
+ bool need_single_thread_pass = false;
};
void evaluate_node(const DepsgraphEvalState *state, OperationNode *operation_node)
@@ -101,6 +106,12 @@ void evaluate_node(const DepsgraphEvalState *state, OperationNode *operation_nod
else {
operation_node->evaluate(depsgraph);
}
+
+ /* Clear the flag early on, allowing partial updates without re-evaluating the same node multiple
+ * times.
+ * This is a thread-safe modification as the node's flags are only read for a non-scheduled nodes
+ * and this node has been scheduled. */
+ operation_node->flag &= ~DEPSOP_FLAG_NEEDS_UPDATE;
}
void deg_task_run_func(TaskPool *pool, void *taskdata)
@@ -116,24 +127,31 @@ void deg_task_run_func(TaskPool *pool, void *taskdata)
schedule_children(state, operation_node, schedule_node_to_pool, pool);
}
-bool check_operation_node_visible(OperationNode *op_node)
+bool check_operation_node_visible(const DepsgraphEvalState *state, OperationNode *op_node)
{
const ComponentNode *comp_node = op_node->owner;
- /* Special exception, copy on write component is to be always evaluated,
- * to keep copied "database" in a consistent state. */
+ /* Special case for copy on write component: it is to be always evaluated, to keep copied
+ * "database" in a consistent state. */
if (comp_node->type == NodeType::COPY_ON_WRITE) {
return true;
}
- return comp_node->affects_directly_visible;
+
+ /* Special case for dynamic visiblity pass: the actual visibility is not yet known, so limit to
+ * only operations which affects visibility. */
+ if (state->stage == EvaluationStage::DYNAMIC_VISIBILITY) {
+ return op_node->flag & OperationFlag::DEPSOP_FLAG_AFFECTS_VISIBILITY;
+ }
+
+ return comp_node->affects_visible_id;
}
-void calculate_pending_parents_for_node(OperationNode *node)
+void calculate_pending_parents_for_node(const DepsgraphEvalState *state, OperationNode *node)
{
/* Update counters, applies for both visible and invisible IDs. */
node->num_links_pending = 0;
node->scheduled = false;
/* Invisible IDs requires no pending operations. */
- if (!check_operation_node_visible(node)) {
+ if (!check_operation_node_visible(state, node)) {
return;
}
/* No need to bother with anything if node is not tagged for update. */
@@ -147,7 +165,7 @@ void calculate_pending_parents_for_node(OperationNode *node)
* calculation, but how is it possible that visible object depends
* on an invisible? This is something what is prohibited after
* deg_graph_build_flush_layers(). */
- if (!check_operation_node_visible(from)) {
+ if (!check_operation_node_visible(state, from)) {
continue;
}
/* No need to wait for operation which is up to date. */
@@ -159,20 +177,24 @@ void calculate_pending_parents_for_node(OperationNode *node)
}
}
-void calculate_pending_parents(Depsgraph *graph)
+void calculate_pending_parents_if_needed(DepsgraphEvalState *state)
{
- for (OperationNode *node : graph->operations) {
- calculate_pending_parents_for_node(node);
+ if (!state->need_update_pending_parents) {
+ return;
}
+
+ for (OperationNode *node : state->graph->operations) {
+ calculate_pending_parents_for_node(state, node);
+ }
+
+ state->need_update_pending_parents = false;
}
void initialize_execution(DepsgraphEvalState *state, Depsgraph *graph)
{
- const bool do_stats = state->do_stats;
- calculate_pending_parents(graph);
/* Clear tags and other things which needs to be clear. */
- for (OperationNode *node : graph->operations) {
- if (do_stats) {
+ if (state->do_stats) {
+ for (OperationNode *node : graph->operations) {
node->stats.reset_current();
}
}
@@ -197,11 +219,10 @@ bool need_evaluate_operation_at_stage(DepsgraphEvalState *state,
case EvaluationStage::COPY_ON_WRITE:
return (component_node->type == NodeType::COPY_ON_WRITE);
+ case EvaluationStage::DYNAMIC_VISIBILITY:
+ return operation_node->flag & OperationFlag::DEPSOP_FLAG_AFFECTS_VISIBILITY;
+
case EvaluationStage::THREADED_EVALUATION:
- /* Sanity check: copy-on-write node should be evaluated already. This will be indicated by
- * scheduled flag (we assume that scheduled operations have been actually handled by previous
- * stage). */
- BLI_assert(operation_node->scheduled || component_node->type != NodeType::COPY_ON_WRITE);
if (is_metaball_object_operation(operation_node)) {
state->need_single_thread_pass = true;
return false;
@@ -227,7 +248,7 @@ void schedule_node(DepsgraphEvalState *state,
ScheduleFunctionArgs... schedule_function_args)
{
/* No need to schedule nodes of invisible ID. */
- if (!check_operation_node_visible(node)) {
+ if (!check_operation_node_visible(state, node)) {
return;
}
/* No need to schedule operations which are not tagged for update, they are
@@ -311,6 +332,8 @@ void evaluate_graph_threaded_stage(DepsgraphEvalState *state,
{
state->stage = stage;
+ calculate_pending_parents_if_needed(state);
+
schedule_graph(state, schedule_node_to_pool, task_pool);
BLI_task_pool_work_and_wait(task_pool);
}
@@ -322,6 +345,8 @@ void evaluate_graph_single_threaded_if_needed(DepsgraphEvalState *state)
return;
}
+ BLI_assert(!state->need_update_pending_parents);
+
state->stage = EvaluationStage::SINGLE_THREADED_WORKAROUND;
GSQueue *evaluation_queue = BLI_gsqueue_new(sizeof(OperationNode *));
@@ -386,7 +411,6 @@ void deg_evaluate_on_refresh(Depsgraph *graph)
DepsgraphEvalState state;
state.graph = graph;
state.do_stats = graph->debug.do_time_debug();
- state.need_single_thread_pass = false;
/* Prepare all nodes for evaluation. */
initialize_execution(&state, graph);
@@ -397,6 +421,10 @@ void deg_evaluate_on_refresh(Depsgraph *graph)
* that if a dependency graph has a cycle evaluation functions will always "see" valid expanded
* datablock. It might not be evaluated yet, but at least the datablock will be valid.
*
+ * - If there is potentially dynamically changing visibility in the graph update the actual
+ * nodes visibilities, so that actual heavy data evaluation can benefit from knowledge that
+ * something heavy is not currently visible.
+ *
* - Multi-threaded evaluation of all possible nodes.
* Certain operations (and their subtrees) could be ignored. For example, meta-balls are not
* safe from threading point of view, so the threaded evaluation will stop at the metaball
@@ -407,6 +435,24 @@ void deg_evaluate_on_refresh(Depsgraph *graph)
TaskPool *task_pool = deg_evaluate_task_pool_create(&state);
evaluate_graph_threaded_stage(&state, task_pool, EvaluationStage::COPY_ON_WRITE);
+
+ if (graph->has_animated_visibility) {
+ /* Update pending parents including only the ones which are affecting operations which are
+ * affecting visibility. */
+ state.need_update_pending_parents = true;
+
+ evaluate_graph_threaded_stage(&state, task_pool, EvaluationStage::DYNAMIC_VISIBILITY);
+
+ deg_graph_flush_visibility_flags_if_needed(graph);
+
+ /* Update parents to an updated visibility and evaluation stage.
+ *
+ * Need to do it regardless of whether visibility is actually changed or not: current state of
+ * the pending parents are all zeroes because it was previously calculated for only visibility
+ * related nodes and those are fully evaluated by now. */
+ state.need_update_pending_parents = true;
+ }
+
evaluate_graph_threaded_stage(&state, task_pool, EvaluationStage::THREADED_EVALUATION);
BLI_task_pool_free(task_pool);
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_visibility.cc b/source/blender/depsgraph/intern/eval/deg_eval_visibility.cc
new file mode 100644
index 00000000000..8bf26e45e2a
--- /dev/null
+++ b/source/blender/depsgraph/intern/eval/deg_eval_visibility.cc
@@ -0,0 +1,147 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup depsgraph
+ */
+
+#include "intern/eval/deg_eval_visibility.h"
+
+#include "DNA_layer_types.h"
+#include "DNA_object_types.h"
+
+#include "BLI_assert.h"
+#include "BLI_stack.h"
+
+#include "DEG_depsgraph.h"
+
+#include "intern/depsgraph.h"
+#include "intern/depsgraph_relation.h"
+#include "intern/node/deg_node.h"
+#include "intern/node/deg_node_component.h"
+#include "intern/node/deg_node_id.h"
+#include "intern/node/deg_node_operation.h"
+
+namespace blender::deg {
+
+void deg_evaluate_object_node_visibility(::Depsgraph *depsgraph, IDNode *id_node)
+{
+ BLI_assert(GS(id_node->id_cow->name) == ID_OB);
+
+ Depsgraph *graph = reinterpret_cast<Depsgraph *>(depsgraph);
+ const Object *object = reinterpret_cast<const Object *>(id_node->id_cow);
+
+ DEG_debug_print_eval(depsgraph, __func__, object->id.name, &object->id);
+
+ const int required_flags = (graph->mode == DAG_EVAL_VIEWPORT) ? BASE_ENABLED_VIEWPORT :
+ BASE_ENABLED_RENDER;
+
+ const bool is_enabled = object->base_flag & required_flags;
+
+ if (id_node->is_enabled_on_eval != is_enabled) {
+ id_node->is_enabled_on_eval = is_enabled;
+
+ /* Tag dependency graph for changed visibility, so that it is updated on all dependencies prior
+ * to a pass of an actual evaluation. */
+ graph->need_update_nodes_visibility = true;
+ }
+}
+
+void deg_graph_flush_visibility_flags(Depsgraph *graph)
+{
+ enum {
+ DEG_NODE_VISITED = (1 << 0),
+ };
+
+ for (IDNode *id_node : graph->id_nodes) {
+ for (ComponentNode *comp_node : id_node->components.values()) {
+ comp_node->possibly_affects_visible_id = id_node->is_visible_on_build;
+ comp_node->affects_visible_id = id_node->is_visible_on_build && id_node->is_enabled_on_eval;
+ }
+ }
+
+ BLI_Stack *stack = BLI_stack_new(sizeof(OperationNode *), "DEG flush layers stack");
+
+ for (OperationNode *op_node : graph->operations) {
+ op_node->custom_flags = 0;
+ op_node->num_links_pending = 0;
+ for (Relation *rel : op_node->outlinks) {
+ if ((rel->from->type == NodeType::OPERATION) && (rel->flag & RELATION_FLAG_CYCLIC) == 0) {
+ ++op_node->num_links_pending;
+ }
+ }
+ if (op_node->num_links_pending == 0) {
+ BLI_stack_push(stack, &op_node);
+ op_node->custom_flags |= DEG_NODE_VISITED;
+ }
+ }
+
+ while (!BLI_stack_is_empty(stack)) {
+ OperationNode *op_node;
+ BLI_stack_pop(stack, &op_node);
+
+ /* Flush flags to parents. */
+ for (Relation *rel : op_node->inlinks) {
+ if (rel->from->type == NodeType::OPERATION) {
+ const OperationNode *op_to = reinterpret_cast<const OperationNode *>(rel->to);
+ const ComponentNode *comp_to = op_to->owner;
+ OperationNode *op_from = reinterpret_cast<OperationNode *>(rel->from);
+ ComponentNode *comp_from = op_from->owner;
+
+ const bool target_possibly_affects_visible_id = comp_to->possibly_affects_visible_id;
+ const bool target_affects_visible_id = comp_to->affects_visible_id;
+
+ op_from->flag |= (op_to->flag & OperationFlag::DEPSOP_FLAG_AFFECTS_VISIBILITY);
+
+ /* Visibility component forces all components of the current ID to be considered as
+ * affecting directly visible. */
+ if (comp_from->type == NodeType::VISIBILITY) {
+ const IDNode *id_node_from = comp_from->owner;
+ if (target_possibly_affects_visible_id) {
+ for (ComponentNode *comp_node : id_node_from->components.values()) {
+ comp_node->possibly_affects_visible_id |= target_possibly_affects_visible_id;
+ }
+ }
+ if (target_affects_visible_id) {
+ for (ComponentNode *comp_node : id_node_from->components.values()) {
+ comp_node->affects_visible_id |= target_affects_visible_id;
+ }
+ }
+ }
+ else {
+ comp_from->possibly_affects_visible_id |= target_possibly_affects_visible_id;
+ comp_from->affects_visible_id |= target_affects_visible_id;
+ }
+ }
+ }
+
+ /* Schedule parent nodes. */
+ for (Relation *rel : op_node->inlinks) {
+ if (rel->from->type == NodeType::OPERATION) {
+ OperationNode *op_from = (OperationNode *)rel->from;
+ if ((rel->flag & RELATION_FLAG_CYCLIC) == 0) {
+ BLI_assert(op_from->num_links_pending > 0);
+ --op_from->num_links_pending;
+ }
+ if ((op_from->num_links_pending == 0) && (op_from->custom_flags & DEG_NODE_VISITED) == 0) {
+ BLI_stack_push(stack, &op_from);
+ op_from->custom_flags |= DEG_NODE_VISITED;
+ }
+ }
+ }
+ }
+ BLI_stack_free(stack);
+
+ graph->need_update_nodes_visibility = false;
+}
+
+void deg_graph_flush_visibility_flags_if_needed(Depsgraph *graph)
+{
+ if (!graph->need_update_nodes_visibility) {
+ return;
+ }
+
+ deg_graph_flush_visibility_flags(graph);
+}
+
+} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_visibility.h b/source/blender/depsgraph/intern/eval/deg_eval_visibility.h
new file mode 100644
index 00000000000..9e9db8ab34a
--- /dev/null
+++ b/source/blender/depsgraph/intern/eval/deg_eval_visibility.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup depsgraph
+ */
+
+#pragma once
+
+struct Depsgraph;
+
+namespace blender::deg {
+
+struct Depsgraph;
+struct IDNode;
+
+/* Evaluate actual node visibility flags based on the current state of object's visibility
+ * restriction flags. */
+void deg_evaluate_object_node_visibility(::Depsgraph *depsgraph, IDNode *id_node);
+
+/* Flush both static and dynamic visibility flags from leaves up to the roots, making it possible
+ * to know whether a node has affect on something (potentially) visible. */
+void deg_graph_flush_visibility_flags(Depsgraph *graph);
+void deg_graph_flush_visibility_flags_if_needed(Depsgraph *graph);
+
+} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/node/deg_node_component.cc b/source/blender/depsgraph/intern/node/deg_node_component.cc
index cfcbec46569..32942f45a4a 100644
--- a/source/blender/depsgraph/intern/node/deg_node_component.cc
+++ b/source/blender/depsgraph/intern/node/deg_node_component.cc
@@ -67,7 +67,10 @@ uint64_t ComponentNode::OperationIDKey::hash() const
}
ComponentNode::ComponentNode()
- : entry_operation(nullptr), exit_operation(nullptr), affects_directly_visible(false)
+ : entry_operation(nullptr),
+ exit_operation(nullptr),
+ possibly_affects_visible_id(false),
+ affects_visible_id(false)
{
operations_map = new Map<ComponentNode::OperationIDKey, OperationNode *>();
}
@@ -90,7 +93,7 @@ string ComponentNode::identifier() const
const string idname = this->owner->name;
const string typebuf = "" + to_string(static_cast<int>(type)) + ")";
return typebuf + name + " : " + idname +
- "( affects_directly_visible: " + (affects_directly_visible ? "true" : "false") + ")";
+ "( affects_visible_id: " + (affects_visible_id ? "true" : "false") + ")";
}
OperationNode *ComponentNode::find_operation(OperationIDKey key) const
diff --git a/source/blender/depsgraph/intern/node/deg_node_component.h b/source/blender/depsgraph/intern/node/deg_node_component.h
index 375a26ab49b..a616dfdfe4c 100644
--- a/source/blender/depsgraph/intern/node/deg_node_component.h
+++ b/source/blender/depsgraph/intern/node/deg_node_component.h
@@ -24,7 +24,6 @@ struct bPoseChannel;
namespace blender::deg {
-struct BoneComponentNode;
struct Depsgraph;
struct IDNode;
struct OperationNode;
@@ -137,9 +136,17 @@ struct ComponentNode : public Node {
return true;
}
- /* Denotes whether this component affects (possibly indirectly) on a
- * directly visible object. */
- bool affects_directly_visible;
+ /* The component has (possibly indirect) effect on a data-block whose node has
+ * is_visible_on_build set to true.
+ *
+ * This field is ensured to be up-to-date prior to `IDNode::finalize_build()`. */
+ bool possibly_affects_visible_id;
+
+ /* Denotes whether this component actually affects (possibly indirectly) on a directly visible
+ * object. Includes possibly run-time visibility update of ID nodes.
+ *
+ * NOTE: Is only reliable after `deg_graph_flush_visibility()`. */
+ bool affects_visible_id;
};
/* ---------------------------------------- */
@@ -197,7 +204,7 @@ DEG_COMPONENT_NODE_DECLARE_GENERIC(Dupli);
DEG_COMPONENT_NODE_DECLARE_GENERIC(Audio);
DEG_COMPONENT_NODE_DECLARE_GENERIC(Armature);
DEG_COMPONENT_NODE_DECLARE_GENERIC(GenericDatablock);
-DEG_COMPONENT_NODE_DECLARE_NO_COW(Visibility);
+DEG_COMPONENT_NODE_DECLARE_NO_COW_TAG_ON_UPDATE(Visibility);
DEG_COMPONENT_NODE_DECLARE_GENERIC(Simulation);
DEG_COMPONENT_NODE_DECLARE_GENERIC(NTreeOutput);
@@ -214,7 +221,7 @@ struct SynchronizationComponentNode : public ComponentNode {
* The design is such that the synchronization is supposed to happen whenever any part of the
* ID changed/evaluated. Here we mark the component as "visible" so that genetic recalc flag
* flushing and scheduling will handle the component in a generic manner. */
- affects_directly_visible = true;
+ affects_visible_id = true;
}
DEG_COMPONENT_NODE_DECLARE;
diff --git a/source/blender/depsgraph/intern/node/deg_node_id.cc b/source/blender/depsgraph/intern/node/deg_node_id.cc
index 99224501e98..735d606ac9e 100644
--- a/source/blender/depsgraph/intern/node/deg_node_id.cc
+++ b/source/blender/depsgraph/intern/node/deg_node_id.cc
@@ -69,7 +69,8 @@ void IDNode::init(const ID *id, const char *UNUSED(subdata))
customdata_masks = DEGCustomDataMeshMasks();
previous_customdata_masks = DEGCustomDataMeshMasks();
linked_state = DEG_ID_LINKED_INDIRECTLY;
- is_directly_visible = true;
+ is_visible_on_build = true;
+ is_enabled_on_eval = true;
is_collection_fully_expanded = false;
has_base = false;
is_user_modified = false;
@@ -138,8 +139,8 @@ string IDNode::identifier() const
BLI_snprintf(orig_ptr, sizeof(orig_ptr), "%p", id_orig);
BLI_snprintf(cow_ptr, sizeof(cow_ptr), "%p", id_cow);
return string(nodeTypeAsString(type)) + " : " + name + " (orig: " + orig_ptr +
- ", eval: " + cow_ptr + ", is_directly_visible " +
- (is_directly_visible ? "true" : "false") + ")";
+ ", eval: " + cow_ptr + ", is_visible_on_build " +
+ (is_visible_on_build ? "true" : "false") + ")";
}
ComponentNode *IDNode::find_component(NodeType type, const char *name) const
@@ -188,7 +189,7 @@ IDComponentsMask IDNode::get_visible_components_mask() const
{
IDComponentsMask result = 0;
for (ComponentNode *comp_node : components.values()) {
- if (comp_node->affects_directly_visible) {
+ if (comp_node->possibly_affects_visible_id) {
const int component_type_as_int = static_cast<int>(comp_node->type);
BLI_assert(component_type_as_int < 64);
result |= (1ULL << component_type_as_int);
diff --git a/source/blender/depsgraph/intern/node/deg_node_id.h b/source/blender/depsgraph/intern/node/deg_node_id.h
index 406ca828049..7f0a656cb8d 100644
--- a/source/blender/depsgraph/intern/node/deg_node_id.h
+++ b/source/blender/depsgraph/intern/node/deg_node_id.h
@@ -91,8 +91,21 @@ struct IDNode : public Node {
eDepsNode_LinkedState_Type linked_state;
- /* Indicates the data-block is visible in the evaluated scene. */
- bool is_directly_visible;
+ /* Indicates the data-block is to be considered visible in the evaluated scene.
+ *
+ * This flag is set during dependency graph build where check for an actual visibility might not
+ * be available yet due to driven or animated restriction flags. So it is more of an intent or,
+ * in other words, plausibility of the data-block to be visible. */
+ bool is_visible_on_build;
+
+ /* Evaluated state of whether evaluation considered this data-block "enabled".
+ *
+ * For objects this is derived from the base restriction flags, which might be animated or
+ * driven. It is set to `BASE_ENABLED_<VIEWPORT, RENDER>` (depending on the graph mode) after
+ * the object's flags from layer were evaluated.
+ *
+ * For other data-types is currently always true. */
+ bool is_enabled_on_eval;
/* For the collection type of ID, denotes whether collection was fully
* recursed into. */
diff --git a/source/blender/depsgraph/intern/node/deg_node_operation.h b/source/blender/depsgraph/intern/node/deg_node_operation.h
index fd772fbce9d..656b27550f6 100644
--- a/source/blender/depsgraph/intern/node/deg_node_operation.h
+++ b/source/blender/depsgraph/intern/node/deg_node_operation.h
@@ -214,6 +214,9 @@ enum OperationFlag {
* and not for connecting to an operation. */
DEPSOP_FLAG_PINNED = (1 << 3),
+ /* The operation directly or indirectly affects ID node visibility. */
+ DEPSOP_FLAG_AFFECTS_VISIBILITY = (1 << 4),
+
/* Set of flags which gets flushed along the relations. */
DEPSOP_FLAG_FLUSH = (DEPSOP_FLAG_USER_MODIFIED),
};
diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt
index 55d789f64b0..d20745f28c0 100644
--- a/source/blender/draw/CMakeLists.txt
+++ b/source/blender/draw/CMakeLists.txt
@@ -375,7 +375,6 @@ set(GLSL_SRC
engines/eevee_next/shaders/eevee_surf_lib.glsl
engines/eevee_next/shaders/eevee_surf_world_frag.glsl
engines/eevee_next/shaders/eevee_velocity_lib.glsl
- engines/eevee_next/shaders/eevee_velocity_resolve_comp.glsl
engines/eevee_next/eevee_defines.hh
engines/eevee_next/eevee_shader_shared.hh
diff --git a/source/blender/draw/engines/eevee_next/eevee_camera.cc b/source/blender/draw/engines/eevee_next/eevee_camera.cc
index 1f65f887d46..b9040f0f3ab 100644
--- a/source/blender/draw/engines/eevee_next/eevee_camera.cc
+++ b/source/blender/draw/engines/eevee_next/eevee_camera.cc
@@ -29,10 +29,8 @@ namespace blender::eevee {
void Camera::init()
{
const Object *camera_eval = inst_.camera_eval_object;
- synced_ = false;
- data_.swap();
- CameraData &data = data_.current();
+ CameraData &data = data_;
if (camera_eval) {
const ::Camera *cam = reinterpret_cast<const ::Camera *>(camera_eval->data);
@@ -78,9 +76,7 @@ void Camera::sync()
{
const Object *camera_eval = inst_.camera_eval_object;
- data_.swap();
-
- CameraData &data = data_.current();
+ CameraData &data = data_;
if (inst_.drw_view) {
DRW_view_viewmat_get(inst_.drw_view, data.viewmat.ptr(), false);
@@ -142,14 +138,8 @@ void Camera::sync()
data.equirect_scale = float2(0.0f);
}
- data_.current().push_update();
-
- synced_ = true;
-
- /* Detect changes in parameters. */
- if (data_.current() != data_.previous()) {
- inst_.sampling.reset();
- }
+ data_.initialized = true;
+ data_.push_update();
}
/** \} */
diff --git a/source/blender/draw/engines/eevee_next/eevee_camera.hh b/source/blender/draw/engines/eevee_next/eevee_camera.hh
index 3b3586190c4..8bf64199246 100644
--- a/source/blender/draw/engines/eevee_next/eevee_camera.hh
+++ b/source/blender/draw/engines/eevee_next/eevee_camera.hh
@@ -83,9 +83,7 @@ class Camera {
Instance &inst_;
/** Double buffered to detect changes and have history for re-projection. */
- SwapChain<CameraDataBuf, 2> data_;
- /** Detects wrong usage. */
- bool synced_ = false;
+ CameraDataBuf data_;
public:
Camera(Instance &inst) : inst_(inst){};
@@ -99,28 +97,28 @@ class Camera {
**/
const CameraData &data_get() const
{
- BLI_assert(synced_);
- return data_.current();
+ BLI_assert(data_.initialized);
+ return data_;
}
const GPUUniformBuf *ubo_get() const
{
- return data_.current();
+ return data_;
}
bool is_panoramic() const
{
- return eevee::is_panoramic(data_.current().type);
+ return eevee::is_panoramic(data_.type);
}
bool is_orthographic() const
{
- return data_.current().type == CAMERA_ORTHO;
+ return data_.type == CAMERA_ORTHO;
}
const float3 &position() const
{
- return *reinterpret_cast<const float3 *>(data_.current().viewinv[3]);
+ return *reinterpret_cast<const float3 *>(data_.viewinv[3]);
}
const float3 &forward() const
{
- return *reinterpret_cast<const float3 *>(data_.current().viewinv[2]);
+ return *reinterpret_cast<const float3 *>(data_.viewinv[2]);
}
};
diff --git a/source/blender/draw/engines/eevee_next/eevee_film.cc b/source/blender/draw/engines/eevee_next/eevee_film.cc
index 1fd4c278c88..d3b09beedaa 100644
--- a/source/blender/draw/engines/eevee_next/eevee_film.cc
+++ b/source/blender/draw/engines/eevee_next/eevee_film.cc
@@ -163,11 +163,13 @@ inline bool operator!=(const FilmData &a, const FilmData &b)
void Film::init(const int2 &extent, const rcti *output_rect)
{
+ Sampling &sampling = inst_.sampling;
+
init_aovs();
{
/* Enable passes that need to be rendered. */
- eViewLayerEEVEEPassType render_passes;
+ eViewLayerEEVEEPassType render_passes = eViewLayerEEVEEPassType(0);
if (inst_.is_viewport()) {
/* Viewport Case. */
@@ -178,6 +180,8 @@ void Film::init(const int2 &extent, const rcti *output_rect)
* Using the render pass ensure we store the center depth. */
render_passes |= EEVEE_RENDER_PASS_Z;
}
+ /* TEST */
+ render_passes |= EEVEE_RENDER_PASS_VECTOR;
}
else {
/* Render Case. */
@@ -211,7 +215,7 @@ void Film::init(const int2 &extent, const rcti *output_rect)
/* TODO(@fclem): Can't we rely on depsgraph update notification? */
if (assign_if_different(enabled_passes_, render_passes)) {
- inst_.sampling.reset();
+ sampling.reset();
}
}
{
@@ -224,14 +228,18 @@ void Film::init(const int2 &extent, const rcti *output_rect)
FilmData data = data_;
data.extent = int2(BLI_rcti_size_x(output_rect), BLI_rcti_size_y(output_rect));
data.offset = int2(output_rect->xmin, output_rect->ymin);
- data.filter_size = clamp_f(inst_.scene->r.gauss, 0.0f, 100.0f);
+ data.extent_inv = 1.0f / float2(data.extent);
+ /* Disable filtering if sample count is 1. */
+ data.filter_size = (sampling.sample_count() == 1) ?
+ 0.0f :
+ clamp_f(inst_.scene->r.gauss, 0.0f, 100.0f);
/* TODO(fclem): parameter hidden in experimental.
* We need to figure out LOD bias first in order to preserve texture crispiness. */
data.scaling_factor = 1;
FilmData &data_prev_ = data_;
if (assign_if_different(data_prev_, data)) {
- inst_.sampling.reset();
+ sampling.reset();
}
const eViewLayerEEVEEPassType data_passes = EEVEE_RENDER_PASS_Z | EEVEE_RENDER_PASS_NORMAL |
@@ -325,7 +333,7 @@ void Film::init(const int2 &extent, const rcti *output_rect)
(data_.value_len > 0) ? data_.value_len : 1);
if (reset > 0) {
- inst_.sampling.reset();
+ sampling.reset();
data_.use_history = 0;
data_.use_reprojection = 0;
@@ -337,6 +345,8 @@ void Film::init(const int2 &extent, const rcti *output_rect)
depth_tx_.clear(float4(0.0f));
}
}
+
+ force_disable_reprojection_ = (inst_.scene->eevee.flag & SCE_EEVEE_TAA_REPROJECTION) == 0;
}
void Film::sync()
@@ -349,12 +359,22 @@ void Film::sync()
/* TODO(fclem): Shader variation for panoramic & scaled resolution. */
RenderBuffers &rbuffers = inst_.render_buffers;
+ VelocityModule &velocity = inst_.velocity;
+
+ eGPUSamplerState filter = GPU_SAMPLER_FILTER;
+
+ /* For viewport, only previous motion is supported.
+ * Still bind previous step to avoid undefined behavior. */
+ eVelocityStep step_next = inst_.is_viewport() ? STEP_PREVIOUS : STEP_NEXT;
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_ALWAYS;
accumulate_ps_ = DRW_pass_create("Film.Accumulate", state);
GPUShader *sh = inst_.shaders.static_shader_get(shader);
DRWShadingGroup *grp = DRW_shgroup_create(sh, accumulate_ps_);
DRW_shgroup_uniform_block_ref(grp, "film_buf", &data_);
+ 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]));
+ DRW_shgroup_uniform_block_ref(grp, "camera_next", &(*velocity.camera_steps[step_next]));
DRW_shgroup_uniform_texture_ref(grp, "depth_tx", &rbuffers.depth_tx);
DRW_shgroup_uniform_texture_ref(grp, "combined_tx", &rbuffers.combined_tx);
DRW_shgroup_uniform_texture_ref(grp, "normal_tx", &rbuffers.normal_tx);
@@ -375,7 +395,7 @@ void Film::sync()
* use image binding instead. */
DRW_shgroup_uniform_image_ref(grp, "in_weight_img", &weight_tx_.current());
DRW_shgroup_uniform_image_ref(grp, "out_weight_img", &weight_tx_.next());
- DRW_shgroup_uniform_image_ref(grp, "in_combined_img", &combined_tx_.current());
+ DRW_shgroup_uniform_texture_ref_ex(grp, "in_combined_tx", &combined_tx_.current(), filter);
DRW_shgroup_uniform_image_ref(grp, "out_combined_img", &combined_tx_.next());
DRW_shgroup_uniform_image_ref(grp, "depth_img", &depth_tx_);
DRW_shgroup_uniform_image_ref(grp, "color_accum_img", &color_accum_tx_);
@@ -395,13 +415,13 @@ void Film::sync()
void Film::end_sync()
{
- if (inst_.sampling.is_reset()) {
- data_.use_history = 0;
- }
+ data_.use_reprojection = inst_.sampling.interactive_mode();
- // if (camera.changed_type) {
- // data_.use_reprojection = false;
- // }
+ /* Just bypass the reprojection and reset the accumulation. */
+ if (force_disable_reprojection_ && inst_.sampling.is_reset()) {
+ data_.use_reprojection = false;
+ data_.use_history = false;
+ }
aovs_info.push_update();
@@ -429,6 +449,11 @@ float2 Film::pixel_jitter_get() const
return jitter;
}
+eViewLayerEEVEEPassType Film::enabled_passes_get() const
+{
+ return enabled_passes_;
+}
+
void Film::update_sample_table()
{
data_.subpixel_offset = pixel_jitter_get();
@@ -528,7 +553,6 @@ void Film::accumulate(const DRWView *view)
/* Use history after first sample. */
if (data_.use_history == 0) {
data_.use_history = 1;
- data_.use_reprojection = 1;
}
}
diff --git a/source/blender/draw/engines/eevee_next/eevee_film.hh b/source/blender/draw/engines/eevee_next/eevee_film.hh
index 7ffbd4e45c6..1be95b3ac6b 100644
--- a/source/blender/draw/engines/eevee_next/eevee_film.hh
+++ b/source/blender/draw/engines/eevee_next/eevee_film.hh
@@ -8,6 +8,12 @@
* The film class handles accumulation of samples with any distorted camera_type
* using a pixel filter. Inputs needs to be jittered so that the filter converges to the right
* result.
+ *
+ * In viewport, we switch between 2 accumulation mode depending on the scene state.
+ * - For static scene, we use a classic weighted accumulation.
+ * - For dynamic scene (if an update is detected), we use a more temporally stable accumulation
+ * following the Temporal Anti-Aliasing method (a.k.a. Temporal Super-Sampling). This does
+ * history reprojection and rectification to avoid most of the flickering.
*/
#pragma once
@@ -43,6 +49,8 @@ class Film {
SwapChain<Texture, 2> weight_tx_;
/** Extent used by the render buffers when rendering the main views. */
int2 render_extent_ = int2(-1);
+ /** User setting to disable reprojection. Useful for debugging or have a more precise render. */
+ bool force_disable_reprojection_ = false;
DRWPass *accumulate_ps_ = nullptr;
@@ -75,10 +83,7 @@ class Film {
float2 pixel_jitter_get() const;
- eViewLayerEEVEEPassType enabled_passes_get() const
- {
- return enabled_passes_;
- }
+ eViewLayerEEVEEPassType enabled_passes_get() const;
static bool pass_is_value(eViewLayerEEVEEPassType pass_type)
{
diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.cc b/source/blender/draw/engines/eevee_next/eevee_instance.cc
index 6098d78b81c..9f8cf6dc6ba 100644
--- a/source/blender/draw/engines/eevee_next/eevee_instance.cc
+++ b/source/blender/draw/engines/eevee_next/eevee_instance.cc
@@ -199,7 +199,7 @@ void Instance::render_sync()
**/
void Instance::render_sample()
{
- if (sampling.finished()) {
+ if (sampling.finished_viewport()) {
film.display();
return;
}
diff --git a/source/blender/draw/engines/eevee_next/eevee_renderbuffers.cc b/source/blender/draw/engines/eevee_next/eevee_renderbuffers.cc
index 6e30ba989df..c60054496c1 100644
--- a/source/blender/draw/engines/eevee_next/eevee_renderbuffers.cc
+++ b/source/blender/draw/engines/eevee_next/eevee_renderbuffers.cc
@@ -56,8 +56,13 @@ void RenderBuffers::acquire(int2 extent, void *owner)
depth_tx.acquire(extent, GPU_DEPTH24_STENCIL8, owner);
combined_tx.acquire(extent, color_format, owner);
+ bool do_vector_render_pass = inst_.film.enabled_passes_get() & EEVEE_RENDER_PASS_VECTOR;
+ /* Only RG16F when only doing only reprojection or motion blur. */
+ eGPUTextureFormat vector_format = do_vector_render_pass ? GPU_RGBA16F : GPU_RG16F;
+ /* TODO(fclem): Make vector pass allocation optional if no TAA or motion blur is needed. */
+ vector_tx.acquire(extent, vector_format, owner);
+
normal_tx.acquire(pass_extent(EEVEE_RENDER_PASS_NORMAL), color_format, owner);
- vector_tx.acquire(pass_extent(EEVEE_RENDER_PASS_VECTOR), color_format, owner);
diffuse_light_tx.acquire(pass_extent(EEVEE_RENDER_PASS_DIFFUSE_LIGHT), color_format, owner);
diffuse_color_tx.acquire(pass_extent(EEVEE_RENDER_PASS_DIFFUSE_COLOR), color_format, owner);
specular_light_tx.acquire(pass_extent(EEVEE_RENDER_PASS_SPECULAR_LIGHT), color_format, owner);
diff --git a/source/blender/draw/engines/eevee_next/eevee_sampling.cc b/source/blender/draw/engines/eevee_next/eevee_sampling.cc
index 2f180e58a0b..ef2469647ef 100644
--- a/source/blender/draw/engines/eevee_next/eevee_sampling.cc
+++ b/source/blender/draw/engines/eevee_next/eevee_sampling.cc
@@ -57,22 +57,21 @@ void Sampling::end_sync()
{
if (reset_) {
viewport_sample_ = 0;
- if (inst_.is_viewport()) {
- interactive_mode_ = true;
- }
}
- if (interactive_mode_) {
- int interactive_sample_count = min_ii(interactive_sample_max_, sample_count_);
+ if (inst_.is_viewport()) {
+ interactive_mode_ = viewport_sample_ < interactive_mode_threshold;
+ if (interactive_mode_) {
+ int interactive_sample_count = min_ii(interactive_sample_max_, sample_count_);
- if (viewport_sample_ < interactive_sample_count) {
- /* Loop over the same starting samples. */
- sample_ = sample_ % interactive_sample_count;
- }
- else {
- /* Break out of the loop and resume normal pattern. */
- sample_ = interactive_sample_count;
- interactive_mode_ = false;
+ if (viewport_sample_ < interactive_sample_count) {
+ /* Loop over the same starting samples. */
+ sample_ = sample_ % interactive_sample_count;
+ }
+ else {
+ /* Break out of the loop and resume normal pattern. */
+ sample_ = interactive_sample_count;
+ }
}
}
}
@@ -138,8 +137,6 @@ void Sampling::step()
viewport_sample_++;
sample_++;
- std::cout << sample_ << " " << viewport_sample_ << std::endl;
-
reset_ = false;
}
@@ -218,7 +215,7 @@ void Sampling::dof_disk_sample_get(float *r_radius, float *r_theta) const
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Sampling patterns
+/** \name Cumulative Distribution Function (CDF)
* \{ */
/* Creates a discrete cumulative distribution function table from a given curvemapping.
diff --git a/source/blender/draw/engines/eevee_next/eevee_sampling.hh b/source/blender/draw/engines/eevee_next/eevee_sampling.hh
index 11daa21629a..c604ecef40b 100644
--- a/source/blender/draw/engines/eevee_next/eevee_sampling.hh
+++ b/source/blender/draw/engines/eevee_next/eevee_sampling.hh
@@ -33,7 +33,7 @@ class Sampling {
/* During interactive rendering, loop over the first few samples. */
constexpr static uint64_t interactive_sample_max_ = 8;
- /** 0 based current sample. */
+ /** 0 based current sample. Might not increase sequentially in viewport. */
uint64_t sample_ = 0;
/** Target sample count. */
uint64_t sample_count_ = 64;
@@ -43,7 +43,7 @@ class Sampling {
uint64_t dof_sample_count_ = 1;
/** Motion blur steps. */
uint64_t motion_blur_steps_ = 1;
- /** Increases if the view and the scene is static. */
+ /** Increases if the view and the scene is static. Does increase sequentially. */
int64_t viewport_sample_ = 0;
/** Tag to reset sampling for the next sample. */
bool reset_ = false;
@@ -52,6 +52,12 @@ class Sampling {
* In interactive mode, image stability is prioritized over quality.
*/
bool interactive_mode_ = false;
+ /**
+ * Sample count after which we use the static accumulation.
+ * Interactive sampling from sample 0 to (interactive_mode_threshold - 1).
+ * Accumulation sampling from sample interactive_mode_threshold to sample_count_.
+ */
+ static constexpr int interactive_mode_threshold = 3;
SamplingDataBuf data_;
@@ -102,13 +108,24 @@ class Sampling {
/* Returns true if rendering has finished. */
bool finished() const
{
- return (sample_ >= sample_count_ - 1);
+ return (sample_ >= sample_count_);
}
/* Returns true if viewport smoothing and sampling has finished. */
bool finished_viewport() const
{
- return finished() && (viewport_sample_ >= interactive_sample_max_);
+ return (viewport_sample_ >= sample_count_) && !interactive_mode_;
+ }
+
+ /* Returns true if viewport renderer is in interactive mode and should use TAA. */
+ bool interactive_mode() const
+ {
+ return interactive_mode_;
+ }
+
+ uint64_t sample_count() const
+ {
+ return sample_count_;
}
/* Return true if we are starting a new motion blur step. We need to run sync again since
diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.cc b/source/blender/draw/engines/eevee_next/eevee_shader.cc
index f5d4af2914e..7db9692783a 100644
--- a/source/blender/draw/engines/eevee_next/eevee_shader.cc
+++ b/source/blender/draw/engines/eevee_next/eevee_shader.cc
@@ -82,8 +82,6 @@ const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_
return "eevee_film_frag";
case FILM_COMP:
return "eevee_film_comp";
- case VELOCITY_RESOLVE:
- return "eevee_velocity_resolve";
/* To avoid compiler warning about missing case. */
case MAX_SHADER_TYPE:
return "";
diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.hh b/source/blender/draw/engines/eevee_next/eevee_shader.hh
index 7a0867820af..280aaab4e1c 100644
--- a/source/blender/draw/engines/eevee_next/eevee_shader.hh
+++ b/source/blender/draw/engines/eevee_next/eevee_shader.hh
@@ -29,8 +29,6 @@ enum eShaderType {
FILM_FRAG = 0,
FILM_COMP,
- VELOCITY_RESOLVE,
-
MAX_SHADER_TYPE,
};
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 62eb5a2b965..9cf7f75b2c3 100644
--- a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh
+++ b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh
@@ -124,7 +124,12 @@ struct CameraData {
float clip_far;
eCameraType type;
- int _pad0;
+ bool initialized;
+
+#ifdef __cplusplus
+ /* Small constructor to allow detecting new buffers. */
+ CameraData() : initialized(false){};
+#endif
};
BLI_STATIC_ASSERT_ALIGN(CameraData, 16)
@@ -156,6 +161,8 @@ struct FilmData {
* pixel if using scaled resolution rendering.
*/
float2 subpixel_offset;
+ /** Scaling factor to convert texel to uvs. */
+ float2 extent_inv;
/** Is true if history is valid and can be sampled. Bypass history to resets accumulation. */
bool1 use_history;
/** Is true if combined buffer is valid and can be re-projected to reduce variance. */
@@ -165,7 +172,6 @@ struct FilmData {
/** Is true if accumulation of filtered passes is needed. */
bool1 any_render_pass_1;
bool1 any_render_pass_2;
- int _pad0, _pad1;
/** Output counts per type. */
int color_len, value_len;
/** Index in color_accum_img or value_accum_img of each pass. -1 if pass is not enabled. */
diff --git a/source/blender/draw/engines/eevee_next/eevee_velocity.cc b/source/blender/draw/engines/eevee_next/eevee_velocity.cc
index 4bd0af8204e..048daf1b2db 100644
--- a/source/blender/draw/engines/eevee_next/eevee_velocity.cc
+++ b/source/blender/draw/engines/eevee_next/eevee_velocity.cc
@@ -9,10 +9,6 @@
* temporal re-projection or motion blur.
*
* It is the module that tracks the objects between frames updates.
- *
- * #VelocityModule contains all motion steps data and logic.
- * #VelocityPass contains the resolve pass for static geometry.
- * #VelocityView is a per view instance that contain the velocity buffer.
*/
#include "BKE_duplilist.h"
@@ -36,8 +32,7 @@ namespace blender::eevee {
void VelocityModule::init()
{
-#if 0 /* TODO renderpasses */
- if (inst_.render && (inst_.render_passes.vector != nullptr)) {
+ if (inst_.render && (inst_.film.enabled_passes_get() & EEVEE_RENDER_PASS_VECTOR)) {
/* No motion blur and the vector pass was requested. Do the step sync here. */
const Scene *scene = inst_.scene;
float initial_time = scene->r.cfra + scene->r.subframe;
@@ -45,7 +40,6 @@ void VelocityModule::init()
step_sync(STEP_NEXT, initial_time + 1.0f);
inst_.set_time(initial_time);
}
-#endif
}
static void step_object_sync_render(void *velocity,
@@ -70,6 +64,11 @@ void VelocityModule::step_camera_sync()
{
inst_.camera.sync();
*camera_steps[step_] = inst_.camera.data_get();
+ /* Fix undefined camera steps when rendering is starting. */
+ if ((step_ == STEP_CURRENT) && (camera_steps[STEP_PREVIOUS]->initialized == false)) {
+ *camera_steps[STEP_PREVIOUS] = *static_cast<CameraData *>(camera_steps[step_]);
+ camera_steps[STEP_PREVIOUS]->initialized = true;
+ }
}
bool VelocityModule::step_object_sync(Object *ob,
@@ -267,6 +266,10 @@ void VelocityModule::end_sync()
inst_.sampling.reset();
}
+ if (inst_.is_viewport() && camera_has_motion()) {
+ inst_.sampling.reset();
+ }
+
for (auto key : deleted_obj) {
velocity_map.remove(key);
}
@@ -300,19 +303,6 @@ void VelocityModule::end_sync()
camera_steps[STEP_CURRENT]->push_update();
camera_steps[STEP_NEXT]->push_update();
indirection_buf.push_update();
-
- {
- resolve_ps_ = DRW_pass_create("Velocity.Resolve", (DRWState)0);
- GPUShader *sh = inst_.shaders.static_shader_get(VELOCITY_RESOLVE);
- DRWShadingGroup *grp = DRW_shgroup_create(sh, resolve_ps_);
- DRW_shgroup_uniform_texture_ref(grp, "depth_tx", &input_depth_tx_);
- DRW_shgroup_uniform_image_ref(grp, "velocity_view_img", &velocity_view_tx_);
- DRW_shgroup_uniform_image_ref(grp, "velocity_camera_img", &velocity_camera_tx_);
- DRW_shgroup_uniform_block(grp, "camera_prev", *camera_steps[STEP_PREVIOUS]);
- DRW_shgroup_uniform_block(grp, "camera_curr", *camera_steps[STEP_CURRENT]);
- DRW_shgroup_uniform_block(grp, "camera_next", *camera_steps[STEP_NEXT]);
- DRW_shgroup_call_compute_ref(grp, resolve_dispatch_size_);
- }
}
bool VelocityModule::object_has_velocity(const Object *ob)
@@ -359,60 +349,15 @@ void VelocityModule::bind_resources(DRWShadingGroup *grp)
DRW_shgroup_storage_block_ref(grp, "velocity_indirection_buf", &indirection_buf);
}
-/* Resolve pass for static geometry and to camera space projection. */
-void VelocityModule::resolve_camera_motion(GPUTexture *depth_tx,
- GPUTexture *velocity_view_tx,
- GPUTexture *velocity_camera_tx)
+bool VelocityModule::camera_has_motion() const
{
- input_depth_tx_ = depth_tx;
- velocity_view_tx_ = velocity_view_tx;
- velocity_camera_tx_ = velocity_camera_tx;
-
- resolve_dispatch_size_.x = divide_ceil_u(GPU_texture_width(depth_tx), 8);
- resolve_dispatch_size_.y = divide_ceil_u(GPU_texture_height(depth_tx), 8);
-
- DRW_draw_pass(resolve_ps_);
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Velocity View
- * \{ */
-
-void VelocityView::sync()
-{
- /* TODO: Remove. */
- velocity_view_tx_.sync();
- velocity_camera_tx_.sync();
-}
-
-void VelocityView::acquire(int2 extent)
-{
- /* WORKAROUND: View name should be unique and static.
- * With this, we can reuse the same texture across views. */
- DrawEngineType *owner = (DrawEngineType *)view_name_.c_str();
-
- /* Only RG16F when only doing only reprojection or motion blur. */
- eGPUTextureFormat format = inst_.is_viewport() ? GPU_RG16F : GPU_RGBA16F;
- velocity_view_tx_.acquire(extent, format, owner);
- if (false /* TODO(fclem): Panoramic camera. */) {
- velocity_camera_tx_.acquire(extent, format, owner);
- }
- else {
- velocity_camera_tx_.acquire(int2(1), format, owner);
+ /* Only valid after sync. */
+ if (inst_.is_viewport()) {
+ /* Viewport has no next step. */
+ return *camera_steps[STEP_PREVIOUS] != *camera_steps[STEP_CURRENT];
}
-}
-
-void VelocityView::resolve(GPUTexture *depth_tx)
-{
- inst_.velocity.resolve_camera_motion(depth_tx, velocity_view_tx_, velocity_camera_tx_);
-}
-
-void VelocityView::release()
-{
- velocity_view_tx_.release();
- velocity_camera_tx_.release();
+ return *camera_steps[STEP_PREVIOUS] != *camera_steps[STEP_CURRENT] &&
+ *camera_steps[STEP_NEXT] != *camera_steps[STEP_CURRENT];
}
/** \} */
diff --git a/source/blender/draw/engines/eevee_next/eevee_velocity.hh b/source/blender/draw/engines/eevee_next/eevee_velocity.hh
index e2606c061e1..826cd631a96 100644
--- a/source/blender/draw/engines/eevee_next/eevee_velocity.hh
+++ b/source/blender/draw/engines/eevee_next/eevee_velocity.hh
@@ -27,8 +27,6 @@ namespace blender::eevee {
/** Container for scene velocity data. */
class VelocityModule {
- friend class VelocityView;
-
public:
struct VelocityObjectData : public VelocityIndex {
/** ID to retrieve the corresponding #VelocityGeometryData after copy. */
@@ -69,15 +67,6 @@ class VelocityModule {
eVelocityStep step_ = STEP_CURRENT;
- DRWPass *resolve_ps_ = nullptr;
-
- /** Reference only. Not owned. */
- GPUTexture *input_depth_tx_;
- GPUTexture *velocity_view_tx_;
- GPUTexture *velocity_camera_tx_;
-
- int3 resolve_dispatch_size_ = int3(1, 1, 1);
-
public:
VelocityModule(Instance &inst) : inst_(inst)
{
@@ -89,6 +78,7 @@ class VelocityModule {
}
for (CameraDataBuf *&step_buf : camera_steps) {
step_buf = new CameraDataBuf();
+ /* */
}
};
@@ -121,56 +111,11 @@ class VelocityModule {
void bind_resources(DRWShadingGroup *grp);
+ bool camera_has_motion() const;
+
private:
bool object_has_velocity(const Object *ob);
bool object_is_deform(const Object *ob);
-
- void resolve_camera_motion(GPUTexture *depth_tx,
- GPUTexture *velocity_view_tx,
- GPUTexture *velocity_camera_tx);
-};
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Velocity
- *
- * \{ */
-
-/**
- * Per view module.
- */
-class VelocityView {
- private:
- Instance &inst_;
-
- StringRefNull view_name_;
-
- TextureFromPool velocity_camera_tx_ = {"velocity_camera_tx_"};
- TextureFromPool velocity_view_tx_ = {"velocity_view_tx_"};
-
- public:
- VelocityView(Instance &inst, const char *name) : inst_(inst), view_name_(name){};
- ~VelocityView(){};
-
- void sync();
-
- void acquire(int2 extent);
- void release();
-
- void resolve(GPUTexture *depth_tx);
-
- /**
- * Getters
- **/
- GPUTexture *view_vectors_get() const
- {
- return velocity_view_tx_;
- }
- GPUTexture *camera_vectors_get() const
- {
- return (velocity_camera_tx_.is_valid()) ? velocity_camera_tx_ : velocity_view_tx_;
- }
};
/** \} */
diff --git a/source/blender/draw/engines/eevee_next/eevee_view.cc b/source/blender/draw/engines/eevee_next/eevee_view.cc
index f4dba47721d..1a222dc4ebd 100644
--- a/source/blender/draw/engines/eevee_next/eevee_view.cc
+++ b/source/blender/draw/engines/eevee_next/eevee_view.cc
@@ -80,7 +80,6 @@ void ShadingView::sync()
// dof_.sync(winmat_p, extent_);
// mb_.sync(extent_);
- velocity_.sync();
// rt_buffer_opaque_.sync(extent_);
// rt_buffer_refract_.sync(extent_);
// inst_.hiz_back.view_sync(extent_);
@@ -103,18 +102,20 @@ void ShadingView::render()
RenderBuffers &rbufs = inst_.render_buffers;
rbufs.acquire(extent_, owner);
- velocity_.acquire(extent_);
combined_fb_.ensure(GPU_ATTACHMENT_TEXTURE(rbufs.depth_tx),
GPU_ATTACHMENT_TEXTURE(rbufs.combined_tx));
prepass_fb_.ensure(GPU_ATTACHMENT_TEXTURE(rbufs.depth_tx),
- GPU_ATTACHMENT_TEXTURE(velocity_.view_vectors_get()));
+ GPU_ATTACHMENT_TEXTURE(rbufs.vector_tx));
update_view();
DRW_stats_group_start(name_);
DRW_view_set_active(render_view_);
- float4 clear_velocity(VELOCITY_INVALID);
+ /* If camera has any motion, compute motion vector in the film pass. Otherwise, we avoid float
+ * precision issue by setting the motion of all static geometry to 0. */
+ float4 clear_velocity = float4(inst_.velocity.camera_has_motion() ? VELOCITY_INVALID : 0.0f);
+
GPU_framebuffer_bind(prepass_fb_);
GPU_framebuffer_clear_color(prepass_fb_, clear_velocity);
/* Alpha stores transmittance. So start at 1. */
@@ -137,18 +138,14 @@ void ShadingView::render()
// inst_.lights.debug_draw(view_fb_);
// inst_.shadows.debug_draw(view_fb_);
- velocity_.resolve(rbufs.depth_tx);
-
// GPUTexture *final_radiance_tx = render_post(combined_tx_);
inst_.film.accumulate(sub_view_);
rbufs.release();
+ postfx_tx_.release();
DRW_stats_group_end();
-
- postfx_tx_.release();
- velocity_.release();
}
GPUTexture *ShadingView::render_post(GPUTexture *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 30e06df9716..c6faebdd0e5 100644
--- a/source/blender/draw/engines/eevee_next/eevee_view.hh
+++ b/source/blender/draw/engines/eevee_next/eevee_view.hh
@@ -44,7 +44,6 @@ class ShadingView {
/** Post-FX modules. */
// DepthOfField dof_;
// MotionBlur mb_;
- VelocityView velocity_;
/** Raytracing persistent buffers. Only opaque and refraction can have surface tracing. */
// RaytraceBuffer rt_buffer_opaque_;
@@ -69,7 +68,7 @@ class ShadingView {
public:
ShadingView(Instance &inst, const char *name, const float (*face_matrix)[4])
- : inst_(inst), name_(name), face_matrix_(face_matrix), velocity_(inst, name){};
+ : inst_(inst), name_(name), face_matrix_(face_matrix){};
~ShadingView(){};
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_camera_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_camera_lib.glsl
index f79e9102d76..2611f714b59 100644
--- a/source/blender/draw/engines/eevee_next/shaders/eevee_camera_lib.glsl
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_camera_lib.glsl
@@ -143,24 +143,10 @@ vec2 camera_uv_from_view(CameraData cam, vec3 vV)
}
}
-vec2 camera_uv_from_world(CameraData cam, vec3 V)
+vec2 camera_uv_from_world(CameraData cam, vec3 P)
{
- vec3 vV = transform_point(cam.viewmat, V);
- switch (cam.type) {
- default:
- case CAMERA_ORTHO:
- return camera_uv_from_view(cam.persmat, false, V);
- case CAMERA_PERSP:
- return camera_uv_from_view(cam.persmat, true, V);
- case CAMERA_PANO_EQUIRECT:
- return camera_equirectangular_from_direction(cam, vV);
- case CAMERA_PANO_EQUISOLID:
- /* ATTR_FALLTHROUGH; */
- case CAMERA_PANO_EQUIDISTANT:
- return camera_fisheye_from_direction(cam, vV);
- case CAMERA_PANO_MIRROR:
- return camera_mirror_ball_from_direction(cam, vV);
- }
+ vec3 vV = transform_direction(cam.viewmat, normalize(P));
+ return camera_uv_from_view(cam, vV);
}
/** \} */
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_film_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_film_frag.glsl
index 6716c0f126e..454c835673b 100644
--- a/source/blender/draw/engines/eevee_next/shaders/eevee_film_frag.glsl
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_film_frag.glsl
@@ -11,7 +11,7 @@ void main()
out_depth = imageLoad(depth_img, texel_film).r;
if (film_buf.display_id == -1) {
- out_color = imageLoad(in_combined_img, texel_film);
+ out_color = texelFetch(in_combined_tx, texel_film, 0);
}
else if (film_buf.display_is_value) {
out_color.rgb = imageLoad(value_accum_img, ivec3(texel_film, film_buf.display_id)).rrr;
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 03af34f27ef..451b8e8fca7 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
@@ -4,7 +4,9 @@
**/
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_camera_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_velocity_lib.glsl)
/* Return scene linear Z depth from the camera or radial depth for panoramic cameras. */
float film_depth_convert_to_scene(float depth)
@@ -16,6 +18,54 @@ 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)
+{
+ vec4 color = texelFetch(combined_tx, texel, 0);
+ /* Can we assume safe color from earlier pass? */
+ // color = safe_color(color);
+ /* 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);
+ return color;
+}
+
+/* Returns a weight based on Luma to reduce the flickering introduced by high energy pixels. */
+float film_luma_weight(float luma)
+{
+ /* 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. */
+ /* TODO(fclem): exposure weighting. */
+ return 1.0 / (4.0 + luma);
+}
+
/* -------------------------------------------------------------------- */
/** \name Filter
* \{ */
@@ -116,18 +166,18 @@ void film_sample_accum_mist(FilmSample samp, inout float accum)
accum += mist * samp.weight;
}
-void film_sample_accum_combined(FilmSample samp, inout vec4 accum)
+void film_sample_accum_combined(FilmSample samp, inout vec4 accum, inout float weight_accum)
{
if (film_buf.combined_id == -1) {
return;
}
- vec4 color = texelFetch(combined_tx, samp.texel, 0);
- /* Convert transmittance to opacity. */
- color.a = saturate(1.0 - color.a);
- /* TODO(fclem) Pre-expose. */
- color.rgb = log2(1.0 + color.rgb);
+ vec4 color = film_texelfetch_as_YCoCg_opacity(combined_tx, samp.texel);
+
+ /* Weight by luma to remove fireflies. */
+ float weight = film_luma_weight(color.x) * samp.weight;
- accum += color * samp.weight;
+ accum += color * weight;
+ weight_accum += weight;
}
/** \} */
@@ -156,46 +206,286 @@ float film_weight_load(ivec2 texel)
/* Repeat texture coordinates as the weight can be optimized to a small portion of the film. */
texel = texel % imageSize(in_weight_img).xy;
- if (film_buf.use_history == false) {
+ if (!film_buf.use_history || film_buf.use_reprojection) {
return 0.0;
}
return imageLoad(in_weight_img, ivec3(texel, WEIGHT_lAYER_ACCUMULATION)).x;
}
-/* Return the motion in pixels. */
-void film_motion_load()
+/* Returns motion in pixel space to retrieve the pixel history. */
+vec2 film_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 = 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);
+ float depth = texelFetch(depth_tx, texel, 0).x;
+ if (min_depth > depth) {
+ min_depth = depth;
+ nearest_texel = texel;
+ }
+ }
+
+ vec4 vector = velocity_resolve(vector_tx, nearest_texel, min_depth);
+
+ /* Transform to pixel space. */
+ vector.xy *= vec2(film_buf.extent);
+
+ return vector.xy;
+}
+
+/* \a t is inter-pixel position. 0 means perfectly on a pixel center.
+ * Returns weights in both dimensions.
+ * Multiply each dimension weights to get final pixel weights. */
+void film_get_catmull_rom_weights(vec2 t, out vec2 weights[4])
+{
+ vec2 t2 = t * t;
+ vec2 t3 = t2 * t;
+ float fc = 0.5; /* Catmull-Rom. */
+
+ vec2 fct = t * fc;
+ vec2 fct2 = t2 * fc;
+ vec2 fct3 = t3 * fc;
+ weights[0] = (fct2 * 2.0 - fct3) - fct;
+ weights[1] = (t3 * 2.0 - fct3) + (-t2 * 3.0 + fct2) + 1.0;
+ weights[2] = (-t3 * 2.0 + fct3) + (t2 * 3.0 - (2.0 * fct2)) + fct;
+ weights[3] = fct3 - fct2;
+}
+
+/* Load color using a special filter to avoid loosing detail.
+ * \a texel is sample position with subpixel accuracy. */
+vec4 film_sample_catmull_rom(sampler2D color_tx, vec2 input_texel)
+{
+ vec2 center_texel;
+ vec2 inter_texel = modf(input_texel, center_texel);
+ vec2 weights[4];
+ film_get_catmull_rom_weights(inter_texel, weights);
+
+#if 0 /* Reference. 16 Taps. */
+ vec4 color = vec4(0.0);
+ for (int y = 0; y < 4; y++) {
+ for (int x = 0; x < 4; x++) {
+ ivec2 texel = ivec2(center_texel) + ivec2(x, y) - 1;
+ texel = clamp(texel, ivec2(0), textureSize(color_tx, 0).xy - 1);
+ color += texelFetch(color_tx, texel, 0) * weights[x].x * weights[y].y;
+ }
+ }
+ return color;
+
+#elif 1 /* Optimize version. 5 Bilinear Taps. */
+ /**
+ * 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(color_tx, uv_12, 0.0) * weight_center;
+ color += textureLod(color_tx, vec2(uv_12.x, uv_0.y), 0.0) * weight_cross.x;
+ color += textureLod(color_tx, vec2(uv_0.x, uv_12.y), 0.0) * weight_cross.y;
+ color += textureLod(color_tx, vec2(uv_3.x, uv_12.y), 0.0) * weight_cross.z;
+ color += textureLod(color_tx, vec2(uv_12.x, uv_3.y), 0.0) * weight_cross.w;
+ /* Re-normalize for the removed corners. */
+ return color / (weight_center + sum(weight_cross));
+
+#else /* Nearest interpolation for debugging. 1 Tap. */
+ ivec2 texel = ivec2(center_texel) + ivec2(greaterThan(inter_texel, vec2(0.5)));
+ texel = clamp(texel, ivec2(0), textureSize(color_tx, 0).xy - 1);
+ return texelFetch(color_tx, texel, 0);
+#endif
+}
+
+/* Return history clipping bounding box in YCoCg color space. */
+void film_combined_neighbor_boundbox(ivec2 texel, out vec4 min_c, out vec4 max_c)
{
- // ivec2 texel_sample = film_sample_get(0, texel_film, distance_sample);
- // vec4 vector = texelFetch(vector_tx, texel_sample);
+ /* Plus (+) shape offsets. */
+ const ivec2 plus_offsets[5] = ivec2[5](ivec2(0, 0), /* Center */
+ ivec2(-1, 0),
+ ivec2(0, -1),
+ ivec2(1, 0),
+ ivec2(0, 1));
+#if 0
+ /**
+ * Compute Variance of neighborhood as described in:
+ * "An Excursion in Temporal Supersampling" by Marco Salvi at GDC 2016.
+ * and:
+ * "A Survey of Temporal Antialiasing Techniques" by Yang et al.
+ */
+
+ /* First 2 moments. */
+ vec4 mu1 = vec4(0), mu2 = vec4(0);
+ for (int i = 0; i < 5; i++) {
+ vec4 color = film_texelfetch_as_YCoCg_opacity(combined_tx, texel + plus_offsets[i]);
+ mu1 += color;
+ mu2 += sqr(color);
+ }
+ mu1 *= (1.0 / 5.0);
+ mu2 *= (1.0 / 5.0);
+
+ /* Extent scaling. Range [0.75..1.25].
+ * Balance between more flickering (0.75) or more ghosting (1.25). */
+ const float gamma = 1.25;
+ /* Standard deviation. */
+ vec4 sigma = sqrt(abs(mu2 - sqr(mu1)));
+ /* eq. 6 in "A Survey of Temporal Antialiasing Techniques". */
+ min_c = mu1 - gamma * sigma;
+ max_c = mu1 + gamma * sigma;
+#else
+ /**
+ * Simple bounding box calculation in YCoCg as described in:
+ * "High Quality Temporal Supersampling" by Brian Karis at Siggraph 2014
+ */
+ min_c = vec4(1e16);
+ max_c = vec4(-1e16);
+ for (int i = 0; i < 5; i++) {
+ vec4 color = film_texelfetch_as_YCoCg_opacity(combined_tx, texel + plus_offsets[i]);
+ min_c = min(min_c, color);
+ max_c = max(max_c, color);
+ }
+ /* (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. */
+ vec4 min_c_3x3 = min_c;
+ vec4 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++) {
+ vec4 color = film_texelfetch_as_YCoCg_opacity(combined_tx, texel + corners[i]);
+ min_c_3x3 = min(min_c_3x3, color);
+ max_c_3x3 = max(max_c_3x3, color);
+ }
+ min_c = (min_c + min_c_3x3) * 0.5;
+ max_c = (max_c + max_c_3x3) * 0.5;
+#endif
+}
- // vector.xy *= film_buf.extent;
+/* 1D equivalent of line_aabb_clipping_dist(). */
+float film_aabb_clipping_dist_alpha(float origin, float direction, float aabb_min, float aabb_max)
+{
+ if (abs(direction) < 1e-5) {
+ return 0.0;
+ }
+ float nearest_plane = (direction > 0.0) ? aabb_min : aabb_max;
+ return (nearest_plane - origin) / direction;
+}
+
+/* Modulate the history color to avoid ghosting artifact. */
+vec4 film_amend_combined_history(vec4 color_history, vec4 src_color, ivec2 src_texel)
+{
+ /* Get local color bounding box of source neighboorhood. */
+ vec4 min_color, max_color;
+ film_combined_neighbor_boundbox(src_texel, min_color, max_color);
+
+ /* Clip instead of clamping to avoid color accumulating in the AABB corners. */
+ vec4 clip_dir = src_color - color_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. */
+ 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);
+
+ return color_history;
+}
+
+float film_history_blend_factor(float velocity,
+ vec2 texel,
+ float luma_incoming,
+ float luma_history)
+{
+ /* 5% of incoming color by default. */
+ float blend = 0.05;
+ /* Blend less history if the pixel has substential velocity. */
+ blend = mix(blend, 0.20, saturate(velocity * 0.02));
+ /* Weight by luma. */
+ blend = max(blend, saturate(0.01 * luma_history / abs(luma_history - luma_incoming)));
+ /* Discard out of view history. */
+ if (any(lessThan(texel, vec2(0))) || any(greaterThanEqual(texel, film_buf.extent))) {
+ blend = 1.0;
+ }
+ /* Discard history if invalid. */
+ if (film_buf.use_history == false) {
+ blend = 1.0;
+ }
+ return blend;
}
/* Returns resolved final color. */
-void film_store_combined(FilmSample dst, vec4 color, inout vec4 display)
+void film_store_combined(
+ FilmSample dst, ivec2 src_texel, vec4 color, float color_weight, inout vec4 display)
{
if (film_buf.combined_id == -1) {
return;
}
- /* Could we assume safe color from earlier pass? */
- color = safe_color(color);
- if (false) {
- /* Re-projection using motion vectors. */
- // ivec2 texel_combined = texel_film + film_motion_load(texel_film);
- // float weight_combined = film_weight_load(texel_combined);
+ vec4 color_src, color_dst;
+ float weight_src, weight_dst;
+
+ /* Undo the weighting to get final spatialy-filtered color. */
+ color_src = color / color_weight;
+
+ if (film_buf.use_reprojection) {
+ /* Interactive accumulation. Do reprojection and Temporal Anti-Aliasing. */
+
+ /* Reproject by finding where this pixel was in the previous frame. */
+ vec2 motion = film_pixel_history_motion_vector(dst.texel);
+ vec2 history_texel = vec2(dst.texel) + motion;
+
+ float velocity = length(motion);
+
+ /* Load weight if it is not uniform accross 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);
+
+ float blend = film_history_blend_factor(velocity, history_texel, color_src.x, color_dst.x);
+
+ color_dst = film_amend_combined_history(color_dst, color_src, src_texel);
+
+ /* Luma weighted blend to avoid flickering. */
+ weight_dst = film_luma_weight(color_dst.x) * (1.0 - blend);
+ weight_src = film_luma_weight(color_src.x) * (blend);
}
-#ifdef USE_NEIGHBORHOOD_CLAMPING
- /* Only do that for combined pass as it has a non-negligeable performance impact. */
- // color = clamp_bbox(color, min, max);
-#endif
+ 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);
+
+ /* Luma weighted blend to avoid flickering. */
+ weight_dst = film_luma_weight(color_dst.x) * dst.weight;
+ weight_src = color_weight;
+ }
+ /* Weighted blend. */
+ color = color_dst * weight_dst + color_src * weight_src;
+ color /= weight_src + weight_dst;
- vec4 dst_color = imageLoad(in_combined_img, dst.texel);
+ color.rgb = film_scene_linear_from_YCoCg(color.rgb);
- color = (dst_color * dst.weight + color) * dst.weight_sum_inv;
+ /* Fix alpha not accumulating to 1 because of float imprecision. */
+ if (color.a > 0.995) {
+ color.a = 1.0;
+ }
- /* TODO(fclem) undo Pre-expose. */
- // color.rgb = exp2(color.rgb) - 1.0;
+ /* Filter NaNs. */
+ if (any(isnan(color))) {
+ color = vec4(0.0, 0.0, 0.0, 1.0);
+ }
if (film_buf.display_id == -1) {
display = color;
@@ -213,6 +503,11 @@ void film_store_color(FilmSample dst, int pass_id, vec4 color, inout vec4 displa
color = (data_film * dst.weight + color) * dst.weight_sum_inv;
+ /* Filter NaNs. */
+ if (any(isnan(color))) {
+ color = vec4(0.0, 0.0, 0.0, 1.0);
+ }
+
if (film_buf.display_id == pass_id) {
display = color;
}
@@ -229,6 +524,11 @@ void film_store_value(FilmSample dst, int pass_id, float value, inout vec4 displ
value = (data_film * dst.weight + value) * dst.weight_sum_inv;
+ /* Filter NaNs. */
+ if (isnan(value)) {
+ value = 0.0;
+ }
+
if (film_buf.display_id == pass_id) {
display = vec4(value, value, value, 1.0);
}
@@ -290,16 +590,28 @@ void film_process_data(ivec2 texel_film, out vec4 out_color, out float out_depth
/* NOTE: We split the accumulations into separate loops to avoid using too much registers and
* maximize occupancy. */
+ if (film_buf.combined_id != -1) {
+ /* NOTE: Do weight accumulation again since we use custom weights. */
+ float weight_accum = 0.0;
+ vec4 combined_accum = vec4(0.0);
+
+ for (int i = 0; i < film_buf.samples_len; i++) {
+ FilmSample src = film_sample_get(i, texel_film);
+ film_sample_accum_combined(src, combined_accum, weight_accum);
+ }
+ film_store_combined(dst, texel_film, combined_accum, weight_accum, out_color);
+ }
+
if (film_buf.has_data) {
- float film_weight = film_distance_load(texel_film);
+ float film_distance = film_distance_load(texel_film);
/* Get sample closest to target texel. It is always sample 0. */
FilmSample film_sample = film_sample_get(0, texel_film);
- if (film_sample.weight < film_weight) {
- float depth = texelFetch(depth_tx, film_sample.texel, 0).x;
+ if (film_buf.use_reprojection || film_sample.weight < film_distance) {
vec4 normal = texelFetch(normal_tx, film_sample.texel, 0);
- vec4 vector = texelFetch(vector_tx, film_sample.texel, 0);
+ float depth = texelFetch(depth_tx, film_sample.texel, 0).x;
+ vec4 vector = velocity_resolve(vector_tx, film_sample.texel, depth);
film_store_depth(texel_film, depth, out_depth);
film_store_data(texel_film, film_buf.normal_id, normal, out_color);
@@ -311,16 +623,6 @@ void film_process_data(ivec2 texel_film, out vec4 out_color, out float out_depth
}
}
- if (film_buf.combined_id != -1) {
- vec4 combined_accum = vec4(0.0);
-
- for (int i = 0; i < film_buf.samples_len; i++) {
- FilmSample src = film_sample_get(i, texel_film);
- film_sample_accum_combined(src, combined_accum);
- }
- film_store_combined(dst, combined_accum, out_color);
- }
-
if (film_buf.any_render_pass_1) {
vec4 diffuse_light_accum = vec4(0.0);
vec4 specular_light_accum = vec4(0.0);
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_depth_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_depth_frag.glsl
index 7ddf941df7c..f19b6038a6a 100644
--- a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_depth_frag.glsl
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_depth_frag.glsl
@@ -72,14 +72,7 @@ void main()
#endif
#ifdef MAT_VELOCITY
- vec4 out_velocity_camera; /* TODO(fclem): Panoramic cameras. */
- velocity_camera(interp.P + motion.prev,
- interp.P,
- interp.P - motion.next,
- out_velocity_camera,
- out_velocity_view);
-
- /* For testing in viewport. */
- out_velocity_view.zw = vec2(0.0);
+ out_velocity = velocity_surface(interp.P + motion.prev, interp.P, interp.P - motion.next);
+ out_velocity = velocity_pack(out_velocity);
#endif
}
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 435ae6658c9..fb9c9faaca2 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
@@ -4,21 +4,28 @@
#ifdef VELOCITY_CAMERA
+vec4 velocity_pack(vec4 data)
+{
+ return data * 0.01;
+}
+
+vec4 velocity_unpack(vec4 data)
+{
+ return data * 100.0;
+}
+
/**
* Given a triple of position, compute the previous and next motion vectors.
- * Returns uv space motion vectors in pairs (motion_prev.xy, motion_next.xy)
+ * Returns uv space motion vectors in pairs (motion_prev.xy, motion_next.xy).
*/
-vec4 velocity_view(vec3 P_prev, vec3 P, vec3 P_next)
+vec4 velocity_surface(vec3 P_prv, vec3 P, vec3 P_nxt)
{
- vec2 prev_uv, curr_uv, next_uv;
-
- prev_uv = transform_point(ProjectionMatrix, transform_point(camera_prev.viewmat, P_prev)).xy;
- curr_uv = transform_point(ViewProjectionMatrix, P).xy;
- next_uv = transform_point(ProjectionMatrix, transform_point(camera_next.viewmat, P_next)).xy;
+ /* NOTE: We don't use the drw_view.persmat to avoid adding the TAA jitter to the velocity. */
+ vec2 prev_uv = project_point(camera_prev.persmat, P_prv).xy;
+ vec2 curr_uv = project_point(camera_curr.persmat, P).xy;
+ vec2 next_uv = project_point(camera_next.persmat, P_nxt).xy;
- vec4 motion;
- motion.xy = prev_uv - curr_uv;
- motion.zw = curr_uv - next_uv;
+ vec4 motion = vec4(prev_uv - curr_uv, curr_uv - next_uv);
/* Convert NDC velocity to UV velocity */
motion *= 0.5;
@@ -26,37 +33,41 @@ vec4 velocity_view(vec3 P_prev, vec3 P, vec3 P_next)
}
/**
- * Given a triple of position, compute the previous and next motion vectors.
- * Returns uv space motion vectors in pairs (motion_prev.xy, motion_next.xy)
- * \a velocity_camera is the motion in film UV space after camera projection.
- * \a velocity_view is the motion in ShadingView UV space. It is different
- * from velocity_camera for multi-view rendering.
+ * Given a view space view vector \a vV, compute the previous and next motion vectors for
+ * background pixels.
+ * Returns uv space motion vectors in pairs (motion_prev.xy, motion_next.xy).
*/
-void velocity_camera(vec3 P_prev, vec3 P, vec3 P_next, out vec4 vel_camera, out vec4 vel_view)
+vec4 velocity_background(vec3 vV)
{
- vec2 prev_uv, curr_uv, next_uv;
- prev_uv = camera_uv_from_world(camera_prev, P_prev);
- curr_uv = camera_uv_from_world(camera_curr, P);
- next_uv = camera_uv_from_world(camera_next, P_next);
-
- vel_camera.xy = prev_uv - curr_uv;
- vel_camera.zw = curr_uv - next_uv;
-
- if (is_panoramic(camera_curr.type)) {
- /* This path is only used if using using panoramic projections. Since the views always have
- * the same 45° aperture angle, we can safely reuse the projection matrix. */
- prev_uv = transform_point(ProjectionMatrix, transform_point(camera_prev.viewmat, P_prev)).xy;
- curr_uv = transform_point(ViewProjectionMatrix, P).xy;
- next_uv = transform_point(ProjectionMatrix, transform_point(camera_next.viewmat, P_next)).xy;
-
- vel_view.xy = prev_uv - curr_uv;
- vel_view.zw = curr_uv - next_uv;
- /* Convert NDC velocity to UV velocity */
- vel_view *= 0.5;
- }
- else {
- vel_view = vel_camera;
+ /* Only transform direction to avoid loosing precision. */
+ vec3 V = transform_direction(camera_curr.viewinv, vV);
+
+ return velocity_surface(V, V, V);
+}
+
+/**
+ * Load and resolve correct velocity as some pixels might still not have correct
+ * motion data for performance reasons.
+ */
+vec4 velocity_resolve(sampler2D vector_tx, ivec2 texel, float depth)
+{
+ vec2 uv = (vec2(texel) + 0.5) / vec2(textureSize(vector_tx, 0).xy);
+ vec4 vector = texelFetch(vector_tx, texel, 0);
+
+ if (vector.x == VELOCITY_INVALID) {
+ bool is_background = (depth == 1.0);
+ if (is_background) {
+ /* NOTE: Use viewCameraVec to avoid imprecision if camera is far from origin. */
+ vec3 vV = viewCameraVec(get_view_space_from_depth(uv, 1.0));
+ return velocity_background(vV);
+ }
+ else {
+ /* Static geometry. No translation in world space. */
+ vec3 P = get_world_space_from_depth(uv, depth);
+ return velocity_surface(P, P, P);
+ }
}
+ return velocity_unpack(vector);
}
#endif
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_velocity_resolve_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_velocity_resolve_comp.glsl
deleted file mode 100644
index b68b2eaf117..00000000000
--- a/source/blender/draw/engines/eevee_next/shaders/eevee_velocity_resolve_comp.glsl
+++ /dev/null
@@ -1,58 +0,0 @@
-
-/**
- * Fullscreen pass that compute motion vector for static geometry.
- * Animated geometry has already written correct motion vectors.
- */
-
-#pragma BLENDER_REQUIRE(common_view_lib.glsl)
-#pragma BLENDER_REQUIRE(eevee_velocity_lib.glsl)
-
-#define is_valid_output(img_) (imageSize(img_).x > 1)
-
-void main()
-{
- ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
- vec4 motion = imageLoad(velocity_view_img, texel);
-
- bool pixel_has_valid_motion = (motion.x != VELOCITY_INVALID);
- float depth = texelFetch(depth_tx, texel, 0).r;
- bool is_background = (depth == 1.0f);
-
- vec2 uv = vec2(texel) * drw_view.viewport_size_inverse;
- vec3 P_next, P_prev, P_curr;
-
- if (pixel_has_valid_motion) {
- /* Animated geometry. View motion already computed during prepass. Convert only to camera. */
- // P_prev = get_world_space_from_depth(uv + motion.xy, 0.5);
- // P_curr = get_world_space_from_depth(uv, 0.5);
- // P_next = get_world_space_from_depth(uv + motion.zw, 0.5);
- return;
- }
- else if (is_background) {
- /* NOTE: Use viewCameraVec to avoid imprecision if camera is far from origin. */
- vec3 vV = viewCameraVec(get_view_space_from_depth(uv, 1.0));
- vec3 V = transform_direction(ViewMatrixInverse, vV);
- /* Background has no motion under camera translation. Translate view vector with the camera. */
- /* WATCH(fclem): Might create precision issues. */
- P_next = camera_next.viewinv[3].xyz + V;
- P_curr = camera_curr.viewinv[3].xyz + V;
- P_prev = camera_prev.viewinv[3].xyz + V;
- }
- else {
- /* Static geometry. No translation in world space. */
- P_curr = get_world_space_from_depth(uv, depth);
- P_prev = P_curr;
- P_next = P_curr;
- }
-
- vec4 vel_camera, vel_view;
- velocity_camera(P_prev, P_curr, P_next, vel_camera, vel_view);
-
- if (in_texture_range(texel, depth_tx)) {
- imageStore(velocity_view_img, texel, vel_view);
-
- if (is_valid_output(velocity_camera_img)) {
- imageStore(velocity_camera_img, texel, vel_camera);
- }
- }
-}
diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_film_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_film_info.hh
index eec7b8ae615..a5baaca51f9 100644
--- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_film_info.hh
+++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_film_info.hh
@@ -4,7 +4,7 @@
#include "gpu_shader_create_info.hh"
GPU_SHADER_CREATE_INFO(eevee_film)
- .uniform_buf(1, "FilmData", "film_buf")
+ .uniform_buf(4, "FilmData", "film_buf")
.sampler(0, ImageType::DEPTH_2D, "depth_tx")
.sampler(1, ImageType::FLOAT_2D, "combined_tx")
.sampler(2, ImageType::FLOAT_2D, "normal_tx")
@@ -20,15 +20,19 @@ GPU_SHADER_CREATE_INFO(eevee_film)
.sampler(12, ImageType::FLOAT_2D, "ambient_occlusion_tx")
.sampler(13, ImageType::FLOAT_2D_ARRAY, "aov_color_tx")
.sampler(14, ImageType::FLOAT_2D_ARRAY, "aov_value_tx")
+ /* Color History for TAA needs to be sampler to leverage bilinear sampling. */
+ .sampler(15, ImageType::FLOAT_2D, "in_combined_tx")
// .sampler(15, ImageType::FLOAT_2D, "cryptomatte_tx") /* TODO */
.image(0, GPU_R32F, Qualifier::READ, ImageType::FLOAT_2D_ARRAY, "in_weight_img")
.image(1, GPU_R32F, Qualifier::WRITE, ImageType::FLOAT_2D_ARRAY, "out_weight_img")
- .image(2, GPU_RGBA16F, Qualifier::READ, ImageType::FLOAT_2D, "in_combined_img")
+ /* Color History for TAA needs to be sampler to leverage bilinear sampling. */
+ //.image(2, GPU_RGBA16F, Qualifier::READ, ImageType::FLOAT_2D, "in_combined_img")
.image(3, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_combined_img")
.image(4, GPU_R32F, Qualifier::READ_WRITE, ImageType::FLOAT_2D, "depth_img")
.image(5, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_2D_ARRAY, "color_accum_img")
.image(6, GPU_R16F, Qualifier::READ_WRITE, ImageType::FLOAT_2D_ARRAY, "value_accum_img")
.additional_info("eevee_shared")
+ .additional_info("eevee_velocity_camera")
.additional_info("draw_view");
GPU_SHADER_CREATE_INFO(eevee_film_frag)
diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_velocity_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_velocity_info.hh
index c6cbf9b1456..6e8e8fb020a 100644
--- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_velocity_info.hh
+++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_velocity_info.hh
@@ -31,26 +31,7 @@ GPU_SHADER_CREATE_INFO(eevee_velocity_geom)
.storage_buf(
7, Qualifier::READ, "VelocityIndex", "velocity_indirection_buf[]", Frequency::PASS)
.vertex_out(eevee_velocity_surface_iface)
- .fragment_out(0, Type::VEC4, "out_velocity_view")
+ .fragment_out(0, Type::VEC4, "out_velocity")
.additional_info("eevee_velocity_camera");
/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Velocity Resolve
- *
- * Computes velocity for static objects.
- * Also converts motion to camera space (as opposed to view space) if needed.
- * \{ */
-
-GPU_SHADER_CREATE_INFO(eevee_velocity_resolve)
- .do_static_compilation(true)
- .local_group_size(8, 8)
- .sampler(0, ImageType::DEPTH_2D, "depth_tx")
- .image(0, GPU_RG16F, Qualifier::READ_WRITE, ImageType::FLOAT_2D, "velocity_view_img")
- .image(1, GPU_RG16F, Qualifier::WRITE, ImageType::FLOAT_2D, "velocity_camera_img")
- .additional_info("eevee_shared")
- .compute_source("eevee_velocity_resolve_comp.glsl")
- .additional_info("draw_view", "eevee_velocity_camera");
-
-/** \} */
diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h
index bb913c53500..c2d26badc4c 100644
--- a/source/blender/draw/intern/DRW_render.h
+++ b/source/blender/draw/intern/DRW_render.h
@@ -887,7 +887,6 @@ bool DRW_object_is_in_edit_mode(const struct Object *ob);
* we are rendering or drawing in the viewport.
*/
int DRW_object_visibility_in_active_context(const struct Object *ob);
-bool DRW_object_is_flat_normal(const struct Object *ob);
bool DRW_object_use_hide_faces(const struct Object *ob);
bool DRW_object_is_visible_psys_in_active_context(const struct Object *object,
diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c
index 5d21ab75650..b2422504825 100644
--- a/source/blender/draw/intern/draw_manager.c
+++ b/source/blender/draw/intern/draw_manager.c
@@ -212,17 +212,6 @@ int DRW_object_visibility_in_active_context(const Object *ob)
return BKE_object_visibility(ob, mode);
}
-bool DRW_object_is_flat_normal(const Object *ob)
-{
- if (ob->type == OB_MESH) {
- const Mesh *me = ob->data;
- if (me->mpoly && me->mpoly[0].flag & ME_SMOOTH) {
- return false;
- }
- }
- return true;
-}
-
bool DRW_object_use_hide_faces(const struct Object *ob)
{
if (ob->type == OB_MESH) {
diff --git a/source/blender/draw/intern/draw_volume.cc b/source/blender/draw/intern/draw_volume.cc
index c4e58ab24cb..8f4383a98d8 100644
--- a/source/blender/draw/intern/draw_volume.cc
+++ b/source/blender/draw/intern/draw_volume.cc
@@ -89,6 +89,10 @@ void DRW_volume_free(void)
static GPUTexture *grid_default_texture(eGPUDefaultValue default_value)
{
+ if (g_data.dummy_one == nullptr) {
+ drw_volume_globals_init();
+ }
+
switch (default_value) {
case GPU_DEFAULT_0:
return g_data.dummy_zero;
diff --git a/source/blender/draw/intern/shaders/common_math_geom_lib.glsl b/source/blender/draw/intern/shaders/common_math_geom_lib.glsl
index 6d4452c18c8..ae82277d9a6 100644
--- a/source/blender/draw/intern/shaders/common_math_geom_lib.glsl
+++ b/source/blender/draw/intern/shaders/common_math_geom_lib.glsl
@@ -5,63 +5,71 @@
/** \name Math intersection & projection functions.
* \{ */
-float point_plane_projection_dist(vec3 lineorigin, vec3 planeorigin, vec3 planenormal)
+float point_plane_projection_dist(vec3 line_origin, vec3 plane_origin, vec3 plane_normal)
{
- return dot(planenormal, planeorigin - lineorigin);
+ return dot(plane_normal, plane_origin - line_origin);
}
-float line_plane_intersect_dist(vec3 lineorigin,
- vec3 linedirection,
- vec3 planeorigin,
- vec3 planenormal)
+float line_plane_intersect_dist(vec3 line_origin,
+ vec3 line_direction,
+ vec3 plane_origin,
+ vec3 plane_normal)
{
- return dot(planenormal, planeorigin - lineorigin) / dot(planenormal, linedirection);
+ return dot(plane_normal, plane_origin - line_origin) / dot(plane_normal, line_direction);
}
-float line_plane_intersect_dist(vec3 lineorigin, vec3 linedirection, vec4 plane)
+float line_plane_intersect_dist(vec3 line_origin, vec3 line_direction, vec4 plane)
{
vec3 plane_co = plane.xyz * (-plane.w / len_squared(plane.xyz));
- vec3 h = lineorigin - plane_co;
- return -dot(plane.xyz, h) / dot(plane.xyz, linedirection);
+ vec3 h = line_origin - plane_co;
+ return -dot(plane.xyz, h) / dot(plane.xyz, line_direction);
}
-vec3 line_plane_intersect(vec3 lineorigin, vec3 linedirection, vec3 planeorigin, vec3 planenormal)
+vec3 line_plane_intersect(vec3 line_origin,
+ vec3 line_direction,
+ vec3 plane_origin,
+ vec3 plane_normal)
{
- float dist = line_plane_intersect_dist(lineorigin, linedirection, planeorigin, planenormal);
- return lineorigin + linedirection * dist;
+ float dist = line_plane_intersect_dist(line_origin, line_direction, plane_origin, plane_normal);
+ return line_origin + line_direction * dist;
}
-vec3 line_plane_intersect(vec3 lineorigin, vec3 linedirection, vec4 plane)
+vec3 line_plane_intersect(vec3 line_origin, vec3 line_direction, vec4 plane)
{
- float dist = line_plane_intersect_dist(lineorigin, linedirection, plane);
- return lineorigin + linedirection * dist;
+ float dist = line_plane_intersect_dist(line_origin, line_direction, plane);
+ return line_origin + line_direction * dist;
}
-float line_aligned_plane_intersect_dist(vec3 lineorigin, vec3 linedirection, vec3 planeorigin)
+float line_aligned_plane_intersect_dist(vec3 line_origin, vec3 line_direction, vec3 plane_origin)
{
/* aligned plane normal */
- vec3 L = planeorigin - lineorigin;
- float diskdist = length(L);
- vec3 planenormal = -normalize(L);
- return -diskdist / dot(planenormal, linedirection);
+ vec3 L = plane_origin - line_origin;
+ float disk_dist = length(L);
+ vec3 plane_normal = -normalize(L);
+ return -disk_dist / dot(plane_normal, line_direction);
}
-vec3 line_aligned_plane_intersect(vec3 lineorigin, vec3 linedirection, vec3 planeorigin)
+vec3 line_aligned_plane_intersect(vec3 line_origin, vec3 line_direction, vec3 plane_origin)
{
- float dist = line_aligned_plane_intersect_dist(lineorigin, linedirection, planeorigin);
+ float dist = line_aligned_plane_intersect_dist(line_origin, line_direction, plane_origin);
if (dist < 0) {
/* if intersection is behind we fake the intersection to be
* really far and (hopefully) not inside the radius of interest */
dist = 1e16;
}
- return lineorigin + linedirection * dist;
+ return line_origin + line_direction * dist;
}
-float line_unit_sphere_intersect_dist(vec3 lineorigin, vec3 linedirection)
+/**
+ * Returns intersection distance between the unit sphere and the line
+ * with the assumption that \a line_origin is contained in the unit sphere.
+ * It will always returns the farthest intersection.
+ */
+float line_unit_sphere_intersect_dist(vec3 line_origin, vec3 line_direction)
{
- float a = dot(linedirection, linedirection);
- float b = dot(linedirection, lineorigin);
- float c = dot(lineorigin, lineorigin) - 1;
+ float a = dot(line_direction, line_direction);
+ float b = dot(line_direction, line_origin);
+ float c = dot(line_origin, line_origin) - 1;
float dist = 1e15;
float determinant = b * b - a * c;
@@ -72,22 +80,44 @@ float line_unit_sphere_intersect_dist(vec3 lineorigin, vec3 linedirection)
return dist;
}
-float line_unit_box_intersect_dist(vec3 lineorigin, vec3 linedirection)
+/**
+ * Returns minimum intersection distance between the unit box and the line
+ * with the assumption that \a line_origin is contained in the unit box.
+ * In other words, it will always returns the farthest intersection.
+ */
+float line_unit_box_intersect_dist(vec3 line_origin, vec3 line_direction)
{
/* https://seblagarde.wordpress.com/2012/09/29/image-based-lighting-approaches-and-parallax-corrected-cubemap/
*/
- vec3 firstplane = (vec3(1.0) - lineorigin) / linedirection;
- vec3 secondplane = (vec3(-1.0) - lineorigin) / linedirection;
- vec3 furthestplane = max(firstplane, secondplane);
+ vec3 first_plane = (vec3(1.0) - line_origin) / line_direction;
+ vec3 second_plane = (vec3(-1.0) - line_origin) / line_direction;
+ vec3 farthest_plane = max(first_plane, second_plane);
- return min_v3(furthestplane);
+ return min_v3(farthest_plane);
}
-float line_unit_box_intersect_dist_safe(vec3 lineorigin, vec3 linedirection)
+float line_unit_box_intersect_dist_safe(vec3 line_origin, vec3 line_direction)
{
- vec3 safe_linedirection = max(vec3(1e-8), abs(linedirection)) *
- select(vec3(1.0), -vec3(1.0), lessThan(linedirection, vec3(0.0)));
- return line_unit_box_intersect_dist(lineorigin, safe_linedirection);
+ vec3 safe_line_direction = max(vec3(1e-8), abs(line_direction)) *
+ select(vec3(1.0), -vec3(1.0), lessThan(line_direction, vec3(0.0)));
+ return line_unit_box_intersect_dist(line_origin, safe_line_direction);
+}
+
+/**
+ * Returns clipping distance (intersection with the nearest plane) with the given axis-aligned
+ * bound box along \a line_direction.
+ * Safe even if \a line_direction is degenerate.
+ * It assumes that an intersection exists (i.e: that \a line_direction points towards the AABB).
+ */
+float line_aabb_clipping_dist(vec3 line_origin, vec3 line_direction, vec3 aabb_min, vec3 aabb_max)
+{
+ vec3 safe_dir = select(line_direction, vec3(1e-5), lessThan(abs(line_direction), vec3(1e-5)));
+ vec3 dir_inv = 1.0 / safe_dir;
+
+ vec3 first_plane = (aabb_min - line_origin) * dir_inv;
+ vec3 second_plane = (aabb_max - line_origin) * dir_inv;
+ vec3 nearest_plane = min(first_plane, second_plane);
+ return max_v3(nearest_plane);
}
/** \} */
@@ -98,8 +128,8 @@ float line_unit_box_intersect_dist_safe(vec3 lineorigin, vec3 linedirection)
void make_orthonormal_basis(vec3 N, out vec3 T, out vec3 B)
{
- vec3 UpVector = abs(N.z) < 0.99999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
- T = normalize(cross(UpVector, N));
+ vec3 up_vector = abs(N.z) < 0.99999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
+ T = normalize(cross(up_vector, N));
B = cross(N, T);
}
diff --git a/source/blender/editors/curve/editcurve_paint.c b/source/blender/editors/curve/editcurve_paint.c
index 6946c09e6f1..7632f1b1e64 100644
--- a/source/blender/editors/curve/editcurve_paint.c
+++ b/source/blender/editors/curve/editcurve_paint.c
@@ -1162,7 +1162,7 @@ static int curve_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
curve_draw_event_add_first(op, event);
}
}
- else if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+ else if (ISMOUSE_MOTION(event->type)) {
if (cdd->state == CURVE_DRAW_PAINTING) {
const float mval_fl[2] = {UNPACK2(event->mval)};
if (len_squared_v2v2(mval_fl, cdd->prev.mval) > square_f(STROKE_SAMPLE_DIST_MIN_PX)) {
diff --git a/source/blender/editors/curve/editcurve_pen.c b/source/blender/editors/curve/editcurve_pen.c
index 729ad46877a..27f4e4fca61 100644
--- a/source/blender/editors/curve/editcurve_pen.c
+++ b/source/blender/editors/curve/editcurve_pen.c
@@ -1622,7 +1622,7 @@ static int curve_pen_modal(bContext *C, wmOperator *op, const wmEvent *event)
}
}
- if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+ if (ISMOUSE_MOTION(event->type)) {
/* Check if dragging */
if (!cpd->dragging && WM_event_drag_test(event, event->prev_press_xy)) {
cpd->dragging = true;
diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c
index 2d613e2f433..d08d56a354a 100644
--- a/source/blender/editors/gpencil/annotate_paint.c
+++ b/source/blender/editors/gpencil/annotate_paint.c
@@ -2641,7 +2641,7 @@ static int annotation_draw_modal(bContext *C, wmOperator *op, const wmEvent *eve
/* handle mode-specific events */
if (p->status == GP_STATUS_PAINTING) {
/* handle painting mouse-movements? */
- if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE) || (p->flags & GP_PAINTFLAG_FIRSTRUN)) {
+ if (ISMOUSE_MOTION(event->type) || (p->flags & GP_PAINTFLAG_FIRSTRUN)) {
/* handle drawing event */
if ((p->flags & GP_PAINTFLAG_FIRSTRUN) == 0) {
annotation_add_missing_events(C, op, event, p);
diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c
index 70486138556..13ea5179b23 100644
--- a/source/blender/editors/gpencil/gpencil_paint.c
+++ b/source/blender/editors/gpencil/gpencil_paint.c
@@ -3755,7 +3755,7 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
/* handle mode-specific events */
if (p->status == GP_STATUS_PAINTING) {
/* handle painting mouse-movements? */
- if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE) || (p->flags & GP_PAINTFLAG_FIRSTRUN)) {
+ if (ISMOUSE_MOTION(event->type) || (p->flags & GP_PAINTFLAG_FIRSTRUN)) {
/* handle drawing event */
bool is_speed_guide = ((guide->use_guide) &&
(p->brush && (p->brush->gpencil_tool == GPAINT_TOOL_DRAW)));
diff --git a/source/blender/editors/include/ED_curves_sculpt.h b/source/blender/editors/include/ED_curves_sculpt.h
index 8aab1533e25..b1c0b649d2b 100644
--- a/source/blender/editors/include/ED_curves_sculpt.h
+++ b/source/blender/editors/include/ED_curves_sculpt.h
@@ -10,8 +10,33 @@
extern "C" {
#endif
+struct Curves;
+
void ED_operatortypes_sculpt_curves(void);
#ifdef __cplusplus
}
#endif
+
+#ifdef __cplusplus
+
+# include "BLI_index_mask.hh"
+# include "BLI_vector.hh"
+
+namespace blender::ed::sculpt_paint {
+
+/**
+ * Find curves that have any point selected (a selection factor greater than zero),
+ * or curves that have their own selection factor greater than zero.
+ */
+IndexMask retrieve_selected_curves(const Curves &curves_id, Vector<int64_t> &r_indices);
+
+/**
+ * Find points that are selected (a selection factor greater than zero),
+ * or points in curves with a selection factor greater than zero).
+ */
+IndexMask retrieve_selected_points(const Curves &curves_id, Vector<int64_t> &r_indices);
+
+} // namespace blender::ed::sculpt_paint
+
+#endif
diff --git a/source/blender/editors/interface/eyedroppers/eyedropper_color.c b/source/blender/editors/interface/eyedroppers/eyedropper_color.c
index a2161008ec4..9c430afd5f0 100644
--- a/source/blender/editors/interface/eyedroppers/eyedropper_color.c
+++ b/source/blender/editors/interface/eyedroppers/eyedropper_color.c
@@ -479,7 +479,7 @@ static int eyedropper_modal(bContext *C, wmOperator *op, const wmEvent *event)
break;
}
}
- else if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+ else if (ISMOUSE_MOTION(event->type)) {
if (eye->accum_start) {
/* button is pressed so keep sampling */
eyedropper_color_sample(C, eye, event->xy);
diff --git a/source/blender/editors/interface/eyedroppers/eyedropper_driver.c b/source/blender/editors/interface/eyedroppers/eyedropper_driver.c
index 14c00c21a5c..a9314df44a5 100644
--- a/source/blender/editors/interface/eyedroppers/eyedropper_driver.c
+++ b/source/blender/editors/interface/eyedroppers/eyedropper_driver.c
@@ -83,14 +83,14 @@ static void driverdropper_sample(bContext *C, wmOperator *op, const wmEvent *eve
if (but == NULL) {
return;
}
- /* Get paths for src... */
+ /* Get paths for the source. */
PointerRNA *target_ptr = &but->rnapoin;
PropertyRNA *target_prop = but->rnaprop;
const int target_index = but->rnaindex;
char *target_path = RNA_path_from_ID_to_property(target_ptr, target_prop);
- /* ... and destination */
+ /* Get paths for the destination. */
char *dst_path = RNA_path_from_ID_to_property(&ddr->ptr, ddr->prop);
/* Now create driver(s) */
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
index 0a50522a141..80fd0cbe16e 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -4509,7 +4509,7 @@ static int ui_do_but_HOTKEYEVT(bContext *C,
}
}
else if (data->state == BUTTON_STATE_WAIT_KEY_EVENT) {
- if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+ if (ISMOUSE_MOTION(event->type)) {
return WM_UI_HANDLER_CONTINUE;
}
if (event->type == EVT_UNKNOWNKEY) {
@@ -4575,7 +4575,7 @@ static int ui_do_but_KEYEVT(bContext *C,
}
}
else if (data->state == BUTTON_STATE_WAIT_KEY_EVENT) {
- if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+ if (ISMOUSE_MOTION(event->type)) {
return WM_UI_HANDLER_CONTINUE;
}
@@ -8097,7 +8097,7 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent *
#ifdef USE_DRAG_MULTINUM
data = but->active;
if (data) {
- if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE) ||
+ if (ISMOUSE_MOTION(event->type) ||
/* if we started dragging, progress on any event */
(data->multi_data.init == BUTTON_MULTI_INIT_SETUP)) {
if (ELEM(but->type, UI_BTYPE_NUM, UI_BTYPE_NUM_SLIDER) &&
@@ -10096,8 +10096,7 @@ static int ui_handle_menu_button(bContext *C, const wmEvent *event, uiPopupBlock
/* Pass, needed to click-exit outside of non-floating menus. */
ui_region_auto_open_clear(but->active->region);
}
- else if ((!ELEM(event->type, MOUSEMOVE, WHEELUPMOUSE, WHEELDOWNMOUSE, MOUSEPAN)) &&
- ISMOUSE(event->type)) {
+ else if (ISMOUSE_BUTTON(event->type)) {
if (!ui_but_contains_point_px(but, but->active->region, event->xy)) {
but = NULL;
}
diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c
index 87bfb7ca0f7..d4a9a4ca4cd 100644
--- a/source/blender/editors/interface/interface_panel.c
+++ b/source/blender/editors/interface/interface_panel.c
@@ -2318,7 +2318,7 @@ int ui_handler_panel_region(bContext *C,
const uiBut *active_but)
{
/* Mouse-move events are handled by separate handlers for dragging and drag collapsing. */
- if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+ if (ISMOUSE_MOTION(event->type)) {
return WM_UI_HANDLER_CONTINUE;
}
diff --git a/source/blender/editors/interface/views/tree_view.cc b/source/blender/editors/interface/views/tree_view.cc
index 21078b711c7..43fdf741ac5 100644
--- a/source/blender/editors/interface/views/tree_view.cc
+++ b/source/blender/editors/interface/views/tree_view.cc
@@ -168,7 +168,8 @@ void AbstractTreeViewItem::collapse_chevron_click_fn(struct bContext *C,
const wmWindow *win = CTX_wm_window(C);
const ARegion *region = CTX_wm_region(C);
- uiViewItemHandle *hovered_item_handle = UI_region_views_find_item_at(region, win->eventstate->xy);
+ uiViewItemHandle *hovered_item_handle = UI_region_views_find_item_at(region,
+ win->eventstate->xy);
AbstractTreeViewItem *hovered_item = from_item_handle<AbstractTreeViewItem>(hovered_item_handle);
BLI_assert(hovered_item != nullptr);
diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c
index 1febc429edc..c5add97fb00 100644
--- a/source/blender/editors/mesh/editmesh_tools.c
+++ b/source/blender/editors/mesh/editmesh_tools.c
@@ -8692,7 +8692,7 @@ static int edbm_point_normals_modal(bContext *C, wmOperator *op, const wmEvent *
* Free the data here, then use #point_normals_ensure to add it back on demand. */
if (ret == OPERATOR_PASS_THROUGH) {
/* Don't free on mouse-move, causes creation/freeing of the loop data in an inefficient way. */
- if (!ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+ if (!ISMOUSE_MOTION(event->type)) {
point_normals_free(op);
}
}
diff --git a/source/blender/editors/object/object_add.cc b/source/blender/editors/object/object_add.cc
index a8e11afba65..9c34e203bee 100644
--- a/source/blender/editors/object/object_add.cc
+++ b/source/blender/editors/object/object_add.cc
@@ -2079,7 +2079,8 @@ static int object_curves_empty_hair_add_exec(bContext *C, wmOperator *op)
Object *surface_ob = CTX_data_active_object(C);
BLI_assert(surface_ob != nullptr);
- Object *curves_ob = ED_object_add_type(C, OB_CURVES, nullptr, nullptr, nullptr, false, local_view_bits);
+ Object *curves_ob = ED_object_add_type(
+ C, OB_CURVES, nullptr, nullptr, nullptr, false, local_view_bits);
BKE_object_apply_mat4(curves_ob, surface_ob->obmat, false, false);
/* Set surface object. */
diff --git a/source/blender/editors/object/object_modifier.cc b/source/blender/editors/object/object_modifier.cc
index 69edd00ae24..e7cfcf48fd3 100644
--- a/source/blender/editors/object/object_modifier.cc
+++ b/source/blender/editors/object/object_modifier.cc
@@ -818,7 +818,7 @@ static bool modifier_apply_obdata(
/* Create a temporary geometry set and component. */
GeometrySet geometry_set;
geometry_set.get_component_for_write<CurveComponent>().replace(
- &curves, GeometryOwnershipType::Editable);
+ &curves, GeometryOwnershipType::ReadOnly);
ModifierEvalContext mectx = {depsgraph, ob, (ModifierApplyFlag)0};
mti->modifyGeometrySet(md_eval, &mectx, &geometry_set);
@@ -833,14 +833,11 @@ static bool modifier_apply_obdata(
.attributes_for_write()
.remove_anonymous();
- /* If the modifier's output is a different curves data-block, copy the relevant information to
- * the original. */
- if (&curves_eval != &curves) {
- blender::bke::CurvesGeometry::wrap(curves.geometry) = std::move(
- blender::bke::CurvesGeometry::wrap(curves_eval.geometry));
- Main *bmain = DEG_get_bmain(depsgraph);
- BKE_object_material_from_eval_data(bmain, ob, &curves_eval.id);
- }
+ /* Copy the relevant information to the original. */
+ blender::bke::CurvesGeometry::wrap(curves.geometry) = std::move(
+ blender::bke::CurvesGeometry::wrap(curves_eval.geometry));
+ Main *bmain = DEG_get_bmain(depsgraph);
+ BKE_object_material_from_eval_data(bmain, ob, &curves_eval.id);
}
else {
/* TODO: implement for point clouds and volumes. */
@@ -1439,7 +1436,6 @@ static int modifier_apply_exec_ex(bContext *C, wmOperator *op, int apply_as, boo
Scene *scene = CTX_data_scene(C);
Object *ob = ED_object_active_context(C);
ModifierData *md = edit_modifier_property_get(op, ob, 0);
- const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type);
const bool do_report = RNA_boolean_get(op->ptr, "report");
const bool do_single_user = RNA_boolean_get(op->ptr, "single_user");
const bool do_merge_customdata = RNA_boolean_get(op->ptr, "merge_customdata");
@@ -1448,6 +1444,8 @@ static int modifier_apply_exec_ex(bContext *C, wmOperator *op, int apply_as, boo
return OPERATOR_CANCELLED;
}
+ const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type);
+
if (do_single_user && ID_REAL_USERS(ob->data) > 1) {
ED_object_single_obdata_user(bmain, scene, ob);
BKE_main_id_newptr_and_tag_clear(bmain);
diff --git a/source/blender/editors/object/object_transform.cc b/source/blender/editors/object/object_transform.cc
index 70c3eed3768..c9c96900af3 100644
--- a/source/blender/editors/object/object_transform.cc
+++ b/source/blender/editors/object/object_transform.cc
@@ -2222,7 +2222,7 @@ static int object_transform_axis_target_modal(bContext *C, wmOperator *op, const
bool is_finished = false;
- if (ISMOUSE(xfd->init_event)) {
+ if (ISMOUSE_BUTTON(xfd->init_event)) {
if ((event->type == xfd->init_event) && (event->val == KM_RELEASE)) {
is_finished = true;
}
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc b/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc
index 10564942ab9..ff27c16dc36 100644
--- a/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc
@@ -366,7 +366,7 @@ void move_last_point_and_resample(MutableSpan<float3> positions, const float3 &n
length_parameterize::sample_at_lengths(orig_lengths, new_lengths, indices, factors);
Array<float3> new_positions(positions.size() - 1);
- length_parameterize::linear_interpolation<float3>(positions, indices, factors, new_positions);
+ length_parameterize::interpolate<float3>(positions, indices, factors, new_positions);
positions.drop_back(1).copy_from(new_positions);
positions.last() = new_last_position;
}
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc b/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc
index 408139d6653..8ef18ba7da7 100644
--- a/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc
@@ -127,7 +127,7 @@ class ShrinkCurvesEffect : public CurvesEffect {
lp::sample_at_lengths(data.old_lengths, data.sample_lengths, data.indices, data.factors);
- lp::linear_interpolation<float3>(data.old_positions, data.indices, data.factors, positions);
+ lp::interpolate<float3>(data.old_positions, data.indices, data.factors, positions);
}
};
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh b/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh
index c31bba2fe1e..61aa7d201b1 100644
--- a/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh
@@ -14,6 +14,8 @@
#include "BKE_attribute.h"
#include "BKE_curves.hh"
+#include "ED_curves_sculpt.h"
+
struct ARegion;
struct RegionView3D;
struct Depsgraph;
@@ -98,12 +100,6 @@ VArray<float> get_curves_selection(const Curves &curves_id);
*/
VArray<float> get_point_selection(const Curves &curves_id);
-/**
- * Find curves that have any point selected (a selection factor greater than zero),
- * or curves that have their own selection factor greater than zero.
- */
-IndexMask retrieve_selected_curves(const Curves &curves_id, Vector<int64_t> &r_indices);
-
void move_last_point_and_resample(MutableSpan<float3> positions, const float3 &new_last_position);
class CurvesSculptCommonContext {
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_selection.cc b/source/blender/editors/sculpt_paint/curves_sculpt_selection.cc
index f620fed5761..5bfc8ccc667 100644
--- a/source/blender/editors/sculpt_paint/curves_sculpt_selection.cc
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_selection.cc
@@ -6,6 +6,8 @@
#include "curves_sculpt_intern.hh"
+#include "ED_curves_sculpt.h"
+
namespace blender::ed::sculpt_paint {
static VArray<float> get_curves_selection(const CurvesGeometry &curves, const eAttrDomain domain)
@@ -62,7 +64,7 @@ static IndexMask retrieve_selected_curves(const CurvesGeometry &curves,
case ATTR_DOMAIN_POINT: {
const VArray<float> selection = curves.selection_point_float();
if (selection.is_single()) {
- return selection.get_internal_single() == 0.0f ? IndexMask(0) :
+ return selection.get_internal_single() <= 0.0f ? IndexMask(0) :
IndexMask(curves.curves_num());
}
return index_mask_ops::find_indices_based_on_predicate(
@@ -78,7 +80,7 @@ static IndexMask retrieve_selected_curves(const CurvesGeometry &curves,
case ATTR_DOMAIN_CURVE: {
const VArray<float> selection = curves.selection_curve_float();
if (selection.is_single()) {
- return selection.get_internal_single() == 0.0f ? IndexMask(0) :
+ return selection.get_internal_single() <= 0.0f ? IndexMask(0) :
IndexMask(curves.curves_num());
}
return index_mask_ops::find_indices_based_on_predicate(
@@ -102,4 +104,49 @@ IndexMask retrieve_selected_curves(const Curves &curves_id, Vector<int64_t> &r_i
r_indices);
}
+static IndexMask retrieve_selected_points(const CurvesGeometry &curves,
+ const eAttrDomain domain,
+ Vector<int64_t> &r_indices)
+{
+ switch (domain) {
+ case ATTR_DOMAIN_POINT: {
+ const VArray<float> selection = curves.selection_point_float();
+ if (selection.is_single()) {
+ return selection.get_internal_single() <= 0.0f ? IndexMask(0) :
+ IndexMask(curves.points_num());
+ }
+ return index_mask_ops::find_indices_based_on_predicate(
+ curves.points_range(), 2048, r_indices, [&](const int i) {
+ return selection[i] > 0.0f;
+ });
+ }
+ case ATTR_DOMAIN_CURVE: {
+ const VArray<float> selection = curves.selection_curve_float();
+ if (selection.is_single()) {
+ return selection.get_internal_single() <= 0.0f ? IndexMask(0) :
+ IndexMask(curves.points_num());
+ }
+ const VArray<float> point_selection = curves.adapt_domain(
+ selection, ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT);
+ return index_mask_ops::find_indices_based_on_predicate(
+ curves.points_range(), 2048, r_indices, [&](const int i) {
+ return point_selection[i] > 0.0f;
+ });
+ }
+ default:
+ BLI_assert_unreachable();
+ return {};
+ }
+}
+
+IndexMask retrieve_selected_points(const Curves &curves_id, Vector<int64_t> &r_indices)
+{
+ if (!(curves_id.flag & CV_SCULPT_SELECTION_ENABLED)) {
+ return CurvesGeometry::wrap(curves_id.geometry).points_range();
+ }
+ return retrieve_selected_points(CurvesGeometry::wrap(curves_id.geometry),
+ eAttrDomain(curves_id.selection_domain),
+ r_indices);
+}
+
} // namespace blender::ed::sculpt_paint
diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c
index 88e7a786a47..97fa2b936e0 100644
--- a/source/blender/editors/sculpt_paint/paint_stroke.c
+++ b/source/blender/editors/sculpt_paint/paint_stroke.c
@@ -123,7 +123,7 @@ typedef struct PaintStroke {
StrokeRedraw redraw;
StrokeDone done;
- bool original; /* Raycast original mesh at start of stroke */
+ bool original; /* Ray-cast original mesh at start of stroke. */
} PaintStroke;
/*** Cursors ***/
@@ -1550,8 +1550,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event, PaintS
copy_v2_fl2(mouse, event->mval[0], event->mval[1]);
paint_stroke_line_constrain(stroke, mouse);
- if (stroke->stroke_started &&
- (first_modal || (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)))) {
+ if (stroke->stroke_started && (first_modal || ISMOUSE_MOTION(event->type))) {
if ((br->mtex.brush_angle_mode & MTEX_ANGLE_RAKE) ||
(br->mask_mtex.brush_angle_mode & MTEX_ANGLE_RAKE)) {
copy_v2_v2(stroke->ups->last_rake, stroke->last_mouse_position);
@@ -1561,7 +1560,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event, PaintS
}
else if (first_modal ||
/* regular dabs */
- (!(br->flag & BRUSH_AIRBRUSH) && (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE))) ||
+ (!(br->flag & BRUSH_AIRBRUSH) && ISMOUSE_MOTION(event->type)) ||
/* airbrush */
((br->flag & BRUSH_AIRBRUSH) && event->type == TIMER &&
event->customdata == stroke->timer)) {
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index 4095dbc0d66..9bc38234a19 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -3366,7 +3366,7 @@ static void do_brush_action(Sculpt *sd,
if (brush->deform_target == BRUSH_DEFORM_TARGET_CLOTH_SIM) {
if (!ss->cache->cloth_sim) {
ss->cache->cloth_sim = SCULPT_cloth_brush_simulation_create(
- ss, 1.0f, 0.0f, 0.0f, false, true);
+ ob, 1.0f, 0.0f, 0.0f, false, true);
SCULPT_cloth_brush_simulation_init(ss, ss->cache->cloth_sim);
}
SCULPT_cloth_brush_store_simulation_state(ss, ss->cache->cloth_sim);
diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.c b/source/blender/editors/sculpt_paint/sculpt_cloth.c
index 9d231f2ccd2..bf3e8a24fdf 100644
--- a/source/blender/editors/sculpt_paint/sculpt_cloth.c
+++ b/source/blender/editors/sculpt_paint/sculpt_cloth.c
@@ -611,13 +611,17 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata,
BKE_pbvh_vertex_iter_end;
}
-static ListBase *cloth_brush_collider_cache_create(Depsgraph *depsgraph)
+static ListBase *cloth_brush_collider_cache_create(Object *object, Depsgraph *depsgraph)
{
ListBase *cache = NULL;
DEG_OBJECT_ITER_BEGIN (depsgraph,
ob,
DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY | DEG_ITER_OBJECT_FLAG_VISIBLE |
DEG_ITER_OBJECT_FLAG_DUPLI) {
+ if (STREQ(object->id.name, ob->id.name)) {
+ continue;
+ }
+
CollisionModifierData *cmd = (CollisionModifierData *)BKE_modifiers_findby_type(
ob, eModifierType_Collision);
if (!cmd) {
@@ -1029,13 +1033,14 @@ static void cloth_sim_initialize_default_node_state(SculptSession *ss,
MEM_SAFE_FREE(nodes);
}
-SculptClothSimulation *SCULPT_cloth_brush_simulation_create(SculptSession *ss,
+SculptClothSimulation *SCULPT_cloth_brush_simulation_create(Object *ob,
const float cloth_mass,
const float cloth_damping,
const float cloth_softbody_strength,
const bool use_collisions,
const bool needs_deform_coords)
{
+ SculptSession *ss = ob->sculpt;
const int totverts = SCULPT_vertex_count_get(ss);
SculptClothSimulation *cloth_sim;
@@ -1073,7 +1078,7 @@ SculptClothSimulation *SCULPT_cloth_brush_simulation_create(SculptSession *ss,
cloth_sim->softbody_strength = cloth_softbody_strength;
if (use_collisions) {
- cloth_sim->collider_list = cloth_brush_collider_cache_create(ss->depsgraph);
+ cloth_sim->collider_list = cloth_brush_collider_cache_create(ob, ss->depsgraph);
}
cloth_sim_initialize_default_node_state(ss, cloth_sim);
@@ -1184,7 +1189,7 @@ void SCULPT_do_cloth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode
/* The simulation structure only needs to be created on the first symmetry pass. */
if (SCULPT_stroke_is_first_brush_step(ss->cache) || !ss->cache->cloth_sim) {
ss->cache->cloth_sim = SCULPT_cloth_brush_simulation_create(
- ss,
+ ob,
brush->cloth_mass,
brush->cloth_damping,
brush->cloth_constraint_softbody_strength,
@@ -1571,7 +1576,7 @@ static int sculpt_cloth_filter_invoke(bContext *C, wmOperator *op, const wmEvent
const float cloth_damping = RNA_float_get(op->ptr, "cloth_damping");
const bool use_collisions = RNA_boolean_get(op->ptr, "use_collisions");
ss->filter_cache->cloth_sim = SCULPT_cloth_brush_simulation_create(
- ss,
+ ob,
cloth_mass,
cloth_damping,
0.0f,
diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h
index 83526006d7d..179e07cc7fa 100644
--- a/source/blender/editors/sculpt_paint/sculpt_intern.h
+++ b/source/blender/editors/sculpt_paint/sculpt_intern.h
@@ -1344,7 +1344,7 @@ void SCULPT_cloth_simulation_free(struct SculptClothSimulation *cloth_sim);
/* Public functions. */
-struct SculptClothSimulation *SCULPT_cloth_brush_simulation_create(struct SculptSession *ss,
+struct SculptClothSimulation *SCULPT_cloth_brush_simulation_create(struct Object *ob,
float cloth_mass,
float cloth_damping,
float cloth_softbody_strength,
diff --git a/source/blender/editors/space_outliner/outliner_tools.cc b/source/blender/editors/space_outliner/outliner_tools.cc
index 9d52103a266..80ee3bbb6a1 100644
--- a/source/blender/editors/space_outliner/outliner_tools.cc
+++ b/source/blender/editors/space_outliner/outliner_tools.cc
@@ -31,8 +31,11 @@
#include "BLI_blenlib.h"
#include "BLI_ghash.h"
+#include "BLI_linklist.h"
+#include "BLI_map.hh"
#include "BLI_set.hh"
#include "BLI_utildefines.h"
+#include "BLI_vector.hh"
#include "BKE_anim_data.h"
#include "BKE_animsys.h"
@@ -90,7 +93,9 @@ static CLG_LogRef LOG = {"ed.outliner.tools"};
using namespace blender::ed::outliner;
+using blender::Map;
using blender::Set;
+using blender::Vector;
/* -------------------------------------------------------------------- */
/** \name ID/Library/Data Set/Un-link Utilities
@@ -777,6 +782,25 @@ static void id_local_fn(bContext *C,
}
}
+struct OutlinerLiboverrideDataIDRoot {
+ /** The linked ID that was selected for override. */
+ ID *id_root_reference;
+
+ /** The root of the override hierarchy to which the override of `id_root` belongs, once
+ * known/created. */
+ ID *id_hierarchy_root_override;
+
+ /** The ID that was detected as being a good candidate as instanciation hint for newly overridden
+ * objects, may be null.
+ *
+ * \note Typically currently only used when the root ID to override is a collection instanced by
+ * an emtpy object. */
+ ID *id_instance_hint;
+
+ /** If this override comes from an instancing object (which would be `id_instance_hint` then). */
+ bool is_override_instancing_object;
+};
+
struct OutlinerLibOverrideData {
bool do_hierarchy;
@@ -789,21 +813,44 @@ struct OutlinerLibOverrideData {
* solving broken overrides while not losing *all* of your overrides. */
bool do_resync_hierarchy_enforce;
- /** The override hierarchy root, when known/created. */
- ID *id_hierarchy_root_override;
-
- /** A hash of the selected tree elements' ID 'uuid'. Used to clear 'system override' flags on
+ /** A set of the selected tree elements' ID 'uuid'. Used to clear 'system override' flags on
* their newly-created liboverrides in post-process step of override hierarchy creation. */
Set<uint> selected_id_uid;
+
+ /** A mapping from the found hierarchy roots to a linked list of IDs to override for each of
+ * these roots.
+ *
+ * \note the key may be either linked (in which case it will be replaced by the newly created
+ * override), or an actual already existing override. */
+ Map<ID *, Vector<OutlinerLiboverrideDataIDRoot>> id_hierarchy_roots;
+
+ /** All 'session_uuid' of all hierarchy root IDs used or created by the operation. */
+ Set<uint> id_hierarchy_roots_uid;
+
+ void id_root_add(ID *id_hierarchy_root_reference,
+ ID *id_root_reference,
+ ID *id_instance_hint,
+ const bool is_override_instancing_object)
+ {
+ OutlinerLiboverrideDataIDRoot id_root_data;
+ id_root_data.id_root_reference = id_root_reference;
+ id_root_data.id_hierarchy_root_override = nullptr;
+ id_root_data.id_instance_hint = id_instance_hint;
+ id_root_data.is_override_instancing_object = is_override_instancing_object;
+
+ Vector<OutlinerLiboverrideDataIDRoot> &value = id_hierarchy_roots.lookup_or_add_default(
+ id_hierarchy_root_reference);
+ value.append(id_root_data);
+ }
};
/* Store 'UUID' of IDs of selected elements in the Outliner tree, before generating the override
* hierarchy. */
-static void id_override_library_create_hierarchy_pre_process_fn(bContext *UNUSED(C),
- ReportList *UNUSED(reports),
+static void id_override_library_create_hierarchy_pre_process_fn(bContext *C,
+ ReportList *reports,
Scene *UNUSED(scene),
- TreeElement *UNUSED(te),
- TreeStoreElem *UNUSED(tsep),
+ TreeElement *te,
+ TreeStoreElem *tsep,
TreeStoreElem *tselem,
void *user_data)
{
@@ -829,28 +876,6 @@ static void id_override_library_create_hierarchy_pre_process_fn(bContext *UNUSED
}
FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
}
-}
-
-static void id_override_library_create_fn(bContext *C,
- ReportList *reports,
- Scene *scene,
- TreeElement *te,
- TreeStoreElem *tsep,
- TreeStoreElem *tselem,
- void *user_data)
-{
- BLI_assert(TSE_IS_REAL_ID(tselem));
-
- /* We can only safely apply this operation on one item at a time, so only do it on the active
- * one. */
- if ((tselem->flag & TSE_ACTIVE) == 0) {
- return;
- }
-
- ID *id_root_reference = tselem->id;
- OutlinerLibOverrideData *data = reinterpret_cast<OutlinerLibOverrideData *>(user_data);
- const bool do_hierarchy = data->do_hierarchy;
- bool success = false;
ID *id_instance_hint = nullptr;
bool is_override_instancing_object = false;
@@ -866,172 +891,242 @@ static void id_override_library_create_fn(bContext *C,
}
}
- if (ID_IS_OVERRIDABLE_LIBRARY(id_root_reference) ||
- (ID_IS_LINKED(id_root_reference) && do_hierarchy)) {
- Main *bmain = CTX_data_main(C);
+ if (!ID_IS_OVERRIDABLE_LIBRARY(id_root_reference) &&
+ !(ID_IS_LINKED(id_root_reference) && do_hierarchy)) {
+ return;
+ }
- id_root_reference->tag |= LIB_TAG_DOIT;
+ Main *bmain = CTX_data_main(C);
- /* For now, remap all local usages of linked ID to local override one here. */
- ID *id_iter;
- FOREACH_MAIN_ID_BEGIN (bmain, id_iter) {
- if (ID_IS_LINKED(id_iter) || ID_IS_OVERRIDE_LIBRARY(id_iter)) {
- id_iter->tag &= ~LIB_TAG_DOIT;
- }
- else {
- id_iter->tag |= LIB_TAG_DOIT;
+ if (do_hierarchy) {
+ /* Tag all linked parents in tree hierarchy to be also overridden. */
+ ID *id_hierarchy_root_reference = id_root_reference;
+ while ((te = te->parent) != nullptr) {
+ if (!TSE_IS_REAL_ID(te->store_elem)) {
+ continue;
}
- }
- FOREACH_MAIN_ID_END;
- if (do_hierarchy) {
- /* Tag all linked parents in tree hierarchy to be also overridden. */
- ID *id_hierarchy_root_reference = id_root_reference;
- while ((te = te->parent) != nullptr) {
- if (!TSE_IS_REAL_ID(te->store_elem)) {
+ /* Tentative hierarchy root. */
+ ID *id_current_hierarchy_root = te->store_elem->id;
+
+ /* If the parent ID is from a different library than the reference root one, we are done
+ * with upwards tree processing in any case. */
+ if (id_current_hierarchy_root->lib != id_root_reference->lib) {
+ if (ID_IS_OVERRIDE_LIBRARY_VIRTUAL(id_current_hierarchy_root)) {
+ /* Virtual overrides (i.e. embedded IDs), we can simply keep processing their parent to
+ * get an actual real override. */
continue;
}
- /* Tentative hierarchy root. */
- ID *id_current_hierarchy_root = te->store_elem->id;
-
- /* If the parent ID is from a different library than the reference root one, we are done
- * with upwards tree processing in any case. */
- if (id_current_hierarchy_root->lib != id_root_reference->lib) {
- if (ID_IS_OVERRIDE_LIBRARY_VIRTUAL(id_current_hierarchy_root)) {
- /* Virtual overrides (i.e. embedded IDs), we can simply keep processing their parent to
- * get an actual real override. */
- continue;
- }
-
- /* If the parent ID is already an override, and is valid (i.e. local override), we can
- * access its hierarchy root directly. */
- if (!ID_IS_LINKED(id_current_hierarchy_root) &&
- ID_IS_OVERRIDE_LIBRARY_REAL(id_current_hierarchy_root) &&
- id_current_hierarchy_root->override_library->reference->lib ==
- id_root_reference->lib) {
- id_hierarchy_root_reference =
- id_current_hierarchy_root->override_library->hierarchy_root;
- BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference));
- break;
- }
-
- if (ID_IS_LINKED(id_current_hierarchy_root)) {
- /* No local 'anchor' was found for the hierarchy to override, do not proceed, as this
- * would most likely generate invisible/confusing/hard to use and manage overrides. */
- BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
- BKE_reportf(reports,
- RPT_WARNING,
- "Invalid anchor ('%s') found, needed to create library override from "
- "data-block '%s'",
- id_current_hierarchy_root->name,
- id_root_reference->name);
- return;
- }
-
- /* In all other cases, `id_current_hierarchy_root` cannot be a valid hierarchy root, so
- * current `id_hierarchy_root_reference` is our best candidate. */
-
+ /* If the parent ID is already an override, and is valid (i.e. local override), we can
+ * access its hierarchy root directly. */
+ if (!ID_IS_LINKED(id_current_hierarchy_root) &&
+ ID_IS_OVERRIDE_LIBRARY_REAL(id_current_hierarchy_root) &&
+ id_current_hierarchy_root->override_library->reference->lib ==
+ id_root_reference->lib) {
+ id_hierarchy_root_reference =
+ id_current_hierarchy_root->override_library->hierarchy_root;
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference));
break;
}
- /* If some element in the tree needs to be overridden, but its ID is not overridable,
- * abort. */
- if (!ID_IS_OVERRIDABLE_LIBRARY_HIERARCHY(id_current_hierarchy_root)) {
+ if (ID_IS_LINKED(id_current_hierarchy_root)) {
+ /* No local 'anchor' was found for the hierarchy to override, do not proceed, as this
+ * would most likely generate invisible/confusing/hard to use and manage overrides. */
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
BKE_reportf(reports,
RPT_WARNING,
- "Could not create library override from data-block '%s', one of its parents "
- "is not overridable ('%s')",
- id_root_reference->name,
- id_current_hierarchy_root->name);
+ "Invalid anchor ('%s') found, needed to create library override from "
+ "data-block '%s'",
+ id_current_hierarchy_root->name,
+ id_root_reference->name);
return;
}
- id_current_hierarchy_root->tag |= LIB_TAG_DOIT;
- id_hierarchy_root_reference = id_current_hierarchy_root;
+
+ /* In all other cases, `id_current_hierarchy_root` cannot be a valid hierarchy root, so
+ * current `id_hierarchy_root_reference` is our best candidate. */
+
+ break;
}
- /* That case can happen when linked data is a complex mix involving several libraries and/or
- * linked overrides. E.g. a mix of overrides from one library, and indirectly linked data
- * from another library. Do not try to support such cases for now. */
- if (!((id_hierarchy_root_reference->lib == id_root_reference->lib) ||
- (!ID_IS_LINKED(id_hierarchy_root_reference) &&
- ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference) &&
- id_hierarchy_root_reference->override_library->reference->lib ==
- id_root_reference->lib))) {
+ /* If some element in the tree needs to be overridden, but its ID is not overridable,
+ * abort. */
+ if (!ID_IS_OVERRIDABLE_LIBRARY_HIERARCHY(id_current_hierarchy_root)) {
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
BKE_reportf(reports,
RPT_WARNING,
- "Invalid hierarchy root ('%s') found, needed to create library override from "
- "data-block '%s'",
- id_hierarchy_root_reference->name,
- id_root_reference->name);
+ "Could not create library override from data-block '%s', one of its parents "
+ "is not overridable ('%s')",
+ id_root_reference->name,
+ id_current_hierarchy_root->name);
return;
}
+ id_current_hierarchy_root->tag |= LIB_TAG_DOIT;
+ id_hierarchy_root_reference = id_current_hierarchy_root;
+ }
+
+ /* That case can happen when linked data is a complex mix involving several libraries and/or
+ * linked overrides. E.g. a mix of overrides from one library, and indirectly linked data
+ * from another library. Do not try to support such cases for now. */
+ if (!((id_hierarchy_root_reference->lib == id_root_reference->lib) ||
+ (!ID_IS_LINKED(id_hierarchy_root_reference) &&
+ ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference) &&
+ id_hierarchy_root_reference->override_library->reference->lib ==
+ id_root_reference->lib))) {
+ BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
+ BKE_reportf(reports,
+ RPT_WARNING,
+ "Invalid hierarchy root ('%s') found, needed to create library override from "
+ "data-block '%s'",
+ id_hierarchy_root_reference->name,
+ id_root_reference->name);
+ return;
+ }
+
+ data->id_root_add(id_hierarchy_root_reference,
+ id_root_reference,
+ id_instance_hint,
+ is_override_instancing_object);
+ }
+ else if (ID_IS_OVERRIDABLE_LIBRARY(id_root_reference)) {
+ data->id_root_add(
+ id_root_reference, id_root_reference, id_instance_hint, is_override_instancing_object);
+ }
+}
+
+static void id_override_library_create_hierarchy(
+ Main &bmain,
+ Scene *scene,
+ ViewLayer *view_layer,
+ OutlinerLibOverrideData &data,
+ ID *id_hierarchy_root_reference,
+ Vector<OutlinerLiboverrideDataIDRoot> &data_idroots,
+ bool &r_aggregated_success)
+{
+ BLI_assert(ID_IS_LINKED(id_hierarchy_root_reference) ||
+ ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference));
+
+ const bool do_hierarchy = data.do_hierarchy;
+ /* NOTE: This process is not the most efficient, but allows to re-use existing code.
+ * If this becomes a bottle-neck at some point, we need to implement a new
+ * `BKE_lib_override_library_hierarchy_create()` function able to process several roots inside of
+ * a same hierarchy in a single call. */
+ for (OutlinerLiboverrideDataIDRoot &data_idroot : data_idroots) {
+ /* For now, remap all local usages of linked ID to local override one here. */
+ ID *id_iter;
+ FOREACH_MAIN_ID_BEGIN (&bmain, id_iter) {
+ if (ID_IS_LINKED(id_iter) || ID_IS_OVERRIDE_LIBRARY(id_iter)) {
+ id_iter->tag &= ~LIB_TAG_DOIT;
+ }
+ else {
+ id_iter->tag |= LIB_TAG_DOIT;
+ }
+ }
+ FOREACH_MAIN_ID_END;
+
+ bool success = false;
+ if (do_hierarchy) {
ID *id_root_override = nullptr;
- success = BKE_lib_override_library_create(bmain,
- CTX_data_scene(C),
- CTX_data_view_layer(C),
+ success = BKE_lib_override_library_create(&bmain,
+ scene,
+ view_layer,
nullptr,
- id_root_reference,
+ data_idroot.id_root_reference,
id_hierarchy_root_reference,
- id_instance_hint,
+ data_idroot.id_instance_hint,
&id_root_override,
- data->do_fully_editable);
-
- BLI_assert(id_root_override != nullptr);
- BLI_assert(!ID_IS_LINKED(id_root_override));
- BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root_override));
- if (ID_IS_LINKED(id_hierarchy_root_reference)) {
- BLI_assert(
- id_root_override->override_library->hierarchy_root->override_library->reference ==
- id_hierarchy_root_reference);
- data->id_hierarchy_root_override = id_root_override->override_library->hierarchy_root;
- }
- else {
- BLI_assert(id_root_override->override_library->hierarchy_root ==
- id_hierarchy_root_reference);
- data->id_hierarchy_root_override = id_root_override->override_library->hierarchy_root;
+ data.do_fully_editable);
+
+ if (success) {
+ BLI_assert(id_root_override != nullptr);
+ BLI_assert(!ID_IS_LINKED(id_root_override));
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root_override));
+
+ ID *id_hierarchy_root_override = id_root_override->override_library->hierarchy_root;
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_override));
+ if (ID_IS_LINKED(id_hierarchy_root_reference)) {
+ BLI_assert(id_hierarchy_root_override->override_library->reference ==
+ id_hierarchy_root_reference);
+ /* If the hierarchy root reference was a linked data, after the first iteration there is
+ * now a matching override, which shall be used for all further partial overrides with
+ * this same hierarchy. */
+ id_hierarchy_root_reference = id_hierarchy_root_override;
+ }
+ else {
+ BLI_assert(id_hierarchy_root_override == id_hierarchy_root_reference);
+ }
+ data_idroot.id_hierarchy_root_override = id_hierarchy_root_override;
+ data.id_hierarchy_roots_uid.add(id_hierarchy_root_override->session_uuid);
}
}
- else if (ID_IS_OVERRIDABLE_LIBRARY(id_root_reference)) {
- success = BKE_lib_override_library_create_from_id(bmain, id_root_reference, true) != nullptr;
+ else if (ID_IS_OVERRIDABLE_LIBRARY(data_idroot.id_root_reference)) {
+ ID *id_root_override = BKE_lib_override_library_create_from_id(
+ &bmain, data_idroot.id_root_reference, true);
+ success = id_root_override != nullptr;
+ if (success) {
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root_override));
+ id_root_override->override_library->flag &= ~IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED;
+ }
/* Cleanup. */
- BKE_main_id_newptr_and_tag_clear(bmain);
- BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
+ BKE_main_id_newptr_and_tag_clear(&bmain);
+ BKE_main_id_tag_all(&bmain, LIB_TAG_DOIT, false);
+ }
+ else {
+ BLI_assert_unreachable();
}
/* Remove the instance empty from this scene, the items now have an overridden collection
* instead. */
- if (success && is_override_instancing_object) {
- ED_object_base_free_and_unlink(bmain, scene, (Object *)id_instance_hint);
+ if (success && data_idroot.is_override_instancing_object) {
+ BLI_assert(GS(data_idroot.id_instance_hint) == ID_OB);
+ ED_object_base_free_and_unlink(
+ &bmain, scene, reinterpret_cast<Object *>(data_idroot.id_instance_hint));
}
- }
- if (!success) {
- BKE_reportf(reports,
- RPT_WARNING,
- "Could not create library override from data-block '%s'",
- id_root_reference->name);
+
+ r_aggregated_success = r_aggregated_success && success;
}
}
/* Clear system override flag from newly created overrides which linked reference were previously
* selected in the Outliner tree. */
-static void id_override_library_create_hierarchy_post_process(bContext *C,
- OutlinerLibOverrideData *data)
+static void id_override_library_create_hierarchy_process(bContext *C,
+ ReportList *reports,
+ OutlinerLibOverrideData &data)
{
Main *bmain = CTX_data_main(C);
- ID *id_hierarchy_root_override = data->id_hierarchy_root_override;
+ Scene *scene = CTX_data_scene(C);
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+ const bool do_hierarchy = data.do_hierarchy;
+
+ bool success = true;
+ for (auto &&[id_hierarchy_root_reference, data_idroots] : data.id_hierarchy_roots.items()) {
+ id_override_library_create_hierarchy(
+ *bmain, scene, view_layer, data, id_hierarchy_root_reference, data_idroots, success);
+ }
+
+ if (!success) {
+ BKE_reportf(reports,
+ RPT_WARNING,
+ "Could not create library override from one or more of the selected data-blocks");
+ }
+
+ if (!do_hierarchy) {
+ return;
+ }
ID *id_iter;
FOREACH_MAIN_ID_BEGIN (bmain, id_iter) {
- if (ID_IS_LINKED(id_iter) || !ID_IS_OVERRIDE_LIBRARY_REAL(id_iter) ||
- id_iter->override_library->hierarchy_root != id_hierarchy_root_override) {
+ if (ID_IS_LINKED(id_iter) || !ID_IS_OVERRIDE_LIBRARY_REAL(id_iter)) {
continue;
}
- if (data->selected_id_uid.contains(id_iter->override_library->reference->session_uuid)) {
+ if (!data.id_hierarchy_roots_uid.contains(
+ id_iter->override_library->hierarchy_root->session_uuid)) {
+ continue;
+ }
+ if (data.selected_id_uid.contains(id_iter->override_library->reference->session_uuid) ||
+ data.selected_id_uid.contains(id_iter->session_uuid)) {
id_iter->override_library->flag &= ~IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED;
}
}
@@ -2254,8 +2349,18 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op)
}
case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE: {
OutlinerLibOverrideData override_data{};
- outliner_do_libdata_operation(
- C, op->reports, scene, space_outliner, id_override_library_create_fn, &override_data);
+ override_data.do_hierarchy = false;
+ override_data.do_fully_editable = true;
+
+ outliner_do_libdata_operation(C,
+ op->reports,
+ scene,
+ space_outliner,
+ id_override_library_create_hierarchy_pre_process_fn,
+ &override_data);
+
+ id_override_library_create_hierarchy_process(C, op->reports, override_data);
+
ED_undo_push(C, "Overridden Data");
break;
}
@@ -2263,15 +2368,15 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op)
OutlinerLibOverrideData override_data{};
override_data.do_hierarchy = true;
override_data.do_fully_editable = U.experimental.use_override_new_fully_editable;
+
outliner_do_libdata_operation(C,
op->reports,
scene,
space_outliner,
id_override_library_create_hierarchy_pre_process_fn,
&override_data);
- outliner_do_libdata_operation(
- C, op->reports, scene, space_outliner, id_override_library_create_fn, &override_data);
- id_override_library_create_hierarchy_post_process(C, &override_data);
+
+ id_override_library_create_hierarchy_process(C, op->reports, override_data);
ED_undo_push(C, "Overridden Data Hierarchy");
break;
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc
index c7653e94b4d..629a0b5802b 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc
@@ -5,6 +5,7 @@
#include "BKE_attribute.hh"
#include "BKE_context.h"
+#include "BKE_curves.hh"
#include "BKE_editmesh.h"
#include "BKE_geometry_fields.hh"
#include "BKE_global.h"
@@ -22,6 +23,7 @@
#include "DEG_depsgraph_query.h"
+#include "ED_curves_sculpt.h"
#include "ED_spreadsheet.h"
#include "NOD_geometry_nodes_eval_log.hh"
@@ -232,23 +234,31 @@ int GeometryDataSource::tot_rows() const
return attributes.domain_size(domain_);
}
-/**
- * Only data sets corresponding to mesh objects in edit mode currently support selection filtering.
- */
bool GeometryDataSource::has_selection_filter() const
{
Object *object_orig = DEG_get_original_object(object_eval_);
- if (object_orig->type != OB_MESH) {
- return false;
- }
- if (object_orig->mode != OB_MODE_EDIT) {
- return false;
- }
- if (component_->type() != GEO_COMPONENT_TYPE_MESH) {
- return false;
+ switch (component_->type()) {
+ case GEO_COMPONENT_TYPE_MESH: {
+ if (object_orig->type != OB_MESH) {
+ return false;
+ }
+ if (object_orig->mode != OB_MODE_EDIT) {
+ return false;
+ }
+ return true;
+ }
+ case GEO_COMPONENT_TYPE_CURVE: {
+ if (object_orig->type != OB_CURVES) {
+ return false;
+ }
+ if (object_orig->mode != OB_MODE_SCULPT_CURVES) {
+ return false;
+ }
+ return true;
+ }
+ default:
+ return false;
}
-
- return true;
}
IndexMask GeometryDataSource::apply_selection_filter(Vector<int64_t> &indices) const
@@ -256,50 +266,73 @@ IndexMask GeometryDataSource::apply_selection_filter(Vector<int64_t> &indices) c
std::lock_guard lock{mutex_};
const IndexMask full_range(this->tot_rows());
- BLI_assert(object_eval_->mode == OB_MODE_EDIT);
- BLI_assert(component_->type() == GEO_COMPONENT_TYPE_MESH);
- Object *object_orig = DEG_get_original_object(object_eval_);
- const MeshComponent *mesh_component = static_cast<const MeshComponent *>(component_);
- const Mesh *mesh_eval = mesh_component->get_for_read();
- Mesh *mesh_orig = (Mesh *)object_orig->data;
- BMesh *bm = mesh_orig->edit_mesh->bm;
- BM_mesh_elem_table_ensure(bm, BM_VERT);
-
- const int *orig_indices = (int *)CustomData_get_layer(&mesh_eval->vdata, CD_ORIGINDEX);
- if (orig_indices != nullptr) {
- /* Use CD_ORIGINDEX layer if it exists. */
- VArray<bool> selection = mesh_component->attributes()->adapt_domain<bool>(
- VArray<bool>::ForFunc(mesh_eval->totvert,
- [bm, orig_indices](int vertex_index) -> bool {
- const int i_orig = orig_indices[vertex_index];
- if (i_orig < 0) {
- return false;
- }
- if (i_orig >= bm->totvert) {
- return false;
- }
- BMVert *vert = bm->vtable[i_orig];
- return BM_elem_flag_test(vert, BM_ELEM_SELECT);
- }),
- ATTR_DOMAIN_POINT,
- domain_);
- return index_mask_ops::find_indices_from_virtual_array(full_range, selection, 1024, indices);
- }
-
- if (mesh_eval->totvert == bm->totvert) {
- /* Use a simple heuristic to match original vertices to evaluated ones. */
- VArray<bool> selection = mesh_component->attributes()->adapt_domain<bool>(
- VArray<bool>::ForFunc(mesh_eval->totvert,
- [bm](int vertex_index) -> bool {
- BMVert *vert = bm->vtable[vertex_index];
- return BM_elem_flag_test(vert, BM_ELEM_SELECT);
- }),
- ATTR_DOMAIN_POINT,
- domain_);
- return index_mask_ops::find_indices_from_virtual_array(full_range, selection, 2048, indices);
- }
-
- return full_range;
+ switch (component_->type()) {
+ case GEO_COMPONENT_TYPE_MESH: {
+ BLI_assert(object_eval_->type == OB_MESH);
+ BLI_assert(object_eval_->mode == OB_MODE_EDIT);
+ Object *object_orig = DEG_get_original_object(object_eval_);
+ const Mesh *mesh_eval = geometry_set_.get_mesh_for_read();
+ const bke::AttributeAccessor attributes_eval = bke::mesh_attributes(*mesh_eval);
+ Mesh *mesh_orig = (Mesh *)object_orig->data;
+ BMesh *bm = mesh_orig->edit_mesh->bm;
+ BM_mesh_elem_table_ensure(bm, BM_VERT);
+
+ const int *orig_indices = (int *)CustomData_get_layer(&mesh_eval->vdata, CD_ORIGINDEX);
+ if (orig_indices != nullptr) {
+ /* Use CD_ORIGINDEX layer if it exists. */
+ VArray<bool> selection = attributes_eval.adapt_domain<bool>(
+ VArray<bool>::ForFunc(mesh_eval->totvert,
+ [bm, orig_indices](int vertex_index) -> bool {
+ const int i_orig = orig_indices[vertex_index];
+ if (i_orig < 0) {
+ return false;
+ }
+ if (i_orig >= bm->totvert) {
+ return false;
+ }
+ const BMVert *vert = BM_vert_at_index(bm, i_orig);
+ return BM_elem_flag_test(vert, BM_ELEM_SELECT);
+ }),
+ ATTR_DOMAIN_POINT,
+ domain_);
+ return index_mask_ops::find_indices_from_virtual_array(
+ full_range, selection, 1024, indices);
+ }
+
+ if (mesh_eval->totvert == bm->totvert) {
+ /* Use a simple heuristic to match original vertices to evaluated ones. */
+ VArray<bool> selection = attributes_eval.adapt_domain<bool>(
+ VArray<bool>::ForFunc(mesh_eval->totvert,
+ [bm](int vertex_index) -> bool {
+ const BMVert *vert = BM_vert_at_index(bm, vertex_index);
+ return BM_elem_flag_test(vert, BM_ELEM_SELECT);
+ }),
+ ATTR_DOMAIN_POINT,
+ domain_);
+ return index_mask_ops::find_indices_from_virtual_array(
+ full_range, selection, 2048, indices);
+ }
+
+ return full_range;
+ }
+ case GEO_COMPONENT_TYPE_CURVE: {
+ BLI_assert(object_eval_->type == OB_CURVES);
+ BLI_assert(object_eval_->mode == OB_MODE_SCULPT_CURVES);
+ const CurveComponent &component = static_cast<const CurveComponent &>(*component_);
+ const Curves &curves_id = *component.get_for_read();
+ switch (domain_) {
+ case ATTR_DOMAIN_POINT:
+ return sculpt_paint::retrieve_selected_points(curves_id, indices);
+ case ATTR_DOMAIN_CURVE:
+ return sculpt_paint::retrieve_selected_curves(curves_id, indices);
+ default:
+ BLI_assert_unreachable();
+ }
+ return full_range;
+ }
+ default:
+ return full_range;
+ }
}
void VolumeDataSource::foreach_default_column_ids(
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh
index 04b4f6d8d06..71bc4768949 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh
@@ -68,10 +68,6 @@ class GeometryDataSource : public DataSource {
return object_eval_;
}
- /**
- * Only data sets corresponding to mesh objects in edit mode currently support selection
- * filtering.
- */
bool has_selection_filter() const override;
IndexMask apply_selection_filter(Vector<int64_t> &indices) const;
diff --git a/source/blender/editors/space_view3d/view3d_navigate_walk.c b/source/blender/editors/space_view3d/view3d_navigate_walk.c
index 471231b5f27..f1e9ac22882 100644
--- a/source/blender/editors/space_view3d/view3d_navigate_walk.c
+++ b/source/blender/editors/space_view3d/view3d_navigate_walk.c
@@ -659,7 +659,7 @@ static void walkEvent(WalkInfo *walk, const wmEvent *event)
if (event->type == TIMER && event->customdata == walk->timer) {
walk->redraw = true;
}
- else if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+ else if (ISMOUSE_MOTION(event->type)) {
#ifdef USE_TABLET_SUPPORT
if ((walk->is_cursor_absolute == false) && event->tablet.is_motion_absolute) {
diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c
index d9e23b98c66..6e47b30ae9d 100644
--- a/source/blender/editors/transform/transform.c
+++ b/source/blender/editors/transform/transform.c
@@ -1327,7 +1327,7 @@ int transformEvent(TransInfo *t, const wmEvent *event)
handled = true;
}
- if (t->redraw && !ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+ if (t->redraw && !ISMOUSE_MOTION(event->type)) {
WM_window_status_area_tag_redraw(CTX_wm_window(t->context));
}
diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c
index e45cac36736..56a7d045dfd 100644
--- a/source/blender/editors/transform/transform_generics.c
+++ b/source/blender/editors/transform/transform_generics.c
@@ -555,7 +555,7 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
}
else {
/* Release confirms preference should not affect node editor (T69288, T70504). */
- if (ISMOUSE(t->launch_event) &&
+ if (ISMOUSE_BUTTON(t->launch_event) &&
((U.flag & USER_RELEASECONFIRM) || (t->spacetype == SPACE_NODE))) {
/* Global "release confirm" on mouse bindings */
t->flag |= T_RELEASE_CONFIRM;
diff --git a/source/blender/geometry/intern/resample_curves.cc b/source/blender/geometry/intern/resample_curves.cc
index 2c3a6c6e0cf..29e358cc3f4 100644
--- a/source/blender/geometry/intern/resample_curves.cc
+++ b/source/blender/geometry/intern/resample_curves.cc
@@ -252,10 +252,10 @@ static Curves *resample_to_uniform(const CurveComponent &src_component,
const IndexRange dst_points = dst_curves.points_for_curve(i_curve);
if (curve_types[i_curve] == CURVE_TYPE_POLY) {
- length_parameterize::linear_interpolation(src.slice(src_points),
- sample_indices.as_span().slice(dst_points),
- sample_factors.as_span().slice(dst_points),
- dst.slice(dst_points));
+ length_parameterize::interpolate(src.slice(src_points),
+ sample_indices.as_span().slice(dst_points),
+ sample_factors.as_span().slice(dst_points),
+ dst.slice(dst_points));
}
else {
const int evaluated_size = src_curves.evaluated_points_for_curve(i_curve).size();
@@ -264,10 +264,10 @@ static Curves *resample_to_uniform(const CurveComponent &src_component,
MutableSpan<T> evaluated = evaluated_buffer.as_mutable_span().cast<T>();
src_curves.interpolate_to_evaluated(i_curve, src.slice(src_points), evaluated);
- length_parameterize::linear_interpolation(evaluated.as_span(),
- sample_indices.as_span().slice(dst_points),
- sample_factors.as_span().slice(dst_points),
- dst.slice(dst_points));
+ length_parameterize::interpolate(evaluated.as_span(),
+ sample_indices.as_span().slice(dst_points),
+ sample_factors.as_span().slice(dst_points),
+ dst.slice(dst_points));
}
}
});
@@ -277,10 +277,10 @@ static Curves *resample_to_uniform(const CurveComponent &src_component,
for (const int i_curve : sliced_selection) {
const IndexRange src_points = src_curves.evaluated_points_for_curve(i_curve);
const IndexRange dst_points = dst_curves.points_for_curve(i_curve);
- length_parameterize::linear_interpolation(evaluated_positions.slice(src_points),
- sample_indices.as_span().slice(dst_points),
- sample_factors.as_span().slice(dst_points),
- dst_positions.slice(dst_points));
+ length_parameterize::interpolate(evaluated_positions.slice(src_points),
+ sample_indices.as_span().slice(dst_points),
+ sample_factors.as_span().slice(dst_points),
+ dst_positions.slice(dst_points));
}
/* Fill the default value for non-interpolating attributes that still must be copied. */
diff --git a/source/blender/gpu/GPU_context.h b/source/blender/gpu/GPU_context.h
index 9d92ea2cad9..a242bb7cc94 100644
--- a/source/blender/gpu/GPU_context.h
+++ b/source/blender/gpu/GPU_context.h
@@ -17,8 +17,8 @@
extern "C" {
#endif
-/* GPU backends abstract the differences between different APIs. GPU_context_create
- * automatically initializes the backend, and GPU_context_discard frees it when there
+/* GPU back-ends abstract the differences between different APIs. #GPU_context_create
+ * automatically initializes the back-end, and #GPU_context_discard frees it when there
* are no more contexts. */
bool GPU_backend_supported(void);
eGPUBackendType GPU_backend_get_type(void);
diff --git a/source/blender/gpu/intern/gpu_shader_builder.cc b/source/blender/gpu/intern/gpu_shader_builder.cc
index fc99b892554..9b699c60126 100644
--- a/source/blender/gpu/intern/gpu_shader_builder.cc
+++ b/source/blender/gpu/intern/gpu_shader_builder.cc
@@ -51,7 +51,6 @@ void ShaderBuilder::init()
void ShaderBuilder::exit()
{
- GPU_backend_exit();
GPU_exit();
GPU_context_discard(gpu_context_);
diff --git a/source/blender/gpu/metal/mtl_backend.mm b/source/blender/gpu/metal/mtl_backend.mm
index 3328855bbf8..83cf3af0804 100644
--- a/source/blender/gpu/metal/mtl_backend.mm
+++ b/source/blender/gpu/metal/mtl_backend.mm
@@ -10,8 +10,8 @@
#include "mtl_backend.hh"
#include "mtl_context.hh"
#include "mtl_framebuffer.hh"
-#include "mtl_uniform_buffer.hh"
#include "mtl_query.hh"
+#include "mtl_uniform_buffer.hh"
#include "gpu_capabilities_private.hh"
#include "gpu_platform_private.hh"
diff --git a/source/blender/gpu/metal/mtl_memory.mm b/source/blender/gpu/metal/mtl_memory.mm
index 48e27dd2bb6..07da489bdbb 100644
--- a/source/blender/gpu/metal/mtl_memory.mm
+++ b/source/blender/gpu/metal/mtl_memory.mm
@@ -68,7 +68,7 @@ gpu::MTLBuffer *MTLBufferPool::allocate_with_data(uint64_t size,
bool cpu_visible,
const void *data)
{
- /* Allocate buffer with default HW-compatible alignemnt of 256 bytes.
+ /* Allocate buffer with default HW-compatible alignment of 256 bytes.
* See https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf for more. */
return this->allocate_aligned_with_data(size, 256, cpu_visible, data);
}
diff --git a/source/blender/gpu/metal/mtl_query.mm b/source/blender/gpu/metal/mtl_query.mm
index 33ae8f554c3..dfda0a8de7f 100644
--- a/source/blender/gpu/metal/mtl_query.mm
+++ b/source/blender/gpu/metal/mtl_query.mm
@@ -104,8 +104,8 @@ void MTLQueryPool::get_occlusion_result(MutableSpan<uint32_t> r_values)
BLI_assert(ctx->get_inside_frame());
}
- /* Wait for GPU operatiosn to complete and for query buffer contents
- * to be synchronised back to host memory. */
+ /* Wait for GPU operations to complete and for query buffer contents
+ * to be synchronized back to host memory. */
GPU_finish();
/* Iterate through all possible visibility buffers and copy results into provided
diff --git a/source/blender/gpu/metal/mtl_uniform_buffer.hh b/source/blender/gpu/metal/mtl_uniform_buffer.hh
index 722d819cf96..789a85f0a92 100644
--- a/source/blender/gpu/metal/mtl_uniform_buffer.hh
+++ b/source/blender/gpu/metal/mtl_uniform_buffer.hh
@@ -25,7 +25,7 @@ class MTLUniformBuf : public UniformBuf {
* have yet been allocated. */
bool has_data_ = false;
- /* Bindstate tracking. */
+ /* Bind-state tracking. */
int bind_slot_ = -1;
MTLContext *bound_ctx_ = nullptr;
diff --git a/source/blender/gpu/metal/mtl_uniform_buffer.mm b/source/blender/gpu/metal/mtl_uniform_buffer.mm
index 3415c41f2cc..4893014dedf 100644
--- a/source/blender/gpu/metal/mtl_uniform_buffer.mm
+++ b/source/blender/gpu/metal/mtl_uniform_buffer.mm
@@ -107,7 +107,7 @@ void MTLUniformBuf::bind(int slot)
MEM_SAFE_FREE(data_);
}
- /* Ensure there is atleast an empty dummy buffer. */
+ /* Ensure there is at least an empty dummy buffer. */
if (metal_buffer_ == nullptr) {
this->update(nullptr);
}
diff --git a/source/blender/makesrna/intern/rna_image.c b/source/blender/makesrna/intern/rna_image.c
index 39f5b6e0e9f..7f134c5055f 100644
--- a/source/blender/makesrna/intern/rna_image.c
+++ b/source/blender/makesrna/intern/rna_image.c
@@ -475,18 +475,19 @@ static int rna_Image_frame_duration_get(PointerRNA *ptr)
Image *ima = (Image *)ptr->owner_id;
int duration = 1;
+ if (!BKE_image_has_anim(ima)) {
+ /* Ensure image has been loaded into memory and frame duration is known. */
+ void *lock;
+ ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock);
+ BKE_image_release_ibuf(ima, ibuf, lock);
+ }
+
if (BKE_image_has_anim(ima)) {
struct anim *anim = ((ImageAnim *)ima->anims.first)->anim;
if (anim) {
duration = IMB_anim_get_duration(anim, IMB_TC_RECORD_RUN);
}
}
- else {
- /* acquire ensures ima->anim is set, if possible! */
- void *lock;
- ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock);
- BKE_image_release_ibuf(ima, ibuf, lock);
- }
return duration;
}
diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c
index 04037a64426..3bcd9cd0441 100644
--- a/source/blender/makesrna/intern/rna_sequencer.c
+++ b/source/blender/makesrna/intern/rna_sequencer.c
@@ -1754,12 +1754,12 @@ static void rna_def_color_balance(BlenderRNA *brna)
prop = RNA_def_property(srna, "invert_gain", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_COLOR_BALANCE_INVERSE_GAIN);
- RNA_def_property_ui_text(prop, "Inverse Gain", "Invert the gain color`");
+ RNA_def_property_ui_text(prop, "Inverse Gain", "Invert the gain color");
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceColorBalance_update");
prop = RNA_def_property(srna, "invert_slope", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_COLOR_BALANCE_INVERSE_SLOPE);
- RNA_def_property_ui_text(prop, "Inverse Slope", "Invert the slope color`");
+ RNA_def_property_ui_text(prop, "Inverse Slope", "Invert the slope color");
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceColorBalance_update");
prop = RNA_def_property(srna, "invert_offset", PROP_BOOLEAN, PROP_NONE);
diff --git a/source/blender/makesrna/intern/rna_wm_api.c b/source/blender/makesrna/intern/rna_wm_api.c
index eb5e2549b1d..b82458c4442 100644
--- a/source/blender/makesrna/intern/rna_wm_api.c
+++ b/source/blender/makesrna/intern/rna_wm_api.c
@@ -625,7 +625,7 @@ static wmEvent *rna_Window_event_add_simulate(wmWindow *win,
return NULL;
}
}
- if (ELEM(type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+ if (ISMOUSE_MOTION(type)) {
if (value != KM_NOTHING) {
BKE_report(reports, RPT_ERROR, "Value: must be 'NOTHING' for motion");
return NULL;
diff --git a/source/blender/windowmanager/intern/wm_event_query.c b/source/blender/windowmanager/intern/wm_event_query.c
index 81044197ae7..a1d94c33f27 100644
--- a/source/blender/windowmanager/intern/wm_event_query.c
+++ b/source/blender/windowmanager/intern/wm_event_query.c
@@ -346,8 +346,8 @@ bool WM_cursor_test_motion_and_update(const int mval[2])
int WM_event_drag_threshold(const struct wmEvent *event)
{
int drag_threshold;
- if (ISMOUSE(event->prev_press_type)) {
- BLI_assert(event->prev_press_type != MOUSEMOVE);
+ BLI_assert(event->prev_press_type != MOUSEMOVE);
+ if (ISMOUSE_BUTTON(event->prev_press_type)) {
/* Using the previous type is important is we want to check the last pressed/released button,
* The `event->type` would include #MOUSEMOVE which is always the case when dragging
* and does not help us know which threshold to use. */
diff --git a/source/blender/windowmanager/intern/wm_event_system.cc b/source/blender/windowmanager/intern/wm_event_system.cc
index 79b303364d8..102441f1b4d 100644
--- a/source/blender/windowmanager/intern/wm_event_system.cc
+++ b/source/blender/windowmanager/intern/wm_event_system.cc
@@ -190,7 +190,7 @@ void wm_event_free(wmEvent *event)
printf("%s: 'is_repeat=true' for non-keyboard event, this should not happen.\n", __func__);
WM_event_print(event);
}
- if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE) && (event->val != KM_NOTHING)) {
+ if (ISMOUSE_MOTION(event->type) && (event->val != KM_NOTHING)) {
printf("%s: 'val != NOTHING' for a cursor motion event, this should not happen.\n", __func__);
WM_event_print(event);
}
@@ -3095,7 +3095,7 @@ static int wm_handlers_do_intern(bContext *C, wmWindow *win, wmEvent *event, Lis
const bool do_debug_handler =
(G.debug & G_DEBUG_HANDLERS) &&
/* Comment this out to flood the console! (if you really want to test). */
- !ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE);
+ !ISMOUSE_MOTION(event->type);
wmWindowManager *wm = CTX_wm_manager(C);
int action = WM_HANDLER_CONTINUE;
@@ -3286,7 +3286,7 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
return action;
}
- if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+ if (ISMOUSE_MOTION(event->type)) {
/* Test for #KM_CLICK_DRAG events. */
/* NOTE(@campbellbarton): Needed so drag can be used for editors that support both click
@@ -3821,15 +3821,14 @@ void wm_event_do_handlers(bContext *C)
/* Active screen might change during handlers, update pointer. */
screen = WM_window_get_active_screen(win);
- if (G.debug & (G_DEBUG_HANDLERS | G_DEBUG_EVENTS) &&
- !ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+ if (G.debug & (G_DEBUG_HANDLERS | G_DEBUG_EVENTS) && !ISMOUSE_MOTION(event->type)) {
printf("\n%s: Handling event\n", __func__);
WM_event_print(event);
}
/* Take care of pie event filter. */
if (wm_event_pie_filter(win, event)) {
- if (!ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+ if (!ISMOUSE_MOTION(event->type)) {
CLOG_INFO(WM_LOG_HANDLERS, 1, "event filtered due to pie button pressed");
}
BLI_remlink(&win->event_queue, event);
@@ -3851,7 +3850,7 @@ void wm_event_do_handlers(bContext *C)
/* Clear tool-tip on mouse move. */
if (screen->tool_tip && screen->tool_tip->exit_on_event) {
- if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+ if (ISMOUSE_MOTION(event->type)) {
if (len_manhattan_v2v2_int(screen->tool_tip->event_xy, event->xy) >
WM_EVENT_CURSOR_MOTION_THRESHOLD) {
WM_tooltip_clear(C, win);
@@ -4975,7 +4974,7 @@ static bool wm_event_is_double_click(const wmEvent *event)
{
if ((event->type == event->prev_type) && (event->prev_val == KM_RELEASE) &&
(event->val == KM_PRESS)) {
- if (ISMOUSE(event->type) && WM_event_drag_test(event, event->prev_press_xy)) {
+ if (ISMOUSE_BUTTON(event->type) && WM_event_drag_test(event, event->prev_press_xy)) {
/* Pass. */
}
else {
diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c
index 33c69a23558..315e4c994ad 100644
--- a/source/blender/windowmanager/intern/wm_operators.c
+++ b/source/blender/windowmanager/intern/wm_operators.c
@@ -939,7 +939,7 @@ int WM_generic_select_modal(bContext *C, wmOperator *op, const wmEvent *event)
return ret_value | OPERATOR_PASS_THROUGH;
}
- if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+ if (ISMOUSE_MOTION(event->type)) {
const int drag_delta[2] = {
mval[0] - event->mval[0],
mval[1] - event->mval[1],
diff --git a/source/blender/windowmanager/wm_event_types.h b/source/blender/windowmanager/wm_event_types.h
index edac3ada73b..b4e81dc54c8 100644
--- a/source/blender/windowmanager/wm_event_types.h
+++ b/source/blender/windowmanager/wm_event_types.h
@@ -44,7 +44,10 @@ enum {
/* non-event, for example disabled timer */
EVENT_NONE = 0x0000,
- /* ********** Start of Input devices. ********** */
+/* ********** Start of Input devices. ********** */
+
+/* Minimum mouse value (inclusive). */
+#define _EVT_MOUSE_MIN 0x0001
/* MOUSE: 0x000x, 0x001x */
LEFTMOUSE = 0x0001,
@@ -74,6 +77,9 @@ enum {
* paint and drawing tools however will want to handle these. */
INBETWEEN_MOUSEMOVE = 0x0011,
+/* Maximum keyboard value (inclusive). */
+#define _EVT_MOUSE_MAX 0x0011 /* 17 */
+
/* IME event, GHOST_kEventImeCompositionStart in ghost */
WM_IME_COMPOSITE_START = 0x0014,
/* IME event, GHOST_kEventImeComposition in ghost */
@@ -379,13 +385,16 @@ enum {
(((event_type) >= EVT_LEFTCTRLKEY && (event_type) <= EVT_LEFTSHIFTKEY) || \
(event_type) == EVT_OSKEY)
-/** Test whether the event is a mouse button. */
-#define ISMOUSE(event_type) \
- (((event_type) >= LEFTMOUSE && (event_type) <= BUTTON7MOUSE) || (event_type) == MOUSESMARTZOOM)
-/** Test whether the event is a mouse wheel. */
-#define ISMOUSE_WHEEL(event_type) ((event_type) >= WHEELUPMOUSE && (event_type) <= WHEELOUTMOUSE)
-/** Test whether the event is a mouse (track-pad) gesture. */
-#define ISMOUSE_GESTURE(event_type) ((event_type) >= MOUSEPAN && (event_type) <= MOUSEROTATE)
+/**
+ * Test whether the event is any kind:
+ * #ISMOUSE_MOTION, #ISMOUSE_BUTTON, #ISMOUSE_WHEEL & #ISMOUSE_GESTURE.
+ *
+ * \note It's best to use more specific check if possible as mixing motion/buttons/gestures
+ * is very broad and not necessarily obvious which kinds of events are important.
+ */
+#define ISMOUSE(event_type) ((event_type) >= _EVT_MOUSE_MIN && (event_type) <= _EVT_MOUSE_MAX)
+/** Test whether the event is a mouse button (excluding mouse-wheel). */
+#define ISMOUSE_MOTION(event_type) ELEM(event_type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)
/** Test whether the event is a mouse button (excluding mouse-wheel). */
#define ISMOUSE_BUTTON(event_type) \
(ELEM(event_type, \
@@ -396,6 +405,10 @@ enum {
BUTTON5MOUSE, \
BUTTON6MOUSE, \
BUTTON7MOUSE))
+/** Test whether the event is a mouse wheel. */
+#define ISMOUSE_WHEEL(event_type) ((event_type) >= WHEELUPMOUSE && (event_type) <= WHEELOUTMOUSE)
+/** Test whether the event is a mouse (track-pad) gesture. */
+#define ISMOUSE_GESTURE(event_type) ((event_type) >= MOUSEPAN && (event_type) <= MOUSESMARTZOOM)
/** Test whether the event is a NDOF event. */
#define ISNDOF(event_type) ((event_type) >= _NDOF_MIN && (event_type) <= _NDOF_MAX)