Collection IO: Enable file exporters to be specified on Collections #116646

Merged
Jesse Yurkovich merged 56 commits from deadpin/blender:collection-io into main 2024-04-08 22:10:52 +02:00
6 changed files with 188 additions and 82 deletions
Showing only changes of commit 76c0e1cafc - Show all commits

View File

@ -57,7 +57,7 @@ class COLLECTION_PT_io_handlers(CollectionButtonsPanel, Panel):
def draw(self, _context):
layout = self.layout
layout.operator("wm.call_menu", text="Add IO Handler", icon='ADD').name = "COLLECTION_MT_io_handler_add"
layout.operator("COLLECTION_OT_debug_io") # DEBUG: temporary button to trigger exporting everything
layout.operator("COLLECTION_OT_io_export_all", icon='EXPORT')
layout.template_collection_exporters()

View File

@ -2350,6 +2350,72 @@ void uiTemplateModifiers(uiLayout * /*layout*/, bContext *C)
#ifdef _MSC_VER
# pragma optimize("", off)
#endif
void draw_export_controls(
bContext *C, uiLayout *layout, PointerRNA *ptr, FileHandlerType *fh, int id)
{
uiLayout *box = uiLayoutBox(layout);
uiLayout *row = uiLayoutRow(box, true);
uiItemR(row, ptr, "filepath", UI_ITEM_NONE, nullptr, ICON_NONE);
uiItemS(row);
uiItemIntO(row, "", ICON_EXPORT, "COLLECTION_OT_io_handler_export", "id", id);
uiItemIntO(row, "", ICON_X, "COLLECTION_OT_io_handler_remove", "id", id);
}
void draw_export_properties(bContext *C, uiLayout *layout, PointerRNA *ptr, FileHandlerType *fh)
{
if (fh->ui_export) {
fh->ui_export(C, layout, ptr, fh);
}
else {
/* TEMP: Just draw the properties like the KeyMap editor for debugging. */
uiLayout *flow = uiLayoutColumnFlow(layout, 2, false);
RNA_STRUCT_BEGIN_SKIP_RNA_TYPE (ptr, prop) {
const bool is_set = RNA_property_is_set(ptr, prop);
uiBut *but;
/* TEMP: Just filter out some extra stuff for better debug layout. */
if (RNA_property_type(prop) == PROP_POINTER) {
continue;
}
const char *prop_name = RNA_property_identifier(prop);
if (STRPREFIX(prop_name, "filter_") || STRPREFIX(prop_name, "check_") ||
STRPREFIX(prop_name, "sort_") || STREQ(prop_name, "display_type") ||
STREQ(prop_name, "filemode"))
{
continue;
}
uiLayout *box = uiLayoutBox(flow);
uiLayoutSetActive(box, is_set);
uiLayout *row = uiLayoutRow(box, false);
/* property value */
uiItemFullR(row, ptr, prop, -1, 0, UI_ITEM_NONE, nullptr, ICON_NONE);
if (is_set) {
/* unset operator */
uiBlock *block = uiLayoutGetBlock(row);
UI_block_emboss_set(block, UI_EMBOSS_NONE);
but = uiDefIconButO(block,
UI_BTYPE_BUT,
"UI_OT_unset_property_button",
WM_OP_EXEC_DEFAULT,
ICON_X,
0,
0,
UI_UNIT_X,
UI_UNIT_Y,
nullptr);
but->rnapoin = *ptr;
but->rnaprop = prop;
UI_block_emboss_set(block, UI_EMBOSS);
}
}
RNA_STRUCT_END;
}
}
void uiTemplateCollectionExporters(uiLayout *layout, bContext *C)
{
ARegion *region = CTX_wm_region(C);
@ -2357,7 +2423,9 @@ void uiTemplateCollectionExporters(uiLayout *layout, bContext *C)
LayerCollection *layer_coll = BKE_view_layer_active_collection_get(view_layer);
ListBase *io_handlers = &layer_coll->collection->io_handlers;
LISTBASE_FOREACH (IOHandlerData *, data, io_handlers) {
/* Draw all the IO handlers. */
int id = 0;
LISTBASE_FOREACH_INDEX (IOHandlerData *, data, io_handlers, id) {
FileHandlerType *fh = BKE_file_handler_find(data->fh_idname);
if (!fh) {
continue;
@ -2368,61 +2436,18 @@ void uiTemplateCollectionExporters(uiLayout *layout, bContext *C)
continue;
}
PointerRNA ptr = RNA_pointer_create(nullptr, ot->srna, data->export_properties);
PointerRNA prop_ptr = RNA_pointer_create(nullptr, ot->srna, data->export_properties);
PointerRNA io_handler_ptr = RNA_pointer_create(nullptr, &RNA_IOHandlerData, data);
if (fh->ui_export) {
fh->ui_export(C, layout, &ptr, fh);
}
else {
/* TEMP: Just draw the properties like the KeyMap editor for debugging. */
uiLayout *flow = uiLayoutColumnFlow(layout, 2, false);
RNA_STRUCT_BEGIN_SKIP_RNA_TYPE (&ptr, prop) {
const bool is_set = RNA_property_is_set(&ptr, prop);
uiBut *but;
/* TEMP: Just filter out some extra stuff for better debug layout. */
if (RNA_property_type(prop) == PROP_POINTER) {
continue;
}
const char *prop_name = RNA_property_identifier(prop);
if (STRPREFIX(prop_name, "filter_") || STRPREFIX(prop_name, "check_") ||
STRPREFIX(prop_name, "sort_") || STREQ(prop_name, "display_type") ||
STREQ(prop_name, "filemode"))
{
continue;
}
uiLayout *box = uiLayoutBox(flow);
uiLayoutSetActive(box, is_set);
uiLayout *row = uiLayoutRow(box, false);
/* property value */
uiItemFullR(row, &ptr, prop, -1, 0, UI_ITEM_NONE, nullptr, ICON_NONE);
if (is_set) {
/* unset operator */
uiBlock *block = uiLayoutGetBlock(row);
UI_block_emboss_set(block, UI_EMBOSS_NONE);
but = uiDefIconButO(block,
UI_BTYPE_BUT,
"UI_OT_unset_property_button",
WM_OP_EXEC_DEFAULT,
ICON_X,
0,
0,
UI_UNIT_X,
UI_UNIT_Y,
nullptr);
but->rnapoin = ptr;
but->rnaprop = prop;
UI_block_emboss_set(block, UI_EMBOSS);
}
}
RNA_STRUCT_END;
if (uiLayout *panel_layout = uiLayoutPanel(C, layout, fh->label, &io_handler_ptr, "is_open")) {
draw_export_controls(C, panel_layout, &prop_ptr, fh, id);
draw_export_properties(C, panel_layout, &prop_ptr, fh);
}
}
}
#ifdef _MSC_VER
# pragma optimize("", on)
#endif
/** \} */

View File

@ -233,12 +233,6 @@ static void wm_usd_export_draw(const bContext *C,
uiLayout *box = uiLayoutBox(layout);
/* TEMP: The filepath layout should eventually happen in some common place. */
if (CTX_wm_space_properties(C)) {
col = uiLayoutColumn(box, true);
uiItemR(col, ptr, "filepath", UI_ITEM_NONE, nullptr, ICON_NONE);
}
col = uiLayoutColumn(box, true);
uiItemR(col, ptr, "selected_objects_only", UI_ITEM_NONE, nullptr, ICON_NONE);
uiItemR(col, ptr, "visible_objects_only", UI_ITEM_NONE, nullptr, ICON_NONE);

View File

@ -446,6 +446,7 @@ static int collection_io_handler_add_exec(bContext *C, wmOperator *op)
IDPropertyTemplate val{};
data->export_properties = IDP_New(IDP_GROUP, &val, "wmOpItemProp");
data->flag |= IO_HANDLER_PANEL_OPEN;
deadpin marked this conversation as resolved Outdated

io_wmOpItemProp is a bit of a strange name. Any reason it's not export_properties?

`io_wmOpItemProp` is a bit of a strange name. Any reason it's not `export_properties`?
BLI_addtail(io_handlers, data);
@ -471,27 +472,98 @@ void COLLECTION_OT_io_handler_add(wmOperatorType *ot)
RNA_def_string(ot->srna, "name", nullptr, MAX_ID_NAME - 2, "Name", "FileHandler idname");
}
static int collection_debug_io_exec(bContext *C, wmOperator * /*op*/)
static int collection_io_handler_remove_exec(bContext *C, wmOperator *op)
{
ViewLayer *view_layer = CTX_data_view_layer(C);
LayerCollection *layer_coll = BKE_view_layer_active_collection_get(view_layer);
ListBase *io_handlers = &layer_coll->collection->io_handlers;
int id = RNA_int_get(op->ptr, "id");
IOHandlerData *data = static_cast<IOHandlerData *>(
BLI_listbase_string_or_index_find(io_handlers, nullptr, 0, id));
if (data->export_properties) {
IDP_FreeProperty(data->export_properties);
data->export_properties = nullptr;
}
BLI_remlink(io_handlers, data);
return OPERATOR_FINISHED;
}
void COLLECTION_OT_io_handler_remove(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Remove IO Handler";
ot->description = "Remove IO Handler";
ot->idname = "COLLECTION_OT_io_handler_remove";
/* api callbacks */
ot->exec = collection_io_handler_remove_exec;
ot->poll = ED_operator_objectmode;
deadpin marked this conversation as resolved Outdated

COLLECTION_OT_exporter_remove

`COLLECTION_OT_exporter_remove`
/* flags */
deadpin marked this conversation as resolved Outdated

Most likely have no consequences here, but would rather remove the link from the listbase before starting to free its data?

Most likely have no consequences here, but would rather remove the link from the listbase before starting to free its data?
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
deadpin marked this conversation as resolved Outdated

Same comment about poll function.

Same comment about poll function.
RNA_def_int(ot->srna, "id", 0, 0, INT_MAX, "Id", "IO Handler id", 0, INT_MAX);
}
static int io_handler_export(bContext *C, IOHandlerData *data)
{
deadpin marked this conversation as resolved Outdated

IO Handler index -> Exporter index

IO Handler index -> Exporter index
FileHandlerType *fh = BKE_file_handler_find(data->fh_idname);
if (!fh) {
return OPERATOR_CANCELLED;
}
wmOperatorType *ot = WM_operatortype_find(fh->export_operator, false);
if (!ot) {
return OPERATOR_CANCELLED;
}
/* Invoke operator with the properties stored on the Collection. */
PointerRNA ptr = RNA_pointer_create(nullptr, ot->srna, data->export_properties);
return WM_operator_name_call_ptr(C, ot, WM_OP_EXEC_DEFAULT, &ptr, nullptr);
}
static int collection_io_handler_export_exec(bContext *C, wmOperator *op)
{
ViewLayer *view_layer = CTX_data_view_layer(C);
LayerCollection *layer_coll = BKE_view_layer_active_collection_get(view_layer);
ListBase *io_handlers = &layer_coll->collection->io_handlers;
int id = RNA_int_get(op->ptr, "id");
IOHandlerData *data = static_cast<IOHandlerData *>(
BLI_listbase_string_or_index_find(io_handlers, nullptr, 0, id));
return io_handler_export(C, data);
}
void COLLECTION_OT_io_handler_export(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Export IO Handler";
ot->description = "Trigger export of the IO Handler";
ot->idname = "COLLECTION_OT_io_handler_export";
/* api callbacks */
ot->exec = collection_io_handler_export_exec;
ot->poll = ED_operator_objectmode;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
RNA_def_int(ot->srna, "id", 0, 0, INT_MAX, "Id", "IO Handler id", 0, INT_MAX);
}
static int collection_io_export_all_exec(bContext *C, wmOperator * /*op*/)
{
ViewLayer *view_layer = CTX_data_view_layer(C);
LayerCollection *layer_coll = BKE_view_layer_active_collection_get(view_layer);
ListBase *io_handlers = &layer_coll->collection->io_handlers;
LISTBASE_FOREACH (IOHandlerData *, data, io_handlers) {
FileHandlerType *fh = BKE_file_handler_find(data->fh_idname);
if (!fh) {
continue;
}
wmOperatorType *ot = WM_operatortype_find(fh->export_operator, false);
if (!ot) {
continue;
}
printf("DEBUG: Invoking %s\n", fh->export_operator);
/* Invoke operator with the properties stored on the Collection. */
PointerRNA ptr = RNA_pointer_create(nullptr, ot->srna, data->export_properties);
WM_operator_name_call_ptr(C, ot, WM_OP_EXEC_DEFAULT, &ptr, nullptr);
io_handler_export(C, data);
/* TODO: Should we continue calling operators if one fails? */
/* TODO: What's the best way to surface the problem? Reports? */
@ -500,15 +572,15 @@ static int collection_debug_io_exec(bContext *C, wmOperator * /*op*/)
return OPERATOR_FINISHED;
}
void COLLECTION_OT_debug_io(wmOperatorType *ot)
void COLLECTION_OT_io_export_all(wmOperatorType *ot)
deadpin marked this conversation as resolved Outdated

Same comment about poll.

Same comment about poll.
{
/* identifiers */
ot->name = "debug: execute handlers";
ot->description = "debug: execute handlers";
ot->idname = "COLLECTION_OT_debug_io";
ot->name = "Export All";
ot->description = "Export all configured IO Handlers";
ot->idname = "COLLECTION_OT_io_export_all";
deadpin marked this conversation as resolved Outdated

IO Handler index -> Exporter index

IO Handler index -> Exporter index
/* api callbacks */
ot->exec = collection_debug_io_exec;
ot->exec = collection_io_export_all_exec;
ot->poll = ED_operator_objectmode;
/* flags */
@ -545,7 +617,9 @@ void collection_io_handler_register()
WM_menutype_add(mt);
WM_operatortype_append(COLLECTION_OT_io_handler_add);
WM_operatortype_append(COLLECTION_OT_debug_io);
WM_operatortype_append(COLLECTION_OT_io_handler_remove);
WM_operatortype_append(COLLECTION_OT_io_handler_export);
WM_operatortype_append(COLLECTION_OT_io_export_all);
}
} // namespace blender::ed::object

View File

@ -15,9 +15,8 @@
#include "DNA_listBase.h"
struct Collection;
struct GHash;
struct Object;
struct PointerRNA;
struct GHash;
/* Light linking relation of a collection or an object. */
typedef struct CollectionLightLinking {
@ -71,8 +70,15 @@ typedef struct IOHandlerData {
char fh_idname[64];
IDProperty *export_properties;
uint32_t flag;
uint32_t _pad0;
} IOHandlerData;
typedef enum IOHandlerPanelFlag {
IO_HANDLER_PANEL_OPEN = 1 << 0,
} IOHandlerPanelFlag;
/* Light linking state of object or collection: defines how they react to the emitters in the
* scene. See the comment for the link_state in the CollectionLightLinking for the details. */
typedef enum eCollectionLightLinkingState {

View File

@ -567,10 +567,17 @@ static void rna_def_collection_child(BlenderRNA *brna)
static void rna_def_io_handler_data(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
srna = RNA_def_struct(brna, "IOHandlerData", nullptr);
RNA_def_struct_sdna(srna, "IOHandlerData");
RNA_def_struct_ui_text(srna, "IO Handler Data", "IO Handlers configured for the collection");
deadpin marked this conversation as resolved Outdated

I wonder if we can just call this CollectionExport? I don't really see where we would generalize this. If we add import at some point I think it would be a separate data types, maybe sharing a base class if there is a reason.

I wonder if we can just call this `CollectionExport`? I don't really see where we would generalize this. If we add import at some point I think it would be a separate data types, maybe sharing a base class if there is a reason.
prop = RNA_def_property(srna, "is_open", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "flag", IO_HANDLER_PANEL_OPEN);
RNA_def_property_ui_text(prop, "Is Open", "Whether the panel is expanded or closed");
RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE);
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_PROPERTIES, nullptr);
}
void RNA_def_collections(BlenderRNA *brna)
deadpin marked this conversation as resolved Outdated

Can you add exporter_properties here, so scripts can modify the settings?

I think you could add a pointer property with a custom get function (and not set function). The get function would then create a pointer with RNA_pointer_create(owner_id, ot->srna, data->export_properties).

If for some reason the operator type is not available, I think it can fall back to type RNA_IDPropertyWrapPtr.

Can you add `exporter_properties` here, so scripts can modify the settings? I think you could add a pointer property with a custom get function (and not set function). The get function would then create a pointer with `RNA_pointer_create(owner_id, ot->srna, data->export_properties)`. If for some reason the operator type is not available, I think it can fall back to type `RNA_IDPropertyWrapPtr`.