diff options
author | Hans Goudey <h.goudey@me.com> | 2022-07-22 02:05:48 +0300 |
---|---|---|
committer | Hans Goudey <h.goudey@me.com> | 2022-07-22 02:05:48 +0300 |
commit | 03c4548e1493a66618c8c79bceab163d2845b965 (patch) | |
tree | dd29865fade7689a05473f3649c16d614bc73a53 /source/blender | |
parent | 0d81fdc91321c3b69b32307c40217a449b7b6c72 (diff) | |
parent | 7a4a6ccad74f9a2094e9f0928f5ac61ffd6346ff (diff) |
Merge branch 'master' into refactor-mesh-hide-generic
Diffstat (limited to 'source/blender')
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) |