diff options
author | Jeroen Bakker <jeroen@blender.org> | 2021-06-01 16:07:46 +0300 |
---|---|---|
committer | Jeroen Bakker <jeroen@blender.org> | 2021-06-01 16:07:46 +0300 |
commit | aa75e383ba46f0e906101bbb3492eff337c267ae (patch) | |
tree | ce89d6a394431a36770de9bb4421a41962862194 /source/blender/blenkernel | |
parent | ee54a8ace753a56236f5d1d647da7a40904af6b3 (diff) | |
parent | 6899dcab5339d1f17dd2ea665a38e4ab28ab23fc (diff) |
Merge branch 'master' into tmp-buildbot-cleanuptmp-buildbot-cleanup
Diffstat (limited to 'source/blender/blenkernel')
-rw-r--r-- | source/blender/blenkernel/BKE_blender_version.h | 2 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_lib_id.h | 6 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_node.h | 12 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/collection.c | 3 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/customdata_file.c | 8 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/editmesh.c | 3 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/geometry_set_instances.cc | 26 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/lib_id.c | 30 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/lib_id_test.cc | 60 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/lib_override.c | 125 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/node.cc | 1 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/writeffmpeg.c | 191 |
12 files changed, 328 insertions, 139 deletions
diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 0bab980cfcd..eb937ac9608 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -39,7 +39,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 1 +#define BLENDER_FILE_SUBVERSION 2 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file diff --git a/source/blender/blenkernel/BKE_lib_id.h b/source/blender/blenkernel/BKE_lib_id.h index 7ac45ac4883..e16507bf3cc 100644 --- a/source/blender/blenkernel/BKE_lib_id.h +++ b/source/blender/blenkernel/BKE_lib_id.h @@ -258,8 +258,10 @@ void BKE_lib_id_swap_full(struct Main *bmain, struct ID *id_a, struct ID *id_b); void id_sort_by_name(struct ListBase *lb, struct ID *id, struct ID *id_sorting_hint); void BKE_lib_id_expand_local(struct Main *bmain, struct ID *id); -bool BKE_id_new_name_validate(struct ListBase *lb, struct ID *id, const char *name) - ATTR_NONNULL(1, 2); +bool BKE_id_new_name_validate(struct ListBase *lb, + struct ID *id, + const char *name, + const bool do_linked_data) ATTR_NONNULL(1, 2); void BKE_lib_id_clear_library_data(struct Main *bmain, struct ID *id); /* Affect whole Main database. */ diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 448f4ae48ad..fb6647cb68d 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1305,15 +1305,18 @@ void ntreeCompositOutputFileUniqueLayer(struct ListBase *list, void ntreeCompositColorBalanceSyncFromLGG(bNodeTree *ntree, bNode *node); void ntreeCompositColorBalanceSyncFromCDL(bNodeTree *ntree, bNode *node); -void ntreeCompositCryptomatteSyncFromAdd(bNode *node); +void ntreeCompositCryptomatteSyncFromAdd(const Scene *scene, bNode *node); void ntreeCompositCryptomatteSyncFromRemove(bNode *node); bNodeSocket *ntreeCompositCryptomatteAddSocket(bNodeTree *ntree, bNode *node); int ntreeCompositCryptomatteRemoveSocket(bNodeTree *ntree, bNode *node); -void ntreeCompositCryptomatteLayerPrefix(const bNode *node, char *r_prefix, size_t prefix_len); +void ntreeCompositCryptomatteLayerPrefix(const Scene *scene, + const bNode *node, + char *r_prefix, + size_t prefix_len); /* Update the runtime layer names with the cryptomatte layer names of the references * render layer or image. */ -void ntreeCompositCryptomatteUpdateLayerNames(bNode *node); -struct CryptomatteSession *ntreeCompositCryptomatteSession(bNode *node); +void ntreeCompositCryptomatteUpdateLayerNames(const Scene *scene, bNode *node); +struct CryptomatteSession *ntreeCompositCryptomatteSession(const Scene *scene, bNode *node); /** \} */ @@ -1426,6 +1429,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_MATERIAL_ASSIGN 1049 #define GEO_NODE_INPUT_MATERIAL 1050 #define GEO_NODE_MATERIAL_REPLACE 1051 +#define GEO_NODE_MESH_TO_CURVE 1052 /** \} */ diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index d8fbdf26d93..be827cd338d 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -1423,7 +1423,8 @@ static bool collection_instance_find_recursive(Collection *collection, } LISTBASE_FOREACH (CollectionChild *, collection_child, &collection->children) { - if (collection_instance_find_recursive(collection_child->collection, instance_collection)) { + if (collection_child->collection != NULL && + collection_instance_find_recursive(collection_child->collection, instance_collection)) { return true; } } diff --git a/source/blender/blenkernel/intern/customdata_file.c b/source/blender/blenkernel/intern/customdata_file.c index 470c2f2d246..4aaecc26eff 100644 --- a/source/blender/blenkernel/intern/customdata_file.c +++ b/source/blender/blenkernel/intern/customdata_file.c @@ -167,7 +167,7 @@ static bool cdf_read_header(CDataFile *cdf) offset += header->structbytes; header->structbytes = sizeof(CDataFileHeader); - if (fseek(f, offset, SEEK_SET) != 0) { + if (BLI_fseek(f, offset, SEEK_SET) != 0) { return false; } @@ -201,7 +201,7 @@ static bool cdf_read_header(CDataFile *cdf) mesh->structbytes = sizeof(CDataFileMeshHeader); } - if (fseek(f, offset, SEEK_SET) != 0) { + if (BLI_fseek(f, offset, SEEK_SET) != 0) { return false; } @@ -233,7 +233,7 @@ static bool cdf_read_header(CDataFile *cdf) offset += layer->structbytes; layer->structbytes = sizeof(CDataFileLayer); - if (fseek(f, offset, SEEK_SET) != 0) { + if (BLI_fseek(f, offset, SEEK_SET) != 0) { return false; } } @@ -321,7 +321,7 @@ bool cdf_read_layer(CDataFile *cdf, CDataFileLayer *blay) offset += cdf->layer[a].datasize; } - return (fseek(cdf->readf, offset, SEEK_SET) == 0); + return (BLI_fseek(cdf->readf, offset, SEEK_SET) == 0); } bool cdf_read_data(CDataFile *cdf, unsigned int size, void *data) diff --git a/source/blender/blenkernel/intern/editmesh.c b/source/blender/blenkernel/intern/editmesh.c index 0fa3bb29ccd..bd76357617a 100644 --- a/source/blender/blenkernel/intern/editmesh.c +++ b/source/blender/blenkernel/intern/editmesh.c @@ -127,9 +127,10 @@ static void editmesh_tessface_calc_intern(BMEditMesh *em) } em->looptris = looptris; + em->tottri = looptris_tot; /* after allocating the em->looptris, we're ready to tessellate */ - BM_mesh_calc_tessellation(em->bm, em->looptris, &em->tottri); + BM_mesh_calc_tessellation(em->bm, em->looptris); } void BKE_editmesh_looptri_calc(BMEditMesh *em) diff --git a/source/blender/blenkernel/intern/geometry_set_instances.cc b/source/blender/blenkernel/intern/geometry_set_instances.cc index 9abd00c2b4f..69840ba1612 100644 --- a/source/blender/blenkernel/intern/geometry_set_instances.cc +++ b/source/blender/blenkernel/intern/geometry_set_instances.cc @@ -544,9 +544,9 @@ static void join_attributes(Span<GeometryInstanceGroup> set_groups, } } -static void join_curve_splines(Span<GeometryInstanceGroup> set_groups, CurveComponent &result) +static CurveEval *join_curve_splines(Span<GeometryInstanceGroup> set_groups) { - CurveEval *new_curve = new CurveEval(); + Vector<SplinePtr> new_splines; for (const GeometryInstanceGroup &set_group : set_groups) { const GeometrySet &set = set_group.geometry_set; if (!set.has_curve()) { @@ -558,10 +558,18 @@ static void join_curve_splines(Span<GeometryInstanceGroup> set_groups, CurveComp for (const float4x4 &transform : set_group.transforms) { SplinePtr new_spline = source_spline->copy(); new_spline->transform(transform); - new_curve->add_spline(std::move(new_spline)); + new_splines.append(std::move(new_spline)); } } } + if (new_splines.is_empty()) { + return nullptr; + } + + CurveEval *new_curve = new CurveEval(); + for (SplinePtr &new_spline : new_splines) { + new_curve->add_spline(std::move(new_spline)); + } for (SplinePtr &spline : new_curve->splines()) { /* Spline instances should have no custom attributes, since they always come @@ -573,8 +581,7 @@ static void join_curve_splines(Span<GeometryInstanceGroup> set_groups, CurveComp } new_curve->attributes.reallocate(new_curve->splines().size()); - - result.replace(new_curve); + return new_curve; } static void join_instance_groups_mesh(Span<GeometryInstanceGroup> set_groups, @@ -639,14 +646,17 @@ static void join_instance_groups_volume(Span<GeometryInstanceGroup> set_groups, { /* Not yet supported. Joining volume grids with the same name requires resampling of at least * one of the grids. The cell size of the resulting volume has to be determined somehow. */ - VolumeComponent &dst_component = result.get_component_for_write<VolumeComponent>(); - UNUSED_VARS(set_groups, dst_component); + UNUSED_VARS(set_groups, result); } static void join_instance_groups_curve(Span<GeometryInstanceGroup> set_groups, GeometrySet &result) { + CurveEval *curve = join_curve_splines(set_groups); + if (curve == nullptr) { + return; + } CurveComponent &dst_component = result.get_component_for_write<CurveComponent>(); - join_curve_splines(set_groups, dst_component); + dst_component.replace(curve); } GeometrySet geometry_set_realize_mesh_for_modifier(const GeometrySet &geometry_set) diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index f93bf494ee9..490abe05169 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -164,7 +164,7 @@ static void lib_id_clear_library_data_ex(Main *bmain, ID *id) id->tag &= ~(LIB_TAG_INDIRECT | LIB_TAG_EXTERN); id->flag &= ~LIB_INDIRECT_WEAK_LINK; if (id_in_mainlist) { - if (BKE_id_new_name_validate(which_libbase(bmain, GS(id->name)), id, NULL)) { + if (BKE_id_new_name_validate(which_libbase(bmain, GS(id->name)), id, NULL, false)) { bmain->is_memfile_undo_written = false; } } @@ -833,7 +833,9 @@ void BKE_libblock_management_main_add(Main *bmain, void *idv) ListBase *lb = which_libbase(bmain, GS(id->name)); BKE_main_lock(bmain); BLI_addtail(lb, id); - BKE_id_new_name_validate(lb, id, NULL); + /* We need to allow adding extra datablocks into libraries too, e.g. to support generating new + * overrides for recursive resync. */ + BKE_id_new_name_validate(lb, id, NULL, true); /* alphabetic insertion: is in new_id */ id->tag &= ~(LIB_TAG_NO_MAIN | LIB_TAG_NO_USER_REFCOUNT); bmain->is_memfile_undo_written = false; @@ -989,7 +991,7 @@ void BKE_main_id_repair_duplicate_names_listbase(ListBase *lb) } for (i = 0; i < lb_len; i++) { if (!BLI_gset_add(gset, id_array[i]->name + 2)) { - BKE_id_new_name_validate(lb, id_array[i], NULL); + BKE_id_new_name_validate(lb, id_array[i], NULL, false); } } BLI_gset_free(gset, NULL); @@ -1092,7 +1094,7 @@ void *BKE_libblock_alloc(Main *bmain, short type, const char *name, const int fl BKE_main_lock(bmain); BLI_addtail(lb, id); - BKE_id_new_name_validate(lb, id, name); + BKE_id_new_name_validate(lb, id, name, false); bmain->is_memfile_undo_written = false; /* alphabetic insertion: is in new_id */ BKE_main_unlock(bmain); @@ -1557,7 +1559,7 @@ static bool check_for_dupid(ListBase *lb, ID *id, char *name, ID **r_id_sorting_ * and that current one is not. */ bool is_valid = false; for (id_test = lb->first; id_test; id_test = id_test->next) { - if (id != id_test && !ID_IS_LINKED(id_test)) { + if (id != id_test && id_test->lib == id->lib) { if (id_test->name[2] == final_name[0] && STREQ(final_name, id_test->name + 2)) { /* We expect final_name to not be already used, so this is a failure. */ is_valid = false; @@ -1613,7 +1615,7 @@ static bool check_for_dupid(ListBase *lb, ID *id, char *name, ID **r_id_sorting_ for (id_test = lb->first; id_test; id_test = id_test->next) { char base_name_test[MAX_ID_NAME - 2]; int number_test; - if ((id != id_test) && !ID_IS_LINKED(id_test) && (name[0] == id_test->name[2]) && + if ((id != id_test) && (id_test->lib == id->lib) && (name[0] == id_test->name[2]) && (ELEM(id_test->name[base_name_len + 2], '.', '\0')) && STREQLEN(name, id_test->name + 2, base_name_len) && (BLI_split_name_num(base_name_test, &number_test, id_test->name + 2, '.') == @@ -1702,15 +1704,18 @@ static bool check_for_dupid(ListBase *lb, ID *id, char *name, ID **r_id_sorting_ * * Only for local IDs (linked ones already have a unique ID in their library). * + * \param do_linked_data if true, also ensure a unique name in case the given \a id is linked + * (otherwise, just ensure that it is properly sorted). + * * \return true if a new name had to be created. */ -bool BKE_id_new_name_validate(ListBase *lb, ID *id, const char *tname) +bool BKE_id_new_name_validate(ListBase *lb, ID *id, const char *tname, const bool do_linked_data) { bool result = false; char name[MAX_ID_NAME - 2]; - /* If library, don't rename, but do ensure proper sorting. */ - if (ID_IS_LINKED(id)) { + /* If library, don't rename (unless explicitely required), but do ensure proper sorting. */ + if (!do_linked_data && ID_IS_LINKED(id)) { id_sort_by_name(lb, id, NULL); return result; @@ -2193,9 +2198,9 @@ void BLI_libblock_ensure_unique_name(Main *bmain, const char *name) /* search for id */ idtest = BLI_findstring(lb, name + 2, offsetof(ID, name) + 2); - if (idtest != NULL) { + if (idtest != NULL && !ID_IS_LINKED(idtest)) { /* BKE_id_new_name_validate also takes care of sorting. */ - BKE_id_new_name_validate(lb, idtest, NULL); + BKE_id_new_name_validate(lb, idtest, NULL, false); bmain->is_memfile_undo_written = false; } } @@ -2205,8 +2210,9 @@ void BLI_libblock_ensure_unique_name(Main *bmain, const char *name) */ void BKE_libblock_rename(Main *bmain, ID *id, const char *name) { + BLI_assert(!ID_IS_LINKED(id)); ListBase *lb = which_libbase(bmain, GS(id->name)); - if (BKE_id_new_name_validate(lb, id, name)) { + if (BKE_id_new_name_validate(lb, id, name, false)) { bmain->is_memfile_undo_written = false; } } diff --git a/source/blender/blenkernel/intern/lib_id_test.cc b/source/blender/blenkernel/intern/lib_id_test.cc index fbe4a15da1c..8e21ae88aa6 100644 --- a/source/blender/blenkernel/intern/lib_id_test.cc +++ b/source/blender/blenkernel/intern/lib_id_test.cc @@ -20,6 +20,7 @@ #include "MEM_guardedalloc.h" #include "BLI_listbase.h" +#include "BLI_string.h" #include "BKE_idtype.h" #include "BKE_lib_id.h" @@ -110,4 +111,63 @@ TEST(lib_id_main_sort, linked_ids_1) test_lib_id_main_sort_free(&ctx); } +TEST(lib_id_main_unique_name, local_ids_1) +{ + LibIDMainSortTestContext ctx = {nullptr}; + test_lib_id_main_sort_init(&ctx); + EXPECT_TRUE(BLI_listbase_is_empty(&ctx.bmain->libraries)); + + ID *id_c = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_C")); + ID *id_a = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_A")); + ID *id_b = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_B")); + test_lib_id_main_sort_check_order({id_a, id_b, id_c}); + + BLI_strncpy(id_c->name, id_a->name, sizeof(id_c->name)); + BKE_id_new_name_validate(&ctx.bmain->objects, id_c, NULL, false); + EXPECT_TRUE(strcmp(id_c->name + 2, "OB_A.001") == 0); + EXPECT_TRUE(strcmp(id_a->name + 2, "OB_A") == 0); + EXPECT_TRUE(ctx.bmain->objects.first == id_a); + EXPECT_TRUE(ctx.bmain->objects.last == id_b); + test_lib_id_main_sort_check_order({id_a, id_c, id_b}); + + test_lib_id_main_sort_free(&ctx); +} + +TEST(lib_id_main_unique_name, linked_ids_1) +{ + LibIDMainSortTestContext ctx = {nullptr}; + test_lib_id_main_sort_init(&ctx); + EXPECT_TRUE(BLI_listbase_is_empty(&ctx.bmain->libraries)); + + Library *lib_a = static_cast<Library *>(BKE_id_new(ctx.bmain, ID_LI, "LI_A")); + Library *lib_b = static_cast<Library *>(BKE_id_new(ctx.bmain, ID_LI, "LI_B")); + ID *id_c = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_C")); + ID *id_a = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_A")); + ID *id_b = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_B")); + + id_a->lib = lib_a; + id_sort_by_name(&ctx.bmain->objects, id_a, nullptr); + id_b->lib = lib_a; + id_sort_by_name(&ctx.bmain->objects, id_b, nullptr); + BLI_strncpy(id_b->name, id_a->name, sizeof(id_b->name)); + BKE_id_new_name_validate(&ctx.bmain->objects, id_b, NULL, true); + EXPECT_TRUE(strcmp(id_b->name + 2, "OB_A.001") == 0); + EXPECT_TRUE(strcmp(id_a->name + 2, "OB_A") == 0); + EXPECT_TRUE(ctx.bmain->objects.first == id_c); + EXPECT_TRUE(ctx.bmain->objects.last == id_b); + test_lib_id_main_sort_check_order({id_c, id_a, id_b}); + + id_b->lib = lib_b; + id_sort_by_name(&ctx.bmain->objects, id_b, nullptr); + BLI_strncpy(id_b->name, id_a->name, sizeof(id_b->name)); + BKE_id_new_name_validate(&ctx.bmain->objects, id_b, NULL, true); + EXPECT_TRUE(strcmp(id_b->name + 2, "OB_A") == 0); + EXPECT_TRUE(strcmp(id_a->name + 2, "OB_A") == 0); + EXPECT_TRUE(ctx.bmain->objects.first == id_c); + EXPECT_TRUE(ctx.bmain->objects.last == id_b); + test_lib_id_main_sort_check_order({id_c, id_a, id_b}); + + test_lib_id_main_sort_free(&ctx); +} + } // namespace blender::bke::tests diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index 0e2317c72de..4d5085d6ad5 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -252,6 +252,13 @@ bool BKE_lib_override_library_is_user_edited(struct ID *id) return false; } + /* A bit weird, but those embedded IDs are handled by their owner ID anyway, so we can just + * assume they are never user-edited, actual proper detection will happen from their owner check. + */ + if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) { + return false; + } + LISTBASE_FOREACH (IDOverrideLibraryProperty *, op, &id->override_library->properties) { LISTBASE_FOREACH (IDOverrideLibraryPropertyOperation *, opop, &op->operations) { if ((opop->flag & IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE) != 0) { @@ -421,7 +428,8 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain, /* If other ID is a linked one, but not from the same library as our reference, then we * consider we should also remap it, as part of recursive resync. */ - if ((other_id->tag & LIB_TAG_DOIT) != 0 && other_id->lib != reference_id->lib) { + if ((other_id->tag & LIB_TAG_DOIT) != 0 && other_id->lib != reference_id->lib && + other_id != local_id) { BKE_libblock_relink_ex(bmain, other_id, reference_id, @@ -461,6 +469,8 @@ typedef struct LibOverrideGroupTagData { ID *id_root; uint tag; uint missing_tag; + /* Whether we are looping on override data, or their references (linked) one. */ + bool is_override; } LibOverrideGroupTagData; /* Tag all IDs in dependency relationships within an override hierarchy/group. @@ -473,6 +483,7 @@ static bool lib_override_hierarchy_dependencies_recursive_tag(LibOverrideGroupTa { Main *bmain = data->bmain; ID *id = data->id_root; + const bool is_override = data->is_override; MainIDRelationsEntry *entry = BLI_ghash_lookup(bmain->relations->relations_from_pointers, id); BLI_assert(entry != NULL); @@ -494,16 +505,16 @@ static bool lib_override_hierarchy_dependencies_recursive_tag(LibOverrideGroupTa } /* We only consider IDs from the same library. */ ID *to_id = *to_id_entry->id_pointer.to; - if (!ID_IS_LINKED(to_id) && !ID_IS_OVERRIDE_LIBRARY(to_id)) { - /* Pure local data is a barrier of dependency in override cases. */ + if (to_id == NULL || to_id->lib != id->lib || + (is_override && !ID_IS_OVERRIDE_LIBRARY(to_id))) { + /* IDs from different libraries, or non-override IDs in case we are processing overrides, are + * both barriers of dependency. */ continue; } - if (to_id != NULL && to_id->lib == id->lib) { - LibOverrideGroupTagData sub_data = *data; - sub_data.id_root = to_id; - if (lib_override_hierarchy_dependencies_recursive_tag(&sub_data)) { - id->tag |= data->tag; - } + LibOverrideGroupTagData sub_data = *data; + sub_data.id_root = to_id; + if (lib_override_hierarchy_dependencies_recursive_tag(&sub_data)) { + id->tag |= data->tag; } } @@ -515,6 +526,7 @@ static void lib_override_linked_group_tag_recursive(LibOverrideGroupTagData *dat Main *bmain = data->bmain; ID *id_owner = data->id_root; BLI_assert(ID_IS_LINKED(id_owner)); + BLI_assert(!data->is_override); const uint tag = data->tag; const uint missing_tag = data->missing_tag; @@ -580,6 +592,7 @@ static void lib_override_linked_group_tag(LibOverrideGroupTagData *data) { Main *bmain = data->bmain; ID *id_root = data->id_root; + BLI_assert(!data->is_override); if ((id_root->tag & LIB_TAG_MISSING)) { id_root->tag |= data->missing_tag; @@ -606,11 +619,12 @@ static void lib_override_linked_group_tag(LibOverrideGroupTagData *data) } } -static void lib_override_local_group_tag_recursive(LibOverrideGroupTagData *data) +static void lib_override_overrides_group_tag_recursive(LibOverrideGroupTagData *data) { Main *bmain = data->bmain; ID *id_owner = data->id_root; BLI_assert(ID_IS_OVERRIDE_LIBRARY(id_owner)); + BLI_assert(data->is_override); const uint tag = data->tag; const uint missing_tag = data->missing_tag; @@ -658,15 +672,16 @@ static void lib_override_local_group_tag_recursive(LibOverrideGroupTagData *data /* Recursively process the dependencies. */ LibOverrideGroupTagData sub_data = *data; sub_data.id_root = to_id; - lib_override_local_group_tag_recursive(&sub_data); + lib_override_overrides_group_tag_recursive(&sub_data); } } /* This will tag all override IDs of an override group defined by the given `id_root`. */ -static void lib_override_local_group_tag(LibOverrideGroupTagData *data) +static void lib_override_overrides_group_tag(LibOverrideGroupTagData *data) { ID *id_root = data->id_root; BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root)); + BLI_assert(data->is_override); if ((id_root->override_library->reference->tag & LIB_TAG_MISSING)) { id_root->tag |= data->missing_tag; @@ -676,14 +691,17 @@ static void lib_override_local_group_tag(LibOverrideGroupTagData *data) } /* Tag all local overrides in id_root's group. */ - lib_override_local_group_tag_recursive(data); + lib_override_overrides_group_tag_recursive(data); } static bool lib_override_library_create_do(Main *bmain, ID *id_root) { BKE_main_relations_create(bmain, 0); - LibOverrideGroupTagData data = { - .bmain = bmain, .id_root = id_root, .tag = LIB_TAG_DOIT, .missing_tag = LIB_TAG_MISSING}; + LibOverrideGroupTagData data = {.bmain = bmain, + .id_root = id_root, + .tag = LIB_TAG_DOIT, + .missing_tag = LIB_TAG_MISSING, + .is_override = false}; lib_override_linked_group_tag(&data); BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false); @@ -934,12 +952,16 @@ bool BKE_lib_override_library_resync(Main *bmain, ID *id_root_reference = id_root->override_library->reference; BKE_main_relations_create(bmain, 0); - LibOverrideGroupTagData data = { - .bmain = bmain, .id_root = id_root, .tag = LIB_TAG_DOIT, .missing_tag = LIB_TAG_MISSING}; - lib_override_local_group_tag(&data); + LibOverrideGroupTagData data = {.bmain = bmain, + .id_root = id_root, + .tag = LIB_TAG_DOIT, + .missing_tag = LIB_TAG_MISSING, + .is_override = true}; + lib_override_overrides_group_tag(&data); BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false); data.id_root = id_root_reference; + data.is_override = false; lib_override_linked_group_tag(&data); BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false); @@ -1277,6 +1299,58 @@ bool BKE_lib_override_library_resync(Main *bmain, return success; } +/* Ensures parent collection (or objects) in the same override group are also tagged for resync. + * + * This is needed since otherwise, some (new) ID added in one sub-collection might be used in + * another unrelated sub-collection, if 'root' collection is not resynced separated resync of those + * sub-collections would be unaware that this is the same ID, and would re-generate several + * overrides for it. + * + * TODO: This is a sub-optimal, simple solution. At some point, we should rather find a way to + * resync a set of 'sub-roots' overrides, instead of having to 'go back' to the real root and + * resync the whole hierarchy. */ +static ID *lib_override_library_main_resync_find_root_recurse(ID *id, int *level) +{ + (*level)++; + ID *return_id = id; + + switch (GS(id->name)) { + case ID_GR: { + /* Find the highest valid collection in the parenting hierarchy. + * Note that in practice, in any decent common case there is only one well defined root + * collection anyway. */ + int max_level = *level; + Collection *collection = (Collection *)id; + LISTBASE_FOREACH (CollectionParent *, collection_parent_iter, &collection->parents) { + Collection *collection_parent = collection_parent_iter->collection; + if (ID_IS_OVERRIDE_LIBRARY_REAL(collection_parent) && + collection_parent->id.lib == id->lib) { + int tmp_level = *level; + ID *tmp_id = lib_override_library_main_resync_find_root_recurse(&collection_parent->id, + &tmp_level); + if (tmp_level > max_level) { + max_level = tmp_level; + return_id = tmp_id; + } + } + } + break; + } + case ID_OB: { + Object *object = (Object *)id; + if (object->parent != NULL && ID_IS_OVERRIDE_LIBRARY_REAL(object->parent) && + object->parent->id.lib == id->lib) { + return_id = lib_override_library_main_resync_find_root_recurse(&object->parent->id, level); + } + break; + } + default: + break; + } + + return return_id; +} + /* Ensure resync of all overrides at one level of indirect usage. * * We need to handle each level independently, since an override at level n may be affected by @@ -1312,7 +1386,8 @@ static void lib_override_library_main_resync_on_library_indirect_level( LibOverrideGroupTagData data = {.bmain = bmain, .id_root = id->override_library->reference, .tag = LIB_TAG_DOIT, - .missing_tag = LIB_TAG_MISSING}; + .missing_tag = LIB_TAG_MISSING, + .is_override = false}; lib_override_linked_group_tag(&data); BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false); lib_override_hierarchy_dependencies_recursive_tag(&data); @@ -1396,6 +1471,9 @@ static void lib_override_library_main_resync_on_library_indirect_level( (!ID_IS_LINKED(id) && library_indirect_level != 0)) { continue; } + + int level = 0; + id = lib_override_library_main_resync_find_root_recurse(id, &level); BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id)); do_continue = true; @@ -1550,9 +1628,12 @@ void BKE_lib_override_library_delete(Main *bmain, ID *id_root) /* Tag all library overrides in the chains of dependencies from the given root one. */ BKE_main_relations_create(bmain, 0); - LibOverrideGroupTagData data = { - .bmain = bmain, .id_root = id_root, .tag = LIB_TAG_DOIT, .missing_tag = LIB_TAG_MISSING}; - lib_override_local_group_tag(&data); + LibOverrideGroupTagData data = {.bmain = bmain, + .id_root = id_root, + .tag = LIB_TAG_DOIT, + .missing_tag = LIB_TAG_MISSING, + .is_override = true}; + lib_override_overrides_group_tag(&data); BKE_main_relations_free(bmain); diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 3377f5c69dc..d0864e85373 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5066,6 +5066,7 @@ static void registerGeometryNodes() register_node_type_geo_mesh_primitive_ico_sphere(); register_node_type_geo_mesh_primitive_line(); register_node_type_geo_mesh_primitive_uv_sphere(); + register_node_type_geo_mesh_to_curve(); register_node_type_geo_object_info(); register_node_type_geo_point_distribute(); register_node_type_geo_point_instance(); diff --git a/source/blender/blenkernel/intern/writeffmpeg.c b/source/blender/blenkernel/intern/writeffmpeg.c index 39f65d76e3c..60c216a8401 100644 --- a/source/blender/blenkernel/intern/writeffmpeg.c +++ b/source/blender/blenkernel/intern/writeffmpeg.c @@ -205,12 +205,11 @@ static int write_audio_frame(FFMpegContext *context) success = -1; } - av_packet_rescale_ts(pkt, c->time_base, context->audio_stream->time_base); - if (pkt->duration > 0) { - pkt->duration = av_rescale_q(pkt->duration, c->time_base, context->audio_stream->time_base); - } - pkt->stream_index = context->audio_stream->index; + av_packet_rescale_ts(pkt, c->time_base, context->audio_stream->time_base); +# ifdef FFMPEG_USE_DURATION_WORKAROUND + my_guess_pkt_duration(context->outfile, context->audio_stream, pkt); +# endif pkt->flags |= AV_PKT_FLAG_KEY; @@ -349,6 +348,10 @@ static int write_video_frame(FFMpegContext *context, int cfra, AVFrame *frame, R packet->stream_index = context->video_stream->index; av_packet_rescale_ts(packet, c->time_base, context->video_stream->time_base); +# ifdef FFMPEG_USE_DURATION_WORKAROUND + my_guess_pkt_duration(context->outfile, context->video_stream, packet); +# endif + if (av_interleaved_write_frame(context->outfile, packet) != 0) { success = -1; break; @@ -515,6 +518,48 @@ static void set_ffmpeg_properties(RenderData *rd, } } +static AVRational calc_time_base(uint den, double num, int codec_id) +{ + /* Convert the input 'num' to an integer. Simply shift the decimal places until we get an integer + * (within a floating point error range). + * For example if we have `den = 3` and `num = 0.1` then the fps is: `den/num = 30` fps. + * When converting this to a FFMPEG time base, we want num to be an integer. + * So we simply move the decimal places of both numbers. i.e. `den = 30`, `num = 1`. */ + float eps = FLT_EPSILON; + const uint DENUM_MAX = (codec_id == AV_CODEC_ID_MPEG4) ? (1UL << 16) - 1 : (1UL << 31) - 1; + + /* Calculate the precision of the initial floating point number. */ + if (num > 1.0) { + const uint num_integer_bits = log2_floor_u((unsigned int)num); + + /* Formula for calculating the epsilon value: (power of two range) / (pow mantissa bits) + * For example, a float has 23 mantissa bits and the float value 3.5f as a pow2 range of + * (4-2=2): + * (2) / pow2(23) = floating point precision for 3.5f + */ + eps = (float)(1 << num_integer_bits) * FLT_EPSILON; + } + + /* Calculate how many decimal shifts we can do until we run out of precision. */ + const int max_num_shift = fabsf(log10f(eps)); + /* Calculate how many times we can shift the denominator. */ + const int max_den_shift = log10f(DENUM_MAX) - log10f(den); + const int max_iter = min_ii(max_num_shift, max_den_shift); + + for (int i = 0; i < max_iter && fabs(num - round(num)) > eps; i++) { + /* Increase the number and denominator until both are integers. */ + num *= 10; + den *= 10; + eps *= 10; + } + + AVRational time_base; + time_base.den = den; + time_base.num = (int)num; + + return time_base; +} + /* prepare a video stream for the output file */ static AVStream *alloc_video_stream(FFMpegContext *context, @@ -545,13 +590,24 @@ static AVStream *alloc_video_stream(FFMpegContext *context, c->codec_id = codec_id; c->codec_type = AVMEDIA_TYPE_VIDEO; + codec = avcodec_find_encoder(c->codec_id); + if (!codec) { + fprintf(stderr, "Couldn't find valid video codec\n"); + avcodec_free_context(&c); + context->video_codec = NULL; + return NULL; + } + + /* Load codec defaults into 'c'. */ + avcodec_get_context_defaults3(c, codec); + /* Get some values from the current render settings */ c->width = rectx; c->height = recty; - /* FIXME: Really bad hack (tm) for NTSC support */ if (context->ffmpeg_type == FFMPEG_DV && rd->frs_sec != 25) { + /* FIXME: Really bad hack (tm) for NTSC support */ c->time_base.den = 2997; c->time_base.num = 100; } @@ -559,21 +615,23 @@ static AVStream *alloc_video_stream(FFMpegContext *context, c->time_base.den = rd->frs_sec; c->time_base.num = (int)rd->frs_sec_base; } - else if (compare_ff(rd->frs_sec_base, 1.001f, 0.000001f)) { - /* This converts xx/1.001 (which is used in presets) to xx000/1001 (which is used in the rest - * of the world, including FFmpeg). */ - c->time_base.den = (int)(rd->frs_sec * 1000); - c->time_base.num = (int)(rd->frs_sec_base * 1000); - } else { - /* This calculates a fraction (DENUM_MAX / num) which approximates the scene frame rate - * (frs_sec / frs_sec_base). It uses the maximum denominator allowed by FFmpeg. - */ - const double DENUM_MAX = (codec_id == AV_CODEC_ID_MPEG4) ? (1UL << 16) - 1 : (1UL << 31) - 1; - const double num = (DENUM_MAX / (double)rd->frs_sec) * rd->frs_sec_base; + c->time_base = calc_time_base(rd->frs_sec, rd->frs_sec_base, codec_id); + } - c->time_base.den = (int)DENUM_MAX; - c->time_base.num = (int)num; + /* As per the time-base documentation here: + * https://www.ffmpeg.org/ffmpeg-codecs.html#Codec-Options + * We want to set the time base to (1 / fps) for fixed frame rate video. + * If it is not possible, we want to set the time-base numbers to something as + * small as possible. + */ + if (c->time_base.num != 1) { + AVRational new_time_base; + if (av_reduce( + &new_time_base.num, &new_time_base.den, c->time_base.num, c->time_base.den, INT_MAX)) { + /* Exact reduction was possible. Use the new value. */ + c->time_base = new_time_base; + } } st->time_base = c->time_base; @@ -585,6 +643,11 @@ static AVStream *alloc_video_stream(FFMpegContext *context, ffmpeg_dict_set_int(&opts, "lossless", 1); } else if (context->ffmpeg_crf >= 0) { + /* As per https://trac.ffmpeg.org/wiki/Encode/VP9 we must set the bit rate to zero when + * encoding with vp9 in crf mode. + * Set this to always be zero for other codecs as well. + * We don't care about bit rate in crf mode. */ + c->bit_rate = 0; ffmpeg_dict_set_int(&opts, "crf", context->ffmpeg_crf); } else { @@ -624,12 +687,6 @@ static AVStream *alloc_video_stream(FFMpegContext *context, } } - codec = avcodec_find_encoder(c->codec_id); - if (!codec) { - avcodec_free_context(&c); - return NULL; - } - /* Be sure to use the correct pixel format(e.g. RGB, YUV) */ if (codec->pix_fmts) { @@ -646,12 +703,6 @@ static AVStream *alloc_video_stream(FFMpegContext *context, c->codec_tag = (('D' << 24) + ('I' << 16) + ('V' << 8) + 'X'); } - if (codec_id == AV_CODEC_ID_H264) { - /* correct wrong default ffmpeg param which crash x264 */ - c->qmin = 10; - c->qmax = 51; - } - /* Keep lossless encodes in the RGB domain. */ if (codec_id == AV_CODEC_ID_HUFFYUV) { if (rd->im_format.planes == R_IMF_PLANES_RGBA) { @@ -711,10 +762,14 @@ static AVStream *alloc_video_stream(FFMpegContext *context, c->thread_type = FF_THREAD_SLICE; } - if (avcodec_open2(c, codec, &opts) < 0) { + int ret = avcodec_open2(c, codec, &opts); + + if (ret < 0) { + fprintf(stderr, "Couldn't initialize video codec: %s\n", av_err2str(ret)); BLI_strncpy(error, IMB_ffmpeg_last_error(), error_size); av_dict_free(&opts); avcodec_free_context(&c); + context->video_codec = NULL; return NULL; } av_dict_free(&opts); @@ -774,6 +829,17 @@ static AVStream *alloc_audio_stream(FFMpegContext *context, c->codec_id = codec_id; c->codec_type = AVMEDIA_TYPE_AUDIO; + codec = avcodec_find_encoder(c->codec_id); + if (!codec) { + fprintf(stderr, "Couldn't find valid audio codec\n"); + avcodec_free_context(&c); + context->audio_codec = NULL; + return NULL; + } + + /* Load codec defaults into 'c'. */ + avcodec_get_context_defaults3(c, codec); + c->sample_rate = rd->ffcodecdata.audio_mixrate; c->bit_rate = context->ffmpeg_audio_bitrate * 1000; c->sample_fmt = AV_SAMPLE_FMT_S16; @@ -803,13 +869,6 @@ static AVStream *alloc_audio_stream(FFMpegContext *context, c->sample_fmt = AV_SAMPLE_FMT_FLT; } - codec = avcodec_find_encoder(c->codec_id); - if (!codec) { - // XXX error("Couldn't find a valid audio codec"); - avcodec_free_context(&c); - return NULL; - } - if (codec->sample_fmts) { /* Check if the preferred sample format for this codec is supported. * this is because, depending on the version of libav, @@ -849,11 +908,14 @@ static AVStream *alloc_audio_stream(FFMpegContext *context, set_ffmpeg_properties(rd, c, "audio", &opts); - if (avcodec_open2(c, codec, &opts) < 0) { - // XXX error("Couldn't initialize audio codec"); + int ret = avcodec_open2(c, codec, &opts); + + if (ret < 0) { + fprintf(stderr, "Couldn't initialize audio codec: %s\n", av_err2str(ret)); BLI_strncpy(error, IMB_ffmpeg_last_error(), error_size); av_dict_free(&opts); avcodec_free_context(&c); + context->audio_codec = NULL; return NULL; } av_dict_free(&opts); @@ -1181,6 +1243,9 @@ static void flush_ffmpeg(FFMpegContext *context) packet->stream_index = context->video_stream->index; av_packet_rescale_ts(packet, c->time_base, context->video_stream->time_base); +# ifdef FFMPEG_USE_DURATION_WORKAROUND + my_guess_pkt_duration(context->outfile, context->video_stream, packet); +# endif int write_ret = av_interleaved_write_frame(context->outfile, packet); if (write_ret != 0) { @@ -1630,49 +1695,7 @@ static void ffmpeg_set_expert_options(RenderData *rd) IDP_FreePropertyContent(rd->ffcodecdata.properties); } - if (codec_id == AV_CODEC_ID_H264) { - /* - * All options here are for x264, but must be set via ffmpeg. - * The names are therefore different - Search for "x264 to FFmpeg option mapping" - * to get a list. - */ - - /* - * Use CABAC coder. Using "coder:1", which should be equivalent, - * crashes Blender for some reason. Either way - this is no big deal. - */ - BKE_ffmpeg_property_add_string(rd, "video", "coder:vlc"); - - /* - * The other options were taken from the libx264-default.preset - * included in the ffmpeg distribution. - */ - - /* This breaks compatibility for QT. */ - // BKE_ffmpeg_property_add_string(rd, "video", "flags:loop"); - BKE_ffmpeg_property_add_string(rd, "video", "cmp:chroma"); - BKE_ffmpeg_property_add_string(rd, "video", "partitions:parti4x4"); /* Deprecated. */ - BKE_ffmpeg_property_add_string(rd, "video", "partitions:partp8x8"); /* Deprecated. */ - BKE_ffmpeg_property_add_string(rd, "video", "partitions:partb8x8"); /* Deprecated. */ - BKE_ffmpeg_property_add_string(rd, "video", "me:hex"); - BKE_ffmpeg_property_add_string(rd, "video", "subq:6"); - BKE_ffmpeg_property_add_string(rd, "video", "me_range:16"); - BKE_ffmpeg_property_add_string(rd, "video", "qdiff:4"); - BKE_ffmpeg_property_add_string(rd, "video", "keyint_min:25"); - BKE_ffmpeg_property_add_string(rd, "video", "sc_threshold:40"); - BKE_ffmpeg_property_add_string(rd, "video", "i_qfactor:0.71"); - BKE_ffmpeg_property_add_string(rd, "video", "b_strategy:1"); - BKE_ffmpeg_property_add_string(rd, "video", "bf:3"); - BKE_ffmpeg_property_add_string(rd, "video", "refs:2"); - BKE_ffmpeg_property_add_string(rd, "video", "qcomp:0.6"); - - BKE_ffmpeg_property_add_string(rd, "video", "trellis:0"); - BKE_ffmpeg_property_add_string(rd, "video", "weightb:1"); - BKE_ffmpeg_property_add_string(rd, "video", "8x8dct:1"); - BKE_ffmpeg_property_add_string(rd, "video", "fast-pskip:1"); - BKE_ffmpeg_property_add_string(rd, "video", "wpredp:2"); - } - else if (codec_id == AV_CODEC_ID_DNXHD) { + if (codec_id == AV_CODEC_ID_DNXHD) { if (rd->ffcodecdata.flags & FFMPEG_LOSSLESS_OUTPUT) { BKE_ffmpeg_property_add_string(rd, "video", "mbd:rd"); } |