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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeroen Bakker <jeroen@blender.org>2021-06-01 16:07:46 +0300
committerJeroen Bakker <jeroen@blender.org>2021-06-01 16:07:46 +0300
commitaa75e383ba46f0e906101bbb3492eff337c267ae (patch)
treece89d6a394431a36770de9bb4421a41962862194 /source/blender/blenkernel
parentee54a8ace753a56236f5d1d647da7a40904af6b3 (diff)
parent6899dcab5339d1f17dd2ea665a38e4ab28ab23fc (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.h2
-rw-r--r--source/blender/blenkernel/BKE_lib_id.h6
-rw-r--r--source/blender/blenkernel/BKE_node.h12
-rw-r--r--source/blender/blenkernel/intern/collection.c3
-rw-r--r--source/blender/blenkernel/intern/customdata_file.c8
-rw-r--r--source/blender/blenkernel/intern/editmesh.c3
-rw-r--r--source/blender/blenkernel/intern/geometry_set_instances.cc26
-rw-r--r--source/blender/blenkernel/intern/lib_id.c30
-rw-r--r--source/blender/blenkernel/intern/lib_id_test.cc60
-rw-r--r--source/blender/blenkernel/intern/lib_override.c125
-rw-r--r--source/blender/blenkernel/intern/node.cc1
-rw-r--r--source/blender/blenkernel/intern/writeffmpeg.c191
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");
}