Collection IO: Enable file exporters to be specified on Collections #116646
|
@ -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()
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
/** \} */
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
||||
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
Brecht Van Lommel
commented
`COLLECTION_OT_exporter_remove`
|
||||
|
||||
/* flags */
|
||||
deadpin marked this conversation as resolved
Outdated
Bastien Montagne
commented
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
Brecht Van Lommel
commented
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
Brecht Van Lommel
commented
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
Brecht Van Lommel
commented
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
Brecht Van Lommel
commented
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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
Brecht Van Lommel
commented
I wonder if we can just call this 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
Brecht Van Lommel
commented
Can you add 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 If for some reason the operator type is not available, I think it can fall back to type 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`.
|
||||
|
|
io_wmOpItemProp
is a bit of a strange name. Any reason it's notexport_properties
?