This is part of a larger refactoring towards a more extensible architecture in Blender (T75724).
A work in progress implementation of the proposal is in the blenloader-decentralization branch.
Steps that can be done now:
- Commit API to master.
- Update writefile.c to use the API (except for very few special cases, search for writer->wd).
- Update direct linking in readfile.c to use the API (except for a few cases, search for reader->fd).
- Implement blendRead and blendWrite callbacks for modifiers (the fluid modifier still has some special handling; haven't changed pointcache reading yet, to avoid merge conflicts with another patch of mine).
- Update lib linking in readfile.c to use the API.
- Update expand in readfile.c to use the API.
First, there needs to be an API that allows specifying how .blend file I/O works for a particular piece of data (e.g. a data block, a node, a modifier, ...). This API is proposed in BLO_read_write.h (source code). Please read the documentation in this header to see how the API works.
Partial copy of the documentation:
This file contains an API that allows different parts of Blender to define what data is stored in .blend files. Four callbacks have to be provided to fully implement .blend I/O for a piece of data. One of those is related to file writing and three for file reading. Reading requires multiple callbacks, due to the way linking between files works. Brief description of the individual callbacks: - Blend Write: Define which structs and memory buffers are saved. - Blend Read Data: Loads structs and memory buffers from file and updates pointers them. - Blend Read Lib: Updates pointers to ID data blocks. - Blend Expand: Defines which other data blocks should be loaded (possibly from other files). Each of these callbacks uses a different API functions.
Using the API
Fortunately, we don't have to start using the API everywhere at the same time. Instead we can gradually move functions from writefile.c and readfile.c to e.g. blenkernel. Furthermore, callbacks can be added to ModifierTypeInfo and IDTypeInfo (and later bNodeType).
/* Is called when the modifier is written to a file. The modifier data struct itself is written * already. * * This method should write any additional arrays and referenced structs that should be * stored in the file. */ void (*blendWrite)(struct BlendWriter *writer, const struct ModifierData *md); /* Is called when the modifier is read from a file. * * It can be used to update pointers to arrays and other structs. Furthermore, fields that have * not been written (e.g. runtime data) can be reset. */ void (*blendRead)(struct BlendDataReader *reader, struct ModifierData *md);
In the branch I implemented these callbacks for almost all modifiers (e.g. hook modifier).
typedef void (*IDTypeBlendWriteFunction)(struct BlendWriter *writer, struct ID *id, const void *id_address); typedef void (*IDTypeBlendReadDataFunction)(struct BlendDataReader *reader, struct ID *id); typedef void (*IDTypeBlendReadLibFunction)(struct BlendLibReader *reader, struct ID *id); typedef void (*IDTypeBlendExpandFunction)(struct BlendExpander *expander, struct ID *id); /** * Write all structs that should be saved in a .blend file. */ IDTypeBlendWriteFunction blend_write; /** * Update pointers for all structs directly owned by this data block. */ IDTypeBlendReadDataFunction blend_read_data; /** * Update pointers to other id data blocks. */ IDTypeBlendReadLibFunction blend_read_lib; /** * Specify which other id data blocks should be loaded when the current one is loaded. */ IDTypeBlendExpandFunction blend_expand;
In order to implement the callbacks mentioned above, I also had to move the I/O code of some other structures out of blenloader. Namely I already moved AnimData, CurveMapping, CurveProfile, IDProperty and some more. It takes quite some time, but it's mostly pattern matching. Usually, I just copy the code from e.g. readfile.c and then update the code line by line.