Collection IO: Enable file exporters to be specified on Collections #116646
@ -1099,7 +1099,7 @@ class Menu(StructRNA, _GenericUI, metaclass=RNAMeta):
|
||||
def path_menu(self, searchpaths, operator, *,
|
||||
props_default=None, prop_filepath="filepath",
|
||||
filter_ext=None, filter_path=None, display_name=None,
|
||||
add_operator=None):
|
||||
add_operator=None, add_operator_props=None):
|
||||
"""
|
||||
Populate a menu from a list of paths.
|
||||
|
||||
@ -1176,6 +1176,9 @@ class Menu(StructRNA, _GenericUI, metaclass=RNAMeta):
|
||||
props = row.operator(add_operator, text="", icon='REMOVE')
|
||||
props.name = name
|
||||
props.remove_name = True
|
||||
if add_operator_props is not None:
|
||||
for attr, value in add_operator_props.items():
|
||||
setattr(props, attr, value)
|
||||
|
||||
if add_operator:
|
||||
wm = bpy.data.window_managers[0]
|
||||
@ -1189,6 +1192,9 @@ class Menu(StructRNA, _GenericUI, metaclass=RNAMeta):
|
||||
|
||||
props = row.operator(add_operator, text="", icon='ADD')
|
||||
props.name = wm.preset_name
|
||||
if add_operator_props is not None:
|
||||
for attr, value in add_operator_props.items():
|
||||
setattr(props, attr, value)
|
||||
|
||||
def draw_preset(self, _context):
|
||||
"""
|
||||
@ -1205,12 +1211,14 @@ class Menu(StructRNA, _GenericUI, metaclass=RNAMeta):
|
||||
ext_valid = getattr(self, "preset_extensions", {".py", ".xml"})
|
||||
props_default = getattr(self, "preset_operator_defaults", None)
|
||||
add_operator = getattr(self, "preset_add_operator", None)
|
||||
add_operator_props = getattr(self, "preset_add_operator_properties", None)
|
||||
self.path_menu(
|
||||
bpy.utils.preset_paths(self.preset_subdir),
|
||||
self.preset_operator,
|
||||
props_default=props_default,
|
||||
filter_ext=lambda ext: ext.lower() in ext_valid,
|
||||
add_operator=add_operator,
|
||||
add_operator_props=add_operator_props,
|
||||
display_name=lambda name: bpy.path.display_name(name, title_case=False)
|
||||
)
|
||||
|
||||
|
@ -7,6 +7,7 @@ from bpy.types import (
|
||||
Menu,
|
||||
Operator,
|
||||
OperatorFileListElement,
|
||||
Panel,
|
||||
WindowManager,
|
||||
)
|
||||
from bpy.props import (
|
||||
@ -18,6 +19,7 @@ from bpy.app.translations import (
|
||||
pgettext_rpt as rpt_,
|
||||
pgettext_data as data_,
|
||||
)
|
||||
from bl_ui.utils import PresetPanel
|
||||
|
||||
|
||||
# For preset popover menu
|
||||
@ -750,6 +752,24 @@ class WM_MT_operator_presets(Menu):
|
||||
preset_operator = "script.execute_preset"
|
||||
|
||||
|
||||
class WM_PT_operator_presets(PresetPanel, Panel):
|
||||
bl_label = "Operator Presets"
|
||||
preset_add_operator = "wm.operator_preset_add"
|
||||
preset_operator = "script.execute_preset"
|
||||
|
||||
@property
|
||||
def preset_subdir(self):
|
||||
return AddPresetOperator.operator_path(self.operator)
|
||||
|
||||
@property
|
||||
def preset_add_operator_properties(self):
|
||||
return {"operator": self.operator}
|
||||
|
||||
def draw(self, context):
|
||||
self.operator = context.active_operator.bl_idname
|
||||
PresetPanel.draw(self, context)
|
||||
|
||||
|
||||
class WM_OT_operator_presets_cleanup(Operator):
|
||||
"""Remove outdated operator properties from presets that may cause problems"""
|
||||
|
||||
@ -921,5 +941,6 @@ classes = (
|
||||
AddPresetEEVEERaytracing,
|
||||
ExecutePreset,
|
||||
WM_MT_operator_presets,
|
||||
WM_PT_operator_presets,
|
||||
WM_OT_operator_presets_cleanup,
|
||||
)
|
||||
|
@ -49,6 +49,23 @@ class COLLECTION_PT_collection_flags(CollectionButtonsPanel, Panel):
|
||||
col.prop(vlc, "indirect_only", toggle=False)
|
||||
|
||||
|
||||
class COLLECTION_PT_exporters(CollectionButtonsPanel, Panel):
|
||||
bl_label = "Exporters"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
collection = context.collection
|
||||
|
||||
row = layout.row()
|
||||
col = row.column()
|
||||
col.operator("wm.call_menu", text="Add", icon='ADD').name = "COLLECTION_MT_exporter_add"
|
||||
col = row.column()
|
||||
col.operator("COLLECTION_OT_export_all", icon='EXPORT')
|
||||
col.enabled = len(collection.exporters) > 0
|
||||
|
||||
layout.template_collection_exporters()
|
||||
|
||||
|
||||
class COLLECTION_MT_context_menu_instance_offset(Menu):
|
||||
bl_label = "Instance Offset"
|
||||
|
||||
@ -114,6 +131,7 @@ classes = (
|
||||
COLLECTION_PT_instancing,
|
||||
COLLECTION_PT_lineart_collection,
|
||||
COLLECTION_PT_collection_custom_props,
|
||||
COLLECTION_PT_exporters,
|
||||
)
|
||||
|
||||
if __name__ == "__main__": # only for live edit.
|
||||
|
@ -262,6 +262,9 @@ class TOPBAR_MT_file(Menu):
|
||||
|
||||
layout.menu("TOPBAR_MT_file_import", icon='IMPORT')
|
||||
layout.menu("TOPBAR_MT_file_export", icon='EXPORT')
|
||||
row = layout.row()
|
||||
row.operator("wm.collection_export_all")
|
||||
row.enabled = context.view_layer.has_export_collections
|
||||
|
||||
layout.separator()
|
||||
|
||||
|
@ -23,6 +23,7 @@ struct BlendDataReader;
|
||||
struct BlendWriter;
|
||||
struct Collection;
|
||||
struct ID;
|
||||
struct CollectionExport;
|
||||
struct Main;
|
||||
struct Object;
|
||||
struct Scene;
|
||||
@ -64,6 +65,12 @@ void BKE_collection_add_from_collection(Main *bmain,
|
||||
* Free (or release) any data used by this collection (does not free the collection itself).
|
||||
*/
|
||||
void BKE_collection_free_data(Collection *collection);
|
||||
|
||||
/**
|
||||
* Free any data used by the IO handler (does not free the IO handler itself).
|
||||
*/
|
||||
void BKE_collection_exporter_free_data(CollectionExport *data);
|
||||
|
||||
/**
|
||||
* Remove a collection, optionally removing its child objects or moving
|
||||
* them to parent collections.
|
||||
|
@ -23,6 +23,8 @@ struct FileHandlerType {
|
||||
char label[OP_MAX_TYPENAME];
|
||||
/** Import operator name. */
|
||||
char import_operator[OP_MAX_TYPENAME];
|
||||
/** Export operator name. */
|
||||
char export_operator[OP_MAX_TYPENAME];
|
||||
/** Formatted string of file extensions supported by the file handler, each extension should
|
||||
* start with a `.` and be separated by `;`. For Example: `".blend;.ble"`. */
|
||||
char file_extensions_str[FH_MAX_FILE_EXTENSIONS_STR];
|
||||
@ -40,6 +42,11 @@ struct FileHandlerType {
|
||||
* Return a vector of indices in #paths of file paths supported by the file handler.
|
||||
*/
|
||||
blender::Vector<int64_t> filter_supported_paths(const blender::Span<std::string> paths) const;
|
||||
|
||||
/**
|
||||
* Generate a default file name for use with this file handler.
|
||||
*/
|
||||
std::string get_default_filename(const StringRefNull name);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include "BKE_main.hh"
|
||||
#include "BKE_object.hh"
|
||||
#include "BKE_preview_image.hh"
|
||||
#include "BKE_report.hh"
|
||||
#include "BKE_rigidbody.h"
|
||||
#include "BKE_scene.hh"
|
||||
|
||||
@ -108,6 +109,7 @@ static void collection_gobject_hash_ensure(Collection *collection);
|
||||
static void collection_gobject_hash_update_object(Collection *collection,
|
||||
Object *ob_old,
|
||||
CollectionObject *cob);
|
||||
static void collection_exporter_copy(Collection *collection, CollectionExport *data);
|
||||
|
||||
/** \} */
|
||||
|
||||
@ -159,6 +161,7 @@ static void collection_copy_data(Main *bmain,
|
||||
|
||||
BLI_listbase_clear(&collection_dst->gobject);
|
||||
BLI_listbase_clear(&collection_dst->children);
|
||||
BLI_listbase_clear(&collection_dst->exporters);
|
||||
BLI_listbase_clear(&collection_dst->runtime.parents);
|
||||
collection_dst->runtime.gobject_hash = nullptr;
|
||||
|
||||
@ -169,6 +172,9 @@ static void collection_copy_data(Main *bmain,
|
||||
LISTBASE_FOREACH (CollectionObject *, cob, &collection_src->gobject) {
|
||||
collection_object_add(bmain, collection_dst, cob->ob, &cob->light_linking, flag, false);
|
||||
}
|
||||
LISTBASE_FOREACH (CollectionExport *, data, &collection_src->exporters) {
|
||||
collection_exporter_copy(collection_dst, data);
|
||||
}
|
||||
}
|
||||
|
||||
static void collection_free_data(ID *id)
|
||||
@ -187,6 +193,11 @@ static void collection_free_data(ID *id)
|
||||
BLI_freelistN(&collection->children);
|
||||
BLI_freelistN(&collection->runtime.parents);
|
||||
|
||||
LISTBASE_FOREACH (CollectionExport *, data, &collection->exporters) {
|
||||
BKE_collection_exporter_free_data(data);
|
||||
}
|
||||
BLI_freelistN(&collection->exporters);
|
||||
|
||||
/* No need for depsgraph tagging here, since the data is being deleted. */
|
||||
collection_object_cache_free(nullptr, collection, LIB_ID_CREATE_NO_DEG_TAG, 0);
|
||||
}
|
||||
@ -274,6 +285,13 @@ void BKE_collection_blend_write_nolib(BlendWriter *writer, Collection *collectio
|
||||
LISTBASE_FOREACH (CollectionChild *, child, &collection->children) {
|
||||
BLO_write_struct(writer, CollectionChild, child);
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (CollectionExport *, data, &collection->exporters) {
|
||||
BLO_write_struct(writer, CollectionExport, data);
|
||||
if (data->export_properties) {
|
||||
IDP_BlendWrite(writer, data->export_properties);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void collection_blend_write(BlendWriter *writer, ID *id, const void *id_address)
|
||||
@ -324,6 +342,12 @@ void BKE_collection_blend_read_data(BlendDataReader *reader, Collection *collect
|
||||
BLO_read_list(reader, &collection->gobject);
|
||||
BLO_read_list(reader, &collection->children);
|
||||
|
||||
BLO_read_list(reader, &collection->exporters);
|
||||
LISTBASE_FOREACH (CollectionExport *, data, &collection->exporters) {
|
||||
BLO_read_data_address(reader, &data->export_properties);
|
||||
IDP_BlendDataRead(reader, &data->export_properties);
|
||||
}
|
||||
|
||||
BLO_read_data_address(reader, &collection->preview);
|
||||
BKE_previewimg_blend_read(reader, collection->preview);
|
||||
}
|
||||
@ -490,6 +514,13 @@ void BKE_collection_free_data(Collection *collection)
|
||||
collection_free_data(&collection->id);
|
||||
}
|
||||
|
||||
void BKE_collection_exporter_free_data(struct CollectionExport *data)
|
||||
{
|
||||
if (data->export_properties) {
|
||||
IDP_FreeProperty(data->export_properties);
|
||||
}
|
||||
}
|
||||
|
||||
bool BKE_collection_delete(Main *bmain, Collection *collection, bool hierarchy)
|
||||
{
|
||||
/* Master collection is not real datablock, can't be removed. */
|
||||
@ -1353,6 +1384,22 @@ static bool collection_object_remove(
|
||||
return true;
|
||||
}
|
||||
|
||||
static void collection_exporter_copy(Collection *collection, CollectionExport *data)
|
||||
{
|
||||
CollectionExport *new_data = MEM_cnew<CollectionExport>("CollectionExport");
|
||||
STRNCPY(new_data->fh_idname, data->fh_idname);
|
||||
new_data->export_properties = IDP_CopyProperty(data->export_properties);
|
||||
new_data->flag = data->flag;
|
||||
|
||||
/* Clear the `filepath` property. */
|
||||
IDProperty *filepath = IDP_GetPropertyFromGroup(new_data->export_properties, "filepath");
|
||||
if (filepath) {
|
||||
IDP_AssignString(filepath, "");
|
||||
}
|
||||
|
||||
BLI_addtail(&collection->exporters, new_data);
|
||||
}
|
||||
|
||||
bool BKE_collection_object_add_notest(Main *bmain, Collection *collection, Object *ob)
|
||||
{
|
||||
if (ob == nullptr) {
|
||||
|
@ -123,4 +123,9 @@ blender::Vector<int64_t> FileHandlerType::filter_supported_paths(
|
||||
return indices;
|
||||
}
|
||||
|
||||
std::string FileHandlerType::get_default_filename(const StringRefNull name)
|
||||
{
|
||||
return name + (file_extensions.is_empty() ? "" : file_extensions.first());
|
||||
}
|
||||
|
||||
} // namespace blender::bke
|
||||
|
@ -1219,6 +1219,10 @@ static void layer_collection_sync(ViewLayer *view_layer,
|
||||
{
|
||||
child_layer->runtime_flag |= LAYER_COLLECTION_VISIBLE_VIEW_LAYER;
|
||||
}
|
||||
|
||||
if (!BLI_listbase_is_empty(&child_collection->exporters)) {
|
||||
view_layer->flag |= VIEW_LAYER_HAS_EXPORT_COLLECTIONS;
|
||||
}
|
||||
deadpin marked this conversation as resolved
|
||||
}
|
||||
|
||||
/* Replace layer collection list with new one. */
|
||||
@ -1356,6 +1360,9 @@ void BKE_layer_collection_sync(const Scene *scene, ViewLayer *view_layer)
|
||||
static_cast<LayerCollection *>(view_layer->layer_collections.first),
|
||||
layer_resync_mempool);
|
||||
|
||||
/* Clear the cached flag indicating if the view layer has a collection exporter set. */
|
||||
view_layer->flag &= ~VIEW_LAYER_HAS_EXPORT_COLLECTIONS;
|
||||
|
||||
/* Generate new layer connections and object bases when collections changed. */
|
||||
ListBase new_object_bases{};
|
||||
const short parent_exclude = 0, parent_restrict = 0, parent_layer_restrict = 0;
|
||||
|
@ -2709,6 +2709,8 @@ void uiTemplateNodeTreeInterface(uiLayout *layout, PointerRNA *ptr);
|
||||
*/
|
||||
void uiTemplateNodeInputs(uiLayout *layout, bContext *C, PointerRNA *ptr);
|
||||
|
||||
void uiTemplateCollectionExporters(uiLayout *layout, bContext *C);
|
||||
|
||||
/**
|
||||
* \return: True if the list item with unfiltered, unordered index \a item_idx is visible given the
|
||||
* current filter settings.
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include "BLI_path_util.h"
|
||||
#include "BLI_rect.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_string_ref.hh"
|
||||
#include "BLI_string_utils.hh"
|
||||
#include "BLI_time.h"
|
||||
#include "BLI_timecode.h"
|
||||
@ -50,6 +51,7 @@
|
||||
#include "BKE_constraint.h"
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_curveprofile.h"
|
||||
#include "BKE_file_handler.hh"
|
||||
#include "BKE_global.hh"
|
||||
#include "BKE_gpencil_modifier_legacy.h"
|
||||
#include "BKE_idprop.hh"
|
||||
@ -2962,6 +2964,89 @@ void uiTemplateOperatorRedoProperties(uiLayout *layout, const bContext *C)
|
||||
}
|
||||
}
|
||||
|
||||
static wmOperator *minimal_operator_create(wmOperatorType *ot, PointerRNA *properties)
|
||||
{
|
||||
/* Copied from #wm_operator_create.
|
||||
* Create a slimmed down operator suitable only for UI drawing. */
|
||||
wmOperator *op = MEM_cnew<wmOperator>(ot->idname);
|
||||
STRNCPY(op->idname, ot->idname);
|
||||
op->type = ot;
|
||||
|
||||
/* Initialize properties but do not assume ownership of them.
|
||||
* This "minimal" operator owns nothing. */
|
||||
op->ptr = MEM_cnew<PointerRNA>("wmOperatorPtrRNA");
|
||||
op->properties = static_cast<IDProperty *>(properties->data);
|
||||
*op->ptr = *properties;
|
||||
|
||||
return op;
|
||||
}
|
||||
|
||||
static void draw_export_controls(
|
||||
bContext *C, uiLayout *layout, const std::string &label, int index, bool valid)
|
||||
{
|
||||
uiItemL(layout, label.c_str(), ICON_NONE);
|
||||
if (valid) {
|
||||
uiItemPopoverPanel(layout, C, "WM_PT_operator_presets", "", ICON_PRESET);
|
||||
uiItemIntO(layout, "", ICON_EXPORT, "COLLECTION_OT_exporter_export", "index", index);
|
||||
uiItemIntO(layout, "", ICON_X, "COLLECTION_OT_exporter_remove", "index", index);
|
||||
}
|
||||
}
|
||||
|
||||
static void draw_export_properties(bContext *C,
|
||||
uiLayout *layout,
|
||||
wmOperator *op,
|
||||
const std::string &filename)
|
||||
{
|
||||
uiLayout *box = uiLayoutBox(layout);
|
||||
|
||||
PropertyRNA *prop = RNA_struct_find_property(op->ptr, "filepath");
|
||||
std::string placeholder = "//" + filename;
|
||||
uiItemFullR(
|
||||
box, op->ptr, prop, RNA_NO_INDEX, 0, UI_ITEM_NONE, nullptr, ICON_NONE, placeholder.c_str());
|
||||
|
||||
template_operator_property_buts_draw_single(C, op, layout, UI_BUT_LABEL_ALIGN_NONE, 0);
|
||||
}
|
||||
|
||||
void uiTemplateCollectionExporters(uiLayout *layout, bContext *C)
|
||||
{
|
||||
Collection *collection = CTX_data_collection(C);
|
||||
ListBase *exporters = &collection->exporters;
|
||||
|
||||
/* Draw all the IO handlers. */
|
||||
int index = 0;
|
||||
LISTBASE_FOREACH_INDEX (CollectionExport *, data, exporters, index) {
|
||||
using namespace blender;
|
||||
PointerRNA exporter_ptr = RNA_pointer_create(&collection->id, &RNA_CollectionExport, data);
|
||||
PanelLayout panel = uiLayoutPanelProp(C, layout, &exporter_ptr, "is_open");
|
||||
|
||||
bke::FileHandlerType *fh = bke::file_handler_find(data->fh_idname);
|
||||
if (!fh) {
|
||||
std::string label = std::string(IFACE_("Undefined")) + " " + data->fh_idname;
|
||||
draw_export_controls(C, panel.header, label, index, false);
|
||||
continue;
|
||||
}
|
||||
|
||||
wmOperatorType *ot = WM_operatortype_find(fh->export_operator, false);
|
||||
if (!ot) {
|
||||
std::string label = std::string(IFACE_("Undefined")) + " " + fh->export_operator;
|
||||
draw_export_controls(C, panel.header, label, index, false);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Assign temporary operator to uiBlock, which takes ownership. */
|
||||
PointerRNA properties = RNA_pointer_create(&collection->id, ot->srna, data->export_properties);
|
||||
wmOperator *op = minimal_operator_create(ot, &properties);
|
||||
UI_block_set_active_operator(uiLayoutGetBlock(panel.header), op, true);
|
||||
|
||||
/* Draw panel header and contents. */
|
||||
std::string label(fh->label);
|
||||
draw_export_controls(C, panel.header, label, index, true);
|
||||
if (panel.body) {
|
||||
draw_export_properties(C, panel.body, op, fh->get_default_filename(collection->id.name + 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
@ -108,6 +108,8 @@ static int wm_obj_export_exec(bContext *C, wmOperator *op)
|
||||
|
||||
export_params.reports = op->reports;
|
||||
|
||||
RNA_string_get(op->ptr, "collection", export_params.collection);
|
||||
|
||||
OBJ_export(C, &export_params);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
@ -197,8 +199,7 @@ static void ui_obj_export_settings(uiLayout *layout, PointerRNA *imfptr)
|
||||
|
||||
static void wm_obj_export_draw(bContext * /*C*/, wmOperator *op)
|
||||
{
|
||||
PointerRNA ptr = RNA_pointer_create(nullptr, op->type->srna, op->properties);
|
||||
ui_obj_export_settings(op->layout, &ptr);
|
||||
ui_obj_export_settings(op->layout, op->ptr);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -387,6 +388,9 @@ void WM_OT_obj_export(wmOperatorType *ot)
|
||||
/* Only show .obj or .mtl files by default. */
|
||||
prop = RNA_def_string(ot->srna, "filter_glob", "*.obj;*.mtl", 0, "Extension Filter", "");
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN);
|
||||
|
||||
prop = RNA_def_string(ot->srna, "collection", nullptr, MAX_IDPROP_NAME, "Collection", nullptr);
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN);
|
||||
}
|
||||
|
||||
static int wm_obj_import_exec(bContext *C, wmOperator *op)
|
||||
@ -550,6 +554,7 @@ void obj_file_handler_add()
|
||||
auto fh = std::make_unique<blender::bke::FileHandlerType>();
|
||||
STRNCPY(fh->idname, "IO_FH_obj");
|
||||
STRNCPY(fh->import_operator, "WM_OT_obj_import");
|
||||
STRNCPY(fh->export_operator, "WM_OT_obj_export");
|
||||
STRNCPY(fh->label, "Wavefront OBJ");
|
||||
STRNCPY(fh->file_extensions_str, ".obj");
|
||||
fh->poll_drop = poll_file_object_drop;
|
||||
|
@ -215,6 +215,7 @@ static int wm_usd_export_exec(bContext *C, wmOperator *op)
|
||||
};
|
||||
|
||||
STRNCPY(params.root_prim_path, root_prim_path);
|
||||
RNA_string_get(op->ptr, "collection", params.collection);
|
||||
|
||||
bool ok = USD_export(C, filepath, ¶ms, as_background_job, op->reports);
|
||||
|
||||
@ -348,6 +349,9 @@ void WM_OT_usd_export(wmOperatorType *ot)
|
||||
"Only export visible objects. Invisible parents of exported objects are "
|
||||
"exported as empty transforms");
|
||||
|
||||
prop = RNA_def_string(ot->srna, "collection", nullptr, MAX_IDPROP_NAME, "Collection", nullptr);
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN);
|
||||
|
||||
RNA_def_boolean(
|
||||
ot->srna,
|
||||
"export_animation",
|
||||
@ -832,6 +836,7 @@ void usd_file_handler_add()
|
||||
auto fh = std::make_unique<blender::bke::FileHandlerType>();
|
||||
STRNCPY(fh->idname, "IO_FH_usd");
|
||||
STRNCPY(fh->import_operator, "WM_OT_usd_import");
|
||||
STRNCPY(fh->export_operator, "WM_OT_usd_export");
|
||||
STRNCPY(fh->label, "Universal Scene Description");
|
||||
STRNCPY(fh->file_extensions_str, ".usd;.usda;.usdc;.usdz");
|
||||
fh->poll_drop = poll_file_object_drop;
|
||||
|
@ -8,6 +8,8 @@
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "BLI_path_util.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "DNA_collection_types.h"
|
||||
@ -16,11 +18,16 @@
|
||||
|
||||
#include "BKE_collection.hh"
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_file_handler.hh"
|
||||
#include "BKE_idprop.hh"
|
||||
#include "BKE_layer.hh"
|
||||
#include "BKE_lib_id.hh"
|
||||
#include "BKE_main.hh"
|
||||
#include "BKE_object.hh"
|
||||
#include "BKE_report.hh"
|
||||
#include "BKE_screen.hh"
|
||||
|
||||
#include "BLT_translation.hh"
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
#include "DEG_depsgraph_build.hh"
|
||||
@ -36,6 +43,7 @@
|
||||
#include "RNA_enum_types.hh"
|
||||
#include "RNA_prototypes.h"
|
||||
|
||||
#include "UI_interface.hh"
|
||||
#include "UI_interface_icons.hh"
|
||||
|
||||
#include "object_intern.hh"
|
||||
@ -416,6 +424,329 @@ void COLLECTION_OT_create(wmOperatorType *ot)
|
||||
ot->srna, "name", "Collection", MAX_ID_NAME - 2, "Name", "Name of the new collection");
|
||||
}
|
||||
|
||||
static bool collection_exporter_poll(bContext *C)
|
||||
{
|
||||
return CTX_data_collection(C) != nullptr;
|
||||
}
|
||||
|
||||
static bool collection_export_all_poll(bContext *C)
|
||||
{
|
||||
return CTX_data_view_layer(C) != nullptr;
|
||||
}
|
||||
|
||||
deadpin marked this conversation as resolved
Outdated
Brecht Van Lommel
commented
Add Add `BKE_report(op->reports, RPT_ERROR, "File handler '%s' not found'", name)`
|
||||
static int collection_exporter_add_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
using namespace blender;
|
||||
Collection *collection = CTX_data_collection(C);
|
||||
deadpin marked this conversation as resolved
Outdated
Brecht Van Lommel
commented
Add Add `BKE_report(op->reports, RPT_ERROR, "File handler operator '%s' not found'", fh->export_operator)`
|
||||
ListBase *exporters = &collection->exporters;
|
||||
|
||||
char name[MAX_ID_NAME - 2]; /* id name */
|
||||
RNA_string_get(op->ptr, "name", name);
|
||||
|
||||
bke::FileHandlerType *fh = bke::file_handler_find(name);
|
||||
if (!fh) {
|
||||
BKE_reportf(op->reports, RPT_ERROR, "File handler '%s' not found", name);
|
||||
return OPERATOR_CANCELLED;
|
||||
deadpin marked this conversation as resolved
Outdated
Brecht Van Lommel
commented
`io_wmOpItemProp` is a bit of a strange name. Any reason it's not `export_properties`?
|
||||
}
|
||||
|
||||
if (!WM_operatortype_find(fh->export_operator, true)) {
|
||||
BKE_reportf(
|
||||
op->reports, RPT_ERROR, "File handler operator '%s' not found", fh->export_operator);
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
/* Add a new #CollectionExport item to our handler list and fill it with #FileHandlerType
|
||||
* information. Also load in the operator's properties now as well. */
|
||||
CollectionExport *data = MEM_cnew<CollectionExport>("CollectionExport");
|
||||
STRNCPY(data->fh_idname, fh->idname);
|
||||
|
||||
IDPropertyTemplate val{};
|
||||
data->export_properties = IDP_New(IDP_GROUP, &val, "export_properties");
|
||||
data->flag |= IO_HANDLER_PANEL_OPEN;
|
||||
deadpin marked this conversation as resolved
Outdated
Brecht Van Lommel
commented
Rename Rename `COLLECTION_OT_io_handler_add` `COLLECTION_OT_exporter_add`. I don't really see a reason to deviate from the user interface name here.
|
||||
|
||||
BLI_addtail(exporters, data);
|
||||
|
||||
deadpin marked this conversation as resolved
Bastien Montagne
commented
Missing deg tagging of the modified collection (likely just using Same below in remove code. Missing deg tagging of the modified collection (likely just using `ID_RECALC_SYNC_TO_EVAL`?).
Same below in remove code.
|
||||
BKE_view_layer_need_resync_tag(CTX_data_view_layer(C));
|
||||
deadpin marked this conversation as resolved
Outdated
Brecht Van Lommel
commented
This needs a poll function that checks This needs a poll function that checks `CTX_data_collection(C) != nullptr`.
|
||||
DEG_id_tag_update(&collection->id, ID_RECALC_SYNC_TO_EVAL);
|
||||
|
||||
deadpin marked this conversation as resolved
Outdated
Bastien Montagne
commented
This should not be called here (same for the remove op code below). Use instead Also, this should be called before deg tagging and notifiers are sent. This should not be called here (same for the remove op code below). Use instead `BKE_view_layer_need_resync_tag`, code that needs to get a valid viewlayer data is in charge of ensuring it itself (via `BKE_view_layer_synced_ensure`).
Also, this should be called before deg tagging and notifiers are sent.
|
||||
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_PROPERTIES, nullptr);
|
||||
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_OUTLINER, nullptr);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void COLLECTION_OT_exporter_add(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Add Exporter";
|
||||
ot->description = "Add Exporter";
|
||||
ot->idname = "COLLECTION_OT_exporter_add";
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = collection_exporter_add_exec;
|
||||
ot->poll = collection_exporter_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
RNA_def_string(ot->srna, "name", nullptr, MAX_ID_NAME - 2, "Name", "FileHandler idname");
|
||||
}
|
||||
|
||||
static int collection_exporter_remove_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Collection *collection = CTX_data_collection(C);
|
||||
ListBase *exporters = &collection->exporters;
|
||||
|
||||
int index = RNA_int_get(op->ptr, "index");
|
||||
CollectionExport *data = static_cast<CollectionExport *>(BLI_findlink(exporters, index));
|
||||
if (!data) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
deadpin marked this conversation as resolved
Outdated
Brecht Van Lommel
commented
`COLLECTION_OT_exporter_remove`
|
||||
|
||||
BLI_remlink(exporters, data);
|
||||
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?
|
||||
BKE_collection_exporter_free_data(data);
|
||||
|
||||
deadpin marked this conversation as resolved
Outdated
Brecht Van Lommel
commented
Same comment about poll function. Same comment about poll function.
|
||||
MEM_freeN(data);
|
||||
|
||||
BKE_view_layer_need_resync_tag(CTX_data_view_layer(C));
|
||||
DEG_id_tag_update(&collection->id, ID_RECALC_SYNC_TO_EVAL);
|
||||
|
||||
deadpin marked this conversation as resolved
Outdated
Brecht Van Lommel
commented
IO Handler index -> Exporter index IO Handler index -> Exporter index
|
||||
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_PROPERTIES, nullptr);
|
||||
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_OUTLINER, nullptr);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static int collection_exporter_remove_invoke(bContext *C,
|
||||
wmOperator *op,
|
||||
const wmEvent * /*event*/)
|
||||
{
|
||||
return WM_operator_confirm_ex(
|
||||
C, op, IFACE_("Remove exporter?"), nullptr, IFACE_("Delete"), ALERT_ICON_NONE, false);
|
||||
}
|
||||
|
||||
void COLLECTION_OT_exporter_remove(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Remove Exporter";
|
||||
ot->description = "Remove Exporter";
|
||||
ot->idname = "COLLECTION_OT_exporter_remove";
|
||||
|
||||
/* api callbacks */
|
||||
ot->invoke = collection_exporter_remove_invoke;
|
||||
ot->exec = collection_exporter_remove_exec;
|
||||
ot->poll = collection_exporter_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "Exporter index", 0, INT_MAX);
|
||||
}
|
||||
|
||||
static int collection_exporter_export(bContext *C,
|
||||
wmOperator *op,
|
||||
CollectionExport *data,
|
||||
Collection *collection)
|
||||
{
|
||||
using namespace blender;
|
||||
bke::FileHandlerType *fh = bke::file_handler_find(data->fh_idname);
|
||||
if (!fh) {
|
||||
BKE_reportf(op->reports, RPT_ERROR, "File handler '%s' not found", data->fh_idname);
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
wmOperatorType *ot = WM_operatortype_find(fh->export_operator, false);
|
||||
if (!ot) {
|
||||
BKE_reportf(
|
||||
op->reports, RPT_ERROR, "File handler operator '%s' not found", fh->export_operator);
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
/* Execute operator with our stored properties. */
|
||||
/* TODO: Cascade settings down from parent collections(?) */
|
||||
IDProperty *op_props = IDP_CopyProperty(data->export_properties);
|
||||
PointerRNA properties = RNA_pointer_create(nullptr, ot->srna, op_props);
|
||||
const char *collection_name = collection->id.name + 2;
|
||||
|
||||
/* Ensure we have a valid filepath set. Create one if the user has not specified anything yet. */
|
||||
deadpin marked this conversation as resolved
Outdated
Brecht Van Lommel
commented
`COLLECTION_OT_export` perhaps.
|
||||
char filepath[FILE_MAX];
|
||||
RNA_string_get(&properties, "filepath", filepath);
|
||||
if (!filepath[0]) {
|
||||
BLI_path_join(
|
||||
deadpin marked this conversation as resolved
Outdated
Brecht Van Lommel
commented
Same comment about poll. Same comment about poll.
|
||||
filepath, sizeof(filepath), "//", fh->get_default_filename(collection_name).c_str());
|
||||
}
|
||||
else {
|
||||
char filename[FILENAME_MAX];
|
||||
BLI_path_split_file_part(filepath, filename, sizeof(filename));
|
||||
deadpin marked this conversation as resolved
Outdated
Brecht Van Lommel
commented
IO Handler index -> Exporter index IO Handler index -> Exporter index
|
||||
if (!filename[0] || !BLI_path_extension(filename)) {
|
||||
BKE_reportf(op->reports, RPT_ERROR, "File path '%s' is not a valid file", filepath);
|
||||
|
||||
IDP_FreeProperty(op_props);
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
}
|
||||
|
||||
const Main *bmain = CTX_data_main(C);
|
||||
BLI_path_abs(filepath, BKE_main_blendfile_path(bmain));
|
||||
|
||||
RNA_string_set(&properties, "filepath", filepath);
|
||||
RNA_string_set(&properties, "collection", collection_name);
|
||||
int op_result = WM_operator_name_call_ptr(C, ot, WM_OP_EXEC_DEFAULT, &properties, nullptr);
|
||||
|
||||
IDP_FreeProperty(op_props);
|
||||
return op_result;
|
||||
}
|
||||
|
||||
static int collection_exporter_export_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Collection *collection = CTX_data_collection(C);
|
||||
ListBase *exporters = &collection->exporters;
|
||||
|
||||
int index = RNA_int_get(op->ptr, "index");
|
||||
CollectionExport *data = static_cast<CollectionExport *>(BLI_findlink(exporters, index));
|
||||
if (!data) {
|
||||
return OPERATOR_CANCELLED;
|
||||
deadpin marked this conversation as resolved
Outdated
Brecht Van Lommel
commented
`COLLECTION_OT_export_all`
|
||||
}
|
||||
|
||||
return collection_exporter_export(C, op, data, collection);
|
||||
}
|
||||
deadpin marked this conversation as resolved
Outdated
Brecht Van Lommel
commented
Same comment about poll. Same comment about poll.
|
||||
|
||||
void COLLECTION_OT_exporter_export(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Export";
|
||||
ot->description = "Invoke the export operation";
|
||||
ot->idname = "COLLECTION_OT_exporter_export";
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = collection_exporter_export_exec;
|
||||
ot->poll = collection_exporter_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "Exporter index", 0, INT_MAX);
|
||||
}
|
||||
|
||||
static int collection_export(bContext *C, wmOperator *op, Collection *collection)
|
||||
{
|
||||
ListBase *exporters = &collection->exporters;
|
||||
|
||||
LISTBASE_FOREACH (CollectionExport *, data, exporters) {
|
||||
if (collection_exporter_export(C, op, data, collection) != OPERATOR_FINISHED) {
|
||||
/* Do not continue calling exporters if we encounter one that fails. */
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
}
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static int collection_io_export_all_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Collection *collection = CTX_data_collection(C);
|
||||
return collection_export(C, op, collection);
|
||||
}
|
||||
|
||||
void COLLECTION_OT_export_all(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Export All";
|
||||
deadpin marked this conversation as resolved
Outdated
Brecht Van Lommel
commented
Same comment about poll. Same comment about poll.
|
||||
ot->description = "Invoke all configured exporters on this collection";
|
||||
ot->idname = "COLLECTION_OT_export_all";
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = collection_io_export_all_exec;
|
||||
ot->poll = collection_exporter_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
static int collection_export_recursive(bContext *C,
|
||||
wmOperator *op,
|
||||
LayerCollection *layer_collection)
|
||||
{
|
||||
/* Skip collections which have been Excluded in the View Layer. */
|
||||
if (layer_collection->flag & LAYER_COLLECTION_EXCLUDE) {
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
if (collection_export(C, op, layer_collection->collection) != OPERATOR_FINISHED) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (LayerCollection *, child, &layer_collection->layer_collections) {
|
||||
if (collection_export_recursive(C, op, child) != OPERATOR_FINISHED) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
}
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static int wm_collection_export_all_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
ViewLayer *view_layer = CTX_data_view_layer(C);
|
||||
LISTBASE_FOREACH (LayerCollection *, layer_collection, &view_layer->layer_collections) {
|
||||
if (collection_export_recursive(C, op, layer_collection) != OPERATOR_FINISHED) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
}
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void WM_OT_collection_export_all(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Export All Collections";
|
||||
ot->description = "Invoke all configured exporters for all collections";
|
||||
ot->idname = "WM_OT_collection_export_all";
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = wm_collection_export_all_exec;
|
||||
ot->poll = collection_export_all_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
static void collection_exporter_menu_draw(const bContext * /*C*/, Menu *menu)
|
||||
{
|
||||
using namespace blender;
|
||||
uiLayout *layout = menu->layout;
|
||||
|
||||
/* Add all file handlers capable of being exported to the menu. */
|
||||
bool at_least_one = false;
|
||||
for (const auto &fh : bke::file_handlers()) {
|
||||
if (WM_operatortype_find(fh->export_operator, true)) {
|
||||
uiItemStringO(
|
||||
layout, fh->label, ICON_NONE, "COLLECTION_OT_exporter_add", "name", fh->idname);
|
||||
at_least_one = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!at_least_one) {
|
||||
uiItemL(layout, IFACE_("No file handlers available"), ICON_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
void collection_exporter_register()
|
||||
{
|
||||
MenuType *mt = MEM_cnew<MenuType>(__func__);
|
||||
STRNCPY(mt->idname, "COLLECTION_MT_exporter_add");
|
||||
STRNCPY(mt->label, N_("Add Exporter"));
|
||||
mt->draw = collection_exporter_menu_draw;
|
||||
|
||||
WM_menutype_add(mt);
|
||||
WM_operatortype_append(COLLECTION_OT_exporter_add);
|
||||
WM_operatortype_append(COLLECTION_OT_exporter_remove);
|
||||
WM_operatortype_append(COLLECTION_OT_exporter_export);
|
||||
WM_operatortype_append(COLLECTION_OT_export_all);
|
||||
WM_operatortype_append(WM_OT_collection_export_all);
|
||||
}
|
||||
|
||||
/****************** properties window operators *********************/
|
||||
|
||||
static int collection_add_exec(bContext *C, wmOperator * /*op*/)
|
||||
|
@ -377,4 +377,6 @@ void OBJECT_OT_datalayout_transfer(wmOperatorType *ot);
|
||||
|
||||
void object_modifier_add_asset_register();
|
||||
|
||||
void collection_exporter_register();
|
||||
|
||||
} // namespace blender::ed::object
|
||||
|
@ -303,6 +303,7 @@ void operatortypes_object()
|
||||
WM_operatortype_append(OBJECT_OT_light_linking_unlink_from_collection);
|
||||
|
||||
object_modifier_add_asset_register();
|
||||
collection_exporter_register();
|
||||
}
|
||||
|
||||
void operatormacros_object()
|
||||
|
@ -3442,6 +3442,15 @@ static void outliner_draw_tree_element(bContext *C,
|
||||
float(startx) + offsx + 2 * ufac, float(*starty) + 2 * ufac, lib_icon, alpha_fac);
|
||||
offsx += UI_UNIT_X + 4 * ufac;
|
||||
}
|
||||
|
||||
if (tselem->type == TSE_LAYER_COLLECTION) {
|
||||
const Collection *collection = (Collection *)tselem->id;
|
||||
if (!BLI_listbase_is_empty(&collection->exporters)) {
|
||||
UI_icon_draw_alpha(
|
||||
float(startx) + offsx + 2 * ufac, float(*starty) + 2 * ufac, ICON_EXPORT, alpha_fac);
|
||||
offsx += UI_UNIT_X + 4 * ufac;
|
||||
}
|
||||
}
|
||||
}
|
||||
GPU_blend(GPU_BLEND_NONE);
|
||||
|
||||
|
@ -25,12 +25,14 @@
|
||||
#include "DEG_depsgraph_build.hh"
|
||||
#include "DEG_depsgraph_query.hh"
|
||||
|
||||
#include "DNA_collection_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "BKE_appdir.hh"
|
||||
#include "BKE_blender_version.h"
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_global.hh"
|
||||
#include "BKE_lib_id.hh"
|
||||
#include "BKE_report.hh"
|
||||
#include "BKE_scene.hh"
|
||||
|
||||
@ -483,7 +485,20 @@ bool USD_export(bContext *C,
|
||||
*
|
||||
* Has to be done from main thread currently, as it may affect Main original data (e.g. when
|
||||
* doing deferred update of the view-layers, see #112534 for details). */
|
||||
if (job->params.visible_objects_only) {
|
||||
if (strlen(job->params.collection) > 0) {
|
||||
Collection *collection = reinterpret_cast<Collection *>(
|
||||
BKE_libblock_find_name(job->bmain, ID_GR, job->params.collection));
|
||||
if (!collection) {
|
||||
BKE_reportf(job->params.worker_status->reports,
|
||||
RPT_ERROR,
|
||||
"USD Export: Unable to find collection %s",
|
||||
job->params.collection);
|
||||
return false;
|
||||
}
|
||||
|
||||
DEG_graph_build_from_collection(job->depsgraph, collection);
|
||||
}
|
||||
else if (job->params.visible_objects_only) {
|
||||
DEG_graph_build_from_view_layer(job->depsgraph);
|
||||
}
|
||||
else {
|
||||
|
@ -85,6 +85,7 @@ struct USDExportParams {
|
||||
bool overwrite_textures = true;
|
||||
bool relative_paths = true;
|
||||
char root_prim_path[1024] = ""; /* FILE_MAX */
|
||||
char collection[MAX_IDPROP_NAME] = "";
|
||||
|
||||
/** Communication structure between the wmJob management code and the worker code. Currently used
|
||||
* to generate safely reports from the worker thread. */
|
||||
|
@ -23,6 +23,7 @@ struct OBJExportParams {
|
||||
char filepath[FILE_MAX];
|
||||
/** Pretend that destination file folder is this, if non-empty. Used only for tests. */
|
||||
char file_base_for_tests[FILE_MAX];
|
||||
char collection[MAX_IDPROP_NAME] = "";
|
||||
|
||||
/** Full path to current blender file (used for comments in output). */
|
||||
const char *blen_filepath;
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <system_error>
|
||||
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_lib_id.hh"
|
||||
#include "BKE_report.hh"
|
||||
#include "BKE_scene.hh"
|
||||
|
||||
@ -21,6 +22,7 @@
|
||||
|
||||
#include "DEG_depsgraph_query.hh"
|
||||
|
||||
#include "DNA_collection_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "ED_object.hh"
|
||||
@ -33,13 +35,23 @@
|
||||
|
||||
namespace blender::io::obj {
|
||||
|
||||
OBJDepsgraph::OBJDepsgraph(const bContext *C, const eEvaluationMode eval_mode)
|
||||
OBJDepsgraph::OBJDepsgraph(const bContext *C,
|
||||
const eEvaluationMode eval_mode,
|
||||
Collection *collection)
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
Main *bmain = CTX_data_main(C);
|
||||
ViewLayer *view_layer = CTX_data_view_layer(C);
|
||||
if (eval_mode == DAG_EVAL_RENDER) {
|
||||
depsgraph_ = DEG_graph_new(bmain, scene, view_layer, DAG_EVAL_RENDER);
|
||||
|
||||
/* If a collection was provided, use it. */
|
||||
if (collection) {
|
||||
depsgraph_ = DEG_graph_new(bmain, scene, view_layer, eval_mode);
|
||||
needs_free_ = true;
|
||||
DEG_graph_build_from_collection(depsgraph_, collection);
|
||||
BKE_scene_graph_evaluated_ensure(depsgraph_, bmain);
|
||||
}
|
||||
else if (eval_mode == DAG_EVAL_RENDER) {
|
||||
depsgraph_ = DEG_graph_new(bmain, scene, view_layer, eval_mode);
|
||||
needs_free_ = true;
|
||||
DEG_graph_build_for_all_objects(depsgraph_);
|
||||
BKE_scene_graph_evaluated_ensure(depsgraph_, bmain);
|
||||
@ -318,7 +330,22 @@ bool append_frame_to_filename(const char *filepath, const int frame, char *r_fil
|
||||
void exporter_main(bContext *C, const OBJExportParams &export_params)
|
||||
{
|
||||
ed::object::mode_set(C, OB_MODE_OBJECT);
|
||||
OBJDepsgraph obj_depsgraph(C, export_params.export_eval_mode);
|
||||
|
||||
Collection *collection = nullptr;
|
||||
if (strlen(export_params.collection) > 0) {
|
||||
Main *bmain = CTX_data_main(C);
|
||||
collection = reinterpret_cast<Collection *>(
|
||||
BKE_libblock_find_name(bmain, ID_GR, export_params.collection));
|
||||
if (!collection) {
|
||||
BKE_reportf(export_params.reports,
|
||||
RPT_ERROR,
|
||||
"OBJ Export: Unable to find collection %s",
|
||||
export_params.collection);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
OBJDepsgraph obj_depsgraph(C, export_params.export_eval_mode, collection);
|
||||
Scene *scene = DEG_get_input_scene(obj_depsgraph.get());
|
||||
const char *filepath = export_params.filepath;
|
||||
|
||||
|
@ -14,6 +14,9 @@
|
||||
|
||||
#include "IO_wavefront_obj.hh"
|
||||
|
||||
struct bContext;
|
||||
struct Collection;
|
||||
|
||||
namespace blender::io::obj {
|
||||
|
||||
/**
|
||||
@ -26,7 +29,7 @@ class OBJDepsgraph : NonMovable, NonCopyable {
|
||||
bool needs_free_ = false;
|
||||
|
||||
public:
|
||||
OBJDepsgraph(const bContext *C, eEvaluationMode eval_mode);
|
||||
OBJDepsgraph(const bContext *C, eEvaluationMode eval_mode, Collection *collection);
|
||||
~OBJDepsgraph();
|
||||
|
||||
Depsgraph *get();
|
||||
|
@ -62,6 +62,23 @@ typedef struct CollectionChild {
|
||||
int _pad;
|
||||
} CollectionChild;
|
||||
|
||||
/* Collection IO property storage and access. */
|
||||
typedef struct CollectionExport {
|
||||
struct CollectionExport *next, *prev;
|
||||
|
||||
/** Identifier that matches the #FileHandlerType.idname. */
|
||||
char fh_idname[64];
|
||||
|
||||
IDProperty *export_properties;
|
||||
uint32_t flag;
|
||||
|
||||
uint32_t _pad0;
|
||||
} CollectionExport;
|
||||
|
||||
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 {
|
||||
@ -116,6 +133,8 @@ typedef struct Collection {
|
||||
/** CollectionChild. */
|
||||
ListBase children;
|
||||
|
||||
ListBase exporters;
|
||||
|
||||
struct PreviewImage *preview;
|
||||
|
||||
unsigned int layer DNA_DEPRECATED;
|
||||
|
@ -275,4 +275,5 @@ enum {
|
||||
/* VIEW_LAYER_DEPRECATED = (1 << 1), */
|
||||
VIEW_LAYER_FREESTYLE = (1 << 2),
|
||||
VIEW_LAYER_OUT_OF_SYNC = (1 << 3),
|
||||
VIEW_LAYER_HAS_EXPORT_COLLECTIONS = (1 << 4),
|
||||
};
|
||||
|
@ -8,6 +8,8 @@
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#include "BKE_file_handler.hh"
|
||||
|
||||
#include "DNA_collection_types.h"
|
||||
|
||||
#include "DNA_lineart_types.h"
|
||||
@ -446,6 +448,25 @@ static void rna_CollectionLightLinking_update(Main *bmain, Scene * /*scene*/, Po
|
||||
DEG_relations_tag_update(bmain);
|
||||
}
|
||||
|
||||
static PointerRNA rna_CollectionExport_export_properties_get(PointerRNA *ptr)
|
||||
{
|
||||
const CollectionExport *data = reinterpret_cast<CollectionExport *>(ptr->data);
|
||||
|
||||
/* If the File Handler or Operator is missing, we allow the data to be accessible
|
||||
* as generic ID properties. */
|
||||
blender::bke::FileHandlerType *fh = blender::bke::file_handler_find(data->fh_idname);
|
||||
if (!fh) {
|
||||
return RNA_pointer_create(ptr->owner_id, &RNA_IDPropertyWrapPtr, data->export_properties);
|
||||
}
|
||||
|
||||
wmOperatorType *ot = WM_operatortype_find(fh->export_operator, false);
|
||||
if (!ot) {
|
||||
return RNA_pointer_create(ptr->owner_id, &RNA_IDPropertyWrapPtr, data->export_properties);
|
||||
}
|
||||
|
||||
return RNA_pointer_create(ptr->owner_id, ot->srna, data->export_properties);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/* collection.objects */
|
||||
@ -566,6 +587,29 @@ static void rna_def_collection_child(BlenderRNA *brna)
|
||||
prop, "Light Linking", "Light linking settings of the collection object");
|
||||
}
|
||||
|
||||
static void rna_def_collection_exporter_data(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna;
|
||||
PropertyRNA *prop;
|
||||
|
||||
srna = RNA_def_struct(brna, "CollectionExport", nullptr);
|
||||
RNA_def_struct_sdna(srna, "CollectionExport");
|
||||
RNA_def_struct_ui_text(srna, "Collection Export Data", "Exporter configured for the collection");
|
||||
|
||||
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);
|
||||
|
||||
prop = RNA_def_property(srna, "export_properties", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_struct_type(prop, "PropertyGroup");
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Export Properties", "Properties associated with the configured exporter");
|
||||
RNA_def_property_pointer_funcs(
|
||||
prop, "rna_CollectionExport_export_properties_get", nullptr, nullptr, nullptr);
|
||||
}
|
||||
|
||||
void RNA_def_collections(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna;
|
||||
@ -651,6 +695,14 @@ void RNA_def_collections(BlenderRNA *brna)
|
||||
RNA_def_property_ui_text(prop,
|
||||
"Collection Children",
|
||||
"Children collections their parent-collection-specific settings");
|
||||
|
||||
/* Export Handlers. */
|
||||
prop = RNA_def_property(srna, "exporters", PROP_COLLECTION, PROP_NONE);
|
||||
RNA_def_property_struct_type(prop, "CollectionExport");
|
||||
RNA_def_property_collection_sdna(prop, nullptr, "exporters", nullptr);
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Collection Export Handlers", "Export Handlers configured for the collection");
|
||||
|
||||
/* TODO(sergey): Functions to link and unlink collections. */
|
||||
|
||||
/* Flags */
|
||||
@ -753,6 +805,7 @@ void RNA_def_collections(BlenderRNA *brna)
|
||||
rna_def_collection_light_linking(brna);
|
||||
rna_def_collection_object(brna);
|
||||
rna_def_collection_child(brna);
|
||||
rna_def_collection_exporter_data(brna);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -659,6 +659,14 @@ void RNA_def_view_layer(BlenderRNA *brna)
|
||||
RNA_def_property_ui_text(prop, "Enabled", "Enable or disable rendering of this View Layer");
|
||||
RNA_def_property_update(prop, NC_SCENE | ND_LAYER, nullptr);
|
||||
|
||||
/* Cached flag indicating if any Collection in this ViewLayer has an Exporter set. */
|
||||
prop = RNA_def_property(srna, "has_export_collections", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
RNA_def_property_boolean_sdna(prop, nullptr, "flag", VIEW_LAYER_HAS_EXPORT_COLLECTIONS);
|
||||
RNA_def_property_ui_text(prop,
|
||||
"Has export collections",
|
||||
"At least one Collection in this View Layer has an exporter");
|
||||
|
||||
prop = RNA_def_property(srna, "use_freestyle", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, nullptr, "flag", VIEW_LAYER_FREESTYLE);
|
||||
RNA_def_property_ui_text(prop, "Freestyle", "Render stylized strokes in this Layer");
|
||||
|
@ -2392,7 +2392,14 @@ static void rna_def_file_handler(BlenderRNA *brna)
|
||||
RNA_def_property_ui_text(
|
||||
prop,
|
||||
"Operator",
|
||||
"Operator that can handle import files with the extensions given in bl_file_extensions");
|
||||
"Operator that can handle import for files with the extensions given in bl_file_extensions");
|
||||
prop = RNA_def_property(srna, "bl_export_operator", PROP_STRING, PROP_NONE);
|
||||
RNA_def_property_string_sdna(prop, nullptr, "type->export_operator");
|
||||
RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL);
|
||||
RNA_def_property_ui_text(
|
||||
prop,
|
||||
"Operator",
|
||||
"Operator that can handle export for files with the extensions given in bl_file_extensions");
|
||||
|
||||
prop = RNA_def_property(srna, "bl_label", PROP_STRING, PROP_NONE);
|
||||
RNA_def_property_string_sdna(prop, nullptr, "type->label");
|
||||
|
@ -1699,6 +1699,10 @@ void RNA_api_ui_layout(StructRNA *srna)
|
||||
RNA_def_function_flag(func, FUNC_USE_CONTEXT);
|
||||
RNA_def_function_ui_description(func, "Generates the UI layout for the modifier stack");
|
||||
|
||||
func = RNA_def_function(srna, "template_collection_exporters", "uiTemplateCollectionExporters");
|
||||
RNA_def_function_flag(func, FUNC_USE_CONTEXT);
|
||||
RNA_def_function_ui_description(func, "Generates the UI layout for collection exporters");
|
||||
|
||||
func = RNA_def_function(srna, "template_constraints", "uiTemplateConstraints");
|
||||
RNA_def_function_flag(func, FUNC_USE_CONTEXT);
|
||||
RNA_def_function_ui_description(func, "Generates the panels for the constraint stack");
|
||||
|
Just to be sure, it is intended that excluded collections are skipped from this test (see the
continue;
statement a few lines above)?Because afaict, below in export operators code, there is no check to skip such collection...
You're right. I do think they should be skipped when Excluded but I'll have to account for this in the export all operator.