diff --git a/source/blender/alembic/intern/abc_exporter.cc b/source/blender/alembic/intern/abc_exporter.cc index 564157fbc97..20ca40c3570 100644 --- a/source/blender/alembic/intern/abc_exporter.cc +++ b/source/blender/alembic/intern/abc_exporter.cc @@ -66,6 +66,7 @@ using Alembic::Abc::OBox3dProperty; ExportSettings::ExportSettings() : scene(NULL) + , logger() , selected_only(false) , visible_layers_only(false) , renderable_only(false) @@ -419,7 +420,7 @@ void AbcExporter::createTransformWriter(Object *ob, Object *parent, Object *dupl /* check if we have already created a transform writer for this object */ if (getXForm(name) != NULL) { - std::cerr << "xform " << name << " already exists\n"; + ABC_LOG(m_settings.logger) << "xform " << name << " already exists!\n"; return; } @@ -513,7 +514,7 @@ void AbcExporter::createShapeWriter(Object *ob, Object *dupliObParent) AbcTransformWriter *xform = getXForm(name); if (!xform) { - std::cerr << __func__ << ": xform " << name << " is NULL\n"; + ABC_LOG(m_settings.logger) << __func__ << ": xform " << name << " is NULL\n"; return; } diff --git a/source/blender/alembic/intern/abc_exporter.h b/source/blender/alembic/intern/abc_exporter.h index b0eb8e185d6..f9fde99acd4 100644 --- a/source/blender/alembic/intern/abc_exporter.h +++ b/source/blender/alembic/intern/abc_exporter.h @@ -28,6 +28,8 @@ #include #include +#include "abc_util.h" + class AbcObjectWriter; class AbcTransformWriter; class ArchiveWriter; @@ -41,6 +43,7 @@ struct ExportSettings { ExportSettings(); Scene *scene; + SimpleLogger logger; bool selected_only; bool visible_layers_only; diff --git a/source/blender/alembic/intern/abc_object.cc b/source/blender/alembic/intern/abc_object.cc index a5b8af542fc..a0cd378ff95 100644 --- a/source/blender/alembic/intern/abc_object.cc +++ b/source/blender/alembic/intern/abc_object.cc @@ -91,7 +91,7 @@ Imath::Box3d AbcObjectWriter::bounds() if (!bb) { if (this->m_object->type != OB_CAMERA) { - std::cerr << "Boundbox is null!\n"; + ABC_LOG(m_settings.logger) << "Bounding box is null!\n"; } return Imath::Box3d(); diff --git a/source/blender/alembic/intern/abc_util.cc b/source/blender/alembic/intern/abc_util.cc index 50fa43a3491..f82b07e91fb 100644 --- a/source/blender/alembic/intern/abc_util.cc +++ b/source/blender/alembic/intern/abc_util.cc @@ -537,3 +537,32 @@ ScopeTimer::~ScopeTimer() { fprintf(stderr, "%s: %fs\n", m_message, PIL_check_seconds_timer() - m_start); } + +/* ********************** */ + +bool SimpleLogger::empty() +{ + return m_stream.tellp() == 0ul; +} + +std::string SimpleLogger::str() const +{ + return m_stream.str(); +} + +void SimpleLogger::clear() +{ + m_stream.clear(); + m_stream.str(""); +} + +std::ostringstream &SimpleLogger::stream() +{ + return m_stream; +} + +std::ostream &operator<<(std::ostream &os, const SimpleLogger &logger) +{ + os << logger.str(); + return os; +} diff --git a/source/blender/alembic/intern/abc_util.h b/source/blender/alembic/intern/abc_util.h index 85ba4d5c9c7..5aa74d232f7 100644 --- a/source/blender/alembic/intern/abc_util.h +++ b/source/blender/alembic/intern/abc_util.h @@ -165,4 +165,48 @@ public: # define SCOPE_TIMER(message) #endif +/* *************************** */ + +/** + * Utility class whose purpose is to more easily log related informations. An + * instance of the SimpleLogger can be created in any context, and will hold a + * copy of all the strings passed to its output stream. + * + * Different instances of the class may be accessed from different threads, + * although accessing the same instance from different threads will lead to race + * conditions. + */ +class SimpleLogger { + std::ostringstream m_stream; + +public: + /** + * Check whether or not the SimpleLogger's stream is empty. + */ + bool empty(); + + /** + * Return a copy of the string contained in the SimpleLogger's stream. + */ + std::string str() const; + + /** + * Remove the bits set on the SimpleLogger's stream and clear its string. + */ + void clear(); + + /** + * Return a reference to the SimpleLogger's stream, in order to e.g. push + * content into it. + */ + std::ostringstream &stream(); +}; + +#define ABC_LOG(logger) logger.stream() + +/** + * Pass the content of the logger's stream to the specified std::ostream. + */ +std::ostream &operator<<(std::ostream &os, const SimpleLogger &logger); + #endif /* __ABC_UTIL_H__ */ diff --git a/source/blender/alembic/intern/alembic_capi.cc b/source/blender/alembic/intern/alembic_capi.cc index dc5146a26e0..6c6f64e1669 100644 --- a/source/blender/alembic/intern/alembic_capi.cc +++ b/source/blender/alembic/intern/alembic_capi.cc @@ -301,10 +301,10 @@ static void export_startjob(void *customdata, short *stop, short *do_update, flo } } catch (const std::exception &e) { - std::cerr << "Abc Export error: " << e.what() << '\n'; + ABC_LOG(data->settings.logger) << "Abc Export error: " << e.what() << '\n'; } catch (...) { - std::cerr << "Abc Export error\n"; + ABC_LOG(data->settings.logger) << "Abc Export: unknown error...\n"; } } @@ -316,6 +316,11 @@ static void export_endjob(void *customdata) BLI_delete(data->filename, false, false); } + if (!data->settings.logger.empty()) { + std::cerr << data->settings.logger; + WM_report(RPT_ERROR, "Errors occured during the export, look in the console to know more..."); + } + G.is_rendering = false; BKE_spacedata_draw_locks(false); } @@ -331,6 +336,22 @@ void ABC_export( job->bmain = CTX_data_main(C); BLI_strncpy(job->filename, filepath, 1024); + /* Alright, alright, alright.... + * + * ExportJobData contains an ExportSettings containing a SimpleLogger. + * + * Since ExportJobData is a C-style struct dynamically allocated with + * MEM_mallocN (see above), its construtor is never called, therefore the + * ExportSettings constructor is not called which implies that the + * SimpleLogger one is not called either. SimpleLogger in turn does not call + * the constructor of its data members which ultimately means that its + * std::ostringstream member has a NULL pointer. To be able to properly use + * the stream's operator<<, the pointer needs to be set, therefore we have + * to properly construct everything. And this is done using the placement + * new operator as here below. It seems hackish, but I'm too lazy to + * do bigger refactor and maybe there is a better way which does not involve + * hardcore refactoring. */ + new (&job->settings) ExportSettings(); job->settings.scene = job->scene; job->settings.frame_start = params->frame_start; job->settings.frame_end = params->frame_end;