diff options
Diffstat (limited to 'source/blender/blenkernel/intern')
34 files changed, 1367 insertions, 871 deletions
diff --git a/source/blender/blenkernel/intern/DerivedMesh.c b/source/blender/blenkernel/intern/DerivedMesh.c index 160adcc3988..8b7329cf940 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.c +++ b/source/blender/blenkernel/intern/DerivedMesh.c @@ -2912,9 +2912,6 @@ DerivedMesh *editbmesh_get_derived_base(Object *obedit, BMEditMesh *em, CustomDa /* get derived mesh from an object, using editbmesh if available. */ DerivedMesh *object_get_derived_final(Object *ob, const bool for_render) { - Mesh *me = ob->data; - BMEditMesh *em = me->edit_btmesh; - if (for_render) { /* TODO(sergey): use proper derived render here in the future. */ return ob->derivedFinal; @@ -2922,9 +2919,13 @@ DerivedMesh *object_get_derived_final(Object *ob, const bool for_render) /* only return the editmesh if its from this object because * we don't a mesh from another object's modifier stack: T43122 */ - if (em && (em->ob == ob)) { - DerivedMesh *dm = em->derivedFinal; - return dm; + if (ob->type == OB_MESH) { + Mesh *me = ob->data; + BMEditMesh *em = me->edit_btmesh; + if (em && (em->ob == ob)) { + DerivedMesh *dm = em->derivedFinal; + return dm; + } } return ob->derivedFinal; diff --git a/source/blender/blenkernel/intern/appdir.c b/source/blender/blenkernel/intern/appdir.c index b1dcc40279f..43fd47981b1 100644 --- a/source/blender/blenkernel/intern/appdir.c +++ b/source/blender/blenkernel/intern/appdir.c @@ -114,18 +114,26 @@ static char *blender_version_decimal(const int ver) * Concatenates path_base, (optional) path_sep and (optional) folder_name into targetpath, * returning true if result points to a directory. */ -static bool test_path(char *targetpath, const char *path_base, const char *path_sep, const char *folder_name) +static bool test_path( + char *targetpath, size_t targetpath_len, + const char *path_base, const char *path_sep, const char *folder_name) { char tmppath[FILE_MAX]; - if (path_sep) BLI_join_dirfile(tmppath, sizeof(tmppath), path_base, path_sep); - else BLI_strncpy(tmppath, path_base, sizeof(tmppath)); - - /* rare cases folder_name is omitted (when looking for ~/.blender/2.xx dir only) */ - if (folder_name) - BLI_make_file_string("/", targetpath, tmppath, folder_name); - else - BLI_strncpy(targetpath, tmppath, sizeof(tmppath)); + if (path_sep) { + BLI_join_dirfile(tmppath, sizeof(tmppath), path_base, path_sep); + } + else { + BLI_strncpy(tmppath, path_base, sizeof(tmppath)); + } + + /* rare cases folder_name is omitted (when looking for ~/.config/blender/2.xx dir only) */ + if (folder_name) { + BLI_join_dirfile(targetpath, targetpath_len, tmppath, folder_name); + } + else { + BLI_strncpy(targetpath, tmppath, targetpath_len); + } /* FIXME: why is "//" on front of tmppath expanded to "/" (by BLI_join_dirfile) * if folder_name is specified but not otherwise? */ @@ -179,7 +187,9 @@ static bool test_env_path(char *path, const char *envvar) * \param ver To construct name of version-specific directory within bprogdir * \return true if such a directory exists. */ -static bool get_path_local(char *targetpath, const char *folder_name, const char *subfolder_name, const int ver) +static bool get_path_local( + char *targetpath, size_t targetpath_len, + const char *folder_name, const char *subfolder_name, const int ver) { char relfolder[FILE_MAX]; @@ -201,11 +211,12 @@ static bool get_path_local(char *targetpath, const char *folder_name, const char /* try EXECUTABLE_DIR/2.5x/folder_name - new default directory for local blender installed files */ #ifdef __APPLE__ - static char osx_resourses[FILE_MAX]; /* due new codesign situation in OSX > 10.9.5 we must move the blender_version dir with contents to Resources */ + /* due new codesign situation in OSX > 10.9.5 we must move the blender_version dir with contents to Resources */ + static char osx_resourses[FILE_MAX]; sprintf(osx_resourses, "%s../Resources", bprogdir); - return test_path(targetpath, osx_resourses, blender_version_decimal(ver), relfolder); + return test_path(targetpath, targetpath_len, osx_resourses, blender_version_decimal(ver), relfolder); #else - return test_path(targetpath, bprogdir, blender_version_decimal(ver), relfolder); + return test_path(targetpath, targetpath_len, bprogdir, blender_version_decimal(ver), relfolder); #endif } @@ -219,7 +230,7 @@ static bool is_portable_install(void) const int ver = BLENDER_VERSION; char path[FILE_MAX]; - return get_path_local(path, "config", NULL, ver); + return get_path_local(path, sizeof(path), "config", NULL, ver); } /** @@ -233,20 +244,22 @@ static bool is_portable_install(void) * \param ver Blender version, used to construct a subdirectory name * \return true if it was able to construct such a path. */ -static bool get_path_user(char *targetpath, const char *folder_name, const char *subfolder_name, const char *envvar, const int ver) +static bool get_path_user( + char *targetpath, size_t targetpath_len, const char *folder_name, const char *subfolder_name, + const char *envvar, const int ver) { char user_path[FILE_MAX]; const char *user_base_path; /* for portable install, user path is always local */ - if (is_portable_install()) - return get_path_local(targetpath, folder_name, subfolder_name, ver); - + if (is_portable_install()) { + return get_path_local(targetpath, targetpath_len, folder_name, subfolder_name, ver); + } user_path[0] = '\0'; if (test_env_path(user_path, envvar)) { if (subfolder_name) { - return test_path(targetpath, user_path, NULL, subfolder_name); + return test_path(targetpath, targetpath_len, user_path, NULL, subfolder_name); } else { BLI_strncpy(targetpath, user_path, FILE_MAX); @@ -266,10 +279,10 @@ static bool get_path_user(char *targetpath, const char *folder_name, const char #endif if (subfolder_name) { - return test_path(targetpath, user_path, folder_name, subfolder_name); + return test_path(targetpath, targetpath_len, user_path, folder_name, subfolder_name); } else { - return test_path(targetpath, user_path, NULL, folder_name); + return test_path(targetpath, targetpath_len, user_path, NULL, folder_name); } } @@ -283,7 +296,9 @@ static bool get_path_user(char *targetpath, const char *folder_name, const char * \param ver Blender version, used to construct a subdirectory name * \return true if it was able to construct such a path. */ -static bool get_path_system(char *targetpath, const char *folder_name, const char *subfolder_name, const char *envvar, const int ver) +static bool get_path_system( + char *targetpath, size_t targetpath_len, const char *folder_name, const char *subfolder_name, + const char *envvar, const int ver) { char system_path[FILE_MAX]; const char *system_base_path; @@ -307,13 +322,13 @@ static bool get_path_system(char *targetpath, const char *folder_name, const cha /* try CWD/release/folder_name */ if (BLI_current_working_dir(cwd, sizeof(cwd))) { - if (test_path(targetpath, cwd, "release", relfolder)) { + if (test_path(targetpath, targetpath_len, cwd, "release", relfolder)) { return true; } } /* try EXECUTABLE_DIR/release/folder_name */ - if (test_path(targetpath, bprogdir, "release", relfolder)) + if (test_path(targetpath, targetpath_len, bprogdir, "release", relfolder)) return true; /* end developer overrides */ @@ -324,7 +339,7 @@ static bool get_path_system(char *targetpath, const char *folder_name, const cha if (test_env_path(system_path, envvar)) { if (subfolder_name) { - return test_path(targetpath, system_path, NULL, subfolder_name); + return test_path(targetpath, targetpath_len, system_path, NULL, subfolder_name); } else { BLI_strncpy(targetpath, system_path, FILE_MAX); @@ -345,57 +360,63 @@ static bool get_path_system(char *targetpath, const char *folder_name, const cha if (subfolder_name) { /* try $BLENDERPATH/folder_name/subfolder_name */ - return test_path(targetpath, system_path, folder_name, subfolder_name); + return test_path(targetpath, targetpath_len, system_path, folder_name, subfolder_name); } else { /* try $BLENDERPATH/folder_name */ - return test_path(targetpath, system_path, NULL, folder_name); + return test_path(targetpath, targetpath_len, system_path, NULL, folder_name); } } -/* get a folder out of the 'folder_id' presets for paths */ -/* returns the path if found, NULL string if not */ -const char *BKE_appdir_folder_id(const int folder_id, const char *subfolder) +/** + * Get a folder out of the 'folder_id' presets for paths. + * returns the path if found, NULL string if not + * + * \param subfolder: The name of a directory to check for, + * this may contain path separators but must resolve to a directory, checked with #BLI_is_dir. + */ +const char *BKE_appdir_folder_id_ex( + const int folder_id, const char *subfolder, + char *path, size_t path_len) { const int ver = BLENDER_VERSION; - static char path[FILE_MAX] = ""; - + switch (folder_id) { case BLENDER_DATAFILES: /* general case */ - if (get_path_user(path, "datafiles", subfolder, "BLENDER_USER_DATAFILES", ver)) break; - if (get_path_local(path, "datafiles", subfolder, ver)) break; - if (get_path_system(path, "datafiles", subfolder, "BLENDER_SYSTEM_DATAFILES", ver)) break; + if (get_path_user(path, path_len, "datafiles", subfolder, "BLENDER_USER_DATAFILES", ver)) break; + if (get_path_local(path, path_len, "datafiles", subfolder, ver)) break; + if (get_path_system(path, path_len, "datafiles", subfolder, "BLENDER_SYSTEM_DATAFILES", ver)) break; return NULL; case BLENDER_USER_DATAFILES: - if (get_path_user(path, "datafiles", subfolder, "BLENDER_USER_DATAFILES", ver)) break; + if (get_path_user(path, path_len, "datafiles", subfolder, "BLENDER_USER_DATAFILES", ver)) break; return NULL; case BLENDER_SYSTEM_DATAFILES: - if (get_path_local(path, "datafiles", subfolder, ver)) break; - if (get_path_system(path, "datafiles", subfolder, "BLENDER_SYSTEM_DATAFILES", ver)) break; + if (get_path_local(path, path_len, "datafiles", subfolder, ver)) break; + if (get_path_system(path, path_len, "datafiles", subfolder, "BLENDER_SYSTEM_DATAFILES", ver)) break; return NULL; case BLENDER_USER_AUTOSAVE: - if (get_path_user(path, "autosave", subfolder, "BLENDER_USER_DATAFILES", ver)) break; + if (get_path_user(path, path_len, "autosave", subfolder, "BLENDER_USER_DATAFILES", ver)) break; return NULL; case BLENDER_USER_CONFIG: - if (get_path_user(path, "config", subfolder, "BLENDER_USER_CONFIG", ver)) break; + if (get_path_user(path, path_len, "config", subfolder, "BLENDER_USER_CONFIG", ver)) break; return NULL; case BLENDER_USER_SCRIPTS: - if (get_path_user(path, "scripts", subfolder, "BLENDER_USER_SCRIPTS", ver)) break; + if (get_path_user(path, path_len, "scripts", subfolder, "BLENDER_USER_SCRIPTS", ver)) break; return NULL; case BLENDER_SYSTEM_SCRIPTS: - if (get_path_local(path, "scripts", subfolder, ver)) break; - if (get_path_system(path, "scripts", subfolder, "BLENDER_SYSTEM_SCRIPTS", ver)) break; + if (get_path_local(path, path_len, "scripts", subfolder, ver)) break; + if (get_path_system(path, path_len, "scripts", subfolder, "BLENDER_SYSTEM_SCRIPTS", ver)) break; return NULL; case BLENDER_SYSTEM_PYTHON: - if (get_path_local(path, "python", subfolder, ver)) break; - if (get_path_system(path, "python", subfolder, "BLENDER_SYSTEM_PYTHON", ver)) break; + if (get_path_local(path, path_len, "python", subfolder, ver)) break; + if (get_path_system(path, path_len, "python", subfolder, "BLENDER_SYSTEM_PYTHON", ver)) break; return NULL; default: @@ -406,6 +427,13 @@ const char *BKE_appdir_folder_id(const int folder_id, const char *subfolder) return path; } +const char *BKE_appdir_folder_id( + const int folder_id, const char *subfolder) +{ + static char path[FILE_MAX] = ""; + return BKE_appdir_folder_id_ex(folder_id, subfolder, path, sizeof(path)); +} + /** * Returns the path to a folder in the user area without checking that it actually exists first. */ @@ -416,16 +444,16 @@ const char *BKE_appdir_folder_id_user_notest(const int folder_id, const char *su switch (folder_id) { case BLENDER_USER_DATAFILES: - get_path_user(path, "datafiles", subfolder, "BLENDER_USER_DATAFILES", ver); + get_path_user(path, sizeof(path), "datafiles", subfolder, "BLENDER_USER_DATAFILES", ver); break; case BLENDER_USER_CONFIG: - get_path_user(path, "config", subfolder, "BLENDER_USER_CONFIG", ver); + get_path_user(path, sizeof(path), "config", subfolder, "BLENDER_USER_CONFIG", ver); break; case BLENDER_USER_AUTOSAVE: - get_path_user(path, "autosave", subfolder, "BLENDER_USER_AUTOSAVE", ver); + get_path_user(path, sizeof(path), "autosave", subfolder, "BLENDER_USER_AUTOSAVE", ver); break; case BLENDER_USER_SCRIPTS: - get_path_user(path, "scripts", subfolder, "BLENDER_USER_SCRIPTS", ver); + get_path_user(path, sizeof(path), "scripts", subfolder, "BLENDER_USER_SCRIPTS", ver); break; default: BLI_assert(0); @@ -469,13 +497,13 @@ const char *BKE_appdir_folder_id_version(const int folder_id, const int ver, con bool ok; switch (folder_id) { case BLENDER_RESOURCE_PATH_USER: - ok = get_path_user(path, NULL, NULL, NULL, ver); + ok = get_path_user(path, sizeof(path), NULL, NULL, NULL, ver); break; case BLENDER_RESOURCE_PATH_LOCAL: - ok = get_path_local(path, NULL, NULL, ver); + ok = get_path_local(path, sizeof(path), NULL, NULL, ver); break; case BLENDER_RESOURCE_PATH_SYSTEM: - ok = get_path_system(path, NULL, NULL, NULL, ver); + ok = get_path_system(path, sizeof(path), NULL, NULL, NULL, ver); break; default: path[0] = '\0'; /* in case do_check is false */ @@ -655,6 +683,48 @@ bool BKE_appdir_program_python_search( return is_found; } +static const char *app_template_directory_search[2] = { + "startup" SEP_STR "bl_app_templates_user", + "startup" SEP_STR "bl_app_templates_system", +}; + +static const int app_template_directory_id[2] = { + BLENDER_USER_SCRIPTS, + BLENDER_SYSTEM_SCRIPTS, +}; + +/** + * Return true if templates exist + */ +bool BKE_appdir_app_template_any(void) +{ + char temp_dir[FILE_MAX]; + for (int i = 0; i < 2; i++) { + if (BKE_appdir_folder_id_ex( + app_template_directory_id[i], app_template_directory_search[i], + temp_dir, sizeof(temp_dir))) + { + return true; + } + } + return false; +} + +bool BKE_appdir_app_template_id_search(const char *app_template, char *path, size_t path_len) +{ + for (int i = 0; i < 2; i++) { + char subdir[FILE_MAX]; + BLI_join_dirfile(subdir, sizeof(subdir), app_template_directory_search[i], app_template); + if (BKE_appdir_folder_id_ex( + app_template_directory_id[i], subdir, + path, path_len)) + { + return true; + } + } + return false; +} + /** * Gets the temp directory when blender first runs. * If the default path is not found, use try $TEMP @@ -713,7 +783,8 @@ static void where_is_temp(char *fullname, char *basename, const size_t maxlen, c BLI_add_slash(fullname); #ifdef WIN32 if (userdir && userdir != fullname) { - BLI_strncpy(userdir, fullname, maxlen); /* also set user pref to show %TEMP%. /tmp/ is just plain confusing for Windows users. */ + /* also set user pref to show %TEMP%. /tmp/ is just plain confusing for Windows users. */ + BLI_strncpy(userdir, fullname, maxlen); } #endif } @@ -755,7 +826,6 @@ static void where_is_temp(char *fullname, char *basename, const size_t maxlen, c void BKE_tempdir_init(char *userdir) { where_is_temp(btempdir_session, btempdir_base, FILE_MAX, userdir); -; } /** diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index 0287d6ae9ca..02cc31e5977 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -1037,6 +1037,17 @@ void armature_deform_verts(Object *armOb, Object *target, DerivedMesh *dm, float if (use_dverts) { defnrToPC = MEM_callocN(sizeof(*defnrToPC) * defbase_tot, "defnrToBone"); defnrToPCIndex = MEM_callocN(sizeof(*defnrToPCIndex) * defbase_tot, "defnrToIndex"); + /* TODO(sergey): Some considerations here: + * + * - Make it more generic function, maybe even keep together with chanhash. + * - Check whether keeping this consistent across frames gives speedup. + * - Don't use hash for small armatures. + */ + GHash *idx_hash = BLI_ghash_ptr_new("pose channel index by name"); + int pchan_index = 0; + for (pchan = armOb->pose->chanbase.first; pchan != NULL; pchan = pchan->next, ++pchan_index) { + BLI_ghash_insert(idx_hash, pchan, SET_INT_IN_POINTER(pchan_index)); + } for (i = 0, dg = target->defbase.first; dg; i++, dg = dg->next) { defnrToPC[i] = BKE_pose_channel_find_name(armOb->pose, dg->name); /* exclude non-deforming bones */ @@ -1045,10 +1056,11 @@ void armature_deform_verts(Object *armOb, Object *target, DerivedMesh *dm, float defnrToPC[i] = NULL; } else { - defnrToPCIndex[i] = BLI_findindex(&armOb->pose->chanbase, defnrToPC[i]); + defnrToPCIndex[i] = GET_INT_FROM_POINTER(BLI_ghash_lookup(idx_hash, defnrToPC[i])); } } } + BLI_ghash_free(idx_hash, NULL, NULL); } } } diff --git a/source/blender/blenkernel/intern/blender.c b/source/blender/blenkernel/intern/blender.c index a8670395fc4..ceb641073e0 100644 --- a/source/blender/blenkernel/intern/blender.c +++ b/source/blender/blenkernel/intern/blender.c @@ -100,6 +100,18 @@ void BKE_blender_free(void) free_nodesystem(); } +void BKE_blender_version_string(char *version_str, size_t maxncpy, short version, short subversion, bool v_prefix, bool include_subversion) +{ + const char *prefix = v_prefix ? "v" : ""; + + if (include_subversion && subversion > 0) { + BLI_snprintf(version_str, maxncpy, "%s%d.%02d.%d", prefix, version / 100, version % 100, subversion); + } + else { + BLI_snprintf(version_str, maxncpy, "%s%d.%02d", prefix, version / 100, version % 100); + } +} + void BKE_blender_globals_init(void) { memset(&G, 0, sizeof(Global)); @@ -110,10 +122,7 @@ void BKE_blender_globals_init(void) strcpy(G.ima, "//"); - if (BLENDER_SUBVERSION) - BLI_snprintf(versionstr, sizeof(versionstr), "v%d.%02d.%d", BLENDER_VERSION / 100, BLENDER_VERSION % 100, BLENDER_SUBVERSION); - else - BLI_snprintf(versionstr, sizeof(versionstr), "v%d.%02d", BLENDER_VERSION / 100, BLENDER_VERSION % 100); + BKE_blender_version_string(versionstr, sizeof(versionstr), BLENDER_VERSION, BLENDER_SUBVERSION, true, true); #ifndef WITH_PYTHON_SECURITY /* default */ G.f |= G_SCRIPT_AUTOEXEC; @@ -141,20 +150,18 @@ static void keymap_item_free(wmKeyMapItem *kmi) MEM_freeN(kmi->ptr); } -/** - * When loading a new userdef from file, - * or when exiting Blender. - */ -void BKE_blender_userdef_free(void) +void BKE_blender_userdef_set_data(UserDef *userdef) { - wmKeyMap *km; - wmKeyMapItem *kmi; - wmKeyMapDiffItem *kmdi; - bAddon *addon, *addon_next; - uiFont *font; - - for (km = U.user_keymaps.first; km; km = km->next) { - for (kmdi = km->diff_items.first; kmdi; kmdi = kmdi->next) { + /* only here free userdef themes... */ + BKE_blender_userdef_free_data(&U); + U = *userdef; +} + +static void userdef_free_keymaps(UserDef *userdef) +{ + for (wmKeyMap *km = userdef->user_keymaps.first, *km_next; km; km = km_next) { + km_next = km->next; + for (wmKeyMapDiffItem *kmdi = km->diff_items.first; kmdi; kmdi = kmdi->next) { if (kmdi->add_item) { keymap_item_free(kmdi->add_item); MEM_freeN(kmdi->add_item); @@ -165,14 +172,21 @@ void BKE_blender_userdef_free(void) } } - for (kmi = km->items.first; kmi; kmi = kmi->next) + for (wmKeyMapItem *kmi = km->items.first; kmi; kmi = kmi->next) { keymap_item_free(kmi); + } BLI_freelistN(&km->diff_items); BLI_freelistN(&km->items); + + MEM_freeN(km); } - - for (addon = U.addons.first; addon; addon = addon_next) { + BLI_listbase_clear(&userdef->user_keymaps); +} + +static void userdef_free_addons(UserDef *userdef) +{ + for (bAddon *addon = userdef->addons.first, *addon_next; addon; addon = addon_next) { addon_next = addon->next; if (addon->prop) { IDP_FreeProperty(addon->prop); @@ -180,19 +194,35 @@ void BKE_blender_userdef_free(void) } MEM_freeN(addon); } + BLI_listbase_clear(&userdef->addons); +} - for (font = U.uifonts.first; font; font = font->next) { +/** + * When loading a new userdef from file, + * or when exiting Blender. + */ +void BKE_blender_userdef_free_data(UserDef *userdef) +{ +#define U _invalid_access_ /* ensure no accidental global access */ +#ifdef U /* quiet warning */ +#endif + + userdef_free_keymaps(userdef); + userdef_free_addons(userdef); + + for (uiFont *font = userdef->uifonts.first; font; font = font->next) { BLF_unload_id(font->blf_id); } BLF_default_set(-1); - BLI_freelistN(&U.autoexec_paths); + BLI_freelistN(&userdef->autoexec_paths); + + BLI_freelistN(&userdef->uistyles); + BLI_freelistN(&userdef->uifonts); + BLI_freelistN(&userdef->themes); - BLI_freelistN(&U.uistyles); - BLI_freelistN(&U.uifonts); - BLI_freelistN(&U.themes); - BLI_freelistN(&U.user_keymaps); +#undef U } /** @@ -208,6 +238,44 @@ void BKE_blender_userdef_refresh(void) } +/** + * Write U from userdef. + * This function defines which settings a template will override for the user preferences. + */ +void BKE_blender_userdef_set_app_template(UserDef *userdef) +{ + /* TODO: + * - keymaps + * - various minor settings (add as needed). + */ + +#define LIST_OVERRIDE(id) { \ + BLI_freelistN(&U.id); \ + BLI_movelisttolist(&U.id, &userdef->id); \ +} ((void)0) + +#define MEMCPY_OVERRIDE(id) \ + memcpy(U.id, userdef->id, sizeof(U.id)); + + /* for some types we need custom free functions */ + userdef_free_addons(&U); + userdef_free_keymaps(&U); + + LIST_OVERRIDE(uistyles); + LIST_OVERRIDE(uifonts); + LIST_OVERRIDE(themes); + LIST_OVERRIDE(addons); + LIST_OVERRIDE(user_keymaps); + + MEMCPY_OVERRIDE(light); + + MEMCPY_OVERRIDE(font_path_ui); + MEMCPY_OVERRIDE(font_path_ui_mono); + +#undef LIST_OVERRIDE +#undef MEMCPY_OVERRIDE +} + /* ***************** testing for break ************* */ static void (*blender_test_break_cb)(void) = NULL; @@ -234,7 +302,7 @@ int BKE_blender_test_break(void) * \note Don't use MEM_mallocN so functions can be registered at any time. * \{ */ -struct AtExitData { +static struct AtExitData { struct AtExitData *next; void (*func)(void *user_data); diff --git a/source/blender/blenkernel/intern/blender_undo.c b/source/blender/blenkernel/intern/blender_undo.c index bc98d6f6805..e3a5edb2049 100644 --- a/source/blender/blenkernel/intern/blender_undo.c +++ b/source/blender/blenkernel/intern/blender_undo.c @@ -108,9 +108,9 @@ static int read_undosave(bContext *C, UndoElem *uel) G.fileflags |= G_FILE_NO_UI; if (UNDO_DISK) - success = (BKE_blendfile_read(C, uel->str, NULL) != BKE_BLENDFILE_READ_FAIL); + success = (BKE_blendfile_read(C, uel->str, NULL, 0) != BKE_BLENDFILE_READ_FAIL); else - success = BKE_blendfile_read_from_memfile(C, &uel->memfile, NULL); + success = BKE_blendfile_read_from_memfile(C, &uel->memfile, NULL, 0); /* restore */ BLI_strncpy(G.main->name, mainstr, sizeof(G.main->name)); /* restore */ @@ -389,7 +389,7 @@ bool BKE_undo_save_file(const char *filename) Main *BKE_undo_get_main(Scene **r_scene) { Main *mainp = NULL; - BlendFileData *bfd = BLO_read_from_memfile(G.main, G.main->name, &curundo->memfile, NULL); + BlendFileData *bfd = BLO_read_from_memfile(G.main, G.main->name, &curundo->memfile, NULL, BLO_READ_SKIP_NONE); if (bfd) { mainp = bfd->main; diff --git a/source/blender/blenkernel/intern/blendfile.c b/source/blender/blenkernel/intern/blendfile.c index 54f709a1e5b..a521d671ea4 100644 --- a/source/blender/blenkernel/intern/blendfile.c +++ b/source/blender/blenkernel/intern/blendfile.c @@ -227,7 +227,7 @@ static void setup_app_data( if (bfd->user) { /* only here free userdef themes... */ - BKE_blender_userdef_free(); + BKE_blender_userdef_free_data(&U); U = *bfd->user; @@ -349,17 +349,23 @@ static int handle_subversion_warning(Main *main, ReportList *reports) return 1; } -int BKE_blendfile_read(bContext *C, const char *filepath, ReportList *reports) +int BKE_blendfile_read( + bContext *C, const char *filepath, + ReportList *reports, int skip_flags) { BlendFileData *bfd; int retval = BKE_BLENDFILE_READ_OK; - if (strstr(filepath, BLENDER_STARTUP_FILE) == NULL) /* don't print user-pref loading */ - printf("read blend: %s\n", filepath); + /* don't print user-pref loading */ + if (strstr(filepath, BLENDER_STARTUP_FILE) == NULL) { + printf("Read blend: %s\n", filepath); + } - bfd = BLO_read_from_file(filepath, reports); + bfd = BLO_read_from_file(filepath, reports, skip_flags); if (bfd) { - if (bfd->user) retval = BKE_BLENDFILE_READ_OK_USERPREFS; + if (bfd->user) { + retval = BKE_BLENDFILE_READ_OK_USERPREFS; + } if (0 == handle_subversion_warning(bfd->main, reports)) { BKE_main_free(bfd->main); @@ -379,11 +385,11 @@ int BKE_blendfile_read(bContext *C, const char *filepath, ReportList *reports) bool BKE_blendfile_read_from_memory( bContext *C, const void *filebuf, int filelength, - ReportList *reports, bool update_defaults) + ReportList *reports, int skip_flags, bool update_defaults) { BlendFileData *bfd; - bfd = BLO_read_from_memory(filebuf, filelength, reports); + bfd = BLO_read_from_memory(filebuf, filelength, reports, skip_flags); if (bfd) { if (update_defaults) BLO_update_defaults_startup_blend(bfd->main); @@ -399,11 +405,11 @@ bool BKE_blendfile_read_from_memory( /* memfile is the undo buffer */ bool BKE_blendfile_read_from_memfile( bContext *C, struct MemFile *memfile, - ReportList *reports) + ReportList *reports, int skip_flags) { BlendFileData *bfd; - bfd = BLO_read_from_memfile(CTX_data_main(C), G.main->name, memfile, reports); + bfd = BLO_read_from_memfile(CTX_data_main(C), G.main->name, memfile, reports, skip_flags); if (bfd) { /* remove the unused screens and wm */ while (bfd->main->wm.first) @@ -420,32 +426,76 @@ bool BKE_blendfile_read_from_memfile( return (bfd != NULL); } +/** + * Utility to make a file 'empty' used for startup to optionally give an empty file. + * Handy for tests. + */ +void BKE_blendfile_read_make_empty(bContext *C) +{ + Main *bmain = CTX_data_main(C); + + ListBase *lbarray[MAX_LIBARRAY]; + ID *id; + int a; + + a = set_listbasepointers(bmain, lbarray); + while (a--) { + id = lbarray[a]->first; + if (id != NULL) { + if (ELEM(GS(id->name), ID_SCE, ID_SCR, ID_WM)) { + continue; + } + while ((id = lbarray[a]->first)) { + BKE_libblock_delete(bmain, id); + } + } + } +} + /* only read the userdef from a .blend */ -int BKE_blendfile_read_userdef(const char *filepath, ReportList *reports) +UserDef *BKE_blendfile_userdef_read(const char *filepath, ReportList *reports) { BlendFileData *bfd; - int retval = BKE_BLENDFILE_READ_FAIL; + UserDef *userdef = NULL; - bfd = BLO_read_from_file(filepath, reports); + bfd = BLO_read_from_file(filepath, reports, BLO_READ_SKIP_ALL & ~BLO_READ_SKIP_USERDEF); if (bfd) { if (bfd->user) { - retval = BKE_BLENDFILE_READ_OK_USERPREFS; + userdef = bfd->user; + } + BKE_main_free(bfd->main); + MEM_freeN(bfd); + } - /* only here free userdef themes... */ - BKE_blender_userdef_free(); + return userdef; +} - U = *bfd->user; - MEM_freeN(bfd->user); + +UserDef *BKE_blendfile_userdef_read_from_memory( + const void *filebuf, int filelength, + ReportList *reports) +{ + BlendFileData *bfd; + UserDef *userdef = NULL; + + bfd = BLO_read_from_memory(filebuf, filelength, reports, BLO_READ_SKIP_ALL & ~BLO_READ_SKIP_USERDEF); + if (bfd) { + if (bfd->user) { + userdef = bfd->user; } BKE_main_free(bfd->main); MEM_freeN(bfd); } + else { + BKE_reports_prepend(reports, "Loading failed: "); + } - return retval; + return userdef; } + /* only write the userdef in a .blend */ -int BKE_blendfile_write_userdef(const char *filepath, ReportList *reports) +int BKE_blendfile_userdef_write(const char *filepath, ReportList *reports) { Main *mainb = MEM_callocN(sizeof(Main), "empty main"); int retval = 0; diff --git a/source/blender/blenkernel/intern/bvhutils.c b/source/blender/blenkernel/intern/bvhutils.c index c1dd3c0a5d8..c0e4ef37a93 100644 --- a/source/blender/blenkernel/intern/bvhutils.c +++ b/source/blender/blenkernel/intern/bvhutils.c @@ -393,7 +393,6 @@ static BVHTree *bvhtree_from_editmesh_verts_create_tree( BMEditMesh *em, const int verts_num, const BLI_bitmap *verts_mask, int verts_num_active) { - BVHTree *tree = NULL; BM_mesh_elem_table_ensure(em->bm, BM_VERT); if (verts_mask) { BLI_assert(IN_RANGE_INCL(verts_num_active, 0, verts_num)); @@ -402,14 +401,15 @@ static BVHTree *bvhtree_from_editmesh_verts_create_tree( verts_num_active = verts_num; } - tree = BLI_bvhtree_new(verts_num_active, epsilon, tree_type, axis); + BVHTree *tree = BLI_bvhtree_new(verts_num_active, epsilon, tree_type, axis); if (tree) { for (int i = 0; i < verts_num; i++) { - if (!verts_mask || BLI_BITMAP_TEST_BOOL(verts_mask, i)) { - BMVert *eve = BM_vert_at_index(em->bm, i); - BLI_bvhtree_insert(tree, i, eve->co, 1); + if (verts_mask && !BLI_BITMAP_TEST_BOOL(verts_mask, i)) { + continue; } + BMVert *eve = BM_vert_at_index(em->bm, i); + BLI_bvhtree_insert(tree, i, eve->co, 1); } BLI_assert(BLI_bvhtree_get_size(tree) == verts_num_active); BLI_bvhtree_balance(tree); @@ -420,31 +420,28 @@ static BVHTree *bvhtree_from_editmesh_verts_create_tree( static BVHTree *bvhtree_from_mesh_verts_create_tree( float epsilon, int tree_type, int axis, - MVert *vert, const int verts_num, + const MVert *vert, const int verts_num, const BLI_bitmap *verts_mask, int verts_num_active) { - BVHTree *tree = NULL; - int i; - if (vert) { - if (verts_mask) { - BLI_assert(IN_RANGE_INCL(verts_num_active, 0, verts_num)); - } - else { - verts_num_active = verts_num; - } + BLI_assert(vert != NULL); + if (verts_mask) { + BLI_assert(IN_RANGE_INCL(verts_num_active, 0, verts_num)); + } + else { + verts_num_active = verts_num; + } - tree = BLI_bvhtree_new(verts_num_active, epsilon, tree_type, axis); + BVHTree *tree = BLI_bvhtree_new(verts_num_active, epsilon, tree_type, axis); - if (tree) { - for (i = 0; i < verts_num; i++) { - if (verts_mask && !BLI_BITMAP_TEST_BOOL(verts_mask, i)) { - continue; - } - BLI_bvhtree_insert(tree, i, vert[i].co, 1); + if (tree) { + for (int i = 0; i < verts_num; i++) { + if (verts_mask && !BLI_BITMAP_TEST_BOOL(verts_mask, i)) { + continue; } - BLI_assert(BLI_bvhtree_get_size(tree) == verts_num_active); - BLI_bvhtree_balance(tree); + BLI_bvhtree_insert(tree, i, vert[i].co, 1); } + BLI_assert(BLI_bvhtree_get_size(tree) == verts_num_active); + BLI_bvhtree_balance(tree); } return tree; @@ -452,31 +449,23 @@ static BVHTree *bvhtree_from_mesh_verts_create_tree( static void bvhtree_from_mesh_verts_setup_data( BVHTreeFromMesh *data, BVHTree *tree, const bool is_cached, float epsilon, - MVert *vert, const bool vert_allocated) + const MVert *vert, const bool vert_allocated) { memset(data, 0, sizeof(*data)); - if (tree) { - data->tree = tree; - data->cached = is_cached; + data->tree = tree; + data->cached = is_cached; - /* a NULL nearest callback works fine - * remember the min distance to point is the same as the min distance to BV of point */ - data->nearest_callback = NULL; - data->raycast_callback = mesh_verts_spherecast; - data->nearest_to_ray_callback = NULL; + /* a NULL nearest callback works fine + * remember the min distance to point is the same as the min distance to BV of point */ + data->nearest_callback = NULL; + data->raycast_callback = mesh_verts_spherecast; - data->vert = vert; - data->vert_allocated = vert_allocated; - //data->face = DM_get_tessface_array(dm, &data->face_allocated); /* XXX WHY???? */ + data->vert = vert; + data->vert_allocated = vert_allocated; + //data->face = DM_get_tessface_array(dm, &data->face_allocated); /* XXX WHY???? */ - data->sphere_radius = epsilon; - } - else { - if (vert_allocated) { - MEM_freeN(vert); - } - } + data->sphere_radius = epsilon; } /* Builds a bvh tree where nodes are the vertices of the given em */ @@ -485,11 +474,9 @@ BVHTree *bvhtree_from_editmesh_verts_ex( const BLI_bitmap *verts_mask, int verts_num_active, float epsilon, int tree_type, int axis) { - int vert_num = em->bm->totvert; - BVHTree *tree = bvhtree_from_editmesh_verts_create_tree( epsilon, tree_type, axis, - em, vert_num, verts_mask, verts_num_active); + em, em->bm->totvert, verts_mask, verts_num_active); if (tree) { memset(data, 0, sizeof(*data)); @@ -497,11 +484,11 @@ BVHTree *bvhtree_from_editmesh_verts_ex( data->em = em; data->nearest_callback = NULL; data->raycast_callback = editmesh_verts_spherecast; - data->nearest_to_ray_callback = NULL; } return tree; } + BVHTree *bvhtree_from_editmesh_verts( BVHTreeFromEditMesh *data, BMEditMesh *em, float epsilon, int tree_type, int axis) @@ -512,8 +499,8 @@ BVHTree *bvhtree_from_editmesh_verts( epsilon, tree_type, axis); } - -/* Builds a bvh tree where nodes are the vertices of the given dm */ +/* Builds a bvh tree where nodes are the vertices of the given dm + * and stores the BVHTree in dm->bvhCache */ BVHTree *bvhtree_from_mesh_verts( BVHTreeFromMesh *data, DerivedMesh *dm, float epsilon, int tree_type, int axis) @@ -553,10 +540,18 @@ BVHTree *bvhtree_from_mesh_verts( /* printf("BVHTree is already build, using cached tree\n"); */ } - /* Setup BVHTreeFromMesh */ - bvhtree_from_mesh_verts_setup_data(data, tree, true, epsilon, vert, vert_allocated); - - return data->tree; + if (tree) { + /* Setup BVHTreeFromMesh */ + bvhtree_from_mesh_verts_setup_data( + data, tree, true, epsilon, vert, vert_allocated); + } + else { + if (vert_allocated) { + MEM_freeN(vert); + } + memset(data, 0, sizeof(*data)); + } + return tree; } /** @@ -566,7 +561,7 @@ BVHTree *bvhtree_from_mesh_verts( * \param verts_num_active if >= 0, number of active verts to add to BVH tree (else will be computed from mask). */ BVHTree *bvhtree_from_mesh_verts_ex( - BVHTreeFromMesh *data, MVert *vert, const int verts_num, const bool vert_allocated, + BVHTreeFromMesh *data, const MVert *vert, const int verts_num, const bool vert_allocated, const BLI_bitmap *verts_mask, int verts_num_active, float epsilon, int tree_type, int axis) { @@ -574,9 +569,10 @@ BVHTree *bvhtree_from_mesh_verts_ex( epsilon, tree_type, axis, vert, verts_num, verts_mask, verts_num_active); /* Setup BVHTreeFromMesh */ - bvhtree_from_mesh_verts_setup_data(data, tree, false, epsilon, vert, vert_allocated); + bvhtree_from_mesh_verts_setup_data( + data, tree, false, epsilon, vert, vert_allocated); - return data->tree; + return tree; } /** \} */ @@ -592,8 +588,6 @@ static BVHTree *bvhtree_from_editmesh_edges_create_tree( BMEditMesh *em, const int edges_num, const BLI_bitmap *edges_mask, int edges_num_active) { - BVHTree *tree = NULL; - int i; BM_mesh_elem_table_ensure(em->bm, BM_EDGE); if (edges_mask) { BLI_assert(IN_RANGE_INCL(edges_num_active, 0, edges_num)); @@ -602,9 +596,10 @@ static BVHTree *bvhtree_from_editmesh_edges_create_tree( edges_num_active = edges_num; } - tree = BLI_bvhtree_new(edges_num_active, epsilon, tree_type, axis); + BVHTree *tree = BLI_bvhtree_new(edges_num_active, epsilon, tree_type, axis); if (tree) { + int i; BMIter iter; BMEdge *eed; BM_ITER_MESH_INDEX (eed, &iter, em->bm, BM_EDGES_OF_MESH, i) { @@ -624,6 +619,62 @@ static BVHTree *bvhtree_from_editmesh_edges_create_tree( return tree; } +static BVHTree *bvhtree_from_mesh_edges_create_tree( + const MVert *vert, const MEdge *edge, const int edge_num, + const BLI_bitmap *edges_mask, int edges_num_active, + float epsilon, int tree_type, int axis) +{ + if (edges_mask) { + BLI_assert(IN_RANGE_INCL(edges_num_active, 0, edge_num)); + } + else { + edges_num_active = edge_num; + } + BLI_assert(vert != NULL); + BLI_assert(edge != NULL); + + /* Create a bvh-tree of the given target */ + BVHTree *tree = BLI_bvhtree_new(edges_num_active, epsilon, tree_type, axis); + if (tree) { + for (int i = 0; i < edge_num; i++) { + if (edges_mask && !BLI_BITMAP_TEST_BOOL(edges_mask, i)) { + continue; + } + float co[2][3]; + copy_v3_v3(co[0], vert[edge[i].v1].co); + copy_v3_v3(co[1], vert[edge[i].v2].co); + + BLI_bvhtree_insert(tree, i, co[0], 2); + } + BLI_bvhtree_balance(tree); + } + + return tree; +} + +static void bvhtree_from_mesh_edges_setup_data( + BVHTreeFromMesh *data, BVHTree *tree, + const bool is_cached, float epsilon, + const MVert *vert, const bool vert_allocated, + const MEdge *edge, const bool edge_allocated) +{ + memset(data, 0, sizeof(*data)); + + data->tree = tree; + + data->cached = is_cached; + + data->nearest_callback = mesh_edges_nearest_point; + data->raycast_callback = mesh_edges_spherecast; + + data->vert = vert; + data->vert_allocated = vert_allocated; + data->edge = edge; + data->edge_allocated = edge_allocated; + + data->sphere_radius = epsilon; +} + /* Builds a bvh tree where nodes are the edges of the given em */ BVHTree *bvhtree_from_editmesh_edges_ex( BVHTreeFromEditMesh *data, BMEditMesh *em, @@ -642,12 +693,11 @@ BVHTree *bvhtree_from_editmesh_edges_ex( data->em = em; data->nearest_callback = NULL; /* TODO */ data->raycast_callback = NULL; /* TODO */ - /* TODO: not urgent however since users currently define own callbacks */ - data->nearest_to_ray_callback = NULL; } return tree; } + BVHTree *bvhtree_from_editmesh_edges( BVHTreeFromEditMesh *data, BMEditMesh *em, float epsilon, int tree_type, int axis) @@ -680,27 +730,13 @@ BVHTree *bvhtree_from_mesh_edges( BLI_rw_mutex_lock(&cache_rwlock, THREAD_LOCK_WRITE); tree = bvhcache_find(dm->bvhCache, BVHTREE_FROM_EDGES); if (tree == NULL) { - int i; - int numEdges = dm->getNumEdges(dm); - - if (vert != NULL && edge != NULL) { - /* Create a bvh-tree of the given target */ - tree = BLI_bvhtree_new(numEdges, epsilon, tree_type, axis); - if (tree != NULL) { - for (i = 0; i < numEdges; i++) { - float co[2][3]; - copy_v3_v3(co[0], vert[edge[i].v1].co); - copy_v3_v3(co[1], vert[edge[i].v2].co); - - BLI_bvhtree_insert(tree, i, co[0], 2); - } - BLI_bvhtree_balance(tree); + tree = bvhtree_from_mesh_edges_create_tree( + vert, edge, dm->getNumEdges(dm), + NULL, -1, epsilon, tree_type, axis); - /* Save on cache for later use */ - /* printf("BVHTree built and saved on cache\n"); */ - bvhcache_insert(&dm->bvhCache, tree, BVHTREE_FROM_EDGES); - } - } + /* Save on cache for later use */ + /* printf("BVHTree built and saved on cache\n"); */ + bvhcache_insert(&dm->bvhCache, tree, BVHTREE_FROM_EDGES); } BLI_rw_mutex_unlock(&cache_rwlock); } @@ -708,24 +744,10 @@ BVHTree *bvhtree_from_mesh_edges( /* printf("BVHTree is already build, using cached tree\n"); */ } - - /* Setup BVHTreeFromMesh */ - memset(data, 0, sizeof(*data)); - data->tree = tree; - - if (data->tree) { - data->cached = true; - - data->nearest_callback = mesh_edges_nearest_point; - data->raycast_callback = mesh_edges_spherecast; - data->nearest_to_ray_callback = NULL; - - data->vert = vert; - data->vert_allocated = vert_allocated; - data->edge = edge; - data->edge_allocated = edge_allocated; - - data->sphere_radius = epsilon; + if (tree) { + /* Setup BVHTreeFromMesh */ + bvhtree_from_mesh_edges_setup_data( + data, tree, true, epsilon, vert, vert_allocated, edge, edge_allocated); } else { if (vert_allocated) { @@ -734,8 +756,33 @@ BVHTree *bvhtree_from_mesh_edges( if (edge_allocated) { MEM_freeN(edge); } + memset(data, 0, sizeof(*data)); } - return data->tree; + return tree; +} + +/** + * Builds a bvh tree where nodes are the given edges . + * \param vert/edge_allocated if true, elem freeing will be done when freeing data. + * \param edges_mask if not null, true elements give which vert to add to BVH tree. + * \param edges_num_active if >= 0, number of active edges to add to BVH tree (else will be computed from mask). + */ +BVHTree *bvhtree_from_mesh_edges_ex( + BVHTreeFromMesh *data, + const MVert *vert, const bool vert_allocated, + const MEdge *edge, const int edges_num, const bool edge_allocated, + const BLI_bitmap *edges_mask, int edges_num_active, + float epsilon, int tree_type, int axis) +{ + BVHTree *tree = bvhtree_from_mesh_edges_create_tree( + vert, edge, edges_num, edges_mask, edges_num_active, + epsilon, tree_type, axis); + + /* Setup BVHTreeFromMesh */ + bvhtree_from_mesh_edges_setup_data( + data, tree, false, epsilon, vert, vert_allocated, edge, edge_allocated); + + return tree; } /** \} */ @@ -748,7 +795,7 @@ BVHTree *bvhtree_from_mesh_edges( static BVHTree *bvhtree_from_mesh_faces_create_tree( float epsilon, int tree_type, int axis, - MVert *vert, MFace *face, const int faces_num, + const MVert *vert, const MFace *face, const int faces_num, const BLI_bitmap *faces_mask, int faces_num_active) { BVHTree *tree = NULL; @@ -792,34 +839,23 @@ static BVHTree *bvhtree_from_mesh_faces_create_tree( static void bvhtree_from_mesh_faces_setup_data( BVHTreeFromMesh *data, BVHTree *tree, const bool is_cached, float epsilon, - MVert *vert, const bool vert_allocated, - MFace *face, const bool face_allocated) + const MVert *vert, const bool vert_allocated, + const MFace *face, const bool face_allocated) { memset(data, 0, sizeof(*data)); - if (tree) { - data->tree = tree; - data->cached = is_cached; + data->tree = tree; + data->cached = is_cached; - data->nearest_callback = mesh_faces_nearest_point; - data->raycast_callback = mesh_faces_spherecast; - data->nearest_to_ray_callback = NULL; + data->nearest_callback = mesh_faces_nearest_point; + data->raycast_callback = mesh_faces_spherecast; - data->vert = vert; - data->vert_allocated = vert_allocated; - data->face = face; - data->face_allocated = face_allocated; + data->vert = vert; + data->vert_allocated = vert_allocated; + data->face = face; + data->face_allocated = face_allocated; - data->sphere_radius = epsilon; - } - else { - if (vert_allocated) { - MEM_freeN(vert); - } - if (face_allocated) { - MEM_freeN(face); - } - } + data->sphere_radius = epsilon; } /* Builds a bvh tree where nodes are the tesselated faces of the given dm */ @@ -862,10 +898,21 @@ BVHTree *bvhtree_from_mesh_faces( /* printf("BVHTree is already build, using cached tree\n"); */ } - /* Setup BVHTreeFromMesh */ - bvhtree_from_mesh_faces_setup_data(data, tree, true, epsilon, vert, vert_allocated, face, face_allocated); - - return data->tree; + if (tree) { + /* Setup BVHTreeFromMesh */ + bvhtree_from_mesh_faces_setup_data( + data, tree, true, epsilon, vert, vert_allocated, face, face_allocated); + } + else { + if (vert_allocated) { + MEM_freeN(vert); + } + if (face_allocated) { + MEM_freeN(face); + } + memset(data, 0, sizeof(*data)); + } + return tree; } /** @@ -876,8 +923,8 @@ BVHTree *bvhtree_from_mesh_faces( * \param numFaces_active if >= 0, number of active faces to add to BVH tree (else will be computed from mask). */ BVHTree *bvhtree_from_mesh_faces_ex( - BVHTreeFromMesh *data, MVert *vert, const bool vert_allocated, - MFace *face, const int numFaces, const bool face_allocated, + BVHTreeFromMesh *data, const MVert *vert, const bool vert_allocated, + const MFace *face, const int numFaces, const bool face_allocated, const BLI_bitmap *faces_mask, int faces_num_active, float epsilon, int tree_type, int axis) { @@ -887,9 +934,10 @@ BVHTree *bvhtree_from_mesh_faces_ex( faces_mask, faces_num_active); /* Setup BVHTreeFromMesh */ - bvhtree_from_mesh_faces_setup_data(data, tree, false, epsilon, vert, vert_allocated, face, face_allocated); + bvhtree_from_mesh_faces_setup_data( + data, tree, false, epsilon, vert, vert_allocated, face, face_allocated); - return data->tree; + return tree; } /** \} */ @@ -1000,34 +1048,20 @@ static void bvhtree_from_mesh_looptri_setup_data( { memset(data, 0, sizeof(*data)); - if (tree) { - data->tree = tree; - data->cached = is_cached; + data->tree = tree; + data->cached = is_cached; - data->nearest_callback = mesh_looptri_nearest_point; - data->raycast_callback = mesh_looptri_spherecast; - data->nearest_to_ray_callback = NULL; + data->nearest_callback = mesh_looptri_nearest_point; + data->raycast_callback = mesh_looptri_spherecast; - data->vert = vert; - data->vert_allocated = vert_allocated; - data->loop = mloop; - data->loop_allocated = loop_allocated; - data->looptri = looptri; - data->looptri_allocated = looptri_allocated; + data->vert = vert; + data->vert_allocated = vert_allocated; + data->loop = mloop; + data->loop_allocated = loop_allocated; + data->looptri = looptri; + data->looptri_allocated = looptri_allocated; - data->sphere_radius = epsilon; - } - else { - if (vert_allocated) { - MEM_freeN((void *)vert); - } - if (loop_allocated) { - MEM_freeN((void *)mloop); - } - if (looptri_allocated) { - MEM_freeN((void *)looptri); - } - } + data->sphere_radius = epsilon; } /** @@ -1072,7 +1106,6 @@ BVHTree *bvhtree_from_editmesh_looptri_ex( data->tree = tree; data->nearest_callback = editmesh_looptri_nearest_point; data->raycast_callback = editmesh_looptri_spherecast; - data->nearest_to_ray_callback = NULL; data->sphere_radius = 0.0f; data->em = em; data->cached = bvhCache != NULL; @@ -1154,14 +1187,28 @@ BVHTree *bvhtree_from_mesh_looptri( /* printf("BVHTree is already build, using cached tree\n"); */ } - /* Setup BVHTreeFromMesh */ - bvhtree_from_mesh_looptri_setup_data( - data, tree, true, epsilon, - mvert, vert_allocated, - mloop, loop_allocated, - looptri, looptri_allocated); + if (tree) { + /* Setup BVHTreeFromMesh */ + bvhtree_from_mesh_looptri_setup_data( + data, tree, true, epsilon, + mvert, vert_allocated, + mloop, loop_allocated, + looptri, looptri_allocated); + } + else { + if (vert_allocated) { + MEM_freeN(mvert); + } + if (loop_allocated) { + MEM_freeN(mloop); + } + if (looptri_allocated) { + MEM_freeN((void *)looptri); + } + memset(data, 0, sizeof(*data)); + } - return data->tree; + return tree; } BVHTree *bvhtree_from_mesh_looptri_ex( @@ -1184,7 +1231,7 @@ BVHTree *bvhtree_from_mesh_looptri_ex( mloop, loop_allocated, looptri, looptri_allocated); - return data->tree; + return tree; } /** \} */ @@ -1204,29 +1251,27 @@ void free_bvhtree_from_editmesh(struct BVHTreeFromEditMesh *data) /* Frees data allocated by a call to bvhtree_from_mesh_*. */ void free_bvhtree_from_mesh(struct BVHTreeFromMesh *data) { - if (data->tree) { - if (!data->cached) { - BLI_bvhtree_free(data->tree); - } - - if (data->vert_allocated) { - MEM_freeN((void *)data->vert); - } - if (data->edge_allocated) { - MEM_freeN((void *)data->edge); - } - if (data->face_allocated) { - MEM_freeN((void *)data->face); - } - if (data->loop_allocated) { - MEM_freeN((void *)data->loop); - } - if (data->looptri_allocated) { - MEM_freeN((void *)data->looptri); - } + if (data->tree && !data->cached) { + BLI_bvhtree_free(data->tree); + } - memset(data, 0, sizeof(*data)); + if (data->vert_allocated) { + MEM_freeN((void *)data->vert); + } + if (data->edge_allocated) { + MEM_freeN((void *)data->edge); } + if (data->face_allocated) { + MEM_freeN((void *)data->face); + } + if (data->loop_allocated) { + MEM_freeN((void *)data->loop); + } + if (data->looptri_allocated) { + MEM_freeN((void *)data->looptri); + } + + memset(data, 0, sizeof(*data)); } diff --git a/source/blender/blenkernel/intern/cdderivedmesh.c b/source/blender/blenkernel/intern/cdderivedmesh.c index fc3e358cb25..7042b46330b 100644 --- a/source/blender/blenkernel/intern/cdderivedmesh.c +++ b/source/blender/blenkernel/intern/cdderivedmesh.c @@ -2398,13 +2398,16 @@ DerivedMesh *CDDM_from_editbmesh(BMEditMesh *em, const bool use_mdisps, const bo use_tessface, em->tottri, (const BMLoop *(*)[3])em->looptris); } -static DerivedMesh *cddm_copy_ex(DerivedMesh *source, int faces_from_tessfaces) +static DerivedMesh *cddm_copy_ex(DerivedMesh *source, + const bool need_tessface_data, + const bool faces_from_tessfaces) { + const bool copy_tessface_data = (faces_from_tessfaces || need_tessface_data); CDDerivedMesh *cddm = cdDM_create("CDDM_copy cddm"); DerivedMesh *dm = &cddm->dm; int numVerts = source->numVertData; int numEdges = source->numEdgeData; - int numTessFaces = source->numTessFaceData; + int numTessFaces = copy_tessface_data ? source->numTessFaceData : 0; int numLoops = source->numLoopData; int numPolys = source->numPolyData; @@ -2414,20 +2417,29 @@ static DerivedMesh *cddm_copy_ex(DerivedMesh *source, int faces_from_tessfaces) source->getVertDataArray(source, CD_ORIGINDEX); source->getEdgeDataArray(source, CD_ORIGINDEX); source->getPolyDataArray(source, CD_ORIGINDEX); + if (copy_tessface_data) { + source->getTessFaceDataArray(source, CD_ORIGINDEX); + } /* this initializes dm, and copies all non mvert/medge/mface layers */ - DM_from_template(dm, source, DM_TYPE_CDDM, numVerts, numEdges, - faces_from_tessfaces ? numTessFaces : 0, + DM_from_template(dm, source, DM_TYPE_CDDM, numVerts, numEdges, numTessFaces, numLoops, numPolys); dm->deformedOnly = source->deformedOnly; dm->cd_flag = source->cd_flag; dm->dirty = source->dirty; - /* Tessellation data is never copied, so tag it here. */ - dm->dirty |= DM_DIRTY_TESS_CDLAYERS; + /* Tessellation data is never copied, so tag it here. + * Only tag dirty layers if we really ignored tessellation faces. + */ + if (!copy_tessface_data) { + dm->dirty |= DM_DIRTY_TESS_CDLAYERS; + } CustomData_copy_data(&source->vertData, &dm->vertData, 0, 0, numVerts); CustomData_copy_data(&source->edgeData, &dm->edgeData, 0, 0, numEdges); + if (copy_tessface_data) { + CustomData_copy_data(&source->faceData, &dm->faceData, 0, 0, numTessFaces); + } /* now add mvert/medge/mface layers */ cddm->mvert = source->dupVertArray(source); @@ -2435,17 +2447,16 @@ static DerivedMesh *cddm_copy_ex(DerivedMesh *source, int faces_from_tessfaces) CustomData_add_layer(&dm->vertData, CD_MVERT, CD_ASSIGN, cddm->mvert, numVerts); CustomData_add_layer(&dm->edgeData, CD_MEDGE, CD_ASSIGN, cddm->medge, numEdges); - - if (!faces_from_tessfaces) { - DM_DupPolys(source, dm); - } - else { - source->getTessFaceDataArray(source, CD_ORIGINDEX); - CustomData_copy_data(&source->faceData, &dm->faceData, 0, 0, numTessFaces); + if (faces_from_tessfaces || copy_tessface_data) { cddm->mface = source->dupTessFaceArray(source); CustomData_add_layer(&dm->faceData, CD_MFACE, CD_ASSIGN, cddm->mface, numTessFaces); + } + if (!faces_from_tessfaces) { + DM_DupPolys(source, dm); + } + else { CDDM_tessfaces_to_faces(dm); } @@ -2457,12 +2468,17 @@ static DerivedMesh *cddm_copy_ex(DerivedMesh *source, int faces_from_tessfaces) DerivedMesh *CDDM_copy(DerivedMesh *source) { - return cddm_copy_ex(source, 0); + return cddm_copy_ex(source, false, false); } DerivedMesh *CDDM_copy_from_tessface(DerivedMesh *source) { - return cddm_copy_ex(source, 1); + return cddm_copy_ex(source, false, true); +} + +DerivedMesh *CDDM_copy_with_tessface(DerivedMesh *source) +{ + return cddm_copy_ex(source, true, false); } /* note, the CD_ORIGINDEX layers are all 0, so if there is a direct diff --git a/source/blender/blenkernel/intern/cloth.c b/source/blender/blenkernel/intern/cloth.c index 28ef3f6f248..ee0fde1ea61 100644 --- a/source/blender/blenkernel/intern/cloth.c +++ b/source/blender/blenkernel/intern/cloth.c @@ -869,12 +869,6 @@ static int cloth_from_object(Object *ob, ClothModifierData *clmd, DerivedMesh *d return 0; } - for ( i = 0; i < dm->getNumVerts(dm); i++) { - if ((!(cloth->verts[i].flags & CLOTH_VERT_FLAG_PINNED)) && (cloth->verts[i].goal > ALMOST_ZERO)) { - cloth_add_spring (clmd, i, i, 0.0, CLOTH_SPRING_TYPE_GOAL); - } - } - // init our solver BPH_cloth_solver_init(ob, clmd); @@ -944,37 +938,6 @@ BLI_INLINE void spring_verts_ordered_set(ClothSpring *spring, int v0, int v1) } } -// be careful: implicit solver has to be resettet when using this one! -// --> only for implicit handling of this spring! -int cloth_add_spring(ClothModifierData *clmd, unsigned int indexA, unsigned int indexB, float restlength, int spring_type) -{ - Cloth *cloth = clmd->clothObject; - ClothSpring *spring = NULL; - - if (cloth) { - // TODO: look if this spring is already there - - spring = (ClothSpring *)MEM_callocN ( sizeof ( ClothSpring ), "cloth spring" ); - - if (!spring) - return 0; - - spring->ij = indexA; - spring->kl = indexB; - spring->restlen = restlength; - spring->type = spring_type; - spring->flags = 0; - spring->stiffness = 0; - - cloth->numsprings++; - - BLI_linklist_prepend ( &cloth->springs, spring ); - - return 1; - } - return 0; -} - static void cloth_free_edgelist(LinkNodePair *edgelist, unsigned int mvert_num) { if (edgelist) { diff --git a/source/blender/blenkernel/intern/colortools.c b/source/blender/blenkernel/intern/colortools.c index 4f3ffed41bc..b3a0895d063 100644 --- a/source/blender/blenkernel/intern/colortools.c +++ b/source/blender/blenkernel/intern/colortools.c @@ -508,7 +508,7 @@ static void calchandle_curvemap( if ((bezt->h2 == HD_AUTO_ANIM) && next && prev) { /* keep horizontal if extrema */ const float ydiff1 = prev->vec[1][1] - bezt->vec[1][1]; const float ydiff2 = next->vec[1][1] - bezt->vec[1][1]; - if ((ydiff1 <= 0.0f && ydiff2 <= 0.0f)|| + if ((ydiff1 <= 0.0f && ydiff2 <= 0.0f) || (ydiff1 >= 0.0f && ydiff2 >= 0.0f)) { bezt->vec[2][1] = bezt->vec[1][1]; diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c index 90a514781d7..439abb1d593 100644 --- a/source/blender/blenkernel/intern/curve.c +++ b/source/blender/blenkernel/intern/curve.c @@ -89,20 +89,33 @@ void BKE_curve_editfont_free(Curve *cu) } } -void BKE_curve_editNurb_keyIndex_free(EditNurb *editnurb) +static void curve_editNurb_keyIndex_cv_free_cb(void *val) { - if (!editnurb->keyindex) { + CVKeyIndex *index = val; + MEM_freeN(index->orig_cv); + MEM_freeN(val); +} + +void BKE_curve_editNurb_keyIndex_delCV(GHash *keyindex, const void *cv) +{ + BLI_assert(keyindex != NULL); + BLI_ghash_remove(keyindex, cv, NULL, curve_editNurb_keyIndex_cv_free_cb); +} + +void BKE_curve_editNurb_keyIndex_free(GHash **keyindex) +{ + if (!(*keyindex)) { return; } - BLI_ghash_free(editnurb->keyindex, NULL, MEM_freeN); - editnurb->keyindex = NULL; + BLI_ghash_free(*keyindex, NULL, curve_editNurb_keyIndex_cv_free_cb); + *keyindex = NULL; } void BKE_curve_editNurb_free(Curve *cu) { if (cu->editnurb) { BKE_nurbList_free(&cu->editnurb->nurbs); - BKE_curve_editNurb_keyIndex_free(cu->editnurb); + BKE_curve_editNurb_keyIndex_free(&cu->editnurb->keyindex); MEM_freeN(cu->editnurb); cu->editnurb = NULL; } diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c index 98d37fb07bf..7c3f0ac630d 100644 --- a/source/blender/blenkernel/intern/customdata.c +++ b/source/blender/blenkernel/intern/customdata.c @@ -33,14 +33,10 @@ /** \file blender/blenkernel/intern/customdata.c * \ingroup bke */ - - -#include <math.h> -#include <string.h> -#include <assert.h> #include "MEM_guardedalloc.h" +#include "DNA_customdata_types.h" #include "DNA_meshdata_types.h" #include "DNA_ID.h" @@ -62,20 +58,16 @@ #include "BKE_mesh_remap.h" #include "BKE_multires.h" -#include "data_transfer_intern.h" - #include "bmesh.h" -#include <math.h> -#include <string.h> +/* only for customdata_data_transfer_interp_normal_normals */ +#include "data_transfer_intern.h" /* number of layers to add when growing a CustomData object */ #define CUSTOMDATA_GROW 5 /* ensure typemap size is ok */ -BLI_STATIC_ASSERT(sizeof(((CustomData *)NULL)->typemap) / - sizeof(((CustomData *)NULL)->typemap[0]) == CD_NUMTYPES, - "size mismatch"); +BLI_STATIC_ASSERT(ARRAY_SIZE(((CustomData *)NULL)->typemap) == CD_NUMTYPES, "size mismatch"); /********************* Layer type information **********************/ @@ -805,18 +797,15 @@ static void layerInterp_mloopcol( const float *sub_weights, int count, void *dest) { MLoopCol *mc = dest; - int i; - const float *sub_weight; struct { float a; float r; float g; float b; - } col; - col.a = col.r = col.g = col.b = 0; + } col = {0}; - sub_weight = sub_weights; - for (i = 0; i < count; ++i) { + const float *sub_weight = sub_weights; + for (int i = 0; i < count; ++i) { float weight = weights ? weights[i] : 1; const MLoopCol *src = sources[i]; if (sub_weights) { @@ -833,19 +822,16 @@ static void layerInterp_mloopcol( col.a += src->a * weight; } } - + + /* Subdivide smooth or fractal can cause problems without clamping * although weights should also not cause this situation */ - CLAMP(col.a, 0.0f, 255.0f); - CLAMP(col.r, 0.0f, 255.0f); - CLAMP(col.g, 0.0f, 255.0f); - CLAMP(col.b, 0.0f, 255.0f); - /* delay writing to the destination incase dest is in sources */ - mc->r = (int)col.r; - mc->g = (int)col.g; - mc->b = (int)col.b; - mc->a = (int)col.a; + /* also delay writing to the destination incase dest is in sources */ + mc->r = CLAMPIS(iroundf(col.r), 0, 255); + mc->g = CLAMPIS(iroundf(col.g), 0, 255); + mc->b = CLAMPIS(iroundf(col.b), 0, 255); + mc->a = CLAMPIS(iroundf(col.a), 0, 255); } static int layerMaxNum_mloopcol(void) @@ -1068,15 +1054,10 @@ static void layerInterp_mcol( /* Subdivide smooth or fractal can cause problems without clamping * although weights should also not cause this situation */ - CLAMP(col[j].a, 0.0f, 255.0f); - CLAMP(col[j].r, 0.0f, 255.0f); - CLAMP(col[j].g, 0.0f, 255.0f); - CLAMP(col[j].b, 0.0f, 255.0f); - - mc[j].a = (int)col[j].a; - mc[j].r = (int)col[j].r; - mc[j].g = (int)col[j].g; - mc[j].b = (int)col[j].b; + mc[j].a = CLAMPIS(iroundf(col[j].a), 0, 255); + mc[j].r = CLAMPIS(iroundf(col[j].r), 0, 255); + mc[j].g = CLAMPIS(iroundf(col[j].g), 0, 255); + mc[j].b = CLAMPIS(iroundf(col[j].b), 0, 255); } } @@ -2595,7 +2576,7 @@ bool CustomData_from_bmeshpoly_test(CustomData *fdata, CustomData *pdata, Custom if (!LAYER_CMP(ldata, CD_TANGENT, fdata, CD_TANGENT)) return false; -#undef TEST_RET +#undef LAYER_CMP /* if no layers are on either CustomData's, * then there was nothing to do... */ diff --git a/source/blender/blenkernel/intern/depsgraph.c b/source/blender/blenkernel/intern/depsgraph.c index 294a4ce76b7..678dc92a5f2 100644 --- a/source/blender/blenkernel/intern/depsgraph.c +++ b/source/blender/blenkernel/intern/depsgraph.c @@ -544,10 +544,16 @@ static void build_dag_object(DagForest *dag, DagNode *scenenode, Main *bmain, Sc if (ct->tar->type == OB_MESH) node3->customdata_mask |= CD_MASK_MDEFORMVERT; } - else if (ELEM(con->type, CONSTRAINT_TYPE_FOLLOWPATH, CONSTRAINT_TYPE_CLAMPTO, CONSTRAINT_TYPE_SPLINEIK)) + else if (ELEM(con->type, CONSTRAINT_TYPE_FOLLOWPATH, + CONSTRAINT_TYPE_CLAMPTO, + CONSTRAINT_TYPE_SPLINEIK, + CONSTRAINT_TYPE_SHRINKWRAP)) + { dag_add_relation(dag, node3, node, DAG_RL_DATA_DATA | DAG_RL_OB_DATA, cti->name); - else + } + else { dag_add_relation(dag, node3, node, DAG_RL_OB_DATA, cti->name); + } } } @@ -881,8 +887,12 @@ static void build_dag_object(DagForest *dag, DagNode *scenenode, Main *bmain, Sc if (obt->type == OB_MESH) node2->customdata_mask |= CD_MASK_MDEFORMVERT; } - else + else if (cti->type == CONSTRAINT_TYPE_SHRINKWRAP) { + dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA | DAG_RL_OB_DATA, cti->name); + } + else { dag_add_relation(dag, node2, node, DAG_RL_OB_OB, cti->name); + } } addtoroot = 0; } diff --git a/source/blender/blenkernel/intern/displist.c b/source/blender/blenkernel/intern/displist.c index 49db75a0474..f8a9d57f579 100644 --- a/source/blender/blenkernel/intern/displist.c +++ b/source/blender/blenkernel/intern/displist.c @@ -819,7 +819,7 @@ static void curve_calc_modifiers_pre(Scene *scene, Object *ob, ListBase *nurb, if (editmode) required_mode |= eModifierMode_Editmode; - if (cu->editnurb == NULL) { + if (!editmode) { keyVerts = BKE_key_evaluate_object(ob, &numVerts); if (keyVerts) { diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c index dc9f3b57f1f..c525e75b825 100644 --- a/source/blender/blenkernel/intern/dynamicpaint.c +++ b/source/blender/blenkernel/intern/dynamicpaint.c @@ -2460,8 +2460,7 @@ static void dynamic_paint_find_island_border( const unsigned int *other_loop_idx = mlooptri[lt_index].tri; /* Check edges for match, looping in the same order as the outer loop. */ - for (int j = 0; j < 3; j++) - { + for (int j = 0; j < 3; j++) { const int overt0 = mloop[other_loop_idx[(j + 0)]].v; const int overt1 = mloop[other_loop_idx[(j + 1) % 3]].v; @@ -2525,8 +2524,7 @@ static void dynamic_paint_find_island_border( int final_pixel[2] = { (int)floorf(tgt_pixel[0] * w), (int)floorf(tgt_pixel[1] * h) }; /* If current pixel uv is outside of texture */ - if (final_pixel[0] < 0 || final_pixel[0] >= w || final_pixel[1] < 0 || final_pixel[1] >= h) - { + if (final_pixel[0] < 0 || final_pixel[0] >= w || final_pixel[1] < 0 || final_pixel[1] >= h) { if (bdata->best_index == NOT_FOUND) bdata->best_index = OUT_OF_TEXTURE; diff --git a/source/blender/blenkernel/intern/editderivedmesh.c b/source/blender/blenkernel/intern/editderivedmesh.c index e7c0e69b1cb..2fffa0dea28 100644 --- a/source/blender/blenkernel/intern/editderivedmesh.c +++ b/source/blender/blenkernel/intern/editderivedmesh.c @@ -642,10 +642,10 @@ static void emDM_recalcLoopTri(DerivedMesh *dm) MLoopTri *lt = &mlooptri[i]; ARRAY_SET_ITEMS( - lt->tri, - BM_elem_index_get(ltri[0]), - BM_elem_index_get(ltri[1]), - BM_elem_index_get(ltri[2])); + lt->tri, + BM_elem_index_get(ltri[0]), + BM_elem_index_get(ltri[1]), + BM_elem_index_get(ltri[2])); lt->poly = BM_elem_index_get(ltri[0]->f); } } diff --git a/source/blender/blenkernel/intern/effect.c b/source/blender/blenkernel/intern/effect.c index fe8f5ebdca6..4eee24b378f 100644 --- a/source/blender/blenkernel/intern/effect.c +++ b/source/blender/blenkernel/intern/effect.c @@ -848,6 +848,14 @@ static void do_physical_effector(EffectorCache *eff, EffectorData *efd, Effected break; case PFIELD_FORCE: normalize_v3(force); + if (pd->flag & PFIELD_GRAVITATION){ /* Option: Multiply by 1/distance^2 */ + if (efd->distance < FLT_EPSILON){ + strength = 0.0f; + } + else { + strength *= powf(efd->distance, -2.0f); + } + } mul_v3_fl(force, strength * efd->falloff); break; case PFIELD_VORTEX: diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index c67a61a5aad..7dbc43e0a32 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -1217,6 +1217,7 @@ bool driver_get_variable_property( return true; } +#if 0 /* Helper function to obtain a pointer to a Pose Channel (for evaluating drivers) */ static bPoseChannel *dtar_get_pchan_ptr(ChannelDriver *driver, DriverTarget *dtar) { @@ -1239,6 +1240,32 @@ static bPoseChannel *dtar_get_pchan_ptr(ChannelDriver *driver, DriverTarget *dta return NULL; } } +#endif + +static short driver_check_valid_targets(ChannelDriver *driver, DriverVar *dvar) +{ + short valid_targets = 0; + + DRIVER_TARGETS_USED_LOOPER(dvar) + { + Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id); + + /* check if this target has valid data */ + if ((ob == NULL) || (GS(ob->id.name) != ID_OB)) { + /* invalid target, so will not have enough targets */ + driver->flag |= DRIVER_FLAG_INVALID; + dtar->flag |= DTAR_FLAG_INVALID; + } + else { + /* target seems to be OK now... */ + dtar->flag &= ~DTAR_FLAG_INVALID; + valid_targets++; + } + } + DRIVER_TARGETS_LOOPER_END + + return valid_targets; +} /* ......... */ @@ -1252,62 +1279,54 @@ static float dvar_eval_singleProp(ChannelDriver *driver, DriverVar *dvar) /* evaluate 'rotation difference' driver variable */ static float dvar_eval_rotDiff(ChannelDriver *driver, DriverVar *dvar) { - DriverTarget *dtar1 = &dvar->targets[0]; - DriverTarget *dtar2 = &dvar->targets[1]; - bPoseChannel *pchan, *pchan2; - float q1[4], q2[4], quat[4], angle; - - /* get pose channels, and check if we've got two */ - pchan = dtar_get_pchan_ptr(driver, dtar1); - pchan2 = dtar_get_pchan_ptr(driver, dtar2); - - if (ELEM(NULL, pchan, pchan2)) { - /* disable this driver, since it doesn't work correctly... */ - driver->flag |= DRIVER_FLAG_INVALID; - - /* check what the error was */ - if ((pchan == NULL) && (pchan2 == NULL)) { - if (G.debug & G_DEBUG) { - printf("Driver Evaluation Error: Rotational difference failed - first 2 targets invalid\n"); - } - - dtar1->flag |= DTAR_FLAG_INVALID; - dtar2->flag |= DTAR_FLAG_INVALID; - } - else if (pchan == NULL) { - if (G.debug & G_DEBUG) { - printf("Driver Evaluation Error: Rotational difference failed - first target not valid PoseChannel\n"); - } - - dtar1->flag |= DTAR_FLAG_INVALID; - dtar2->flag &= ~DTAR_FLAG_INVALID; - } - else if (pchan2 == NULL) { - if (G.debug & G_DEBUG) { - printf("Driver Evaluation Error: Rotational difference failed - second target not valid PoseChannel\n"); - } - - dtar1->flag &= ~DTAR_FLAG_INVALID; - dtar2->flag |= DTAR_FLAG_INVALID; + short valid_targets = driver_check_valid_targets(driver, dvar); + + /* make sure we have enough valid targets to use - all or nothing for now... */ + if (driver_check_valid_targets(driver, dvar) != 2) { + if (G.debug & G_DEBUG) { + printf("RotDiff DVar: not enough valid targets (n = %d) (a = %p, b = %p)\n", + valid_targets, dvar->targets[0].id, dvar->targets[1].id); } - - /* stop here... */ return 0.0f; } - else { - dtar1->flag &= ~DTAR_FLAG_INVALID; - dtar2->flag &= ~DTAR_FLAG_INVALID; + + float (*mat[2])[4]; + + /* NOTE: for now, these are all just worldspace */ + for (int i = 0; i < 2; i++) { + /* get pointer to loc values to store in */ + DriverTarget *dtar = &dvar->targets[i]; + Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id); + bPoseChannel *pchan; + + /* after the checks above, the targets should be valid here... */ + BLI_assert((ob != NULL) && (GS(ob->id.name) == ID_OB)); + + /* try to get posechannel */ + pchan = BKE_pose_channel_find_name(ob->pose, dtar->pchan_name); + + /* check if object or bone */ + if (pchan) { + /* bone */ + mat[i] = pchan->pose_mat; + } + else { + /* object */ + mat[i] = ob->obmat; + } } - + + float q1[4], q2[4], quat[4], angle; + /* use the final posed locations */ - mat4_to_quat(q1, pchan->pose_mat); - mat4_to_quat(q2, pchan2->pose_mat); - + mat4_to_quat(q1, mat[0]); + mat4_to_quat(q2, mat[1]); + invert_qt_normalized(q1); mul_qt_qtqt(quat, q1, q2); angle = 2.0f * (saacos(quat[0])); angle = ABS(angle); - + return (angle > (float)M_PI) ? (float)((2.0f * (float)M_PI) - angle) : (float)(angle); } @@ -1317,32 +1336,8 @@ static float dvar_eval_locDiff(ChannelDriver *driver, DriverVar *dvar) { float loc1[3] = {0.0f, 0.0f, 0.0f}; float loc2[3] = {0.0f, 0.0f, 0.0f}; - short valid_targets = 0; - - /* Perform two passes - * - * FIRST PASS - to just check that everything works... - * NOTE: we use loops here to reduce code duplication, though in practice, - * there can only be 2 items or else we run into some problems later - */ - DRIVER_TARGETS_USED_LOOPER(dvar) - { - Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id); - - /* check if this target has valid data */ - if ((ob == NULL) || (GS(ob->id.name) != ID_OB)) { - /* invalid target, so will not have enough targets */ - driver->flag |= DRIVER_FLAG_INVALID; - dtar->flag |= DTAR_FLAG_INVALID; - } - else { - /* target seems to be OK now... */ - dtar->flag &= ~DTAR_FLAG_INVALID; - valid_targets++; - } - } - DRIVER_TARGETS_LOOPER_END - + short valid_targets = driver_check_valid_targets(driver, dvar); + /* make sure we have enough valid targets to use - all or nothing for now... */ if (valid_targets < dvar->num_targets) { if (G.debug & G_DEBUG) { @@ -1351,8 +1346,7 @@ static float dvar_eval_locDiff(ChannelDriver *driver, DriverVar *dvar) } return 0.0f; } - - + /* SECOND PASS: get two location values */ /* NOTE: for now, these are all just worldspace */ DRIVER_TARGETS_USED_LOOPER(dvar) @@ -1570,7 +1564,7 @@ static DriverVarTypeInfo dvar_types[MAX_DVAR_TYPES] = { BEGIN_DVAR_TYPEDEF(DVAR_TYPE_ROT_DIFF) dvar_eval_rotDiff, /* eval callback */ 2, /* number of targets used */ - {"Bone 1", "Bone 2"}, /* UI names for targets */ + {"Object/Bone 1", "Object/Bone 2"}, /* UI names for targets */ {DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY, DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* flags */ END_DVAR_TYPEDEF, diff --git a/source/blender/blenkernel/intern/idprop.c b/source/blender/blenkernel/intern/idprop.c index b2641b110f8..a596869e5e8 100644 --- a/source/blender/blenkernel/intern/idprop.c +++ b/source/blender/blenkernel/intern/idprop.c @@ -130,18 +130,22 @@ void IDP_FreeIDPArray(IDProperty *prop) MEM_freeN(prop->data.pointer); } -/*shallow copies item*/ +/* shallow copies item */ void IDP_SetIndexArray(IDProperty *prop, int index, IDProperty *item) { IDProperty *old; BLI_assert(prop->type == IDP_IDPARRAY); + if (index >= prop->len || index < 0) + return; + old = GETPROP(prop, index); - if (index >= prop->len || index < 0) return; - if (item != old) IDP_FreeProperty(old); - - memcpy(GETPROP(prop, index), item, sizeof(IDProperty)); + if (item != old) { + IDP_FreeProperty(old); + + memcpy(old, item, sizeof(IDProperty)); + } } IDProperty *IDP_GetIndexArray(IDProperty *prop, int index) diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index a2d94ccc478..318f6480aaf 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -3159,7 +3159,7 @@ static ImBuf *load_sequence_single(Image *ima, ImageUser *iuser, int frame, cons struct ImBuf *ibuf; char name[FILE_MAX]; int flag; - ImageUser iuser_t; + ImageUser iuser_t = {0}; /* XXX temp stuff? */ if (ima->lastframe != frame) @@ -3167,8 +3167,12 @@ static ImBuf *load_sequence_single(Image *ima, ImageUser *iuser, int frame, cons ima->lastframe = frame; - if (iuser) + if (iuser) { iuser_t = *iuser; + } + else { + /* TODO(sergey): Do we need to initialize something here? */ + } iuser_t.view = view_id; BKE_image_user_file_path(&iuser_t, ima, name); diff --git a/source/blender/blenkernel/intern/lattice.c b/source/blender/blenkernel/intern/lattice.c index b0671f33094..54cfae620a1 100644 --- a/source/blender/blenkernel/intern/lattice.c +++ b/source/blender/blenkernel/intern/lattice.c @@ -67,7 +67,7 @@ #include "BKE_deform.h" -/* Workaround for cyclic depenndnecy with curves. +/* Workaround for cyclic dependency with curves. * In such case curve_cache might not be ready yet, */ #define CYCLIC_DEPENDENCY_WORKAROUND diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c index 6b4b492b212..e380b5c17e0 100644 --- a/source/blender/blenkernel/intern/library.c +++ b/source/blender/blenkernel/intern/library.c @@ -1863,7 +1863,7 @@ void BKE_library_make_local( /* Special case for objects because we don't want proxy pointers to be * cleared yet. This will happen down the road in this function. */ - BKE_object_make_local_ex(bmain, (Object*)id, true, false); + BKE_object_make_local_ex(bmain, (Object *)id, true, false); } else { id_make_local(bmain, id, false, true); diff --git a/source/blender/blenkernel/intern/library_remap.c b/source/blender/blenkernel/intern/library_remap.c index a408b498f18..b6f4621a0b3 100644 --- a/source/blender/blenkernel/intern/library_remap.c +++ b/source/blender/blenkernel/intern/library_remap.c @@ -179,6 +179,7 @@ static int foreach_libblock_remap_callback(void *user_data, ID *id_self, ID **id * on the other hand since they get reset to lib data on file open/reload it is indirect too... * Edit Mode is also a 'skip direct' case. */ const bool is_obj = (GS(id->name) == ID_OB); + const bool is_obj_proxy = (is_obj && (((Object *)id)->proxy || ((Object *)id)->proxy_group)); const bool is_obj_editmode = (is_obj && BKE_object_is_in_editmode((Object *)id)); const bool is_never_null = ((cb_flag & IDWALK_CB_NEVER_NULL) && (new_id == NULL) && (id_remap_data->flag & ID_REMAP_FORCE_NEVER_NULL_USAGE) == 0); @@ -231,7 +232,7 @@ static int foreach_libblock_remap_callback(void *user_data, ID *id_self, ID **id /* We cannot affect old_id->us directly, LIB_TAG_EXTRAUSER(_SET) are assumed to be set as needed, * that extra user is processed in final handling... */ } - if (!is_indirect) { + if (!is_indirect || is_obj_proxy) { id_remap_data->status |= ID_REMAP_IS_LINKED_DIRECT; } } diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c index af02e02b017..c12890a354e 100644 --- a/source/blender/blenkernel/intern/mesh.c +++ b/source/blender/blenkernel/intern/mesh.c @@ -39,7 +39,9 @@ #include "BLI_utildefines.h" #include "BLI_math.h" +#include "BLI_linklist.h" #include "BLI_listbase.h" +#include "BLI_memarena.h" #include "BLI_edgehash.h" #include "BLI_string.h" @@ -66,6 +68,11 @@ #include "DEG_depsgraph.h" +/* Define for cases when you want extra validation of mesh + * after certain modifications. + */ +// #undef VALIDATE_MESH + enum { MESHCMP_DVERT_WEIGHTMISMATCH = 1, MESHCMP_DVERT_GROUPMISMATCH, @@ -2048,7 +2055,7 @@ void BKE_mesh_mselect_active_set(Mesh *me, int index, int type) (me->mselect[me->totselect - 1].type == type)); } -void BKE_mesh_calc_normals_split(Mesh *mesh) +void BKE_mesh_calc_normals_split_ex(Mesh *mesh, MLoopNorSpaceArray *r_lnors_spacearr) { float (*r_loopnors)[3]; float (*polynors)[3]; @@ -2083,113 +2090,339 @@ void BKE_mesh_calc_normals_split(Mesh *mesh) BKE_mesh_normals_loop_split( mesh->mvert, mesh->totvert, mesh->medge, mesh->totedge, mesh->mloop, r_loopnors, mesh->totloop, mesh->mpoly, (const float (*)[3])polynors, mesh->totpoly, - (mesh->flag & ME_AUTOSMOOTH) != 0, mesh->smoothresh, NULL, clnors, NULL); + (mesh->flag & ME_AUTOSMOOTH) != 0, mesh->smoothresh, r_lnors_spacearr, clnors, NULL); if (free_polynors) { MEM_freeN(polynors); } } -/* Spli faces based on the edge angle. - * Matches behavior of face splitting in render engines. - */ -void BKE_mesh_split_faces(Mesh *mesh) +void BKE_mesh_calc_normals_split(Mesh *mesh) { - const int num_verts = mesh->totvert; - const int num_edges = mesh->totedge; - const int num_polys = mesh->totpoly; + BKE_mesh_calc_normals_split_ex(mesh, NULL); +} + +/* Split faces helper functions. */ + +typedef struct SplitFaceNewVert { + struct SplitFaceNewVert *next; + int new_index; + int orig_index; + float *vnor; +} SplitFaceNewVert; + +typedef struct SplitFaceNewEdge { + struct SplitFaceNewEdge *next; + int new_index; + int orig_index; + int v1; + int v2; +} SplitFaceNewEdge; + +/* Detect needed new vertices, and update accordingly loops' vertex indices. + * WARNING! Leaves mesh in invalid state. */ +static int split_faces_prepare_new_verts( + const Mesh *mesh, MLoopNorSpaceArray *lnors_spacearr, SplitFaceNewVert **new_verts, MemArena *memarena, + bool *r_need_vnors_recalc) +{ + /* Note: if lnors_spacearr is NULL, ther is no autosmooth handling, and we only split out flat polys. */ + const int num_loops = mesh->totloop; + int num_verts = mesh->totvert; MVert *mvert = mesh->mvert; - MEdge *medge = mesh->medge; MLoop *mloop = mesh->mloop; - MPoly *mpoly = mesh->mpoly; - float (*lnors)[3]; - int poly, num_new_verts = 0; - if ((mesh->flag & ME_AUTOSMOOTH) == 0) { - return; - } - BKE_mesh_tessface_clear(mesh); - /* Compute loop normals if needed. */ - if (!CustomData_has_layer(&mesh->ldata, CD_NORMAL)) { - BKE_mesh_calc_normals_split(mesh); - } - lnors = CustomData_get_layer(&mesh->ldata, CD_NORMAL); - /* Count number of vertices to be split. */ - for (poly = 0; poly < num_polys; poly++) { - MPoly *mp = &mpoly[poly]; - int loop; - for (loop = 0; loop < mp->totloop; loop++) { - MLoop *ml = &mloop[mp->loopstart + loop]; - MVert *mv = &mvert[ml->v]; - float vn[3]; - normal_short_to_float_v3(vn, mv->no); - if (!equals_v3v3(vn, lnors[mp->loopstart + loop])) { - num_new_verts++; + + BLI_bitmap *verts_used = BLI_BITMAP_NEW(num_verts, __func__); + + if (lnors_spacearr) { + BLI_bitmap *done_loops = BLI_BITMAP_NEW(num_loops, __func__); + + MLoop *ml = mloop; + MLoopNorSpace **lnor_space = lnors_spacearr->lspacearr; + for (int loop_idx = 0; loop_idx < num_loops; loop_idx++, ml++, lnor_space++) { + if (!BLI_BITMAP_TEST(done_loops, loop_idx)) { + const int vert_idx = ml->v; + const bool vert_used = BLI_BITMAP_TEST_BOOL(verts_used, vert_idx); + /* If vert is already used by another smooth fan, we need a new vert for this one. */ + const int new_vert_idx = vert_used ? num_verts++ : vert_idx; + + BLI_assert(*lnor_space); + + if ((*lnor_space)->loops) { + for (LinkNode *lnode = (*lnor_space)->loops; lnode; lnode = lnode->next) { + const int ml_fan_idx = GET_INT_FROM_POINTER(lnode->link); + BLI_BITMAP_ENABLE(done_loops, ml_fan_idx); + if (vert_used) { + mloop[ml_fan_idx].v = new_vert_idx; + } + } + } + else { + /* Single loop in this fan... */ + BLI_BITMAP_ENABLE(done_loops, loop_idx); + if (vert_used) { + ml->v = new_vert_idx; + } + } + + if (!vert_used) { + BLI_BITMAP_ENABLE(verts_used, vert_idx); + /* We need to update that vertex's normal here, we won't go over it again. */ + /* This is important! *DO NOT* set vnor to final computed lnor, vnor should always be defined to + * 'automatic normal' value computed from its polys, not some custom normal. + * Fortunately, that's the loop normal space's 'lnor' reference vector. ;) */ + normal_float_to_short_v3(mvert[vert_idx].no, (*lnor_space)->vec_lnor); + } + else { + /* Add new vert to list. */ + SplitFaceNewVert *new_vert = BLI_memarena_alloc(memarena, sizeof(*new_vert)); + new_vert->orig_index = vert_idx; + new_vert->new_index = new_vert_idx; + new_vert->vnor = (*lnor_space)->vec_lnor; /* See note above. */ + new_vert->next = *new_verts; + *new_verts = new_vert; + } } } + + MEM_freeN(done_loops); } - if (num_new_verts == 0) { - /* No new vertices are to be added, can do early exit. */ - return; - } - /* Reallocate all vert and edge related data. */ - mesh->totvert += num_new_verts; - mesh->totedge += 2 * num_new_verts; - CustomData_realloc(&mesh->vdata, mesh->totvert); - CustomData_realloc(&mesh->edata, mesh->totedge); - /* Update pointers to a newly allocated memory. */ - BKE_mesh_update_customdata_pointers(mesh, false); - mvert = mesh->mvert; - medge = mesh->medge; - /* Perform actual vertex split. */ - num_new_verts = 0; - for (poly = 0; poly < num_polys; poly++) { - MPoly *mp = &mpoly[poly]; - int loop; - for (loop = 0; loop < mp->totloop; loop++) { - int poly_loop = mp->loopstart + loop; - MLoop *ml = &mloop[poly_loop]; - MVert *mv = &mvert[ml->v]; - float vn[3]; - normal_short_to_float_v3(vn, mv->no); - if (!equals_v3v3(vn, lnors[mp->loopstart + loop])) { - int poly_loop_prev = mp->loopstart + (loop + mp->totloop - 1) % mp->totloop; - MLoop *ml_prev = &mloop[poly_loop_prev]; - int new_edge_prev, new_edge; - /* Cretae new vertex. */ - int new_vert = num_verts + num_new_verts; - CustomData_copy_data(&mesh->vdata, &mesh->vdata, - ml->v, new_vert, 1); - normal_float_to_short_v3(mvert[new_vert].no, - lnors[poly_loop]); - /* Create new edges. */ - new_edge_prev = num_edges + 2 * num_new_verts; - new_edge = num_edges + 2 * num_new_verts + 1; - CustomData_copy_data(&mesh->edata, &mesh->edata, - ml_prev->e, new_edge_prev, 1); - CustomData_copy_data(&mesh->edata, &mesh->edata, - ml->e, new_edge, 1); - if (medge[new_edge_prev].v1 == ml->v) { - medge[new_edge_prev].v1 = new_vert; + else { + /* No loop normal spaces available, we only split out flat polys. */ + const int num_polys = mesh->totpoly; + const MPoly *mpoly = mesh->mpoly; + + /* We do that in two loops, to keep original edges/verts to smooth polys preferencially. */ + const MPoly *mp = mpoly; + for (int i = 0; i < num_polys; i++, mp++) { + if (mp->flag & ME_SMOOTH) { + const MLoop *ml = &mloop[mp->loopstart]; + for (int j = 0; j < mp->totloop; j++, ml++) { + /* Just mark the vertex as used/reserved, that way neighbor flat polys, if any, + * will have to create their own. */ + BLI_BITMAP_ENABLE(verts_used, ml->v); } - else { - medge[new_edge_prev].v2 = new_vert; + } + } + + mp = mpoly; + for (int i = 0; i < num_polys; i++, mp++) { + if (!(mp->flag & ME_SMOOTH)) { + MLoop *ml = &mloop[mp->loopstart]; + for (int j = 0; j < mp->totloop; j++, ml++) { + const int vert_idx = ml->v; + + if (BLI_BITMAP_TEST(verts_used, vert_idx)) { + /* Add new vert to list. */ + const int new_vert_idx = num_verts++; + ml->v = new_vert_idx; + + SplitFaceNewVert *new_vert = BLI_memarena_alloc(memarena, sizeof(*new_vert)); + new_vert->orig_index = vert_idx; + new_vert->new_index = new_vert_idx; + new_vert->vnor = NULL; /* See note below about normals. */ + new_vert->next = *new_verts; + *new_verts = new_vert; + } + else { + BLI_BITMAP_ENABLE(verts_used, vert_idx); + } } - if (medge[new_edge].v1 == ml->v) { - medge[new_edge].v1 = new_vert; + /* Note: there is no way to get new normals for smooth vertices here (and we don't have direct access + * to poly normals either for flat ones), so we'll have to recompute all vnors at the end... */ + *r_need_vnors_recalc = true; + } + } + } + + MEM_freeN(verts_used); + + return num_verts - mesh->totvert; +} + +/* Detect needed new edges, and update accordingly loops' edge indices. + * WARNING! Leaves mesh in invalid state. */ +static int split_faces_prepare_new_edges( + const Mesh *mesh, SplitFaceNewEdge **new_edges, MemArena *memarena) +{ + const int num_polys = mesh->totpoly; + int num_edges = mesh->totedge; + MEdge *medge = mesh->medge; + MLoop *mloop = mesh->mloop; + const MPoly *mpoly = mesh->mpoly; + + BLI_bitmap *edges_used = BLI_BITMAP_NEW(num_edges, __func__); + EdgeHash *edges_hash = BLI_edgehash_new_ex(__func__, num_edges); + + const MPoly *mp = mpoly; + for (int poly_idx = 0; poly_idx < num_polys; poly_idx++, mp++) { + MLoop *ml_prev = &mloop[mp->loopstart + mp->totloop - 1]; + MLoop *ml = &mloop[mp->loopstart]; + for (int loop_idx = 0; loop_idx < mp->totloop; loop_idx++, ml++) { + void **eval; + if (!BLI_edgehash_ensure_p(edges_hash, ml_prev->v, ml->v, &eval)) { + const int edge_idx = ml_prev->e; + + /* That edge has not been encountered yet, define it. */ + if (BLI_BITMAP_TEST(edges_used, edge_idx)) { + /* Original edge has already been used, we need to define a new one. */ + const int new_edge_idx = num_edges++; + *eval = SET_INT_IN_POINTER(new_edge_idx); + ml_prev->e = new_edge_idx; + + SplitFaceNewEdge *new_edge = BLI_memarena_alloc(memarena, sizeof(*new_edge)); + new_edge->orig_index = edge_idx; + new_edge->new_index = new_edge_idx; + new_edge->v1 = ml_prev->v; + new_edge->v2 = ml->v; + new_edge->next = *new_edges; + *new_edges = new_edge; } else { - medge[new_edge].v2 = new_vert; + /* We can re-use original edge. */ + medge[edge_idx].v1 = ml_prev->v; + medge[edge_idx].v2 = ml->v; + *eval = SET_INT_IN_POINTER(edge_idx); + BLI_BITMAP_ENABLE(edges_used, edge_idx); } - - ml->v = new_vert; - ml_prev->e = new_edge_prev; - ml->e = new_edge; - num_new_verts++; } + else { + /* Edge already known, just update loop's edge index. */ + ml_prev->e = GET_INT_FROM_POINTER(*eval); + } + + ml_prev = ml; + } + } + + MEM_freeN(edges_used); + BLI_edgehash_free(edges_hash, NULL); + + return num_edges - mesh->totedge; +} + +/* Perform actual split of vertices. */ +static void split_faces_split_new_verts( + Mesh *mesh, SplitFaceNewVert *new_verts, const int num_new_verts) +{ + const int num_verts = mesh->totvert - num_new_verts; + MVert *mvert = mesh->mvert; + + /* Remember new_verts is a single linklist, so its items are in reversed order... */ + MVert *new_mv = &mvert[mesh->totvert - 1]; + for (int i = mesh->totvert - 1; i >= num_verts ; i--, new_mv--, new_verts = new_verts->next) { + BLI_assert(new_verts->new_index == i); + BLI_assert(new_verts->new_index != new_verts->orig_index); + CustomData_copy_data(&mesh->vdata, &mesh->vdata, new_verts->orig_index, i, 1); + if (new_verts->vnor) { + normal_float_to_short_v3(new_mv->no, new_verts->vnor); } } } +/* Perform actual split of edges. */ +static void split_faces_split_new_edges( + Mesh *mesh, SplitFaceNewEdge *new_edges, const int num_new_edges) +{ + const int num_edges = mesh->totedge - num_new_edges; + MEdge *medge = mesh->medge; + + /* Remember new_edges is a single linklist, so its items are in reversed order... */ + MEdge *new_med = &medge[mesh->totedge - 1]; + for (int i = mesh->totedge - 1; i >= num_edges ; i--, new_med--, new_edges = new_edges->next) { + BLI_assert(new_edges->new_index == i); + BLI_assert(new_edges->new_index != new_edges->orig_index); + CustomData_copy_data(&mesh->edata, &mesh->edata, new_edges->orig_index, i, 1); + new_med->v1 = new_edges->v1; + new_med->v2 = new_edges->v2; + } +} + +/* Split faces based on the edge angle and loop normals. + * Matches behavior of face splitting in render engines. + * + * NOTE: Will leave CD_NORMAL loop data layer which is + * used by render engines to set shading up. + */ +void BKE_mesh_split_faces(Mesh *mesh, bool free_loop_normals) +{ + const int num_polys = mesh->totpoly; + + if (num_polys == 0) { + return; + } + BKE_mesh_tessface_clear(mesh); + + MLoopNorSpaceArray *lnors_spacearr = NULL; + MemArena *memarena; + bool need_vnors_recalc = false; + + if (mesh->flag & ME_AUTOSMOOTH) { + lnors_spacearr = MEM_callocN(sizeof(*lnors_spacearr), __func__); + /* Compute loop normals and loop normal spaces (a.k.a. smooth fans of faces around vertices). */ + BKE_mesh_calc_normals_split_ex(mesh, lnors_spacearr); + /* Stealing memarena from loop normals space array. */ + memarena = lnors_spacearr->mem; + } + else { + /* We still have to split out flat faces... */ + memarena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); + } + + SplitFaceNewVert *new_verts = NULL; + SplitFaceNewEdge *new_edges = NULL; + + /* Detect loop normal spaces (a.k.a. smooth fans) that will need a new vert. */ + const int num_new_verts = split_faces_prepare_new_verts(mesh, lnors_spacearr, &new_verts, memarena, &need_vnors_recalc); + + if (num_new_verts > 0) { + /* Reminder: beyond this point, there is no way out, mesh is in invalid state (due to early-reassignment of + * loops' vertex and edge indices to new, to-be-created split ones). */ + + const int num_new_edges = split_faces_prepare_new_edges(mesh, &new_edges, memarena); + /* We can have to split a vertex without having to add a single new edge... */ + const bool do_edges = (num_new_edges > 0); + + /* Reallocate all vert and edge related data. */ + mesh->totvert += num_new_verts; + mesh->totedge += num_new_edges; + CustomData_realloc(&mesh->vdata, mesh->totvert); + if (do_edges) { + CustomData_realloc(&mesh->edata, mesh->totedge); + } + /* Update pointers to a newly allocated memory. */ + BKE_mesh_update_customdata_pointers(mesh, false); + + /* Perform actual split of vertices and edges. */ + split_faces_split_new_verts(mesh, new_verts, num_new_verts); + if (do_edges) { + split_faces_split_new_edges(mesh, new_edges, num_new_edges); + } + } + + /* Note: after this point mesh is expected to be valid again. */ + + /* CD_NORMAL is expected to be temporary only. */ + if (free_loop_normals) { + CustomData_free_layers(&mesh->ldata, CD_NORMAL, mesh->totloop); + } + + if (lnors_spacearr) { + /* Also frees new_verts/edges temp data, since we used its memarena to allocate them. */ + BKE_lnor_spacearr_free(lnors_spacearr); + MEM_freeN(lnors_spacearr); + } + else { + BLI_memarena_free(memarena); + } + + if (need_vnors_recalc) { + BKE_mesh_calc_normals(mesh); + } +#ifdef VALIDATE_MESH + BKE_mesh_validate(mesh, true, true); +#endif +} + /* settings: 1 - preview, 2 - render */ Mesh *BKE_mesh_new_from_object( Main *bmain, Scene *sce, Object *ob, diff --git a/source/blender/blenkernel/intern/mesh_evaluate.c b/source/blender/blenkernel/intern/mesh_evaluate.c index f9eba118383..0d0055113b7 100644 --- a/source/blender/blenkernel/intern/mesh_evaluate.c +++ b/source/blender/blenkernel/intern/mesh_evaluate.c @@ -304,13 +304,13 @@ void BKE_mesh_calc_normals_poly( void BKE_mesh_calc_normals(Mesh *mesh) { #ifdef DEBUG_TIME - TIMEIT_START(BKE_mesh_calc_normals); + TIMEIT_START_AVERAGED(BKE_mesh_calc_normals); #endif BKE_mesh_calc_normals_poly(mesh->mvert, NULL, mesh->totvert, mesh->mloop, mesh->mpoly, mesh->totloop, mesh->totpoly, NULL, false); #ifdef DEBUG_TIME - TIMEIT_END(BKE_mesh_calc_normals); + TIMEIT_END_AVERAGED(BKE_mesh_calc_normals); #endif } @@ -630,7 +630,6 @@ typedef struct LoopSplitTaskDataCommon { * Note we do not need to protect it, though, since two different tasks will *always* affect different * elements in the arrays. */ MLoopNorSpaceArray *lnors_spacearr; - BLI_bitmap *sharp_verts; float (*loopnors)[3]; short (*clnors_data)[2]; @@ -643,11 +642,8 @@ typedef struct LoopSplitTaskDataCommon { const int *loop_to_poly; const float (*polynors)[3]; + int numLoops; int numPolys; - - /* ***** Workers communication. ***** */ - ThreadQueue *task_queue; - } LoopSplitTaskDataCommon; #define INDEX_UNSET INT_MIN @@ -655,6 +651,50 @@ typedef struct LoopSplitTaskDataCommon { /* See comment about edge_to_loops below. */ #define IS_EDGE_SHARP(_e2l) (ELEM((_e2l)[1], INDEX_UNSET, INDEX_INVALID)) +static void loop_manifold_fan_around_vert_next( + const MLoop *mloops, const MPoly *mpolys, + const int *loop_to_poly, const int *e2lfan_curr, const uint mv_pivot_index, + const MLoop **r_mlfan_curr, int *r_mlfan_curr_index, int *r_mlfan_vert_index, int *r_mpfan_curr_index) +{ + const MLoop *mlfan_next; + const MPoly *mpfan_next; + + /* Warning! This is rather complex! + * We have to find our next edge around the vertex (fan mode). + * First we find the next loop, which is either previous or next to mlfan_curr_index, depending + * whether both loops using current edge are in the same direction or not, and whether + * mlfan_curr_index actually uses the vertex we are fanning around! + * mlfan_curr_index is the index of mlfan_next here, and mlfan_next is not the real next one + * (i.e. not the future mlfan_curr)... + */ + *r_mlfan_curr_index = (e2lfan_curr[0] == *r_mlfan_curr_index) ? e2lfan_curr[1] : e2lfan_curr[0]; + *r_mpfan_curr_index = loop_to_poly[*r_mlfan_curr_index]; + + BLI_assert(*r_mlfan_curr_index >= 0); + BLI_assert(*r_mpfan_curr_index >= 0); + + mlfan_next = &mloops[*r_mlfan_curr_index]; + mpfan_next = &mpolys[*r_mpfan_curr_index]; + if (((*r_mlfan_curr)->v == mlfan_next->v && (*r_mlfan_curr)->v == mv_pivot_index) || + ((*r_mlfan_curr)->v != mlfan_next->v && (*r_mlfan_curr)->v != mv_pivot_index)) + { + /* We need the previous loop, but current one is our vertex's loop. */ + *r_mlfan_vert_index = *r_mlfan_curr_index; + if (--(*r_mlfan_curr_index) < mpfan_next->loopstart) { + *r_mlfan_curr_index = mpfan_next->loopstart + mpfan_next->totloop - 1; + } + } + else { + /* We need the next loop, which is also our vertex's loop. */ + if (++(*r_mlfan_curr_index) >= mpfan_next->loopstart + mpfan_next->totloop) { + *r_mlfan_curr_index = mpfan_next->loopstart; + } + *r_mlfan_vert_index = *r_mlfan_curr_index; + } + *r_mlfan_curr = &mloops[*r_mlfan_curr_index]; + /* And now we are back in sync, mlfan_curr_index is the index of mlfan_curr! Pff! */ +} + static void split_loop_nor_single_do(LoopSplitTaskDataCommon *common_data, LoopSplitTaskData *data) { MLoopNorSpaceArray *lnors_spacearr = common_data->lnors_spacearr; @@ -680,7 +720,7 @@ static void split_loop_nor_single_do(LoopSplitTaskDataCommon *common_data, LoopS */ copy_v3_v3(*lnor, polynors[mp_index]); - /* printf("BASIC: handling loop %d / edge %d / vert %d / poly %d\n", ml_curr_index, ml_curr->e, ml_curr->v, mp_index); */ +// printf("BASIC: handling loop %d / edge %d / vert %d / poly %d\n", ml_curr_index, ml_curr->e, ml_curr->v, mp_index); /* If needed, generate this (simple!) lnor space. */ if (lnors_spacearr) { @@ -747,8 +787,7 @@ static void split_loop_nor_fan_do(LoopSplitTaskDataCommon *common_data, LoopSpli const MEdge *me_org = &medges[ml_curr->e]; /* ml_curr would be mlfan_prev if we needed that one */ const int *e2lfan_curr; float vec_curr[3], vec_prev[3], vec_org[3]; - const MLoop *mlfan_curr, *mlfan_next; - const MPoly *mpfan_next; + const MLoop *mlfan_curr; float lnor[3] = {0.0f, 0.0f, 0.0f}; /* mlfan_vert_index: the loop of our current edge might not be the loop of our current vertex! */ int mlfan_curr_index, mlfan_vert_index, mpfan_curr_index; @@ -787,7 +826,7 @@ static void split_loop_nor_fan_do(LoopSplitTaskDataCommon *common_data, LoopSpli } } - /* printf("FAN: vert %d, start edge %d\n", mv_pivot_index, ml_curr->e); */ +// printf("FAN: vert %d, start edge %d\n", mv_pivot_index, ml_curr->e); while (true) { const MEdge *me_curr = &medges[mlfan_curr->e]; @@ -803,7 +842,7 @@ static void split_loop_nor_fan_do(LoopSplitTaskDataCommon *common_data, LoopSpli normalize_v3(vec_curr); } - /* printf("\thandling edge %d / loop %d\n", mlfan_curr->e, mlfan_curr_index); */ +// printf("\thandling edge %d / loop %d\n", mlfan_curr->e, mlfan_curr_index); { /* Code similar to accumulate_vertex_normals_poly. */ @@ -845,46 +884,16 @@ static void split_loop_nor_fan_do(LoopSplitTaskDataCommon *common_data, LoopSpli /* Current edge is sharp and we have finished with this fan of faces around this vert, * or this vert is smooth, and we have completed a full turn around it. */ - /* printf("FAN: Finished!\n"); */ +// printf("FAN: Finished!\n"); break; } copy_v3_v3(vec_prev, vec_curr); - /* Warning! This is rather complex! - * We have to find our next edge around the vertex (fan mode). - * First we find the next loop, which is either previous or next to mlfan_curr_index, depending - * whether both loops using current edge are in the same direction or not, and whether - * mlfan_curr_index actually uses the vertex we are fanning around! - * mlfan_curr_index is the index of mlfan_next here, and mlfan_next is not the real next one - * (i.e. not the future mlfan_curr)... - */ - mlfan_curr_index = (e2lfan_curr[0] == mlfan_curr_index) ? e2lfan_curr[1] : e2lfan_curr[0]; - mpfan_curr_index = loop_to_poly[mlfan_curr_index]; - - BLI_assert(mlfan_curr_index >= 0); - BLI_assert(mpfan_curr_index >= 0); - - mlfan_next = &mloops[mlfan_curr_index]; - mpfan_next = &mpolys[mpfan_curr_index]; - if ((mlfan_curr->v == mlfan_next->v && mlfan_curr->v == mv_pivot_index) || - (mlfan_curr->v != mlfan_next->v && mlfan_curr->v != mv_pivot_index)) - { - /* We need the previous loop, but current one is our vertex's loop. */ - mlfan_vert_index = mlfan_curr_index; - if (--mlfan_curr_index < mpfan_next->loopstart) { - mlfan_curr_index = mpfan_next->loopstart + mpfan_next->totloop - 1; - } - } - else { - /* We need the next loop, which is also our vertex's loop. */ - if (++mlfan_curr_index >= mpfan_next->loopstart + mpfan_next->totloop) { - mlfan_curr_index = mpfan_next->loopstart; - } - mlfan_vert_index = mlfan_curr_index; - } - mlfan_curr = &mloops[mlfan_curr_index]; - /* And now we are back in sync, mlfan_curr_index is the index of mlfan_curr! Pff! */ + /* Find next loop of the smooth fan. */ + loop_manifold_fan_around_vert_next( + mloops, mpolys, loop_to_poly, e2lfan_curr, mv_pivot_index, + &mlfan_curr, &mlfan_curr_index, &mlfan_vert_index, &mpfan_curr_index); e2lfan_curr = edge_to_loops[mlfan_curr->e]; } @@ -955,31 +964,25 @@ static void loop_split_worker_do( } } -static void loop_split_worker(TaskPool * __restrict UNUSED(pool), void *taskdata, int UNUSED(threadid)) +static void loop_split_worker(TaskPool * __restrict pool, void *taskdata, int UNUSED(threadid)) { - LoopSplitTaskDataCommon *common_data = taskdata; - LoopSplitTaskData *data_buff; + LoopSplitTaskDataCommon *common_data = BLI_task_pool_userdata(pool); + LoopSplitTaskData *data = taskdata; /* Temp edge vectors stack, only used when computing lnor spacearr. */ BLI_Stack *edge_vectors = common_data->lnors_spacearr ? BLI_stack_new(sizeof(float[3]), __func__) : NULL; #ifdef DEBUG_TIME - TIMEIT_START(loop_split_worker); + TIMEIT_START_AVERAGED(loop_split_worker); #endif - while ((data_buff = BLI_thread_queue_pop(common_data->task_queue))) { - LoopSplitTaskData *data = data_buff; - int i; - - for (i = 0; i < LOOP_SPLIT_TASK_BLOCK_SIZE; i++, data++) { - /* A NULL ml_curr is used to tag ended data! */ - if (data->ml_curr == NULL) { - break; - } - loop_split_worker_do(common_data, data, edge_vectors); + for (int i = 0; i < LOOP_SPLIT_TASK_BLOCK_SIZE; i++, data++) { + /* A NULL ml_curr is used to tag ended data! */ + if (data->ml_curr == NULL) { + break; } - MEM_freeN(data_buff); + loop_split_worker_do(common_data, data, edge_vectors); } if (edge_vectors) { @@ -987,49 +990,117 @@ static void loop_split_worker(TaskPool * __restrict UNUSED(pool), void *taskdata } #ifdef DEBUG_TIME - TIMEIT_END(loop_split_worker); + TIMEIT_END_AVERAGED(loop_split_worker); #endif } -/* Note we use data_buff to detect whether we are in threaded context or not, in later case it is NULL. */ -static void loop_split_generator_do(LoopSplitTaskDataCommon *common_data, const bool threaded) +/* Check whether gievn loop is part of an unknown-so-far cyclic smooth fan, or not. + * Needed because cyclic smooth fans have no obvious 'entry point', and yet we need to walk them once, and only once. */ +static bool loop_split_generator_check_cyclic_smooth_fan( + const MLoop *mloops, const MPoly *mpolys, + const int (*edge_to_loops)[2], const int *loop_to_poly, const int *e2l_prev, BLI_bitmap *skip_loops, + const MLoop *ml_curr, const MLoop *ml_prev, const int ml_curr_index, const int ml_prev_index, + const int mp_curr_index) +{ + const unsigned int mv_pivot_index = ml_curr->v; /* The vertex we are "fanning" around! */ + const int *e2lfan_curr; + const MLoop *mlfan_curr; + /* mlfan_vert_index: the loop of our current edge might not be the loop of our current vertex! */ + int mlfan_curr_index, mlfan_vert_index, mpfan_curr_index; + + e2lfan_curr = e2l_prev; + if (IS_EDGE_SHARP(e2lfan_curr)) { + /* Sharp loop, so not a cyclic smooth fan... */ + return false; + } + + mlfan_curr = ml_prev; + mlfan_curr_index = ml_prev_index; + mlfan_vert_index = ml_curr_index; + mpfan_curr_index = mp_curr_index; + + BLI_assert(mlfan_curr_index >= 0); + BLI_assert(mlfan_vert_index >= 0); + BLI_assert(mpfan_curr_index >= 0); + + BLI_assert(!BLI_BITMAP_TEST(skip_loops, mlfan_vert_index)); + BLI_BITMAP_ENABLE(skip_loops, mlfan_vert_index); + + while(true) { + /* Find next loop of the smooth fan. */ + loop_manifold_fan_around_vert_next( + mloops, mpolys, loop_to_poly, e2lfan_curr, mv_pivot_index, + &mlfan_curr, &mlfan_curr_index, &mlfan_vert_index, &mpfan_curr_index); + + e2lfan_curr = edge_to_loops[mlfan_curr->e]; + + if (IS_EDGE_SHARP(e2lfan_curr)) { + /* Sharp loop/edge, so not a cyclic smooth fan... */ + return false; + } + /* Smooth loop/edge... */ + else if (BLI_BITMAP_TEST(skip_loops, mlfan_vert_index)) { + if (mlfan_vert_index == ml_curr_index) { + /* We walked around a whole cyclic smooth fan without finding any already-processed loop, means we can + * use initial ml_curr/ml_prev edge as start for this smooth fan. */ + return true; + } + /* ... already checked in some previous looping, we can abort. */ + return false; + } + else { + /* ... we can skip it in future, and keep checking the smooth fan. */ + BLI_BITMAP_ENABLE(skip_loops, mlfan_vert_index); + } + } +} + +static void loop_split_generator(TaskPool *pool, LoopSplitTaskDataCommon *common_data) { MLoopNorSpaceArray *lnors_spacearr = common_data->lnors_spacearr; - BLI_bitmap *sharp_verts = common_data->sharp_verts; float (*loopnors)[3] = common_data->loopnors; const MLoop *mloops = common_data->mloops; const MPoly *mpolys = common_data->mpolys; + const int *loop_to_poly = common_data->loop_to_poly; const int (*edge_to_loops)[2] = common_data->edge_to_loops; + const int numLoops = common_data->numLoops; const int numPolys = common_data->numPolys; const MPoly *mp; int mp_index; - LoopSplitTaskData *data, *data_buff = NULL, data_mem; + const MLoop *ml_curr; + const MLoop *ml_prev; + int ml_curr_index; + int ml_prev_index; + + BLI_bitmap *skip_loops = BLI_BITMAP_NEW(numLoops, __func__); + + LoopSplitTaskData *data_buff = NULL; int data_idx = 0; /* Temp edge vectors stack, only used when computing lnor spacearr (and we are not multi-threading). */ - BLI_Stack *edge_vectors = (lnors_spacearr && !data_buff) ? BLI_stack_new(sizeof(float[3]), __func__) : NULL; + BLI_Stack *edge_vectors = NULL; #ifdef DEBUG_TIME - TIMEIT_START(loop_split_generator); + TIMEIT_START_AVERAGED(loop_split_generator); #endif - if (!threaded) { - memset(&data_mem, 0, sizeof(data_mem)); - data = &data_mem; + if (!pool) { + if (lnors_spacearr) { + edge_vectors = BLI_stack_new(sizeof(float[3]), __func__); + } } /* We now know edges that can be smoothed (with their vector, and their two loops), and edges that will be hard! * Now, time to generate the normals. */ for (mp = mpolys, mp_index = 0; mp_index < numPolys; mp++, mp_index++) { - const MLoop *ml_curr, *ml_prev; float (*lnors)[3]; const int ml_last_index = (mp->loopstart + mp->totloop) - 1; - int ml_curr_index = mp->loopstart; - int ml_prev_index = ml_last_index; + ml_curr_index = mp->loopstart; + ml_prev_index = ml_last_index; ml_curr = &mloops[ml_curr_index]; ml_prev = &mloops[ml_prev_index]; @@ -1039,23 +1110,40 @@ static void loop_split_generator_do(LoopSplitTaskDataCommon *common_data, const const int *e2l_curr = edge_to_loops[ml_curr->e]; const int *e2l_prev = edge_to_loops[ml_prev->e]; - if (!IS_EDGE_SHARP(e2l_curr) && (!lnors_spacearr || BLI_BITMAP_TEST_BOOL(sharp_verts, ml_curr->v))) { - /* A smooth edge, and we are not generating lnor_spacearr, or the related vertex is sharp. - * We skip it because it is either: - * - in the middle of a 'smooth fan' already computed (or that will be as soon as we hit - * one of its ends, i.e. one of its two sharp edges), or... - * - the related vertex is a "full smooth" one, in which case pre-populated normals from vertex - * are just fine (or it has already be handled in a previous loop in case of needed lnors spacearr)! - */ - /* printf("Skipping loop %d / edge %d / vert %d(%d)\n", ml_curr_index, ml_curr->e, ml_curr->v, sharp_verts[ml_curr->v]); */ +// printf("Checking loop %d / edge %u / vert %u (sharp edge: %d, skiploop: %d)...", +// ml_curr_index, ml_curr->e, ml_curr->v, IS_EDGE_SHARP(e2l_curr), BLI_BITMAP_TEST_BOOL(skip_loops, ml_curr_index)); + + /* A smooth edge, we have to check for cyclic smooth fan case. + * If we find a new, never-processed cyclic smooth fan, we can do it now using that loop/edge as + * 'entry point', otherwise we can skip it. */ + /* Note: In theory, we could make loop_split_generator_check_cyclic_smooth_fan() store + * mlfan_vert_index'es and edge indexes in two stacks, to avoid having to fan again around the vert during + * actual computation of clnor & clnorspace. However, this would complicate the code, add more memory usage, + * and despite its logical complexity, loop_manifold_fan_around_vert_next() is quite cheap in term of + * CPU cycles, so really think it's not worth it. */ + if (!IS_EDGE_SHARP(e2l_curr) && + (BLI_BITMAP_TEST(skip_loops, ml_curr_index) || + !loop_split_generator_check_cyclic_smooth_fan( + mloops, mpolys, edge_to_loops, loop_to_poly, e2l_prev, skip_loops, + ml_curr, ml_prev, ml_curr_index, ml_prev_index, mp_index))) + { +// printf("SKIPPING!\n"); } else { - if (threaded) { + LoopSplitTaskData *data, data_local; + +// printf("PROCESSING!\n"); + + if (pool) { if (data_idx == 0) { data_buff = MEM_callocN(sizeof(*data_buff) * LOOP_SPLIT_TASK_BLOCK_SIZE, __func__); } data = &data_buff[data_idx]; } + else { + data = &data_local; + memset(data, 0, sizeof(*data)); + } if (IS_EDGE_SHARP(e2l_curr) && IS_EDGE_SHARP(e2l_prev)) { data->lnor = lnors; @@ -1091,22 +1179,18 @@ static void loop_split_generator_do(LoopSplitTaskDataCommon *common_data, const data->mp_index = mp_index; if (lnors_spacearr) { data->lnor_space = BKE_lnor_space_create(lnors_spacearr); - /* Tag related vertex as sharp, to avoid fanning around it again (in case it was a smooth one). - * This *has* to be done outside of workers tasks! */ - BLI_BITMAP_ENABLE(sharp_verts, ml_curr->v); } } - if (threaded) { + if (pool) { data_idx++; if (data_idx == LOOP_SPLIT_TASK_BLOCK_SIZE) { - BLI_thread_queue_push(common_data->task_queue, data_buff); + BLI_task_pool_push(pool, loop_split_worker, data_buff, true, TASK_PRIORITY_LOW); data_idx = 0; } } else { loop_split_worker_do(common_data, data, edge_vectors); - memset(data, 0, sizeof(data_mem)); } } @@ -1115,44 +1199,32 @@ static void loop_split_generator_do(LoopSplitTaskDataCommon *common_data, const } } - if (threaded) { - /* Last block of data... Since it is calloc'ed and we use first NULL item as stopper, everything is fine. */ - if (LIKELY(data_idx)) { - BLI_thread_queue_push(common_data->task_queue, data_buff); - } - - /* This will signal all other worker threads to wake up and finish! */ - BLI_thread_queue_nowait(common_data->task_queue); + /* Last block of data... Since it is calloc'ed and we use first NULL item as stopper, everything is fine. */ + if (pool && data_idx) { + BLI_task_pool_push(pool, loop_split_worker, data_buff, true, TASK_PRIORITY_LOW); } if (edge_vectors) { BLI_stack_free(edge_vectors); } + MEM_freeN(skip_loops); #ifdef DEBUG_TIME - TIMEIT_END(loop_split_generator); + TIMEIT_END_AVERAGED(loop_split_generator); #endif } -static void loop_split_generator(TaskPool * __restrict UNUSED(pool), void *taskdata, int UNUSED(threadid)) -{ - LoopSplitTaskDataCommon *common_data = taskdata; - - loop_split_generator_do(common_data, true); -} - /** * Compute split normals, i.e. vertex normals associated with each poly (hence 'loop normals'). * Useful to materialize sharp edges (or non-smooth faces) without actually modifying the geometry (splitting edges). */ void BKE_mesh_normals_loop_split( - const MVert *mverts, const int numVerts, MEdge *medges, const int numEdges, + const MVert *mverts, const int UNUSED(numVerts), MEdge *medges, const int numEdges, MLoop *mloops, float (*r_loopnors)[3], const int numLoops, MPoly *mpolys, const float (*polynors)[3], const int numPolys, const bool use_split_normals, float split_angle, MLoopNorSpaceArray *r_lnors_spacearr, short (*clnors_data)[2], int *r_loop_to_poly) { - /* For now this is not supported. If we do not use split normals, we do not generate anything fancy! */ BLI_assert(use_split_normals || !(r_lnors_spacearr)); @@ -1185,8 +1257,6 @@ void BKE_mesh_normals_loop_split( return; } - { - /* Mapping edge -> loops. * If that edge is used by more than two loops (polys), it is always sharp (and tagged as such, see below). * We also use the second loop index as a kind of flag: smooth edge: > 0, @@ -1196,33 +1266,25 @@ void BKE_mesh_normals_loop_split( * store the negated value of loop index instead of INDEX_INVALID to retrieve the real value later in code). * Note also that lose edges always have both values set to 0! */ - int (*edge_to_loops)[2] = MEM_callocN(sizeof(int[2]) * (size_t)numEdges, __func__); + int (*edge_to_loops)[2] = MEM_callocN(sizeof(*edge_to_loops) * (size_t)numEdges, __func__); /* Simple mapping from a loop to its polygon index. */ - int *loop_to_poly = r_loop_to_poly ? r_loop_to_poly : MEM_mallocN(sizeof(int) * (size_t)numLoops, __func__); + int *loop_to_poly = r_loop_to_poly ? r_loop_to_poly : MEM_mallocN(sizeof(*loop_to_poly) * (size_t)numLoops, __func__); MPoly *mp; - int mp_index, me_index; - bool check_angle = (split_angle < (float)M_PI); - int i; + int mp_index; - BLI_bitmap *sharp_verts = NULL; - MLoopNorSpaceArray _lnors_spacearr = {NULL}; + /* When using custom loop normals, disable the angle feature! */ + const bool check_angle = (split_angle < (float)M_PI) && (clnors_data == NULL); - LoopSplitTaskDataCommon common_data = {NULL}; + MLoopNorSpaceArray _lnors_spacearr = {NULL}; #ifdef DEBUG_TIME - TIMEIT_START(BKE_mesh_normals_loop_split); + TIMEIT_START_AVERAGED(BKE_mesh_normals_loop_split); #endif if (check_angle) { - /* When using custom loop normals, disable the angle feature! */ - if (clnors_data) { - check_angle = false; - } - else { - split_angle = cosf(split_angle); - } + split_angle = cosf(split_angle); } if (!r_lnors_spacearr && clnors_data) { @@ -1231,7 +1293,6 @@ void BKE_mesh_normals_loop_split( } if (r_lnors_spacearr) { BKE_lnor_spacearr_init(r_lnors_spacearr, numLoops); - sharp_verts = BLI_BITMAP_NEW((size_t)numVerts, __func__); } /* This first loop check which edges are actually smooth, and compute edge vectors. */ @@ -1285,60 +1346,38 @@ void BKE_mesh_normals_loop_split( } } - if (r_lnors_spacearr) { - /* Tag vertices that have at least one sharp edge as 'sharp' (used for the lnor spacearr computation). - * XXX This third loop over edges is a bit disappointing, could not find any other way yet. - * Not really performance-critical anyway. - */ - for (me_index = 0; me_index < numEdges; me_index++) { - const int *e2l = edge_to_loops[me_index]; - const MEdge *me = &medges[me_index]; - if (IS_EDGE_SHARP(e2l)) { - BLI_BITMAP_ENABLE(sharp_verts, me->v1); - BLI_BITMAP_ENABLE(sharp_verts, me->v2); - } - } - } - /* Init data common to all tasks. */ - common_data.lnors_spacearr = r_lnors_spacearr; - common_data.loopnors = r_loopnors; - common_data.clnors_data = clnors_data; - - common_data.mverts = mverts; - common_data.medges = medges; - common_data.mloops = mloops; - common_data.mpolys = mpolys; - common_data.sharp_verts = sharp_verts; - common_data.edge_to_loops = (const int(*)[2])edge_to_loops; - common_data.loop_to_poly = loop_to_poly; - common_data.polynors = polynors; - common_data.numPolys = numPolys; + LoopSplitTaskDataCommon common_data = { + .lnors_spacearr = r_lnors_spacearr, + .loopnors = r_loopnors, + .clnors_data = clnors_data, + .mverts = mverts, + .medges = medges, + .mloops = mloops, + .mpolys = mpolys, + .edge_to_loops = (const int(*)[2])edge_to_loops, + .loop_to_poly = loop_to_poly, + .polynors = polynors, + .numLoops = numLoops, + .numPolys = numPolys, + }; if (numLoops < LOOP_SPLIT_TASK_BLOCK_SIZE * 8) { /* Not enough loops to be worth the whole threading overhead... */ - loop_split_generator_do(&common_data, false); + loop_split_generator(NULL, &common_data); } else { TaskScheduler *task_scheduler; TaskPool *task_pool; - int nbr_workers; - - common_data.task_queue = BLI_thread_queue_init(); task_scheduler = BLI_task_scheduler_get(); - task_pool = BLI_task_pool_create(task_scheduler, NULL); + task_pool = BLI_task_pool_create(task_scheduler, &common_data); + + loop_split_generator(task_pool, &common_data); - nbr_workers = max_ii(2, BLI_task_scheduler_num_threads(task_scheduler)); - for (i = 1; i < nbr_workers; i++) { - BLI_task_pool_push(task_pool, loop_split_worker, &common_data, false, TASK_PRIORITY_HIGH); - } - BLI_task_pool_push(task_pool, loop_split_generator, &common_data, false, TASK_PRIORITY_HIGH); BLI_task_pool_work_and_wait(task_pool); BLI_task_pool_free(task_pool); - - BLI_thread_queue_free(common_data.task_queue); } MEM_freeN(edge_to_loops); @@ -1347,17 +1386,14 @@ void BKE_mesh_normals_loop_split( } if (r_lnors_spacearr) { - MEM_freeN(sharp_verts); if (r_lnors_spacearr == &_lnors_spacearr) { BKE_lnor_spacearr_free(r_lnors_spacearr); } } #ifdef DEBUG_TIME - TIMEIT_END(BKE_mesh_normals_loop_split); + TIMEIT_END_AVERAGED(BKE_mesh_normals_loop_split); #endif - - } } #undef INDEX_UNSET diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index ff8be5892e9..6e754755cf3 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -2236,66 +2236,6 @@ void BKE_boundbox_minmax(const BoundBox *bb, float obmat[4][4], float r_min[3], } } -/** - * Returns a BBox which each dimensions are at least epsilon. - * \note In case a given dimension needs to be enlarged, its final value will be in [epsilon, 3 * epsilon] range. - * - * \param bb the input bbox to check. - * \param bb_temp the temp bbox to modify (\a bb content is never changed). - * \param epsilon the minimum dimension to ensure. - * \return either bb (if nothing needed to be changed) or bb_temp. - */ -BoundBox *BKE_boundbox_ensure_minimum_dimensions(BoundBox *bb, BoundBox *bb_temp, const float epsilon) -{ - if (fabsf(bb->vec[0][0] - bb->vec[4][0]) < epsilon) { - /* Flat along X axis... */ - *bb_temp = *bb; - bb = bb_temp; - bb->vec[0][0] -= epsilon; - bb->vec[1][0] -= epsilon; - bb->vec[2][0] -= epsilon; - bb->vec[3][0] -= epsilon; - bb->vec[4][0] += epsilon; - bb->vec[5][0] += epsilon; - bb->vec[6][0] += epsilon; - bb->vec[7][0] += epsilon; - } - - if (fabsf(bb->vec[0][1] - bb->vec[3][1]) < epsilon) { - /* Flat along Y axis... */ - if (bb != bb_temp) { - *bb_temp = *bb; - bb = bb_temp; - } - bb->vec[0][1] -= epsilon; - bb->vec[1][1] -= epsilon; - bb->vec[4][1] -= epsilon; - bb->vec[5][1] -= epsilon; - bb->vec[2][1] += epsilon; - bb->vec[3][1] += epsilon; - bb->vec[6][1] += epsilon; - bb->vec[7][1] += epsilon; - } - - if (fabsf(bb->vec[0][2] - bb->vec[1][2]) < epsilon) { - /* Flat along Z axis... */ - if (bb != bb_temp) { - *bb_temp = *bb; - bb = bb_temp; - } - bb->vec[0][2] -= epsilon; - bb->vec[3][2] -= epsilon; - bb->vec[4][2] -= epsilon; - bb->vec[7][2] -= epsilon; - bb->vec[1][2] += epsilon; - bb->vec[2][2] += epsilon; - bb->vec[5][2] += epsilon; - bb->vec[6][2] += epsilon; - } - - return bb; -} - BoundBox *BKE_object_boundbox_get(Object *ob) { BoundBox *bb = NULL; diff --git a/source/blender/blenkernel/intern/object_update.c b/source/blender/blenkernel/intern/object_update.c index 5cb704e4737..a531466294e 100644 --- a/source/blender/blenkernel/intern/object_update.c +++ b/source/blender/blenkernel/intern/object_update.c @@ -54,6 +54,7 @@ #include "BKE_editmesh.h" #include "BKE_object.h" #include "BKE_particle.h" +#include "BKE_pointcache.h" #include "BKE_scene.h" #include "BKE_material.h" #include "BKE_image.h" @@ -347,3 +348,9 @@ void BKE_object_eval_uber_data(EvaluationContext *eval_ctx, ob->recalc &= ~(OB_RECALC_DATA | OB_RECALC_TIME); } + +void BKE_object_eval_cloth(EvaluationContext *UNUSED(eval_ctx), Scene *scene, Object *object) +{ + DEBUG_PRINT("%s on %s\n", __func__, object->id.name); + BKE_ptcache_object_reset(scene, object, PTCACHE_RESET_DEPSGRAPH); +} diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index 1ea27558545..228ae6afaba 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -633,8 +633,9 @@ void psys_render_set(Object *ob, ParticleSystem *psys, float viewmat[4][4], floa data->childcachebufs.last = psys->childcachebufs.last; data->totchildcache = psys->totchildcache; - if (psmd->dm_final) - data->dm = CDDM_copy(psmd->dm_final); + if (psmd->dm_final) { + data->dm = CDDM_copy_with_tessface(psmd->dm_final); + } data->totdmvert = psmd->totdmvert; data->totdmedge = psmd->totdmedge; data->totdmface = psmd->totdmface; diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 56bfe5d7ff1..906fa0134a0 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -1510,8 +1510,6 @@ static void scene_update_object_func(TaskPool * __restrict pool, void *taskdata, if (add_to_stats) { StatisicsEntry *entry; - BLI_assert(threadid < BLI_pool_get_num_threads(pool)); - entry = MEM_mallocN(sizeof(StatisicsEntry), "update thread statistics"); entry->object = object; entry->start_time = start_time; @@ -1631,10 +1629,11 @@ static bool scene_need_update_objects(Main *bmain) static void scene_update_objects(EvaluationContext *eval_ctx, Main *bmain, Scene *scene, Scene *scene_parent) { - TaskScheduler *task_scheduler = BLI_task_scheduler_get(); + TaskScheduler *task_scheduler; TaskPool *task_pool; ThreadedObjectUpdateState state; bool need_singlethread_pass; + bool need_free_scheduler; /* Early check for whether we need to invoke all the task-based * things (spawn new ppol, traverse dependency graph and so on). @@ -1651,6 +1650,15 @@ static void scene_update_objects(EvaluationContext *eval_ctx, Main *bmain, Scene state.scene = scene; state.scene_parent = scene_parent; + if (G.debug & G_DEBUG_DEPSGRAPH_NO_THREADS) { + task_scheduler = BLI_task_scheduler_create(1); + need_free_scheduler = true; + } + else { + task_scheduler = BLI_task_scheduler_get(); + need_free_scheduler = false; + } + /* Those are only needed when blender is run with --debug argument. */ if (G.debug & G_DEBUG_DEPSGRAPH) { const int tot_thread = BLI_task_scheduler_num_threads(task_scheduler); @@ -1665,9 +1673,6 @@ static void scene_update_objects(EvaluationContext *eval_ctx, Main *bmain, Scene #endif task_pool = BLI_task_pool_create(task_scheduler, &state); - if (G.debug & G_DEBUG_DEPSGRAPH_NO_THREADS) { - BLI_pool_set_num_threads(task_pool, 1); - } DAG_threaded_update_begin(scene, scene_update_object_add_task, task_pool); BLI_task_pool_work_and_wait(task_pool); @@ -1700,6 +1705,10 @@ static void scene_update_objects(EvaluationContext *eval_ctx, Main *bmain, Scene if (need_singlethread_pass) { scene_update_all_bases(eval_ctx, scene, scene_parent); } + + if (need_free_scheduler) { + BLI_task_scheduler_free(task_scheduler); + } } static void scene_update_tagged_recursive(EvaluationContext *eval_ctx, Main *bmain, Scene *scene, Scene *scene_parent) diff --git a/source/blender/blenkernel/intern/sequencer.c b/source/blender/blenkernel/intern/sequencer.c index 1d2f5aee440..2de7cd49901 100644 --- a/source/blender/blenkernel/intern/sequencer.c +++ b/source/blender/blenkernel/intern/sequencer.c @@ -480,55 +480,74 @@ void BKE_sequencer_editing_free(Scene *scene) static void sequencer_imbuf_assign_spaces(Scene *scene, ImBuf *ibuf) { - if (ibuf->rect_float) { + if (ibuf->rect != NULL) { + IMB_colormanagement_assign_rect_colorspace(ibuf, scene->sequencer_colorspace_settings.name); + } + if (ibuf->rect_float != NULL) { IMB_colormanagement_assign_float_colorspace(ibuf, scene->sequencer_colorspace_settings.name); } } void BKE_sequencer_imbuf_to_sequencer_space(Scene *scene, ImBuf *ibuf, bool make_float) { - const char *from_colorspace = IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_SCENE_LINEAR); + /* Early output check: if both buffers are NULL we have nothing to convert. */ + if (ibuf->rect_float == NULL && ibuf->rect == NULL) { + return; + } + /* Get common conversion settings. */ const char *to_colorspace = scene->sequencer_colorspace_settings.name; - const char *float_colorspace = IMB_colormanagement_get_float_colorspace(ibuf); - - if (!ibuf->rect_float) { - if (ibuf->rect) { - const char *byte_colorspace = IMB_colormanagement_get_rect_colorspace(ibuf); - if (make_float || !STREQ(to_colorspace, byte_colorspace)) { - /* If byte space is not in sequencer's working space, we deliver float color space, - * this is to to prevent data loss. - */ - - /* when converting byte buffer to float in sequencer we need to make float - * buffer be in sequencer's working space, which is currently only doable - * from linear space. - */ - - /* - * OCIO_TODO: would be nice to support direct single transform from byte to sequencer's - */ - - IMB_float_from_rect(ibuf); - } - else { - return; - } + /* Perform actual conversion logic. */ + if (ibuf->rect_float == NULL) { + /* We are not requested to give float buffer and byte buffer is already + * in thee required colorspace. Can skip doing anything here. + */ + const char *from_colorspace = IMB_colormanagement_get_rect_colorspace(ibuf); + if (!make_float && STREQ(from_colorspace, to_colorspace)) { + return; + } + if (false) { + /* The idea here is to provide as fast playback as possible and + * enforcing float buffer here (a) uses more cache memory (b) might + * make some other effects slower to apply. + * + * However, this might also have negative effect by adding weird + * artifacts which will then not happen in final render. + */ + IMB_colormanagement_transform_byte_threaded( + (unsigned char *)ibuf->rect, ibuf->x, ibuf->y, ibuf->channels, + from_colorspace, to_colorspace); } else { - return; + /* We perform conversion to a float buffer so we don't worry about + * precision loss. + */ + imb_addrectfloatImBuf(ibuf); + IMB_colormanagement_transform_from_byte_threaded( + ibuf->rect_float, (unsigned char *)ibuf->rect, + ibuf->x, ibuf->y, ibuf->channels, + from_colorspace, to_colorspace); + /* We don't need byte buffer anymore. */ + imb_freerectImBuf(ibuf); } } - - if (from_colorspace && from_colorspace[0] != '\0') { - if (ibuf->rect) + else { + const char *from_colorspace = IMB_colormanagement_get_float_colorspace(ibuf); + /* Unknown input color space, can't perform conversion. */ + if (from_colorspace == NULL || from_colorspace[0] == '\0') { + return; + } + /* We don't want both byte and float buffers around: they'll either run + * out of sync or conversion of byte buffer will loose precision in there. + */ + if (ibuf->rect != NULL) { imb_freerectImBuf(ibuf); - - if (!STREQ(float_colorspace, to_colorspace)) { - IMB_colormanagement_transform_threaded(ibuf->rect_float, ibuf->x, ibuf->y, ibuf->channels, - from_colorspace, to_colorspace, true); - sequencer_imbuf_assign_spaces(scene, ibuf); } + IMB_colormanagement_transform_threaded(ibuf->rect_float, + ibuf->x, ibuf->y, ibuf->channels, + from_colorspace, to_colorspace, + true); } + sequencer_imbuf_assign_spaces(scene, ibuf); } void BKE_sequencer_imbuf_from_sequencer_space(Scene *scene, ImBuf *ibuf) diff --git a/source/blender/blenkernel/intern/text.c b/source/blender/blenkernel/intern/text.c index 88575c7d3be..298790a9e9b 100644 --- a/source/blender/blenkernel/intern/text.c +++ b/source/blender/blenkernel/intern/text.c @@ -1935,7 +1935,7 @@ void txt_do_undo(Text *text) int op = text->undo_buf[text->undo_pos]; int prev_flags; unsigned int linep; - unsigned int uchar; + unsigned int uni_char; unsigned int curln, selln; unsigned short curc, selc; unsigned short charp; @@ -1971,14 +1971,14 @@ void txt_do_undo(Text *text) case UNDO_BS_3: case UNDO_BS_4: charp = op - UNDO_BS_1 + 1; - uchar = txt_undo_read_unicode(text->undo_buf, &text->undo_pos, charp); + uni_char = txt_undo_read_unicode(text->undo_buf, &text->undo_pos, charp); /* get and restore the cursors */ txt_undo_read_cur(text->undo_buf, &text->undo_pos, &curln, &curc); txt_move_to(text, curln, curc, 0); txt_move_to(text, curln, curc, 1); - txt_add_char(text, uchar); + txt_add_char(text, uni_char); text->undo_pos--; break; @@ -1988,14 +1988,14 @@ void txt_do_undo(Text *text) case UNDO_DEL_3: case UNDO_DEL_4: charp = op - UNDO_DEL_1 + 1; - uchar = txt_undo_read_unicode(text->undo_buf, &text->undo_pos, charp); + uni_char = txt_undo_read_unicode(text->undo_buf, &text->undo_pos, charp); /* get and restore the cursors */ txt_undo_read_cur(text->undo_buf, &text->undo_pos, &curln, &curc); txt_move_to(text, curln, curc, 0); txt_move_to(text, curln, curc, 1); - txt_add_char(text, uchar); + txt_add_char(text, uni_char); txt_move_left(text, 0); @@ -2163,7 +2163,7 @@ void txt_do_redo(Text *text) char *buf; unsigned int linep; unsigned short charp; - unsigned int uchar; + unsigned int uni_uchar; unsigned int curln, selln; unsigned short curc, selc; @@ -2190,9 +2190,9 @@ void txt_do_redo(Text *text) txt_move_to(text, curln, curc, 1); charp = op - UNDO_INSERT_1 + 1; - uchar = txt_redo_read_unicode(text->undo_buf, &text->undo_pos, charp); + uni_uchar = txt_redo_read_unicode(text->undo_buf, &text->undo_pos, charp); - txt_add_char(text, uchar); + txt_add_char(text, uni_uchar); break; case UNDO_BS_1: diff --git a/source/blender/blenkernel/intern/texture.c b/source/blender/blenkernel/intern/texture.c index 2d3ecad19ad..60990c03b0e 100644 --- a/source/blender/blenkernel/intern/texture.c +++ b/source/blender/blenkernel/intern/texture.c @@ -1485,9 +1485,11 @@ bool BKE_texture_dependsOnTime(const struct Tex *texture) /* ------------------------------------------------------------------------- */ -void BKE_texture_get_value( +void BKE_texture_get_value_ex( const Scene *scene, Tex *texture, - float *tex_co, TexResult *texres, bool use_color_management) + float *tex_co, TexResult *texres, + struct ImagePool *pool, + bool use_color_management) { int result_type; bool do_color_manage = false; @@ -1497,7 +1499,7 @@ void BKE_texture_get_value( } /* no node textures for now */ - result_type = multitex_ext_safe(texture, tex_co, texres, NULL, do_color_manage, false); + result_type = multitex_ext_safe(texture, tex_co, texres, pool, do_color_manage, false); /* if the texture gave an RGB value, we assume it didn't give a valid * intensity, since this is in the context of modifiers don't use perceptual color conversion. @@ -1510,3 +1512,10 @@ void BKE_texture_get_value( copy_v3_fl(&texres->tr, texres->tin); } } + +void BKE_texture_get_value( + const Scene *scene, Tex *texture, + float *tex_co, TexResult *texres, bool use_color_management) +{ + BKE_texture_get_value_ex(scene, texture, tex_co, texres, NULL, use_color_management); +} diff --git a/source/blender/blenkernel/intern/tracking_stabilize.c b/source/blender/blenkernel/intern/tracking_stabilize.c index 36b24fbb2dc..3dfaa1ed77d 100644 --- a/source/blender/blenkernel/intern/tracking_stabilize.c +++ b/source/blender/blenkernel/intern/tracking_stabilize.c @@ -755,7 +755,8 @@ static void average_marker_positions(StabContext *ctx, int framenr, float r_ref_ if (ok) { r_ref_pos[0] /= weight_sum; r_ref_pos[1] /= weight_sum; - } else { + } + else { /* No usable tracking data on any track on this frame. * Use data from neighbouring frames to extrapolate... */ @@ -784,7 +785,8 @@ static void average_marker_positions(StabContext *ctx, int framenr, float r_ref_ * Also default to this frame when we're in a gap */ average_marker_positions(ctx, next_lower, r_ref_pos); - } else if (next_higher < MAXFRAME) { + } + else if (next_higher < MAXFRAME) { average_marker_positions(ctx, next_higher, r_ref_pos); } use_values_from_fcurves(ctx, false); @@ -1167,7 +1169,8 @@ static void stabilization_calculate_data(StabContext *ctx, if (ctx->stab->flag & TRACKING_STABILIZE_SCALE) { *r_scale = expf(scale_step * scaleinf); /* Averaged in log scale */ - } else { + } + else { *r_scale = 1.0f; } @@ -1180,8 +1183,8 @@ static void stabilization_calculate_data(StabContext *ctx, */ get_animated_target_pos(ctx, framenr, target_pos); sub_v2_v2(r_translation, target_pos); - *r_angle -= get_animated_target_rot(ctx,framenr); - target_scale = get_animated_target_scale(ctx,framenr); + *r_angle -= get_animated_target_rot(ctx, framenr); + target_scale = get_animated_target_scale(ctx, framenr); if (target_scale != 0.0f) { *r_scale /= target_scale; /* target_scale is an expected/intended reference zoom value */ diff --git a/source/blender/blenkernel/intern/writeffmpeg.c b/source/blender/blenkernel/intern/writeffmpeg.c index 9994d479ce7..156b74f5c3d 100644 --- a/source/blender/blenkernel/intern/writeffmpeg.c +++ b/source/blender/blenkernel/intern/writeffmpeg.c @@ -568,7 +568,8 @@ static AVStream *alloc_video_stream(FFMpegContext *context, RenderData *rd, int if (context->ffmpeg_crf >= 0) { ffmpeg_dict_set_int(&opts, "crf", context->ffmpeg_crf); - } else { + } + else { c->bit_rate = context->ffmpeg_video_bitrate * 1000; c->rc_max_rate = rd->ffcodecdata.rc_max_rate * 1000; c->rc_min_rate = rd->ffcodecdata.rc_min_rate * 1000; @@ -576,8 +577,8 @@ static AVStream *alloc_video_stream(FFMpegContext *context, RenderData *rd, int } if (context->ffmpeg_preset) { - char const * preset_name; - switch(context->ffmpeg_preset) { + char const *preset_name; + switch (context->ffmpeg_preset) { case FFM_PRESET_ULTRAFAST: preset_name = "ultrafast"; break; case FFM_PRESET_SUPERFAST: preset_name = "superfast"; break; case FFM_PRESET_VERYFAST: preset_name = "veryfast"; break; |