diff options
Diffstat (limited to 'source/blender/blenloader/intern/writefile.c')
-rw-r--r-- | source/blender/blenloader/intern/writefile.c | 833 |
1 files changed, 562 insertions, 271 deletions
diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 599e592c77d..73bab07a008 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -133,6 +133,7 @@ #include "DNA_sdna_types.h" #include "DNA_sequence_types.h" #include "DNA_shader_fx_types.h" +#include "DNA_simulation_types.h" #include "DNA_sound_types.h" #include "DNA_space_types.h" #include "DNA_speaker_types.h" @@ -156,14 +157,17 @@ #include "BKE_constraint.h" #include "BKE_curve.h" #include "BKE_fcurve.h" +#include "BKE_fcurve_driver.h" #include "BKE_global.h" // for G #include "BKE_gpencil_modifier.h" #include "BKE_idtype.h" #include "BKE_layer.h" +#include "BKE_lib_id.h" #include "BKE_lib_override.h" #include "BKE_main.h" #include "BKE_modifier.h" #include "BKE_node.h" +#include "BKE_object.h" #include "BKE_pointcache.h" #include "BKE_report.h" #include "BKE_sequencer.h" @@ -173,6 +177,7 @@ #include "BLO_blend_defs.h" #include "BLO_blend_validate.h" +#include "BLO_read_write.h" #include "BLO_readfile.h" #include "BLO_undofile.h" #include "BLO_writefile.h" @@ -323,12 +328,7 @@ typedef struct { bool error; /** #MemFile writing (used for undo). */ - struct { - MemFile *current; - MemFile *compare; - /** Use to de-duplicate chunks when writing. */ - MemFileChunk *compare_chunk; - } mem; + MemFileWriteData mem; /** When true, write to #WriteData.current, could also call 'is_undo'. */ bool use_memfile; @@ -340,6 +340,10 @@ typedef struct { WriteWrap *ww; } WriteData; +typedef struct BlendWriter { + WriteData *wd; +} BlendWriter; + static WriteData *writedata_new(WriteWrap *ww) { WriteData *wd = MEM_callocN(sizeof(*wd), "writedata"); @@ -367,7 +371,7 @@ static void writedata_do_write(WriteData *wd, const void *mem, int memlen) /* memory based save */ if (wd->use_memfile) { - memfile_chunk_add(wd->mem.current, mem, memlen, &wd->mem.compare_chunk); + BLO_memfile_chunk_add(&wd->mem, mem, memlen); } else { if (wd->ww->write(wd->ww, mem, memlen) != memlen) { @@ -468,9 +472,7 @@ static WriteData *mywrite_begin(WriteWrap *ww, MemFile *compare, MemFile *curren WriteData *wd = writedata_new(ww); if (current != NULL) { - wd->mem.current = current; - wd->mem.compare = compare; - wd->mem.compare_chunk = compare ? compare->chunks.first : NULL; + BLO_memfile_write_init(&wd->mem, current, compare); wd->use_memfile = true; } @@ -490,12 +492,58 @@ static bool mywrite_end(WriteData *wd) wd->buf_used_len = 0; } + if (wd->use_memfile) { + BLO_memfile_write_finalize(&wd->mem); + } + const bool err = wd->error; writedata_free(wd); return err; } +/** + * Start writing of data related to a single ID. + * + * Only does something when storing an undo step. + */ +static void mywrite_id_begin(WriteData *wd, ID *id) +{ + if (wd->use_memfile) { + wd->mem.current_id_session_uuid = id->session_uuid; + + /* If current next memchunk does not match the ID we are about to write, try to find the + * correct memchunk in the mapping using ID's session_uuid. */ + if (wd->mem.id_session_uuid_mapping != NULL && + (wd->mem.reference_current_chunk == NULL || + wd->mem.reference_current_chunk->id_session_uuid != id->session_uuid)) { + void *ref = BLI_ghash_lookup(wd->mem.id_session_uuid_mapping, + POINTER_FROM_UINT(id->session_uuid)); + if (ref != NULL) { + wd->mem.reference_current_chunk = ref; + } + /* Else, no existing memchunk found, i.e. this is supposed to be a new ID. */ + } + /* Otherwise, we try with the current memchunk in any case, whether it is matching current + * ID's session_uuid or not. */ + } +} + +/** + * Start writing of data related to a single ID. + * + * Only does something when storing an undo step. + */ +static void mywrite_id_end(WriteData *wd, ID *UNUSED(id)) +{ + if (wd->use_memfile) { + /* Very important to do it after every ID write now, otherwise we cannot know whether a + * specific ID changed or not. */ + mywrite_flush(wd); + wd->mem.current_id_session_uuid = MAIN_ID_SESSION_UUID_UNSET; + } +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -631,79 +679,86 @@ static void writelist_id(WriteData *wd, int filecode, const char *structname, co * These functions are used by blender's .blend system for file saving/loading. * \{ */ -void IDP_WriteProperty_OnlyData(const IDProperty *prop, void *wd); -void IDP_WriteProperty(const IDProperty *prop, void *wd); +void IDP_WriteProperty_OnlyData(const IDProperty *prop, BlendWriter *writer); +void IDP_WriteProperty(const IDProperty *prop, WriteData *wd); +void IDP_WriteProperty_new_api(const IDProperty *prop, BlendWriter *writer); -static void IDP_WriteArray(const IDProperty *prop, void *wd) +static void IDP_WriteArray(const IDProperty *prop, BlendWriter *writer) { /*REMEMBER to set totalen to len in the linking code!!*/ if (prop->data.pointer) { - writedata(wd, DATA, MEM_allocN_len(prop->data.pointer), prop->data.pointer); + BLO_write_raw(writer, MEM_allocN_len(prop->data.pointer), prop->data.pointer); if (prop->subtype == IDP_GROUP) { IDProperty **array = prop->data.pointer; int a; for (a = 0; a < prop->len; a++) { - IDP_WriteProperty(array[a], wd); + IDP_WriteProperty_new_api(array[a], writer); } } } } -static void IDP_WriteIDPArray(const IDProperty *prop, void *wd) +static void IDP_WriteIDPArray(const IDProperty *prop, BlendWriter *writer) { /*REMEMBER to set totalen to len in the linking code!!*/ if (prop->data.pointer) { const IDProperty *array = prop->data.pointer; int a; - writestruct(wd, DATA, IDProperty, prop->len, array); + BLO_write_struct_array(writer, IDProperty, prop->len, array); for (a = 0; a < prop->len; a++) { - IDP_WriteProperty_OnlyData(&array[a], wd); + IDP_WriteProperty_OnlyData(&array[a], writer); } } } -static void IDP_WriteString(const IDProperty *prop, void *wd) +static void IDP_WriteString(const IDProperty *prop, BlendWriter *writer) { /*REMEMBER to set totalen to len in the linking code!!*/ - writedata(wd, DATA, prop->len, prop->data.pointer); + BLO_write_raw(writer, prop->len, prop->data.pointer); } -static void IDP_WriteGroup(const IDProperty *prop, void *wd) +static void IDP_WriteGroup(const IDProperty *prop, BlendWriter *writer) { IDProperty *loop; for (loop = prop->data.group.first; loop; loop = loop->next) { - IDP_WriteProperty(loop, wd); + IDP_WriteProperty_new_api(loop, writer); } } /* Functions to read/write ID Properties */ -void IDP_WriteProperty_OnlyData(const IDProperty *prop, void *wd) +void IDP_WriteProperty_OnlyData(const IDProperty *prop, BlendWriter *writer) { switch (prop->type) { case IDP_GROUP: - IDP_WriteGroup(prop, wd); + IDP_WriteGroup(prop, writer); break; case IDP_STRING: - IDP_WriteString(prop, wd); + IDP_WriteString(prop, writer); break; case IDP_ARRAY: - IDP_WriteArray(prop, wd); + IDP_WriteArray(prop, writer); break; case IDP_IDPARRAY: - IDP_WriteIDPArray(prop, wd); + IDP_WriteIDPArray(prop, writer); break; } } -void IDP_WriteProperty(const IDProperty *prop, void *wd) +void IDP_WriteProperty_new_api(const IDProperty *prop, BlendWriter *writer) { - writestruct(wd, DATA, IDProperty, 1, prop); - IDP_WriteProperty_OnlyData(prop, wd); + BLO_write_struct(writer, IDProperty, prop); + IDP_WriteProperty_OnlyData(prop, writer); +} + +void IDP_WriteProperty(const IDProperty *prop, WriteData *wd) +{ + BlendWriter writer = {wd}; + IDP_WriteProperty_new_api(prop, &writer); } static void write_iddata(WriteData *wd, ID *id) @@ -717,13 +772,11 @@ static void write_iddata(WriteData *wd, ID *id) writestruct(wd, DATA, IDOverrideLibrary, 1, id->override_library); writelist(wd, DATA, IDOverrideLibraryProperty, &id->override_library->properties); - for (IDOverrideLibraryProperty *op = id->override_library->properties.first; op; - op = op->next) { + LISTBASE_FOREACH (IDOverrideLibraryProperty *, op, &id->override_library->properties) { writedata(wd, DATA, strlen(op->rna_path) + 1, op->rna_path); writelist(wd, DATA, IDOverrideLibraryPropertyOperation, &op->operations); - for (IDOverrideLibraryPropertyOperation *opop = op->operations.first; opop; - opop = opop->next) { + LISTBASE_FOREACH (IDOverrideLibraryPropertyOperation *, opop, &op->operations) { if (opop->subitem_reference_name) { writedata( wd, DATA, strlen(opop->subitem_reference_name) + 1, opop->subitem_reference_name); @@ -734,11 +787,6 @@ static void write_iddata(WriteData *wd, ID *id) } } } - - /* Clear the accumulated recalc flags in case of undo step saving. */ - if (wd->use_memfile) { - id->recalc_undo_accumulated = 0; - } } static void write_previews(WriteData *wd, const PreviewImage *prv_orig) @@ -859,19 +907,19 @@ static void write_fcurves(WriteData *wd, ListBase *fcurves) } } -static void write_action(WriteData *wd, bAction *act) +static void write_action(WriteData *wd, bAction *act, const void *id_address) { if (act->id.us > 0 || wd->use_memfile) { - writestruct(wd, ID_AC, bAction, 1, act); + writestruct_at_address(wd, ID_AC, bAction, 1, id_address, act); write_iddata(wd, &act->id); write_fcurves(wd, &act->curves); - for (bActionGroup *grp = act->groups.first; grp; grp = grp->next) { + LISTBASE_FOREACH (bActionGroup *, grp, &act->groups) { writestruct(wd, DATA, bActionGroup, 1, grp); } - for (TimeMarker *marker = act->markers.first; marker; marker = marker->next) { + LISTBASE_FOREACH (TimeMarker *, marker, &act->markers) { writestruct(wd, DATA, TimeMarker, 1, marker); } } @@ -996,9 +1044,19 @@ static void write_node_socket_default_value(WriteData *wd, bNodeSocket *sock) case SOCK_STRING: writestruct(wd, DATA, bNodeSocketValueString, 1, sock->default_value); break; + case SOCK_OBJECT: + writestruct(wd, DATA, bNodeSocketValueObject, 1, sock->default_value); + break; + case SOCK_IMAGE: + writestruct(wd, DATA, bNodeSocketValueImage, 1, sock->default_value); + break; case __SOCK_MESH: case SOCK_CUSTOM: case SOCK_SHADER: + case SOCK_EMITTERS: + case SOCK_EVENTS: + case SOCK_FORCES: + case SOCK_CONTROL_FLOW: BLI_assert(false); break; } @@ -1139,11 +1197,6 @@ static void write_nodetree_nolib(WriteData *wd, bNodeTree *ntree) for (sock = ntree->outputs.first; sock; sock = sock->next) { write_node_socket_interface(wd, sock); } - - /* Clear the accumulated recalc flags in case of undo step saving. */ - if (wd->use_memfile) { - ntree->id.recalc_undo_accumulated = 0; - } } /** @@ -1235,14 +1288,14 @@ static void write_userdef(WriteData *wd, const UserDef *userdef) { writestruct(wd, USER, UserDef, 1, userdef); - for (const bTheme *btheme = userdef->themes.first; btheme; btheme = btheme->next) { + LISTBASE_FOREACH (const bTheme *, btheme, &userdef->themes) { writestruct(wd, DATA, bTheme, 1, btheme); } - for (const wmKeyMap *keymap = userdef->user_keymaps.first; keymap; keymap = keymap->next) { + LISTBASE_FOREACH (const wmKeyMap *, keymap, &userdef->user_keymaps) { writestruct(wd, DATA, wmKeyMap, 1, keymap); - for (const wmKeyMapDiffItem *kmdi = keymap->diff_items.first; kmdi; kmdi = kmdi->next) { + LISTBASE_FOREACH (const wmKeyMapDiffItem *, kmdi, &keymap->diff_items) { writestruct(wd, DATA, wmKeyMapDiffItem, 1, kmdi); if (kmdi->remove_item) { write_keymapitem(wd, kmdi->remove_item); @@ -1252,21 +1305,21 @@ static void write_userdef(WriteData *wd, const UserDef *userdef) } } - for (const wmKeyMapItem *kmi = keymap->items.first; kmi; kmi = kmi->next) { + LISTBASE_FOREACH (const wmKeyMapItem *, kmi, &keymap->items) { write_keymapitem(wd, kmi); } } - for (const wmKeyConfigPref *kpt = userdef->user_keyconfig_prefs.first; kpt; kpt = kpt->next) { + LISTBASE_FOREACH (const wmKeyConfigPref *, kpt, &userdef->user_keyconfig_prefs) { writestruct(wd, DATA, wmKeyConfigPref, 1, kpt); if (kpt->prop) { IDP_WriteProperty(kpt->prop, wd); } } - for (const bUserMenu *um = userdef->user_menus.first; um; um = um->next) { + LISTBASE_FOREACH (const bUserMenu *, um, &userdef->user_menus) { writestruct(wd, DATA, bUserMenu, 1, um); - for (const bUserMenuItem *umi = um->items.first; umi; umi = umi->next) { + LISTBASE_FOREACH (const bUserMenuItem *, umi, &um->items) { if (umi->type == USER_MENU_TYPE_OPERATOR) { const bUserMenuItem_Op *umi_op = (const bUserMenuItem_Op *)umi; writestruct(wd, DATA, bUserMenuItem_Op, 1, umi_op); @@ -1288,19 +1341,18 @@ static void write_userdef(WriteData *wd, const UserDef *userdef) } } - for (const bAddon *bext = userdef->addons.first; bext; bext = bext->next) { + LISTBASE_FOREACH (const bAddon *, bext, &userdef->addons) { writestruct(wd, DATA, bAddon, 1, bext); if (bext->prop) { IDP_WriteProperty(bext->prop, wd); } } - for (const bPathCompare *path_cmp = userdef->autoexec_paths.first; path_cmp; - path_cmp = path_cmp->next) { + LISTBASE_FOREACH (const bPathCompare *, path_cmp, &userdef->autoexec_paths) { writestruct(wd, DATA, bPathCompare, 1, path_cmp); } - for (const uiStyle *style = userdef->uistyles.first; style; style = style->next) { + LISTBASE_FOREACH (const uiStyle *, style, &userdef->uistyles) { writestruct(wd, DATA, uiStyle, 1, style); } } @@ -1396,11 +1448,11 @@ static void write_pointcaches(WriteData *wd, ListBase *ptcaches) } } -static void write_particlesettings(WriteData *wd, ParticleSettings *part) +static void write_particlesettings(WriteData *wd, ParticleSettings *part, const void *id_address) { if (part->id.us > 0 || wd->use_memfile) { /* write LibData */ - writestruct(wd, ID_PA, ParticleSettings, 1, part); + writestruct_at_address(wd, ID_PA, ParticleSettings, 1, id_address, part); write_iddata(wd, &part->id); if (part->adt) { @@ -1420,7 +1472,7 @@ static void write_particlesettings(WriteData *wd, ParticleSettings *part) write_curvemapping(wd, part->twistcurve); } - for (ParticleDupliWeight *dw = part->instance_weights.first; dw; dw = dw->next) { + LISTBASE_FOREACH (ParticleDupliWeight *, dw, &part->instance_weights) { /* update indices, but only if dw->ob is set (can be NULL after loading e.g.) */ if (dw->ob != NULL) { dw->index = 0; @@ -1440,7 +1492,7 @@ static void write_particlesettings(WriteData *wd, ParticleSettings *part) if (part->boids && part->phystype == PART_PHYS_BOIDS) { writestruct(wd, DATA, BoidSettings, 1, part->boids); - for (BoidState *state = part->boids->states.first; state; state = state->next) { + LISTBASE_FOREACH (BoidState *, state, &part->boids->states) { write_boid_state(wd, state); } } @@ -1625,14 +1677,14 @@ static void write_pose(WriteData *wd, bPose *pose) static void write_defgroups(WriteData *wd, ListBase *defbase) { - for (bDeformGroup *defgroup = defbase->first; defgroup; defgroup = defgroup->next) { + LISTBASE_FOREACH (bDeformGroup *, defgroup, defbase) { writestruct(wd, DATA, bDeformGroup, 1, defgroup); } } static void write_fmaps(WriteData *wd, ListBase *fbase) { - for (bFaceMap *fmap = fbase->first; fmap; fmap = fmap->next) { + LISTBASE_FOREACH (bFaceMap *, fmap, fbase) { writestruct(wd, DATA, bFaceMap, 1, fmap); } } @@ -1646,7 +1698,7 @@ static void write_modifiers(WriteData *wd, ListBase *modbase) } for (md = modbase->first; md; md = md->next) { - const ModifierTypeInfo *mti = modifierType_getInfo(md->type); + const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type); if (mti == NULL) { return; } @@ -1829,7 +1881,7 @@ static void write_gpencil_modifiers(WriteData *wd, ListBase *modbase) } for (md = modbase->first; md; md = md->next) { - const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(md->type); + const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(md->type); if (mti == NULL) { return; } @@ -1896,7 +1948,7 @@ static void write_shaderfxs(WriteData *wd, ListBase *fxbase) } for (fx = fxbase->first; fx; fx = fx->next) { - const ShaderFxTypeInfo *fxi = BKE_shaderfxType_getInfo(fx->type); + const ShaderFxTypeInfo *fxi = BKE_shaderfx_get_info(fx->type); if (fxi == NULL) { return; } @@ -1905,11 +1957,14 @@ static void write_shaderfxs(WriteData *wd, ListBase *fxbase) } } -static void write_object(WriteData *wd, Object *ob) +static void write_object(WriteData *wd, Object *ob, const void *id_address) { if (ob->id.us > 0 || wd->use_memfile) { + /* Clean up, important in undo case to reduce false detection of changed datablocks. */ + BKE_object_runtime_reset(ob); + /* write LibData */ - writestruct(wd, ID_OB, Object, 1, ob); + writestruct_at_address(wd, ID_OB, Object, 1, id_address, ob); write_iddata(wd, &ob->id); if (ob->adt) { @@ -1970,11 +2025,15 @@ static void write_object(WriteData *wd, Object *ob) } } -static void write_vfont(WriteData *wd, VFont *vf) +static void write_vfont(WriteData *wd, VFont *vf, const void *id_address) { if (vf->id.us > 0 || wd->use_memfile) { + /* Clean up, important in undo case to reduce false detection of changed datablocks. */ + vf->data = NULL; + vf->temp_pf = NULL; + /* write LibData */ - writestruct(wd, ID_VF, VFont, 1, vf); + writestruct_at_address(wd, ID_VF, VFont, 1, id_address, vf); write_iddata(wd, &vf->id); /* direct data */ @@ -1986,11 +2045,11 @@ static void write_vfont(WriteData *wd, VFont *vf) } } -static void write_key(WriteData *wd, Key *key) +static void write_key(WriteData *wd, Key *key, const void *id_address) { if (key->id.us > 0 || wd->use_memfile) { /* write LibData */ - writestruct(wd, ID_KE, Key, 1, key); + writestruct_at_address(wd, ID_KE, Key, 1, id_address, key); write_iddata(wd, &key->id); if (key->adt) { @@ -1998,7 +2057,7 @@ static void write_key(WriteData *wd, Key *key) } /* direct data */ - for (KeyBlock *kb = key->block.first; kb; kb = kb->next) { + LISTBASE_FOREACH (KeyBlock *, kb, &key->block) { writestruct(wd, DATA, KeyBlock, 1, kb); if (kb->data) { writedata(wd, DATA, kb->totelem * key->elemsize, kb->data); @@ -2007,28 +2066,36 @@ static void write_key(WriteData *wd, Key *key) } } -static void write_camera(WriteData *wd, Camera *cam) +static void write_camera(WriteData *wd, Camera *cam, const void *id_address) { if (cam->id.us > 0 || wd->use_memfile) { /* write LibData */ - writestruct(wd, ID_CA, Camera, 1, cam); + writestruct_at_address(wd, ID_CA, Camera, 1, id_address, cam); write_iddata(wd, &cam->id); if (cam->adt) { write_animdata(wd, cam->adt); } - for (CameraBGImage *bgpic = cam->bg_images.first; bgpic; bgpic = bgpic->next) { + LISTBASE_FOREACH (CameraBGImage *, bgpic, &cam->bg_images) { writestruct(wd, DATA, CameraBGImage, 1, bgpic); } } } -static void write_mball(WriteData *wd, MetaBall *mb) +static void write_mball(WriteData *wd, MetaBall *mb, const void *id_address) { if (mb->id.us > 0 || wd->use_memfile) { + /* Clean up, important in undo case to reduce false detection of changed datablocks. */ + BLI_listbase_clear(&mb->disp); + mb->editelems = NULL; + /* Must always be cleared (meta's don't have their own edit-data). */ + mb->needs_flush_to_id = 0; + mb->lastelem = NULL; + mb->batch_cache = NULL; + /* write LibData */ - writestruct(wd, ID_MB, MetaBall, 1, mb); + writestruct_at_address(wd, ID_MB, MetaBall, 1, id_address, mb); write_iddata(wd, &mb->id); /* direct data */ @@ -2037,17 +2104,22 @@ static void write_mball(WriteData *wd, MetaBall *mb) write_animdata(wd, mb->adt); } - for (MetaElem *ml = mb->elems.first; ml; ml = ml->next) { + LISTBASE_FOREACH (MetaElem *, ml, &mb->elems) { writestruct(wd, DATA, MetaElem, 1, ml); } } } -static void write_curve(WriteData *wd, Curve *cu) +static void write_curve(WriteData *wd, Curve *cu, const void *id_address) { if (cu->id.us > 0 || wd->use_memfile) { + /* Clean up, important in undo case to reduce false detection of changed datablocks. */ + cu->editnurb = NULL; + cu->editfont = NULL; + cu->batch_cache = NULL; + /* write LibData */ - writestruct(wd, ID_CU, Curve, 1, cu); + writestruct_at_address(wd, ID_CU, Curve, 1, id_address, cu); write_iddata(wd, &cu->id); /* direct data */ @@ -2063,10 +2135,10 @@ static void write_curve(WriteData *wd, Curve *cu) } else { /* is also the order of reading */ - for (Nurb *nu = cu->nurb.first; nu; nu = nu->next) { + LISTBASE_FOREACH (Nurb *, nu, &cu->nurb) { writestruct(wd, DATA, Nurb, 1, nu); } - for (Nurb *nu = cu->nurb.first; nu; nu = nu->next) { + LISTBASE_FOREACH (Nurb *, nu, &cu->nurb) { if (nu->type == CU_BEZIER) { writestruct(wd, DATA, BezTriple, nu->pntsu, nu->bezt); } @@ -2186,7 +2258,7 @@ static void write_customdata(WriteData *wd, datasize = structnum * count; writestruct_id(wd, DATA, structname, datasize, layer->data); } - else { + else if (!wd->use_memfile) { /* Do not warn on undo. */ printf("%s error: layer '%s':%d - can't be written to file\n", __func__, structname, @@ -2200,19 +2272,14 @@ static void write_customdata(WriteData *wd, } } -static void write_mesh(WriteData *wd, Mesh *mesh) +static void write_mesh(WriteData *wd, Mesh *mesh, const void *id_address) { if (mesh->id.us > 0 || wd->use_memfile) { - /* Write a copy of the mesh with possibly reduced number of data layers. - * Don't edit the original since other threads might be reading it. */ - Mesh *old_mesh = mesh; - Mesh copy_mesh = *mesh; - mesh = ©_mesh; - /* cache only - don't write */ mesh->mface = NULL; mesh->totface = 0; memset(&mesh->fdata, 0, sizeof(mesh->fdata)); + memset(&mesh->runtime, 0, sizeof(mesh->runtime)); /* Reduce xdata layers, fill xlayers with layers to be written. * This makes xdata invalid for Blender, which is why we made a @@ -2229,7 +2296,7 @@ static void write_mesh(WriteData *wd, Mesh *mesh) CustomData_file_write_prepare(&mesh->ldata, &llayers, llayers_buff, ARRAY_SIZE(llayers_buff)); CustomData_file_write_prepare(&mesh->pdata, &players, players_buff, ARRAY_SIZE(players_buff)); - writestruct_at_address(wd, ID_ME, Mesh, 1, old_mesh, mesh); + writestruct_at_address(wd, ID_ME, Mesh, 1, id_address, mesh); write_iddata(wd, &mesh->id); /* direct data */ @@ -2247,9 +2314,6 @@ static void write_mesh(WriteData *wd, Mesh *mesh) write_customdata(wd, &mesh->id, mesh->totloop, &mesh->ldata, llayers, CD_MASK_MESH.lmask); write_customdata(wd, &mesh->id, mesh->totpoly, &mesh->pdata, players, CD_MASK_MESH.pmask); - /* restore pointer */ - mesh = old_mesh; - /* free temporary data */ if (vlayers && vlayers != vlayers_buff) { MEM_freeN(vlayers); @@ -2269,11 +2333,15 @@ static void write_mesh(WriteData *wd, Mesh *mesh) } } -static void write_lattice(WriteData *wd, Lattice *lt) +static void write_lattice(WriteData *wd, Lattice *lt, const void *id_address) { if (lt->id.us > 0 || wd->use_memfile) { + /* Clean up, important in undo case to reduce false detection of changed datablocks. */ + lt->editlatt = NULL; + lt->batch_cache = NULL; + /* write LibData */ - writestruct(wd, ID_LT, Lattice, 1, lt); + writestruct_at_address(wd, ID_LT, Lattice, 1, id_address, lt); write_iddata(wd, <->id); /* write animdata */ @@ -2288,7 +2356,7 @@ static void write_lattice(WriteData *wd, Lattice *lt) } } -static void write_image(WriteData *wd, Image *ima) +static void write_image(WriteData *wd, Image *ima, const void *id_address) { if (ima->id.us > 0 || wd->use_memfile) { ImagePackedFile *imapf; @@ -2301,7 +2369,7 @@ static void write_image(WriteData *wd, Image *ima) } /* write LibData */ - writestruct(wd, ID_IM, Image, 1, ima); + writestruct_at_address(wd, ID_IM, Image, 1, id_address, ima); write_iddata(wd, &ima->id); for (imapf = ima->packedfiles.first; imapf; imapf = imapf->next) { @@ -2315,7 +2383,7 @@ static void write_image(WriteData *wd, Image *ima) write_previews(wd, ima->preview); - for (ImageView *iv = ima->views.first; iv; iv = iv->next) { + LISTBASE_FOREACH (ImageView *, iv, &ima->views) { writestruct(wd, DATA, ImageView, 1, iv); } writestruct(wd, DATA, Stereo3dFormat, 1, ima->stereo3d_format); @@ -2328,11 +2396,11 @@ static void write_image(WriteData *wd, Image *ima) } } -static void write_texture(WriteData *wd, Tex *tex) +static void write_texture(WriteData *wd, Tex *tex, const void *id_address) { if (tex->id.us > 0 || wd->use_memfile) { /* write LibData */ - writestruct(wd, ID_TE, Tex, 1, tex); + writestruct_at_address(wd, ID_TE, Tex, 1, id_address, tex); write_iddata(wd, &tex->id); if (tex->adt) { @@ -2354,11 +2422,15 @@ static void write_texture(WriteData *wd, Tex *tex) } } -static void write_material(WriteData *wd, Material *ma) +static void write_material(WriteData *wd, Material *ma, const void *id_address) { if (ma->id.us > 0 || wd->use_memfile) { + /* Clean up, important in undo case to reduce false detection of changed datablocks. */ + ma->texpaintslot = NULL; + BLI_listbase_clear(&ma->gpumaterial); + /* write LibData */ - writestruct(wd, ID_MA, Material, 1, ma); + writestruct_at_address(wd, ID_MA, Material, 1, id_address, ma); write_iddata(wd, &ma->id); if (ma->adt) { @@ -2380,11 +2452,14 @@ static void write_material(WriteData *wd, Material *ma) } } -static void write_world(WriteData *wd, World *wrld) +static void write_world(WriteData *wd, World *wrld, const void *id_address) { if (wrld->id.us > 0 || wd->use_memfile) { + /* Clean up, important in undo case to reduce false detection of changed datablocks. */ + BLI_listbase_clear(&wrld->gpumaterial); + /* write LibData */ - writestruct(wd, ID_WO, World, 1, wrld); + writestruct_at_address(wd, ID_WO, World, 1, id_address, wrld); write_iddata(wd, &wrld->id); if (wrld->adt) { @@ -2401,11 +2476,11 @@ static void write_world(WriteData *wd, World *wrld) } } -static void write_light(WriteData *wd, Light *la) +static void write_light(WriteData *wd, Light *la, const void *id_address) { if (la->id.us > 0 || wd->use_memfile) { /* write LibData */ - writestruct(wd, ID_LA, Light, 1, la); + writestruct_at_address(wd, ID_LA, Light, 1, id_address, la); write_iddata(wd, &la->id); if (la->adt) { @@ -2431,25 +2506,26 @@ static void write_collection_nolib(WriteData *wd, Collection *collection) /* Shared function for collection data-blocks and scene master collection. */ write_previews(wd, collection->preview); - for (CollectionObject *cob = collection->gobject.first; cob; cob = cob->next) { + LISTBASE_FOREACH (CollectionObject *, cob, &collection->gobject) { writestruct(wd, DATA, CollectionObject, 1, cob); } - for (CollectionChild *child = collection->children.first; child; child = child->next) { + LISTBASE_FOREACH (CollectionChild *, child, &collection->children) { writestruct(wd, DATA, CollectionChild, 1, child); } - - /* Clear the accumulated recalc flags in case of undo step saving. */ - if (wd->use_memfile) { - collection->id.recalc_undo_accumulated = 0; - } } -static void write_collection(WriteData *wd, Collection *collection) +static void write_collection(WriteData *wd, Collection *collection, const void *id_address) { if (collection->id.us > 0 || wd->use_memfile) { + /* Clean up, important in undo case to reduce false detection of changed datablocks. */ + collection->flag &= ~COLLECTION_HAS_OBJECT_CACHE; + collection->tag = 0; + BLI_listbase_clear(&collection->object_cache); + BLI_listbase_clear(&collection->parents); + /* write LibData */ - writestruct(wd, ID_GR, Collection, 1, collection); + writestruct_at_address(wd, ID_GR, Collection, 1, id_address, collection); write_iddata(wd, &collection->id); write_collection_nolib(wd, collection); @@ -2507,7 +2583,7 @@ static void write_paint(WriteData *wd, Paint *p) static void write_layer_collections(WriteData *wd, ListBase *lb) { - for (LayerCollection *lc = lb->first; lc; lc = lc->next) { + LISTBASE_FOREACH (LayerCollection *, lc, lb) { writestruct(wd, DATA, LayerCollection, 1, lc); write_layer_collections(wd, &lc->layer_collections); @@ -2523,12 +2599,11 @@ static void write_view_layer(WriteData *wd, ViewLayer *view_layer) IDP_WriteProperty(view_layer->id_properties, wd); } - for (FreestyleModuleConfig *fmc = view_layer->freestyle_config.modules.first; fmc; - fmc = fmc->next) { + LISTBASE_FOREACH (FreestyleModuleConfig *, fmc, &view_layer->freestyle_config.modules) { writestruct(wd, DATA, FreestyleModuleConfig, 1, fmc); } - for (FreestyleLineSet *fls = view_layer->freestyle_config.linesets.first; fls; fls = fls->next) { + LISTBASE_FOREACH (FreestyleLineSet *, fls, &view_layer->freestyle_config.linesets) { writestruct(wd, DATA, FreestyleLineSet, 1, fls); } write_layer_collections(wd, &view_layer->layer_collections); @@ -2564,10 +2639,16 @@ static void write_lightcache(WriteData *wd, LightCache *cache) writestruct(wd, DATA, LightProbeCache, cache->cube_len, cache->cube_data); } -static void write_scene(WriteData *wd, Scene *sce) +static void write_scene(WriteData *wd, Scene *sce, const void *id_address) { + if (wd->use_memfile) { + /* Clean up, important in undo case to reduce false detection of changed datablocks. */ + /* XXX This UI data should not be stored in Scene at all... */ + memset(&sce->cursor, 0, sizeof(sce->cursor)); + } + /* write LibData */ - writestruct(wd, ID_SCE, Scene, 1, sce); + writestruct_at_address(wd, ID_SCE, Scene, 1, id_address, sce); write_iddata(wd, &sce->id); if (sce->adt) { @@ -2714,7 +2795,7 @@ static void write_scene(WriteData *wd, Scene *sce) SEQ_END; /* new; meta stack too, even when its nasty restore code */ - for (MetaStack *ms = ed->metastack.first; ms; ms = ms->next) { + LISTBASE_FOREACH (MetaStack *, ms, &ed->metastack) { writestruct(wd, DATA, MetaStack, 1, ms); } } @@ -2733,17 +2814,17 @@ static void write_scene(WriteData *wd, Scene *sce) } /* writing dynamic list of TimeMarkers to the blend file */ - for (TimeMarker *marker = sce->markers.first; marker; marker = marker->next) { + LISTBASE_FOREACH (TimeMarker *, marker, &sce->markers) { writestruct(wd, DATA, TimeMarker, 1, marker); } /* writing dynamic list of TransformOrientations to the blend file */ - for (TransformOrientation *ts = sce->transform_spaces.first; ts; ts = ts->next) { + LISTBASE_FOREACH (TransformOrientation *, ts, &sce->transform_spaces) { writestruct(wd, DATA, TransformOrientation, 1, ts); } /* writing MultiView to the blend file */ - for (SceneRenderView *srv = sce->r.views.first; srv; srv = srv->next) { + LISTBASE_FOREACH (SceneRenderView *, srv, &sce->r.views) { writestruct(wd, DATA, SceneRenderView, 1, srv); } @@ -2769,7 +2850,7 @@ static void write_scene(WriteData *wd, Scene *sce) write_previews(wd, sce->preview); write_curvemapping_curves(wd, &sce->r.mblur_shutter_curve); - for (ViewLayer *view_layer = sce->view_layers.first; view_layer; view_layer = view_layer->next) { + LISTBASE_FOREACH (ViewLayer *, view_layer, &sce->view_layers) { write_view_layer(wd, view_layer); } @@ -2790,11 +2871,19 @@ static void write_scene(WriteData *wd, Scene *sce) BLI_assert(sce->layer_properties == NULL); } -static void write_gpencil(WriteData *wd, bGPdata *gpd) +static void write_gpencil(WriteData *wd, bGPdata *gpd, const void *id_address) { if (gpd->id.us > 0 || wd->use_memfile) { + /* Clean up, important in undo case to reduce false detection of changed data-blocks. */ + /* XXX not sure why the whole run-time data is not cleared in reading code, + * for now mimicking it here. */ + gpd->runtime.sbuffer = NULL; + gpd->runtime.sbuffer_used = 0; + gpd->runtime.sbuffer_size = 0; + gpd->runtime.tot_cp_points = 0; + /* write gpd data block to file */ - writestruct(wd, ID_GD, bGPdata, 1, gpd); + writestruct_at_address(wd, ID_GD, bGPdata, 1, id_address, gpd); write_iddata(wd, &gpd->id); if (gpd->adt) { @@ -2914,35 +3003,33 @@ static void write_soops(WriteData *wd, SpaceOutliner *so) static void write_panel_list(WriteData *wd, ListBase *lb) { - for (Panel *pa = lb->first; pa; pa = pa->next) { - writestruct(wd, DATA, Panel, 1, pa); - write_panel_list(wd, &pa->children); + LISTBASE_FOREACH (Panel *, panel, lb) { + writestruct(wd, DATA, Panel, 1, panel); + write_panel_list(wd, &panel->children); } } static void write_area_regions(WriteData *wd, ScrArea *area) { - for (ARegion *region = area->regionbase.first; region; region = region->next) { + LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { write_region(wd, region, area->spacetype); write_panel_list(wd, ®ion->panels); - for (PanelCategoryStack *pc_act = region->panels_category_active.first; pc_act; - pc_act = pc_act->next) { + LISTBASE_FOREACH (PanelCategoryStack *, pc_act, ®ion->panels_category_active) { writestruct(wd, DATA, PanelCategoryStack, 1, pc_act); } - for (uiList *ui_list = region->ui_lists.first; ui_list; ui_list = ui_list->next) { + LISTBASE_FOREACH (uiList *, ui_list, ®ion->ui_lists) { write_uilist(wd, ui_list); } - for (uiPreview *ui_preview = region->ui_previews.first; ui_preview; - ui_preview = ui_preview->next) { + LISTBASE_FOREACH (uiPreview *, ui_preview, ®ion->ui_previews) { writestruct(wd, DATA, uiPreview, 1, ui_preview); } } - for (SpaceLink *sl = area->spacedata.first; sl; sl = sl->next) { - for (ARegion *region = sl->regionbase.first; region; region = region->next) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + LISTBASE_FOREACH (ARegion *, region, &sl->regionbase) { write_region(wd, region, sl->spacetype); } @@ -3055,7 +3142,7 @@ static void write_area_map(WriteData *wd, ScrAreaMap *area_map) { writelist(wd, DATA, ScrVert, &area_map->vertbase); writelist(wd, DATA, ScrEdge, &area_map->edgebase); - for (ScrArea *area = area_map->areabase.first; area; area = area->next) { + LISTBASE_FOREACH (ScrArea *, area, &area_map->areabase) { area->butspacetype = area->spacetype; /* Just for compatibility, will be reset below. */ writestruct(wd, DATA, ScrArea, 1, area); @@ -3070,13 +3157,13 @@ static void write_area_map(WriteData *wd, ScrAreaMap *area_map) } } -static void write_windowmanager(WriteData *wd, wmWindowManager *wm) +static void write_windowmanager(BlendWriter *writer, wmWindowManager *wm, const void *id_address) { - writestruct(wd, ID_WM, wmWindowManager, 1, wm); - write_iddata(wd, &wm->id); - write_wm_xr_data(wd, &wm->xr); + BLO_write_id_struct(writer, wmWindowManager, id_address, &wm->id); + write_iddata(writer->wd, &wm->id); + write_wm_xr_data(writer->wd, &wm->xr); - for (wmWindow *win = wm->windows.first; win; win = win->next) { + LISTBASE_FOREACH (wmWindow *, win, &wm->windows) { #ifndef WITH_GLOBAL_AREA_WRITING /* Don't write global areas yet, while we make changes to them. */ ScrAreaMap global_areas = win->global_areas; @@ -3086,12 +3173,12 @@ static void write_windowmanager(WriteData *wd, wmWindowManager *wm) /* update deprecated screen member (for so loading in 2.7x uses the correct screen) */ win->screen = BKE_workspace_active_screen_get(win->workspace_hook); - writestruct(wd, DATA, wmWindow, 1, win); - writestruct(wd, DATA, WorkSpaceInstanceHook, 1, win->workspace_hook); - writestruct(wd, DATA, Stereo3dFormat, 1, win->stereo3d_format); + BLO_write_struct(writer, wmWindow, win); + BLO_write_struct(writer, WorkSpaceInstanceHook, win->workspace_hook); + BLO_write_struct(writer, Stereo3dFormat, win->stereo3d_format); #ifdef WITH_GLOBAL_AREA_WRITING - write_area_map(wd, &win->global_areas); + write_area_map(writer->wd, &win->global_areas); #else win->global_areas = global_areas; #endif @@ -3101,19 +3188,19 @@ static void write_windowmanager(WriteData *wd, wmWindowManager *wm) } } -static void write_screen(WriteData *wd, bScreen *sc) +static void write_screen(WriteData *wd, bScreen *screen, const void *id_address) { /* Screens are reference counted, only saved if used by a workspace. */ - if (sc->id.us > 0 || wd->use_memfile) { + if (screen->id.us > 0 || wd->use_memfile) { /* write LibData */ /* in 2.50+ files, the file identifier for screens is patched, forward compatibility */ - writestruct(wd, ID_SCRN, bScreen, 1, sc); - write_iddata(wd, &sc->id); + writestruct_at_address(wd, ID_SCRN, bScreen, 1, id_address, screen); + write_iddata(wd, &screen->id); - write_previews(wd, sc->preview); + write_previews(wd, screen->preview); /* direct data */ - write_area_map(wd, AREAMAP_FROM_SCREEN(sc)); + write_area_map(wd, AREAMAP_FROM_SCREEN(screen)); } } @@ -3132,15 +3219,22 @@ static void write_bone(WriteData *wd, Bone *bone) } /* Write Children */ - for (Bone *cbone = bone->childbase.first; cbone; cbone = cbone->next) { + LISTBASE_FOREACH (Bone *, cbone, &bone->childbase) { write_bone(wd, cbone); } } -static void write_armature(WriteData *wd, bArmature *arm) +static void write_armature(WriteData *wd, bArmature *arm, const void *id_address) { if (arm->id.us > 0 || wd->use_memfile) { - writestruct(wd, ID_AR, bArmature, 1, arm); + /* Clean up, important in undo case to reduce false detection of changed datablocks. */ + arm->bonehash = NULL; + arm->edbo = NULL; + /* Must always be cleared (armatures don't have their own edit-data). */ + arm->needs_flush_to_id = 0; + arm->act_edbone = NULL; + + writestruct_at_address(wd, ID_AR, bArmature, 1, id_address, arm); write_iddata(wd, &arm->id); if (arm->adt) { @@ -3148,20 +3242,24 @@ static void write_armature(WriteData *wd, bArmature *arm) } /* Direct data */ - for (Bone *bone = arm->bonebase.first; bone; bone = bone->next) { + LISTBASE_FOREACH (Bone *, bone, &arm->bonebase) { write_bone(wd, bone); } } } -static void write_text(WriteData *wd, Text *text) +static void write_text(WriteData *wd, Text *text, const void *id_address) { + /* Note: we are clearing local temp data here, *not* the flag in the actual 'real' ID. */ if ((text->flags & TXT_ISMEM) && (text->flags & TXT_ISEXT)) { text->flags &= ~TXT_ISEXT; } + /* Clean up, important in undo case to reduce false detection of changed datablocks. */ + text->compiled = NULL; + /* write LibData */ - writestruct(wd, ID_TXT, Text, 1, text); + writestruct_at_address(wd, ID_TXT, Text, 1, id_address, text); write_iddata(wd, &text->id); if (text->name) { @@ -3170,21 +3268,21 @@ static void write_text(WriteData *wd, Text *text) if (!(text->flags & TXT_ISEXT)) { /* now write the text data, in two steps for optimization in the readfunction */ - for (TextLine *tmp = text->lines.first; tmp; tmp = tmp->next) { + LISTBASE_FOREACH (TextLine *, tmp, &text->lines) { writestruct(wd, DATA, TextLine, 1, tmp); } - for (TextLine *tmp = text->lines.first; tmp; tmp = tmp->next) { + LISTBASE_FOREACH (TextLine *, tmp, &text->lines) { writedata(wd, DATA, tmp->len + 1, tmp->line); } } } -static void write_speaker(WriteData *wd, Speaker *spk) +static void write_speaker(WriteData *wd, Speaker *spk, const void *id_address) { if (spk->id.us > 0 || wd->use_memfile) { /* write LibData */ - writestruct(wd, ID_SPK, Speaker, 1, spk); + writestruct_at_address(wd, ID_SPK, Speaker, 1, id_address, spk); write_iddata(wd, &spk->id); if (spk->adt) { @@ -3193,11 +3291,17 @@ static void write_speaker(WriteData *wd, Speaker *spk) } } -static void write_sound(WriteData *wd, bSound *sound) +static void write_sound(WriteData *wd, bSound *sound, const void *id_address) { if (sound->id.us > 0 || wd->use_memfile) { + /* Clean up, important in undo case to reduce false detection of changed datablocks. */ + sound->tags = 0; + sound->handle = NULL; + sound->playback_handle = NULL; + sound->spinlock = NULL; + /* write LibData */ - writestruct(wd, ID_SO, bSound, 1, sound); + writestruct_at_address(wd, ID_SO, bSound, 1, id_address, sound); write_iddata(wd, &sound->id); if (sound->packedfile) { @@ -3208,11 +3312,11 @@ static void write_sound(WriteData *wd, bSound *sound) } } -static void write_probe(WriteData *wd, LightProbe *prb) +static void write_probe(WriteData *wd, LightProbe *prb, const void *id_address) { if (prb->id.us > 0 || wd->use_memfile) { /* write LibData */ - writestruct(wd, ID_LP, LightProbe, 1, prb); + writestruct_at_address(wd, ID_LP, LightProbe, 1, id_address, prb); write_iddata(wd, &prb->id); if (prb->adt) { @@ -3221,10 +3325,18 @@ static void write_probe(WriteData *wd, LightProbe *prb) } } -static void write_nodetree(WriteData *wd, bNodeTree *ntree) +static void write_nodetree(WriteData *wd, bNodeTree *ntree, const void *id_address) { if (ntree->id.us > 0 || wd->use_memfile) { - writestruct(wd, ID_NT, bNodeTree, 1, ntree); + /* Clean up, important in undo case to reduce false detection of changed datablocks. */ + ntree->init = 0; /* to set callbacks and force setting types */ + ntree->is_updating = false; + ntree->typeinfo = NULL; + ntree->interface_type = NULL; + ntree->progress = NULL; + ntree->execdata = NULL; + + writestruct_at_address(wd, ID_NT, bNodeTree, 1, id_address, ntree); /* Note that trees directly used by other IDs (materials etc.) are not 'real' ID, they cannot * be linked, etc., so we write actual id data here only, for 'real' ID trees. */ write_iddata(wd, &ntree->id); @@ -3233,10 +3345,10 @@ static void write_nodetree(WriteData *wd, bNodeTree *ntree) } } -static void write_brush(WriteData *wd, Brush *brush) +static void write_brush(WriteData *wd, Brush *brush, const void *id_address) { if (brush->id.us > 0 || wd->use_memfile) { - writestruct(wd, ID_BR, Brush, 1, brush); + writestruct_at_address(wd, ID_BR, Brush, 1, id_address, brush); write_iddata(wd, &brush->id); if (brush->curve) { @@ -3255,6 +3367,24 @@ static void write_brush(WriteData *wd, Brush *brush) if (brush->gpencil_settings->curve_jitter) { write_curvemapping(wd, brush->gpencil_settings->curve_jitter); } + if (brush->gpencil_settings->curve_rand_pressure) { + write_curvemapping(wd, brush->gpencil_settings->curve_rand_pressure); + } + if (brush->gpencil_settings->curve_rand_strength) { + write_curvemapping(wd, brush->gpencil_settings->curve_rand_strength); + } + if (brush->gpencil_settings->curve_rand_uv) { + write_curvemapping(wd, brush->gpencil_settings->curve_rand_uv); + } + if (brush->gpencil_settings->curve_rand_hue) { + write_curvemapping(wd, brush->gpencil_settings->curve_rand_hue); + } + if (brush->gpencil_settings->curve_rand_saturation) { + write_curvemapping(wd, brush->gpencil_settings->curve_rand_saturation); + } + if (brush->gpencil_settings->curve_rand_value) { + write_curvemapping(wd, brush->gpencil_settings->curve_rand_value); + } } if (brush->gradient) { writestruct(wd, DATA, ColorBand, 1, brush->gradient); @@ -3262,11 +3392,11 @@ static void write_brush(WriteData *wd, Brush *brush) } } -static void write_palette(WriteData *wd, Palette *palette) +static void write_palette(WriteData *wd, Palette *palette, const void *id_address) { if (palette->id.us > 0 || wd->use_memfile) { PaletteColor *color; - writestruct(wd, ID_PAL, Palette, 1, palette); + writestruct_at_address(wd, ID_PAL, Palette, 1, id_address, palette); write_iddata(wd, &palette->id); for (color = palette->colors.first; color; color = color->next) { @@ -3275,10 +3405,10 @@ static void write_palette(WriteData *wd, Palette *palette) } } -static void write_paintcurve(WriteData *wd, PaintCurve *pc) +static void write_paintcurve(WriteData *wd, PaintCurve *pc, const void *id_address) { if (pc->id.us > 0 || wd->use_memfile) { - writestruct(wd, ID_PC, PaintCurve, 1, pc); + writestruct_at_address(wd, ID_PC, PaintCurve, 1, id_address, pc); write_iddata(wd, &pc->id); writestruct(wd, DATA, PaintCurvePoint, pc->tot_points, pc->points); @@ -3324,13 +3454,18 @@ static void write_movieReconstruction(WriteData *wd, MovieTrackingReconstruction } } -static void write_movieclip(WriteData *wd, MovieClip *clip) +static void write_movieclip(WriteData *wd, MovieClip *clip, const void *id_address) { if (clip->id.us > 0 || wd->use_memfile) { + /* Clean up, important in undo case to reduce false detection of changed datablocks. */ + clip->anim = NULL; + clip->tracking_context = NULL; + clip->tracking.stats = NULL; + MovieTracking *tracking = &clip->tracking; MovieTrackingObject *object; - writestruct(wd, ID_MC, MovieClip, 1, clip); + writestruct_at_address(wd, ID_MC, MovieClip, 1, id_address, clip); write_iddata(wd, &clip->id); if (clip->adt) { @@ -3354,12 +3489,12 @@ static void write_movieclip(WriteData *wd, MovieClip *clip) } } -static void write_mask(WriteData *wd, Mask *mask) +static void write_mask(WriteData *wd, Mask *mask, const void *id_address) { if (mask->id.us > 0 || wd->use_memfile) { MaskLayer *masklay; - writestruct(wd, ID_MSK, Mask, 1, mask); + writestruct_at_address(wd, ID_MSK, Mask, 1, id_address, mask); write_iddata(wd, &mask->id); if (mask->adt) { @@ -3661,10 +3796,10 @@ static void write_linestyle_geometry_modifiers(WriteData *wd, ListBase *modifier } } -static void write_linestyle(WriteData *wd, FreestyleLineStyle *linestyle) +static void write_linestyle(WriteData *wd, FreestyleLineStyle *linestyle, const void *id_address) { if (linestyle->id.us > 0 || wd->use_memfile) { - writestruct(wd, ID_LS, FreestyleLineStyle, 1, linestyle); + writestruct_at_address(wd, ID_LS, FreestyleLineStyle, 1, id_address, linestyle); write_iddata(wd, &linestyle->id); if (linestyle->adt) { @@ -3687,10 +3822,16 @@ static void write_linestyle(WriteData *wd, FreestyleLineStyle *linestyle) } } -static void write_cachefile(WriteData *wd, CacheFile *cache_file) +static void write_cachefile(WriteData *wd, CacheFile *cache_file, const void *id_address) { if (cache_file->id.us > 0 || wd->use_memfile) { - writestruct(wd, ID_CF, CacheFile, 1, cache_file); + /* Clean up, important in undo case to reduce false detection of changed datablocks. */ + BLI_listbase_clear(&cache_file->object_paths); + cache_file->handle = NULL; + memset(cache_file->handle_filepath, 0, sizeof(cache_file->handle_filepath)); + cache_file->handle_readers = NULL; + + writestruct_at_address(wd, ID_CF, CacheFile, 1, id_address, cache_file); if (cache_file->adt) { write_animdata(wd, cache_file->adt); @@ -3698,39 +3839,31 @@ static void write_cachefile(WriteData *wd, CacheFile *cache_file) } } -static void write_workspace(WriteData *wd, WorkSpace *workspace) +static void write_workspace(BlendWriter *writer, WorkSpace *workspace, const void *id_address) { - ListBase *layouts = BKE_workspace_layouts_get(workspace); - - writestruct(wd, ID_WS, WorkSpace, 1, workspace); - write_iddata(wd, &workspace->id); - writelist(wd, DATA, WorkSpaceLayout, layouts); - writelist(wd, DATA, WorkSpaceDataRelation, &workspace->hook_layout_relations); - writelist(wd, DATA, wmOwnerID, &workspace->owner_ids); - writelist(wd, DATA, bToolRef, &workspace->tools); - for (bToolRef *tref = workspace->tools.first; tref; tref = tref->next) { + BLO_write_id_struct(writer, WorkSpace, id_address, &workspace->id); + write_iddata(writer->wd, &workspace->id); + BLO_write_struct_list(writer, WorkSpaceLayout, &workspace->layouts); + BLO_write_struct_list(writer, WorkSpaceDataRelation, &workspace->hook_layout_relations); + BLO_write_struct_list(writer, wmOwnerID, &workspace->owner_ids); + BLO_write_struct_list(writer, bToolRef, &workspace->tools); + LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) { if (tref->properties) { - IDP_WriteProperty(tref->properties, wd); + IDP_WriteProperty_new_api(tref->properties, writer); } } } -static void write_hair(WriteData *wd, Hair *hair) +static void write_hair(WriteData *wd, Hair *hair, const void *id_address) { if (hair->id.us > 0 || wd->use_memfile) { - /* Write a copy of the hair with possibly reduced number of data layers. - * Don't edit the original since other threads might be reading it. */ - Hair *old_hair = hair; - Hair copy_hair = *hair; - hair = ©_hair; - CustomDataLayer *players = NULL, players_buff[CD_TEMP_CHUNK_SIZE]; CustomDataLayer *clayers = NULL, clayers_buff[CD_TEMP_CHUNK_SIZE]; CustomData_file_write_prepare(&hair->pdata, &players, players_buff, ARRAY_SIZE(players_buff)); CustomData_file_write_prepare(&hair->cdata, &clayers, clayers_buff, ARRAY_SIZE(clayers_buff)); /* Write LibData */ - writestruct_at_address(wd, ID_HA, Hair, 1, old_hair, hair); + writestruct_at_address(wd, ID_HA, Hair, 1, id_address, hair); write_iddata(wd, &hair->id); /* Direct data */ @@ -3748,27 +3881,18 @@ static void write_hair(WriteData *wd, Hair *hair) if (clayers && clayers != clayers_buff) { MEM_freeN(clayers); } - - /* restore pointer */ - hair = old_hair; } } -static void write_pointcloud(WriteData *wd, PointCloud *pointcloud) +static void write_pointcloud(WriteData *wd, PointCloud *pointcloud, const void *id_address) { if (pointcloud->id.us > 0 || wd->use_memfile) { - /* Write a copy of the pointcloud with possibly reduced number of data layers. - * Don't edit the original since other threads might be reading it. */ - PointCloud *old_pointcloud = pointcloud; - PointCloud copy_pointcloud = *pointcloud; - pointcloud = ©_pointcloud; - CustomDataLayer *players = NULL, players_buff[CD_TEMP_CHUNK_SIZE]; CustomData_file_write_prepare( &pointcloud->pdata, &players, players_buff, ARRAY_SIZE(players_buff)); /* Write LibData */ - writestruct_at_address(wd, ID_PT, PointCloud, 1, old_pointcloud, pointcloud); + writestruct_at_address(wd, ID_PT, PointCloud, 1, id_address, pointcloud); write_iddata(wd, &pointcloud->id); /* Direct data */ @@ -3786,11 +3910,14 @@ static void write_pointcloud(WriteData *wd, PointCloud *pointcloud) } } -static void write_volume(WriteData *wd, Volume *volume) +static void write_volume(WriteData *wd, Volume *volume, const void *id_address) { if (volume->id.us > 0 || wd->use_memfile) { + /* Clean up, important in undo case to reduce false detection of changed datablocks. */ + volume->runtime.grids = 0; + /* write LibData */ - writestruct(wd, ID_VO, Volume, 1, volume); + writestruct_at_address(wd, ID_VO, Volume, 1, id_address, volume); write_iddata(wd, &volume->id); /* direct data */ @@ -3807,6 +3934,24 @@ static void write_volume(WriteData *wd, Volume *volume) } } +static void write_simulation(WriteData *wd, Simulation *simulation) +{ + if (simulation->id.us > 0 || wd->use_memfile) { + writestruct(wd, ID_SIM, Simulation, 1, simulation); + write_iddata(wd, &simulation->id); + + if (simulation->adt) { + write_animdata(wd, simulation->adt); + } + + /* nodetree is integral part of simulation, no libdata */ + if (simulation->nodetree) { + writestruct(wd, DATA, bNodeTree, 1, simulation->nodetree); + write_nodetree_nolib(wd, simulation->nodetree); + } + } +} + /* Keep it last of write_foodata functions. */ static void write_libraries(WriteData *wd, Main *main) { @@ -3822,6 +3967,11 @@ static void write_libraries(WriteData *wd, Main *main) if (main->curlib && main->curlib->packedfile) { found_one = true; } + else if (wd->use_memfile) { + /* When writing undo step we always write all existing libraries, makes reading undo step + * much easier when dealing with purely indirectly used libraries. */ + found_one = true; + } else { found_one = false; while (!found_one && tot--) { @@ -3909,12 +4059,12 @@ static void write_global(WriteData *wd, int fileflags, Main *mainvar) fg.globalf = G.f; BLI_strncpy(fg.filename, mainvar->name, sizeof(fg.filename)); - sprintf(subvstr, "%4d", BLENDER_SUBVERSION); + sprintf(subvstr, "%4d", BLENDER_FILE_SUBVERSION); memcpy(fg.subvstr, subvstr, 4); - fg.subversion = BLENDER_SUBVERSION; - fg.minversion = BLENDER_MINVERSION; - fg.minsubversion = BLENDER_MINSUBVERSION; + fg.subversion = BLENDER_FILE_SUBVERSION; + fg.minversion = BLENDER_FILE_MIN_VERSION; + fg.minsubversion = BLENDER_FILE_MIN_SUBVERSION; #ifdef WITH_BUILDINFO { extern unsigned long build_commit_timestamp; @@ -3968,7 +4118,7 @@ static bool write_file_handle(Main *mainvar, "BLENDER%c%c%.3d", (sizeof(void *) == 8) ? '-' : '_', (ENDIAN_ORDER == B_ENDIAN) ? 'V' : 'v', - BLENDER_VERSION); + BLENDER_FILE_VERSION); mywrite(wd, buf, 12); @@ -3983,6 +4133,7 @@ static bool write_file_handle(Main *mainvar, OverrideLibraryStorage *override_storage = wd->use_memfile ? NULL : BKE_lib_override_library_operations_store_initialize(); +#define ID_BUFFER_STATIC_SIZE 8192 /* This outer loop allows to save first data-blocks from real mainvar, * then the temp ones from override process, * if needed, without duplicating whole code. */ @@ -3993,10 +4144,18 @@ static bool write_file_handle(Main *mainvar, while (a--) { ID *id = lbarray[a]->first; - if (id && GS(id->name) == ID_LI) { + if (id == NULL || GS(id->name) == ID_LI) { continue; /* Libraries are handled separately below. */ } + char id_buffer_static[ID_BUFFER_STATIC_SIZE]; + void *id_buffer = id_buffer_static; + const size_t idtype_struct_size = BKE_idtype_get_info_from_id(id)->struct_size; + if (idtype_struct_size > ID_BUFFER_STATIC_SIZE) { + BLI_assert(0); + id_buffer = MEM_mallocN(idtype_struct_size, __func__); + } + for (; id; id = id->next) { /* We should never attempt to write non-regular IDs * (i.e. all kind of temp/runtime ones). */ @@ -4009,117 +4168,155 @@ static bool write_file_handle(Main *mainvar, BKE_lib_override_library_operations_store_start(bmain, override_storage, id); } + if (wd->use_memfile) { + /* Record the changes that happened up to this undo push in + * recalc_up_to_undo_push, and clear recalc_after_undo_push again + * to start accumulating for the next undo push. */ + id->recalc_up_to_undo_push = id->recalc_after_undo_push; + id->recalc_after_undo_push = 0; + + bNodeTree *nodetree = ntreeFromID(id); + if (nodetree != NULL) { + nodetree->id.recalc_up_to_undo_push = nodetree->id.recalc_after_undo_push; + nodetree->id.recalc_after_undo_push = 0; + } + if (GS(id->name) == ID_SCE) { + Scene *scene = (Scene *)id; + if (scene->master_collection != NULL) { + scene->master_collection->id.recalc_up_to_undo_push = + scene->master_collection->id.recalc_after_undo_push; + scene->master_collection->id.recalc_after_undo_push = 0; + } + } + } + + mywrite_id_begin(wd, id); + + memcpy(id_buffer, id, idtype_struct_size); + + ((ID *)id_buffer)->tag = 0; + /* Those listbase data change every time we add/remove an ID, and also often when renaming + * one (due to re-sorting). This avoids generating a lot of false 'is changed' detections + * between undo steps. */ + ((ID *)id_buffer)->prev = NULL; + ((ID *)id_buffer)->next = NULL; + + BlendWriter writer = {wd}; + switch ((ID_Type)GS(id->name)) { case ID_WM: - write_windowmanager(wd, (wmWindowManager *)id); + write_windowmanager(&writer, (wmWindowManager *)id_buffer, id); break; case ID_WS: - write_workspace(wd, (WorkSpace *)id); + write_workspace(&writer, (WorkSpace *)id_buffer, id); break; case ID_SCR: - write_screen(wd, (bScreen *)id); + write_screen(wd, (bScreen *)id_buffer, id); break; case ID_MC: - write_movieclip(wd, (MovieClip *)id); + write_movieclip(wd, (MovieClip *)id_buffer, id); break; case ID_MSK: - write_mask(wd, (Mask *)id); + write_mask(wd, (Mask *)id_buffer, id); break; case ID_SCE: - write_scene(wd, (Scene *)id); + write_scene(wd, (Scene *)id_buffer, id); break; case ID_CU: - write_curve(wd, (Curve *)id); + write_curve(wd, (Curve *)id_buffer, id); break; case ID_MB: - write_mball(wd, (MetaBall *)id); + write_mball(wd, (MetaBall *)id_buffer, id); break; case ID_IM: - write_image(wd, (Image *)id); + write_image(wd, (Image *)id_buffer, id); break; case ID_CA: - write_camera(wd, (Camera *)id); + write_camera(wd, (Camera *)id_buffer, id); break; case ID_LA: - write_light(wd, (Light *)id); + write_light(wd, (Light *)id_buffer, id); break; case ID_LT: - write_lattice(wd, (Lattice *)id); + write_lattice(wd, (Lattice *)id_buffer, id); break; case ID_VF: - write_vfont(wd, (VFont *)id); + write_vfont(wd, (VFont *)id_buffer, id); break; case ID_KE: - write_key(wd, (Key *)id); + write_key(wd, (Key *)id_buffer, id); break; case ID_WO: - write_world(wd, (World *)id); + write_world(wd, (World *)id_buffer, id); break; case ID_TXT: - write_text(wd, (Text *)id); + write_text(wd, (Text *)id_buffer, id); break; case ID_SPK: - write_speaker(wd, (Speaker *)id); + write_speaker(wd, (Speaker *)id_buffer, id); break; case ID_LP: - write_probe(wd, (LightProbe *)id); + write_probe(wd, (LightProbe *)id_buffer, id); break; case ID_SO: - write_sound(wd, (bSound *)id); + write_sound(wd, (bSound *)id_buffer, id); break; case ID_GR: - write_collection(wd, (Collection *)id); + write_collection(wd, (Collection *)id_buffer, id); break; case ID_AR: - write_armature(wd, (bArmature *)id); + write_armature(wd, (bArmature *)id_buffer, id); break; case ID_AC: - write_action(wd, (bAction *)id); + write_action(wd, (bAction *)id_buffer, id); break; case ID_OB: - write_object(wd, (Object *)id); + write_object(wd, (Object *)id_buffer, id); break; case ID_MA: - write_material(wd, (Material *)id); + write_material(wd, (Material *)id_buffer, id); break; case ID_TE: - write_texture(wd, (Tex *)id); + write_texture(wd, (Tex *)id_buffer, id); break; case ID_ME: - write_mesh(wd, (Mesh *)id); + write_mesh(wd, (Mesh *)id_buffer, id); break; case ID_PA: - write_particlesettings(wd, (ParticleSettings *)id); + write_particlesettings(wd, (ParticleSettings *)id_buffer, id); break; case ID_NT: - write_nodetree(wd, (bNodeTree *)id); + write_nodetree(wd, (bNodeTree *)id_buffer, id); break; case ID_BR: - write_brush(wd, (Brush *)id); + write_brush(wd, (Brush *)id_buffer, id); break; case ID_PAL: - write_palette(wd, (Palette *)id); + write_palette(wd, (Palette *)id_buffer, id); break; case ID_PC: - write_paintcurve(wd, (PaintCurve *)id); + write_paintcurve(wd, (PaintCurve *)id_buffer, id); break; case ID_GD: - write_gpencil(wd, (bGPdata *)id); + write_gpencil(wd, (bGPdata *)id_buffer, id); break; case ID_LS: - write_linestyle(wd, (FreestyleLineStyle *)id); + write_linestyle(wd, (FreestyleLineStyle *)id_buffer, id); break; case ID_CF: - write_cachefile(wd, (CacheFile *)id); + write_cachefile(wd, (CacheFile *)id_buffer, id); break; case ID_HA: - write_hair(wd, (Hair *)id); + write_hair(wd, (Hair *)id_buffer, id); break; case ID_PT: - write_pointcloud(wd, (PointCloud *)id); + write_pointcloud(wd, (PointCloud *)id_buffer, id); break; case ID_VO: - write_volume(wd, (Volume *)id); + write_volume(wd, (Volume *)id_buffer, id); + break; + case ID_SIM: + write_simulation(wd, (Simulation *)id); break; case ID_LI: /* Do nothing, handled below - and should never be reached. */ @@ -4138,11 +4335,11 @@ static bool write_file_handle(Main *mainvar, BKE_lib_override_library_operations_store_end(override_storage, id); } - if (wd->use_memfile) { - /* Very important to do it after every ID write now, otherwise we cannot know whether a - * specific ID changed or not. */ - mywrite_flush(wd); - } + mywrite_id_end(wd, id); + } + + if (id_buffer != id_buffer_static) { + MEM_SAFE_FREE(id_buffer); } mywrite_flush(wd); @@ -4277,8 +4474,8 @@ bool BLO_write_file(Main *mainvar, BLI_split_dir_part(filepath, dir_dst, sizeof(dir_dst)); /* Just in case there is some subtle difference. */ - BLI_cleanup_path(mainvar->name, dir_dst); - BLI_cleanup_path(mainvar->name, dir_src); + BLI_path_normalize(mainvar->name, dir_dst); + BLI_path_normalize(mainvar->name, dir_src); if (G.relbase_valid && (BLI_path_cmp(dir_dst, dir_src) == 0)) { /* Saved to same path. Nothing to do. */ @@ -4353,4 +4550,98 @@ bool BLO_write_file_mem(Main *mainvar, MemFile *compare, MemFile *current, int w return (err == 0); } +void BLO_write_raw(BlendWriter *writer, int size_in_bytes, const void *data_ptr) +{ + writedata(writer->wd, DATA, size_in_bytes, data_ptr); +} + +void BLO_write_struct_by_name(BlendWriter *writer, const char *struct_name, const void *data_ptr) +{ + int struct_id = BLO_get_struct_id_by_name(writer, struct_name); + BLO_write_struct_by_id(writer, struct_id, data_ptr); +} + +void BLO_write_struct_array_by_name(BlendWriter *writer, + const char *struct_name, + int array_size, + const void *data_ptr) +{ + int struct_id = BLO_get_struct_id_by_name(writer, struct_name); + BLO_write_struct_array_by_id(writer, struct_id, array_size, data_ptr); +} + +void BLO_write_struct_by_id(BlendWriter *writer, int struct_id, const void *data_ptr) +{ + writestruct_nr(writer->wd, DATA, struct_id, 1, data_ptr); +} + +void BLO_write_struct_array_by_id(BlendWriter *writer, + int struct_id, + int array_size, + const void *data_ptr) +{ + writestruct_nr(writer->wd, DATA, struct_id, array_size, data_ptr); +} + +void BLO_write_struct_list_by_id(BlendWriter *writer, int struct_id, ListBase *list) +{ + writelist_nr(writer->wd, DATA, struct_id, list); +} + +void BLO_write_struct_list_by_name(BlendWriter *writer, const char *struct_name, ListBase *list) +{ + BLO_write_struct_list_by_id(writer, BLO_get_struct_id_by_name(writer, struct_name), list); +} + +void blo_write_id_struct(BlendWriter *writer, int struct_id, const void *id_address, const ID *id) +{ + writestruct_at_address_nr(writer->wd, GS(id->name), struct_id, 1, id_address, id); +} + +int BLO_get_struct_id_by_name(BlendWriter *writer, const char *struct_name) +{ + int struct_id = DNA_struct_find_nr(writer->wd->sdna, struct_name); + BLI_assert(struct_id >= 0); + return struct_id; +} + +void BLO_write_int32_array(BlendWriter *writer, int size, const int32_t *data_ptr) +{ + BLO_write_raw(writer, sizeof(int32_t) * size, data_ptr); +} + +void BLO_write_uint32_array(BlendWriter *writer, int size, const uint32_t *data_ptr) +{ + BLO_write_raw(writer, sizeof(uint32_t) * size, data_ptr); +} + +void BLO_write_float_array(BlendWriter *writer, int size, const float *data_ptr) +{ + BLO_write_raw(writer, sizeof(float) * size, data_ptr); +} + +void BLO_write_float3_array(BlendWriter *writer, int size, const float *data_ptr) +{ + BLO_write_raw(writer, sizeof(float) * 3 * size, data_ptr); +} + +/** + * Write a null terminated string. + */ +void BLO_write_string(BlendWriter *writer, const char *str) +{ + if (str != NULL) { + BLO_write_raw(writer, strlen(str) + 1, str); + } +} + +/** + * Sometimes different data is written depending on whether the file is saved to disk or used for + * undo. This function returns true when the current file-writing is done for undo. + */ +bool BLO_write_is_undo(BlendWriter *writer) +{ + return writer->wd->use_memfile; +} + /** \} */ |