Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source/blender/io/alembic/CMakeLists.txt19
-rw-r--r--source/blender/io/alembic/exporter/abc_archive.cc255
-rw-r--r--source/blender/io/alembic/exporter/abc_archive.h87
-rw-r--r--source/blender/io/alembic/exporter/abc_export_capi.cc229
-rw-r--r--source/blender/io/alembic/exporter/abc_exporter.cc681
-rw-r--r--source/blender/io/alembic/exporter/abc_exporter.h131
-rw-r--r--source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc261
-rw-r--r--source/blender/io/alembic/exporter/abc_hierarchy_iterator.h90
-rw-r--r--source/blender/io/alembic/exporter/abc_subdiv_disabler.cc107
-rw-r--r--source/blender/io/alembic/exporter/abc_subdiv_disabler.h (renamed from source/blender/io/alembic/exporter/abc_writer_archive.h)39
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_abstract.cc101
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_abstract.h77
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_archive.cc106
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_camera.cc89
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_camera.h31
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_curves.cc79
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_curves.h32
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_hair.cc118
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_hair.h39
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_mball.cc67
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_mball.h31
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_mesh.cc578
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_mesh.h84
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_nurbs.cc92
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_nurbs.h33
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_object.cc95
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_object.h73
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_points.cc100
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_points.h29
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_transform.cc119
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_transform.h41
-rw-r--r--source/blender/io/alembic/intern/alembic_capi.cc19
-rw-r--r--source/blender/io/common/IO_abstract_hierarchy_iterator.h4
-rw-r--r--tests/gtests/alembic/CMakeLists.txt2
-rw-r--r--tests/gtests/alembic/abc_export_test.cc215
-rw-r--r--tests/gtests/alembic/abc_matrix_test.cc8
-rw-r--r--tests/python/alembic_tests.py4
37 files changed, 2019 insertions, 2146 deletions
diff --git a/source/blender/io/alembic/CMakeLists.txt b/source/blender/io/alembic/CMakeLists.txt
index c0c91099ad2..da36272b850 100644
--- a/source/blender/io/alembic/CMakeLists.txt
+++ b/source/blender/io/alembic/CMakeLists.txt
@@ -30,6 +30,7 @@ set(INC
../../makesdna
../../makesrna
../../windowmanager
+ ../../../../intern/clog
../../../../intern/guardedalloc
../../../../intern/utfconv
)
@@ -54,16 +55,17 @@ set(SRC
intern/abc_util.cc
intern/alembic_capi.cc
+ exporter/abc_archive.cc
exporter/abc_export_capi.cc
- exporter/abc_exporter.cc
- exporter/abc_writer_archive.cc
+ exporter/abc_hierarchy_iterator.cc
+ exporter/abc_subdiv_disabler.cc
+ exporter/abc_writer_abstract.cc
exporter/abc_writer_camera.cc
exporter/abc_writer_curves.cc
exporter/abc_writer_hair.cc
- exporter/abc_writer_mball.cc
exporter/abc_writer_mesh.cc
+ exporter/abc_writer_mball.cc
exporter/abc_writer_nurbs.cc
- exporter/abc_writer_object.cc
exporter/abc_writer_points.cc
exporter/abc_writer_transform.cc
@@ -80,15 +82,16 @@ set(SRC
intern/abc_reader_transform.h
intern/abc_util.h
- exporter/abc_exporter.h
- exporter/abc_writer_archive.h
+ exporter/abc_archive.h
+ exporter/abc_hierarchy_iterator.h
+ exporter/abc_subdiv_disabler.h
+ exporter/abc_writer_abstract.h
exporter/abc_writer_camera.h
exporter/abc_writer_curves.h
exporter/abc_writer_hair.h
- exporter/abc_writer_mball.h
exporter/abc_writer_mesh.h
+ exporter/abc_writer_mball.h
exporter/abc_writer_nurbs.h
- exporter/abc_writer_object.h
exporter/abc_writer_points.h
exporter/abc_writer_transform.h
)
diff --git a/source/blender/io/alembic/exporter/abc_archive.cc b/source/blender/io/alembic/exporter/abc_archive.cc
new file mode 100644
index 00000000000..dbf046e6dfe
--- /dev/null
+++ b/source/blender/io/alembic/exporter/abc_archive.cc
@@ -0,0 +1,255 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+
+#include "abc_archive.h"
+
+#include "BKE_blender_version.h"
+#include "BKE_main.h"
+#include "BKE_scene.h"
+
+#include "DEG_depsgraph_query.h"
+
+#include "DNA_scene_types.h"
+
+#include <Alembic/AbcCoreOgawa/All.h>
+#include <Alembic/AbcGeom/All.h>
+
+namespace blender {
+namespace io {
+namespace alembic {
+
+using Alembic::Abc::ErrorHandler;
+using Alembic::Abc::kWrapExisting;
+using Alembic::Abc::MetaData;
+using Alembic::Abc::OArchive;
+using Alembic::Abc::TimeSampling;
+using Alembic::Abc::TimeSamplingPtr;
+using Alembic::Abc::TimeSamplingType;
+
+static MetaData create_abc_metadata(const Main *bmain, double scene_fps)
+{
+ MetaData abc_metadata;
+
+ std::string abc_user_description(bmain->name);
+ if (abc_user_description.empty()) {
+ abc_user_description = "unknown";
+ }
+
+ abc_metadata.set(Alembic::Abc::kApplicationNameKey, "Blender");
+ abc_metadata.set(Alembic::Abc::kUserDescriptionKey, abc_user_description);
+ abc_metadata.set("blender_version", std::string("v") + BKE_blender_version_string());
+ abc_metadata.set("FramesPerTimeUnit", std::to_string(scene_fps));
+
+ time_t raw_time;
+ time(&raw_time);
+ char buffer[128];
+
+#if defined _WIN32 || defined _WIN64
+ ctime_s(buffer, 128, &raw_time);
+#else
+ ctime_r(&raw_time, buffer);
+#endif
+
+ const std::size_t buffer_len = strlen(buffer);
+ if (buffer_len > 0 && buffer[buffer_len - 1] == '\n') {
+ buffer[buffer_len - 1] = '\0';
+ }
+
+ abc_metadata.set(Alembic::Abc::kDateWrittenKey, buffer);
+ return abc_metadata;
+}
+
+static OArchive *create_archive(std::ofstream *abc_ostream,
+ const std::string &filename,
+ MetaData &abc_metadata)
+{
+ /* Use stream to support unicode character paths on Windows. */
+#ifdef WIN32
+ UTF16_ENCODE(filename);
+ std::wstring wstr(filename_16);
+ abc_ostream->open(wstr.c_str(), std::ios::out | std::ios::binary);
+ UTF16_UN_ENCODE(filename);
+#else
+ abc_ostream->open(filename, std::ios::out | std::ios::binary);
+#endif
+
+ ErrorHandler::Policy policy = ErrorHandler::kThrowPolicy;
+
+ Alembic::AbcCoreOgawa::WriteArchive archive_writer;
+ return new OArchive(archive_writer(abc_ostream, abc_metadata), kWrapExisting, policy);
+}
+
+/* Construct list of shutter samples.
+ *
+ * These are taken from the interval [shutter open, shutter close),
+ * uniformly sampled with 'nr_of_samples' samples.
+ *
+ * TODO(Sybren): test that the above interval is indeed half-open.
+ *
+ * If 'time_relative' is true, samples are returned as time (in seconds) from params.frame_start.
+ * If 'time_relative' is false, samples are returned as fractional frames from 0.
+ * */
+static void get_shutter_samples(double scene_fps,
+ const AlembicExportParams &params,
+ int nr_of_samples,
+ bool time_relative,
+ std::vector<double> &r_samples)
+{
+ int frame_offset = time_relative ? params.frame_start : 0;
+ double time_factor = time_relative ? scene_fps : 1.0;
+ double shutter_open = params.shutter_open;
+ double shutter_close = params.shutter_close;
+ double time_inc = (shutter_close - shutter_open) / nr_of_samples;
+
+ /* sample between shutter open & close */
+ for (int sample = 0; sample < nr_of_samples; sample++) {
+ double sample_time = shutter_open + time_inc * sample;
+ double time = (frame_offset + sample_time) / time_factor;
+
+ r_samples.push_back(time);
+ }
+}
+
+static TimeSamplingPtr create_time_sampling(double scene_fps,
+ const AlembicExportParams &params,
+ int nr_of_samples)
+{
+ std::vector<double> samples;
+
+ if (params.frame_start == params.frame_end) {
+ return TimeSamplingPtr(new TimeSampling());
+ }
+
+ get_shutter_samples(scene_fps, params, nr_of_samples, true, samples);
+
+ TimeSamplingType ts(static_cast<uint32_t>(samples.size()), 1.0 / scene_fps);
+ return TimeSamplingPtr(new TimeSampling(ts, samples));
+}
+
+static void get_frames(double scene_fps,
+ const AlembicExportParams &params,
+ unsigned int nr_of_samples,
+ std::set<double> &r_frames)
+{
+ /* Get one set of shutter samples, then add those around each frame to export. */
+ std::vector<double> shutter_samples;
+ get_shutter_samples(scene_fps, params, nr_of_samples, false, shutter_samples);
+
+ for (double frame = params.frame_start; frame <= params.frame_end; frame += 1.0) {
+ for (size_t j = 0; j < nr_of_samples; j++) {
+ r_frames.insert(frame + shutter_samples[j]);
+ }
+ }
+}
+
+/* ****************************************************************** */
+
+ABCArchive::ABCArchive(const Main *bmain,
+ const Scene *scene,
+ AlembicExportParams params,
+ std::string filename)
+ : archive(nullptr)
+{
+ double scene_fps = FPS;
+ MetaData abc_metadata = create_abc_metadata(bmain, scene_fps);
+
+ // Create the Archive.
+ archive = create_archive(&abc_ostream_, filename, abc_metadata);
+
+ // Create time samples for transforms and shapes.
+ TimeSamplingPtr ts_xform;
+ TimeSamplingPtr ts_shapes;
+
+ ts_xform = create_time_sampling(scene_fps, params, params.frame_samples_xform);
+ time_sampling_index_transforms_ = archive->addTimeSampling(*ts_xform);
+
+ const bool export_animation = params.frame_start != params.frame_end;
+ if (!export_animation || params.frame_samples_shape == params.frame_samples_xform) {
+ ts_shapes = ts_xform;
+ time_sampling_index_shapes_ = time_sampling_index_transforms_;
+ }
+ else {
+ ts_shapes = create_time_sampling(scene_fps, params, params.frame_samples_shape);
+ time_sampling_index_shapes_ = archive->addTimeSampling(*ts_shapes);
+ }
+
+ // Construct the frames to export.
+ get_frames(scene_fps, params, params.frame_samples_xform, xform_frames_);
+ get_frames(scene_fps, params, params.frame_samples_shape, shape_frames_);
+
+ // Merge all frames to get the final set of frames to export.
+ export_frames_.insert(xform_frames_.begin(), xform_frames_.end());
+ export_frames_.insert(shape_frames_.begin(), shape_frames_.end());
+
+ abc_archive_bbox_ = Alembic::AbcGeom::CreateOArchiveBounds(*archive,
+ time_sampling_index_transforms_);
+}
+
+ABCArchive::~ABCArchive()
+{
+ delete archive;
+}
+
+uint32_t ABCArchive::time_sampling_index_transforms() const
+{
+ return time_sampling_index_transforms_;
+}
+
+uint32_t ABCArchive::time_sampling_index_shapes() const
+{
+ return time_sampling_index_shapes_;
+}
+
+ABCArchive::Frames::const_iterator ABCArchive::frames_begin() const
+{
+ return export_frames_.begin();
+}
+ABCArchive::Frames::const_iterator ABCArchive::frames_end() const
+{
+ return export_frames_.end();
+}
+size_t ABCArchive::total_frame_count() const
+{
+ return export_frames_.size();
+}
+
+bool ABCArchive::is_xform_frame(double frame) const
+{
+ return xform_frames_.find(frame) != xform_frames_.end();
+}
+bool ABCArchive::is_shape_frame(double frame) const
+{
+ return shape_frames_.find(frame) != shape_frames_.end();
+}
+ExportSubset ABCArchive::export_subset_for_frame(double frame) const
+{
+ ExportSubset subset;
+ subset.transforms = is_xform_frame(frame);
+ subset.shapes = is_shape_frame(frame);
+ return subset;
+}
+
+void ABCArchive::update_bounding_box(const Imath::Box3d &bounds)
+{
+ abc_archive_bbox_.set(bounds);
+}
+
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/alembic/exporter/abc_archive.h b/source/blender/io/alembic/exporter/abc_archive.h
new file mode 100644
index 00000000000..43d0acf2520
--- /dev/null
+++ b/source/blender/io/alembic/exporter/abc_archive.h
@@ -0,0 +1,87 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup Alembic
+ */
+
+#pragma once
+
+#include "ABC_alembic.h"
+#include "IO_abstract_hierarchy_iterator.h"
+
+#include <Alembic/Abc/OArchive.h>
+#include <Alembic/Abc/OTypedScalarProperty.h>
+
+#include <fstream>
+#include <set>
+#include <string>
+
+struct Main;
+struct Scene;
+
+namespace blender {
+namespace io {
+namespace alembic {
+
+/* Container for an Alembic archive and time sampling info.
+ *
+ * Constructor arguments are used to create the correct output stream and to set the archive's
+ * metadata. */
+class ABCArchive {
+ public:
+ typedef std::set<double> Frames;
+
+ Alembic::Abc::OArchive *archive;
+
+ ABCArchive(const Main *bmain,
+ const Scene *scene,
+ AlembicExportParams params,
+ std::string filename);
+ ~ABCArchive();
+
+ uint32_t time_sampling_index_transforms() const;
+ uint32_t time_sampling_index_shapes() const;
+
+ Frames::const_iterator frames_begin() const;
+ Frames::const_iterator frames_end() const;
+ size_t total_frame_count() const;
+
+ bool is_xform_frame(double frame) const;
+ bool is_shape_frame(double frame) const;
+
+ ExportSubset export_subset_for_frame(double frame) const;
+
+ void update_bounding_box(const Imath::Box3d &bounds);
+
+ private:
+ std::ofstream abc_ostream_;
+ uint32_t time_sampling_index_transforms_;
+ uint32_t time_sampling_index_shapes_;
+
+ Frames xform_frames_;
+ Frames shape_frames_;
+ Frames export_frames_;
+
+ Alembic::Abc::OBox3dProperty abc_archive_bbox_;
+};
+
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/alembic/exporter/abc_export_capi.cc b/source/blender/io/alembic/exporter/abc_export_capi.cc
index c0d04b50acd..fbc5b2d5c02 100644
--- a/source/blender/io/alembic/exporter/abc_export_capi.cc
+++ b/source/blender/io/alembic/exporter/abc_export_capi.cc
@@ -18,18 +18,15 @@
*/
#include "ABC_alembic.h"
-#include "abc_writer_camera.h"
-#include "abc_writer_curves.h"
-#include "abc_writer_hair.h"
-#include "abc_writer_mesh.h"
-#include "abc_writer_nurbs.h"
-#include "abc_writer_points.h"
-#include "abc_writer_transform.h"
+#include "abc_archive.h"
+#include "abc_hierarchy_iterator.h"
+#include "abc_subdiv_disabler.h"
#include "MEM_guardedalloc.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_build.h"
+#include "DEG_depsgraph_query.h"
#include "DNA_modifier_types.h"
#include "DNA_scene_types.h"
@@ -47,179 +44,163 @@
#include "WM_api.h"
#include "WM_types.h"
-using namespace blender::io::alembic;
+#include "CLG_log.h"
+static CLG_LogRef LOG = {"io.alembic"};
+
+#include <algorithm>
struct ExportJobData {
- ViewLayer *view_layer;
Main *bmain;
+ Depsgraph *depsgraph;
wmWindowManager *wm;
- char filename[1024];
- ExportSettings settings;
-
- short *stop;
- short *do_update;
- float *progress;
+ char filename[FILE_MAX];
+ AlembicExportParams params;
bool was_canceled;
bool export_ok;
};
+namespace blender {
+namespace io {
+namespace alembic {
+
+// Construct the depsgraph for exporting.
+static void build_depsgraph(Depsgraph *depsgraph, Main *bmain)
+{
+ Scene *scene = DEG_get_input_scene(depsgraph);
+ ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph);
+ DEG_graph_build_from_view_layer(depsgraph, bmain, scene, view_layer);
+}
+
static void export_startjob(void *customdata, short *stop, short *do_update, float *progress)
{
ExportJobData *data = static_cast<ExportJobData *>(customdata);
+ data->was_canceled = false;
- data->stop = stop;
- data->do_update = do_update;
- data->progress = progress;
-
- /* XXX annoying hack: needed to prevent data corruption when changing
- * scene frame in separate threads
- */
G.is_rendering = true;
WM_set_locked_interface(data->wm, true);
G.is_break = false;
- DEG_graph_build_from_view_layer(
- data->settings.depsgraph, data->bmain, data->settings.scene, data->view_layer);
- BKE_scene_graph_update_tagged(data->settings.depsgraph, data->bmain);
+ *progress = 0.0f;
+ *do_update = true;
- try {
- AbcExporter exporter(data->bmain, data->filename, data->settings);
+ build_depsgraph(data->depsgraph, data->bmain);
+ SubdivModifierDisabler subdiv_disabler(data->depsgraph);
+ if (!data->params.apply_subdiv) {
+ subdiv_disabler.disable_modifiers();
+ }
+ BKE_scene_graph_update_tagged(data->depsgraph, data->bmain);
- Scene *scene = data->settings.scene; /* for the CFRA macro */
- const int orig_frame = CFRA;
+ // For restoring the current frame after exporting animation is done.
+ Scene *scene = DEG_get_input_scene(data->depsgraph);
+ const int orig_frame = CFRA;
+ const bool export_animation = (data->params.frame_start != data->params.frame_end);
- data->was_canceled = false;
- exporter(do_update, progress, &data->was_canceled);
+ // Create the Alembic archive.
+ ABCArchive abc_archive(data->bmain, scene, data->params, std::string(data->filename));
- if (CFRA != orig_frame) {
- CFRA = orig_frame;
+ ABCHierarchyIterator iter(data->depsgraph, &abc_archive, data->params);
- BKE_scene_graph_update_for_newframe(data->settings.depsgraph, data->bmain);
- }
+ if (export_animation) {
+ CLOG_INFO(&LOG, 2, "Exporting animation");
+
+ // Writing the animated frames is not 100% of the work, but it's our best guess.
+ const float progress_per_frame = 1.0f / std::max(size_t(1), abc_archive.total_frame_count());
+ ABCArchive::Frames::const_iterator frame_it = abc_archive.frames_begin();
+ const ABCArchive::Frames::const_iterator frames_end = abc_archive.frames_end();
+
+ for (; frame_it != frames_end; frame_it++) {
+ double frame = *frame_it;
+
+ if (G.is_break || (stop != nullptr && *stop)) {
+ break;
+ }
+
+ // Update the scene for the next frame to render.
+ scene->r.cfra = static_cast<int>(frame);
+ scene->r.subframe = frame - scene->r.cfra;
+ BKE_scene_graph_update_for_newframe(data->depsgraph, data->bmain);
- data->export_ok = !data->was_canceled;
+ CLOG_INFO(&LOG, 2, "Exporting frame %.2f", frame);
+ ExportSubset export_subset = abc_archive.export_subset_for_frame(frame);
+ iter.set_export_subset(export_subset);
+ iter.iterate_and_write();
+
+ *progress += progress_per_frame;
+ *do_update = true;
+ }
}
- catch (const std::exception &e) {
- ABC_LOG(data->settings.logger) << "Abc Export error: " << e.what() << '\n';
+ else {
+ // If we're not animating, a single iteration over all objects is enough.
+ iter.iterate_and_write();
}
- catch (...) {
- ABC_LOG(data->settings.logger) << "Abc Export: unknown error...\n";
+
+ iter.release_writers();
+
+ // Finish up by going back to the keyframe that was current before we started.
+ if (CFRA != orig_frame) {
+ CFRA = orig_frame;
+ BKE_scene_graph_update_for_newframe(data->depsgraph, data->bmain);
}
+
+ data->export_ok = !data->was_canceled;
+
+ *progress = 1.0f;
+ *do_update = true;
}
static void export_endjob(void *customdata)
{
ExportJobData *data = static_cast<ExportJobData *>(customdata);
- DEG_graph_free(data->settings.depsgraph);
+ DEG_graph_free(data->depsgraph);
if (data->was_canceled && BLI_exists(data->filename)) {
BLI_delete(data->filename, false, false);
}
- std::string log = data->settings.logger.str();
- if (!log.empty()) {
- std::cerr << log;
- WM_report(RPT_ERROR, "Errors occurred during the export, look in the console to know more...");
- }
-
G.is_rendering = false;
WM_set_locked_interface(data->wm, false);
}
-bool ABC_export(struct Scene *scene,
- struct bContext *C,
+} // namespace alembic
+} // namespace io
+} // namespace blender
+
+bool ABC_export(Scene *scene,
+ bContext *C,
const char *filepath,
- const struct AlembicExportParams *params,
+ const AlembicExportParams *params,
bool as_background_job)
{
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+
ExportJobData *job = static_cast<ExportJobData *>(
MEM_mallocN(sizeof(ExportJobData), "ExportJobData"));
- job->view_layer = CTX_data_view_layer(C);
job->bmain = CTX_data_main(C);
job->wm = CTX_wm_manager(C);
job->export_ok = false;
- 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 constructor 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 = scene;
- job->settings.depsgraph = DEG_graph_new(job->bmain, scene, job->view_layer, DAG_EVAL_RENDER);
-
- /* TODO(Sybren): for now we only export the active scene layer.
- * Later in the 2.8 development process this may be replaced by using
- * a specific collection for Alembic I/O, which can then be toggled
- * between "real" objects and cached Alembic files. */
- job->settings.view_layer = job->view_layer;
-
- job->settings.frame_start = params->frame_start;
- job->settings.frame_end = params->frame_end;
- job->settings.frame_samples_xform = params->frame_samples_xform;
- job->settings.frame_samples_shape = params->frame_samples_shape;
- job->settings.shutter_open = params->shutter_open;
- job->settings.shutter_close = params->shutter_close;
-
- /* TODO(Sybren): For now this is ignored, until we can get selection
- * detection working through Base pointers (instead of ob->flags). */
- job->settings.selected_only = params->selected_only;
-
- job->settings.export_face_sets = params->face_sets;
- job->settings.export_normals = params->normals;
- job->settings.export_uvs = params->uvs;
- job->settings.export_vcols = params->vcolors;
- job->settings.export_hair = params->export_hair;
- job->settings.export_particles = params->export_particles;
- job->settings.apply_subdiv = params->apply_subdiv;
- job->settings.curves_as_mesh = params->curves_as_mesh;
- job->settings.flatten_hierarchy = params->flatten_hierarchy;
-
- /* TODO(Sybren): visible_layer & renderable only is ignored for now,
- * to be replaced with collections later in the 2.8 dev process
- * (also see note above). */
- job->settings.visible_objects_only = params->visible_objects_only;
- job->settings.renderable_only = params->renderable_only;
-
- job->settings.use_subdiv_schema = params->use_subdiv_schema;
- job->settings.pack_uv = params->packuv;
- job->settings.global_scale = params->global_scale;
- job->settings.triangulate = params->triangulate;
- job->settings.quad_method = params->quad_method;
- job->settings.ngon_method = params->ngon_method;
-
- if (job->settings.frame_start > job->settings.frame_end) {
- std::swap(job->settings.frame_start, job->settings.frame_end);
- }
+ BLI_strncpy(job->filename, filepath, sizeof(job->filename));
+
+ job->depsgraph = DEG_graph_new(
+ job->bmain, scene, view_layer, DAG_EVAL_RENDER /* TODO(Sybren): params->evaluation_mode */);
+ job->params = *params;
bool export_ok = false;
if (as_background_job) {
- wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C),
- CTX_wm_window(C),
- job->settings.scene,
- "Alembic Export",
- WM_JOB_PROGRESS,
- WM_JOB_TYPE_ALEMBIC);
+ wmJob *wm_job = WM_jobs_get(
+ job->wm, CTX_wm_window(C), scene, "Alembic Export", WM_JOB_PROGRESS, WM_JOB_TYPE_ALEMBIC);
/* setup job */
WM_jobs_customdata_set(wm_job, job, MEM_freeN);
WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_FRAME, NC_SCENE | ND_FRAME);
- WM_jobs_callbacks(wm_job, export_startjob, NULL, NULL, export_endjob);
+ WM_jobs_callbacks(wm_job,
+ blender::io::alembic::export_startjob,
+ NULL,
+ NULL,
+ blender::io::alembic::export_endjob);
WM_jobs_start(CTX_wm_manager(C), wm_job);
}
@@ -228,8 +209,8 @@ bool ABC_export(struct Scene *scene,
short stop = 0, do_update = 0;
float progress = 0.f;
- export_startjob(job, &stop, &do_update, &progress);
- export_endjob(job);
+ blender::io::alembic::export_startjob(job, &stop, &do_update, &progress);
+ blender::io::alembic::export_endjob(job);
export_ok = job->export_ok;
MEM_freeN(job);
diff --git a/source/blender/io/alembic/exporter/abc_exporter.cc b/source/blender/io/alembic/exporter/abc_exporter.cc
deleted file mode 100644
index 321bb5fcaba..00000000000
--- a/source/blender/io/alembic/exporter/abc_exporter.cc
+++ /dev/null
@@ -1,681 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-/** \file
- * \ingroup balembic
- */
-
-#include "abc_exporter.h"
-
-#include <cmath>
-
-#include "abc_writer_archive.h"
-#include "abc_writer_camera.h"
-#include "abc_writer_curves.h"
-#include "abc_writer_hair.h"
-#include "abc_writer_mball.h"
-#include "abc_writer_mesh.h"
-#include "abc_writer_nurbs.h"
-#include "abc_writer_points.h"
-#include "abc_writer_transform.h"
-#include "intern/abc_util.h"
-
-#include "DNA_camera_types.h"
-#include "DNA_curve_types.h"
-#include "DNA_fluid_types.h"
-#include "DNA_mesh_types.h"
-#include "DNA_meta_types.h"
-#include "DNA_modifier_types.h"
-#include "DNA_object_types.h"
-#include "DNA_scene_types.h"
-#include "DNA_space_types.h" /* for FILE_MAX */
-
-#include "BLI_string.h"
-
-#ifdef WIN32
-/* needed for MSCV because of snprintf from BLI_string */
-# include "BLI_winstuff.h"
-#endif
-
-#include "BKE_duplilist.h"
-#include "BKE_global.h"
-#include "BKE_idprop.h"
-#include "BKE_layer.h"
-#include "BKE_main.h"
-#include "BKE_mball.h"
-#include "BKE_modifier.h"
-#include "BKE_particle.h"
-#include "BKE_scene.h"
-
-#include "DEG_depsgraph_query.h"
-
-using Alembic::Abc::OBox3dProperty;
-using Alembic::Abc::TimeSamplingPtr;
-
-/* ************************************************************************** */
-
-namespace blender {
-namespace io {
-namespace alembic {
-
-ExportSettings::ExportSettings()
- : scene(NULL),
- view_layer(NULL),
- depsgraph(NULL),
- logger(),
- selected_only(false),
- visible_objects_only(false),
- renderable_only(false),
- frame_start(1),
- frame_end(1),
- frame_samples_xform(1),
- frame_samples_shape(1),
- shutter_open(0.0),
- shutter_close(1.0),
- global_scale(1.0f),
- flatten_hierarchy(false),
- export_normals(false),
- export_uvs(false),
- export_vcols(false),
- export_face_sets(false),
- export_vweigths(false),
- export_hair(true),
- export_particles(true),
- apply_subdiv(false),
- use_subdiv_schema(false),
- export_child_hairs(true),
- pack_uv(false),
- triangulate(false),
- quad_method(0),
- ngon_method(0)
-{
-}
-
-static bool object_is_smoke_sim(Object *ob)
-{
- ModifierData *md = BKE_modifiers_findby_type(ob, eModifierType_Fluid);
-
- if (md) {
- FluidModifierData *smd = reinterpret_cast<FluidModifierData *>(md);
- return (smd->type == MOD_FLUID_TYPE_DOMAIN && smd->domain &&
- smd->domain->type == FLUID_DOMAIN_TYPE_GAS);
- }
-
- return false;
-}
-
-static bool object_type_is_exportable(Scene *scene, Object *ob)
-{
- switch (ob->type) {
- case OB_MESH:
- if (object_is_smoke_sim(ob)) {
- return false;
- }
-
- return true;
- case OB_EMPTY:
- case OB_CURVE:
- case OB_SURF:
- case OB_CAMERA:
- return true;
- case OB_MBALL:
- return AbcMBallWriter::isBasisBall(scene, ob);
- default:
- return false;
- }
-}
-
-/**
- * Returns whether this object should be exported into the Alembic file.
- *
- * \param settings: export settings, used for options like 'selected only'.
- * \param base: the object's base in question.
- * \param is_duplicated: Normally false; true when the object is instanced
- * into the scene by a dupli-object (e.g. part of a dupli-group).
- * This ignores selection and layer visibility,
- * and assumes that the dupli-object itself (e.g. the group-instantiating empty) is exported.
- */
-static bool export_object(const ExportSettings *const settings,
- const Base *const base,
- bool is_duplicated)
-{
- if (!is_duplicated) {
- View3D *v3d = NULL;
-
- /* These two tests only make sense when the object isn't being instanced
- * into the scene. When it is, its exportability is determined by
- * its dupli-object and the DupliObject::no_draw property. */
- if (settings->selected_only && !BASE_SELECTED(v3d, base)) {
- return false;
- }
- // FIXME Sybren: handle these cleanly (maybe just remove code),
- // now using active scene layer instead.
- if (settings->visible_objects_only && !BASE_VISIBLE(v3d, base)) {
- return false;
- }
- }
-
- Object *ob_eval = DEG_get_evaluated_object(settings->depsgraph, base->object);
- if ((ob_eval->id.tag & LIB_TAG_COPIED_ON_WRITE) == 0) {
- /* XXX fix after 2.80: the object was not part of the depsgraph, and thus we cannot get the
- * evaluated copy to export. This will be handled more elegantly in the new
- * AbstractHierarchyIterator that Sybren is working on. This condition is temporary, and avoids
- * a BLI_assert() failure getting the evaluated mesh of this object. */
- return false;
- }
-
- // if (settings->renderable_only && (ob->restrictflag & OB_RESTRICT_RENDER)) {
- // return false;
- // }
-
- return true;
-}
-
-/* ************************************************************************** */
-
-AbcExporter::AbcExporter(Main *bmain, const char *filename, ExportSettings &settings)
- : m_bmain(bmain),
- m_settings(settings),
- m_filename(filename),
- m_trans_sampling_index(0),
- m_shape_sampling_index(0),
- m_writer(NULL)
-{
-}
-
-AbcExporter::~AbcExporter()
-{
- /* Free xforms map */
- m_xforms_type::iterator it_x, e_x;
- for (it_x = m_xforms.begin(), e_x = m_xforms.end(); it_x != e_x; ++it_x) {
- delete it_x->second;
- }
-
- /* Free shapes vector */
- for (int i = 0, e = m_shapes.size(); i != e; i++) {
- delete m_shapes[i];
- }
-
- delete m_writer;
-}
-
-void AbcExporter::getShutterSamples(unsigned int nr_of_samples,
- bool time_relative,
- std::vector<double> &samples)
-{
- Scene *scene = m_settings.scene; /* for use in the FPS macro */
- samples.clear();
-
- unsigned int frame_offset = time_relative ? m_settings.frame_start : 0;
- double time_factor = time_relative ? FPS : 1.0;
- double shutter_open = m_settings.shutter_open;
- double shutter_close = m_settings.shutter_close;
- double time_inc = (shutter_close - shutter_open) / nr_of_samples;
-
- /* sample between shutter open & close */
- for (int sample = 0; sample < nr_of_samples; sample++) {
- double sample_time = shutter_open + time_inc * sample;
- double time = (frame_offset + sample_time) / time_factor;
-
- samples.push_back(time);
- }
-}
-
-Alembic::Abc::TimeSamplingPtr AbcExporter::createTimeSampling(double step)
-{
- std::vector<double> samples;
-
- if (m_settings.frame_start == m_settings.frame_end) {
- return TimeSamplingPtr(new Alembic::Abc::TimeSampling());
- }
-
- getShutterSamples(step, true, samples);
-
- /* TODO(Sybren): shouldn't we use the FPS macro here? */
- Alembic::Abc::TimeSamplingType ts(static_cast<uint32_t>(samples.size()),
- 1.0 / m_settings.scene->r.frs_sec);
-
- return TimeSamplingPtr(new Alembic::Abc::TimeSampling(ts, samples));
-}
-
-void AbcExporter::getFrameSet(unsigned int nr_of_samples, std::set<double> &frames)
-{
- frames.clear();
-
- std::vector<double> shutter_samples;
-
- getShutterSamples(nr_of_samples, false, shutter_samples);
-
- for (double frame = m_settings.frame_start; frame <= m_settings.frame_end; frame += 1.0) {
- for (size_t j = 0; j < nr_of_samples; j++) {
- frames.insert(frame + shutter_samples[j]);
- }
- }
-}
-
-void AbcExporter::operator()(short *do_update, float *progress, bool *was_canceled)
-{
- std::string abc_scene_name;
-
- if (m_bmain->name[0] != '\0') {
- char scene_file_name[FILE_MAX];
- BLI_strncpy(scene_file_name, m_bmain->name, FILE_MAX);
- abc_scene_name = scene_file_name;
- }
- else {
- abc_scene_name = "untitled";
- }
-
- m_writer = new ArchiveWriter(m_filename, abc_scene_name, m_settings.scene);
-
- /* Create time samplings for transforms and shapes. */
-
- TimeSamplingPtr trans_time = createTimeSampling(m_settings.frame_samples_xform);
-
- m_trans_sampling_index = m_writer->archive().addTimeSampling(*trans_time);
-
- TimeSamplingPtr shape_time;
-
- if ((m_settings.frame_samples_shape == m_settings.frame_samples_xform) ||
- (m_settings.frame_start == m_settings.frame_end)) {
- shape_time = trans_time;
- m_shape_sampling_index = m_trans_sampling_index;
- }
- else {
- shape_time = createTimeSampling(m_settings.frame_samples_shape);
- m_shape_sampling_index = m_writer->archive().addTimeSampling(*shape_time);
- }
-
- OBox3dProperty archive_bounds_prop = Alembic::AbcGeom::CreateOArchiveBounds(
- m_writer->archive(), m_trans_sampling_index);
-
- createTransformWritersHierarchy();
- createShapeWriters();
-
- /* Make a list of frames to export. */
-
- std::set<double> xform_frames;
- getFrameSet(m_settings.frame_samples_xform, xform_frames);
-
- std::set<double> shape_frames;
- getFrameSet(m_settings.frame_samples_shape, shape_frames);
-
- /* Merge all frames needed. */
- std::set<double> frames(xform_frames);
- frames.insert(shape_frames.begin(), shape_frames.end());
-
- /* Export all frames. */
-
- std::set<double>::const_iterator begin = frames.begin();
- std::set<double>::const_iterator end = frames.end();
-
- const float size = static_cast<float>(frames.size());
- size_t i = 0;
-
- for (; begin != end; ++begin) {
- *progress = (++i / size);
- *do_update = 1;
-
- if (G.is_break) {
- *was_canceled = true;
- break;
- }
-
- const double frame = *begin;
-
- /* 'frame' is offset by start frame, so need to cancel the offset. */
- setCurrentFrame(m_bmain, frame);
-
- if (shape_frames.count(frame) != 0) {
- for (int i = 0, e = m_shapes.size(); i != e; i++) {
- m_shapes[i]->write();
- }
- }
-
- if (xform_frames.count(frame) == 0) {
- continue;
- }
-
- m_xforms_type::iterator xit, xe;
- for (xit = m_xforms.begin(), xe = m_xforms.end(); xit != xe; ++xit) {
- xit->second->write();
- }
-
- /* Save the archive 's bounding box. */
- Imath::Box3d bounds;
-
- for (xit = m_xforms.begin(), xe = m_xforms.end(); xit != xe; ++xit) {
- Imath::Box3d box = xit->second->bounds();
- bounds.extendBy(box);
- }
-
- archive_bounds_prop.set(bounds);
- }
-}
-
-void AbcExporter::createTransformWritersHierarchy()
-{
- for (Base *base = static_cast<Base *>(m_settings.view_layer->object_bases.first); base;
- base = base->next) {
- Object *ob = base->object;
-
- if (export_object(&m_settings, base, false)) {
- switch (ob->type) {
- case OB_LAMP:
- case OB_LATTICE:
- case OB_SPEAKER:
- /* We do not export transforms for objects of these classes. */
- break;
- default:
- exploreTransform(base, ob, ob->parent, NULL);
- }
- }
- }
-}
-
-void AbcExporter::exploreTransform(Base *base,
- Object *object,
- Object *parent,
- Object *dupliObParent)
-{
- /* If an object isn't exported itself, its duplilist shouldn't be
- * exported either. */
- if (!export_object(&m_settings, base, dupliObParent != NULL)) {
- return;
- }
-
- Object *ob = DEG_get_evaluated_object(m_settings.depsgraph, object);
- if (object_type_is_exportable(m_settings.scene, ob)) {
- createTransformWriter(ob, parent, dupliObParent);
- }
-
- ListBase *lb = object_duplilist(m_settings.depsgraph, m_settings.scene, ob);
-
- if (lb) {
- DupliObject *link = static_cast<DupliObject *>(lb->first);
- Object *dupli_ob = NULL;
- Object *dupli_parent = NULL;
-
- for (; link; link = link->next) {
- /* This skips things like custom bone shapes. */
- if (m_settings.renderable_only && link->no_draw) {
- continue;
- }
-
- if (link->type == OB_DUPLICOLLECTION) {
- dupli_ob = link->ob;
- dupli_parent = (dupli_ob->parent) ? dupli_ob->parent : ob;
-
- exploreTransform(base, dupli_ob, dupli_parent, ob);
- }
- }
-
- free_object_duplilist(lb);
- }
-}
-
-AbcTransformWriter *AbcExporter::createTransformWriter(Object *ob,
- Object *parent,
- Object *dupliObParent)
-{
- /* An object should not be its own parent, or we'll get infinite loops. */
- BLI_assert(ob != parent);
- BLI_assert(ob != dupliObParent);
-
- std::string name;
- if (m_settings.flatten_hierarchy) {
- name = get_id_name(ob);
- }
- else {
- name = get_object_dag_path_name(ob, dupliObParent);
- }
-
- /* check if we have already created a transform writer for this object */
- AbcTransformWriter *my_writer = getXForm(name);
- if (my_writer != NULL) {
- return my_writer;
- }
-
- AbcTransformWriter *parent_writer = NULL;
- Alembic::Abc::OObject alembic_parent;
-
- if (m_settings.flatten_hierarchy || parent == NULL) {
- /* Parentless objects still have the "top object" as parent
- * in Alembic. */
- alembic_parent = m_writer->archive().getTop();
- }
- else {
- /* Since there are so many different ways to find parents (as evident
- * in the number of conditions below), we can't really look up the
- * parent by name. We'll just call createTransformWriter(), which will
- * return the parent's AbcTransformWriter pointer. */
- if (parent->parent) {
- if (parent == dupliObParent) {
- parent_writer = createTransformWriter(parent, parent->parent, NULL);
- }
- else {
- parent_writer = createTransformWriter(parent, parent->parent, dupliObParent);
- }
- }
- else if (parent == dupliObParent) {
- if (dupliObParent->parent == NULL) {
- parent_writer = createTransformWriter(parent, NULL, NULL);
- }
- else {
- parent_writer = createTransformWriter(
- parent, dupliObParent->parent, dupliObParent->parent);
- }
- }
- else {
- parent_writer = createTransformWriter(parent, dupliObParent, dupliObParent);
- }
-
- BLI_assert(parent_writer);
- alembic_parent = parent_writer->alembicXform();
- }
-
- my_writer = new AbcTransformWriter(
- ob, alembic_parent, parent_writer, m_trans_sampling_index, m_settings);
-
- /* When flattening, the matrix of the dupliobject has to be added. */
- if (m_settings.flatten_hierarchy && dupliObParent) {
- my_writer->m_proxy_from = dupliObParent;
- }
-
- m_xforms[name] = my_writer;
- return my_writer;
-}
-
-void AbcExporter::createShapeWriters()
-{
- for (Base *base = static_cast<Base *>(m_settings.view_layer->object_bases.first); base;
- base = base->next) {
- exploreObject(base, base->object, NULL);
- }
-}
-
-void AbcExporter::exploreObject(Base *base, Object *object, Object *dupliObParent)
-{
- /* If an object isn't exported itself, its duplilist shouldn't be
- * exported either. */
- if (!export_object(&m_settings, base, dupliObParent != NULL)) {
- return;
- }
-
- Object *ob = DEG_get_evaluated_object(m_settings.depsgraph, object);
- createShapeWriter(ob, dupliObParent);
-
- ListBase *lb = object_duplilist(m_settings.depsgraph, m_settings.scene, ob);
-
- if (lb) {
- DupliObject *link = static_cast<DupliObject *>(lb->first);
-
- for (; link; link = link->next) {
- /* This skips things like custom bone shapes. */
- if (m_settings.renderable_only && link->no_draw) {
- continue;
- }
- if (link->type == OB_DUPLICOLLECTION) {
- exploreObject(base, link->ob, ob);
- }
- }
-
- free_object_duplilist(lb);
- }
-}
-
-void AbcExporter::createParticleSystemsWriters(Object *ob, AbcTransformWriter *xform)
-{
- if (!m_settings.export_hair && !m_settings.export_particles) {
- return;
- }
-
- ParticleSystem *psys = static_cast<ParticleSystem *>(ob->particlesystem.first);
-
- for (; psys; psys = psys->next) {
- if (!psys_check_enabled(ob, psys, G.is_rendering) || !psys->part) {
- continue;
- }
-
- if (m_settings.export_hair && psys->part->type == PART_HAIR) {
- m_settings.export_child_hairs = true;
- m_shapes.push_back(new AbcHairWriter(ob, xform, m_shape_sampling_index, m_settings, psys));
- }
- else if (m_settings.export_particles &&
- (psys->part->type == PART_EMITTER || psys->part->type == PART_FLUID_FLIP ||
- psys->part->type == PART_FLUID_SPRAY || psys->part->type == PART_FLUID_BUBBLE ||
- psys->part->type == PART_FLUID_FOAM || psys->part->type == PART_FLUID_TRACER ||
- psys->part->type == PART_FLUID_SPRAYFOAM ||
- psys->part->type == PART_FLUID_SPRAYBUBBLE ||
- psys->part->type == PART_FLUID_FOAMBUBBLE ||
- psys->part->type == PART_FLUID_SPRAYFOAMBUBBLE)) {
- m_shapes.push_back(new AbcPointsWriter(ob, xform, m_shape_sampling_index, m_settings, psys));
- }
- }
-}
-
-void AbcExporter::createShapeWriter(Object *ob, Object *dupliObParent)
-{
- if (!object_type_is_exportable(m_settings.scene, ob)) {
- return;
- }
-
- std::string name;
-
- if (m_settings.flatten_hierarchy) {
- name = get_id_name(ob);
- }
- else {
- name = get_object_dag_path_name(ob, dupliObParent);
- }
-
- AbcTransformWriter *xform = getXForm(name);
-
- if (!xform) {
- ABC_LOG(m_settings.logger) << __func__ << ": xform " << name << " is NULL\n";
- return;
- }
-
- createParticleSystemsWriters(ob, xform);
-
- switch (ob->type) {
- case OB_MESH: {
- Mesh *me = static_cast<Mesh *>(ob->data);
-
- if (!me) {
- return;
- }
-
- m_shapes.push_back(new AbcMeshWriter(ob, xform, m_shape_sampling_index, m_settings));
- break;
- }
- case OB_SURF: {
- Curve *cu = static_cast<Curve *>(ob->data);
-
- if (!cu) {
- return;
- }
-
- AbcObjectWriter *writer;
- if (m_settings.curves_as_mesh) {
- writer = new AbcCurveMeshWriter(ob, xform, m_shape_sampling_index, m_settings);
- }
- else {
- writer = new AbcNurbsWriter(ob, xform, m_shape_sampling_index, m_settings);
- }
- m_shapes.push_back(writer);
- break;
- }
- case OB_CURVE: {
- Curve *cu = static_cast<Curve *>(ob->data);
-
- if (!cu) {
- return;
- }
-
- AbcObjectWriter *writer;
- if (m_settings.curves_as_mesh) {
- writer = new AbcCurveMeshWriter(ob, xform, m_shape_sampling_index, m_settings);
- }
- else {
- writer = new AbcCurveWriter(ob, xform, m_shape_sampling_index, m_settings);
- }
- m_shapes.push_back(writer);
- break;
- }
- case OB_CAMERA: {
- Camera *cam = static_cast<Camera *>(ob->data);
-
- if (cam->type == CAM_PERSP) {
- m_shapes.push_back(new AbcCameraWriter(ob, xform, m_shape_sampling_index, m_settings));
- }
-
- break;
- }
- case OB_MBALL: {
- MetaBall *mball = static_cast<MetaBall *>(ob->data);
- if (!mball) {
- return;
- }
-
- m_shapes.push_back(
- new AbcMBallWriter(m_bmain, ob, xform, m_shape_sampling_index, m_settings));
- break;
- }
- }
-}
-
-AbcTransformWriter *AbcExporter::getXForm(const std::string &name)
-{
- std::map<std::string, AbcTransformWriter *>::iterator it = m_xforms.find(name);
-
- if (it == m_xforms.end()) {
- return NULL;
- }
-
- return it->second;
-}
-
-void AbcExporter::setCurrentFrame(Main *bmain, double t)
-{
- m_settings.scene->r.cfra = static_cast<int>(t);
- m_settings.scene->r.subframe = static_cast<float>(t) - m_settings.scene->r.cfra;
- BKE_scene_graph_update_for_newframe(m_settings.depsgraph, bmain);
-}
-
-} // namespace alembic
-} // namespace io
-} // namespace blender
diff --git a/source/blender/io/alembic/exporter/abc_exporter.h b/source/blender/io/alembic/exporter/abc_exporter.h
deleted file mode 100644
index af30f2ceb50..00000000000
--- a/source/blender/io/alembic/exporter/abc_exporter.h
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-#pragma once
-
-/** \file
- * \ingroup balembic
- */
-
-#include <Alembic/Abc/All.h>
-#include <map>
-#include <set>
-#include <vector>
-
-#include "intern/abc_util.h"
-
-struct Base;
-struct Depsgraph;
-struct Main;
-struct Object;
-struct Scene;
-struct ViewLayer;
-
-namespace blender {
-namespace io {
-namespace alembic {
-
-class AbcObjectWriter;
-class AbcTransformWriter;
-class ArchiveWriter;
-
-struct ExportSettings {
- ExportSettings();
-
- Scene *scene;
- /** Scene layer to export; all its objects will be exported, unless selected_only=true. */
- ViewLayer *view_layer;
- Depsgraph *depsgraph;
- SimpleLogger logger;
-
- bool selected_only;
- bool visible_objects_only;
- bool renderable_only;
-
- double frame_start, frame_end;
- double frame_samples_xform;
- double frame_samples_shape;
- double shutter_open;
- double shutter_close;
- float global_scale;
-
- bool flatten_hierarchy;
-
- bool export_normals;
- bool export_uvs;
- bool export_vcols;
- bool export_face_sets;
- bool export_vweigths;
- bool export_hair;
- bool export_particles;
-
- bool apply_subdiv;
- bool curves_as_mesh;
- bool use_subdiv_schema;
- bool export_child_hairs;
- bool pack_uv;
- bool triangulate;
-
- int quad_method;
- int ngon_method;
-};
-
-class AbcExporter {
- Main *m_bmain;
- ExportSettings &m_settings;
-
- const char *m_filename;
-
- unsigned int m_trans_sampling_index, m_shape_sampling_index;
-
- ArchiveWriter *m_writer;
-
- /* mapping from name to transform writer */
- typedef std::map<std::string, AbcTransformWriter *> m_xforms_type;
- m_xforms_type m_xforms;
-
- std::vector<AbcObjectWriter *> m_shapes;
-
- public:
- AbcExporter(Main *bmain, const char *filename, ExportSettings &settings);
- ~AbcExporter();
-
- void operator()(short *do_update, float *progress, bool *was_canceled);
-
- protected:
- void getShutterSamples(unsigned int nr_of_samples,
- bool time_relative,
- std::vector<double> &samples);
- void getFrameSet(unsigned int nr_of_samples, std::set<double> &frames);
-
- private:
- Alembic::Abc::TimeSamplingPtr createTimeSampling(double step);
-
- void createTransformWritersHierarchy();
- AbcTransformWriter *createTransformWriter(Object *ob, Object *parent, Object *dupliObParent);
- void exploreTransform(Base *base, Object *object, Object *parent, Object *dupliObParent);
- void exploreObject(Base *base, Object *object, Object *dupliObParent);
- void createShapeWriters();
- void createShapeWriter(Object *ob, Object *dupliObParent);
- void createParticleSystemsWriters(Object *ob, AbcTransformWriter *xform);
-
- AbcTransformWriter *getXForm(const std::string &name);
-
- void setCurrentFrame(Main *bmain, double t);
-};
-
-} // namespace alembic
-} // namespace io
-} // namespace blender
diff --git a/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc b/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc
new file mode 100644
index 00000000000..90004c0e85b
--- /dev/null
+++ b/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc
@@ -0,0 +1,261 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+
+#include "abc_hierarchy_iterator.h"
+#include "abc_writer_abstract.h"
+#include "abc_writer_camera.h"
+#include "abc_writer_curves.h"
+#include "abc_writer_hair.h"
+#include "abc_writer_mball.h"
+#include "abc_writer_mesh.h"
+#include "abc_writer_nurbs.h"
+#include "abc_writer_points.h"
+#include "abc_writer_transform.h"
+
+#include <memory>
+#include <string>
+
+#include "BLI_assert.h"
+
+#include "DEG_depsgraph_query.h"
+
+#include "DNA_ID.h"
+#include "DNA_layer_types.h"
+#include "DNA_object_types.h"
+
+namespace blender {
+namespace io {
+namespace alembic {
+
+ABCHierarchyIterator::ABCHierarchyIterator(Depsgraph *depsgraph,
+ ABCArchive *abc_archive,
+ const AlembicExportParams &params)
+ : AbstractHierarchyIterator(depsgraph), abc_archive_(abc_archive), params_(params)
+{
+}
+
+void ABCHierarchyIterator::iterate_and_write()
+{
+ AbstractHierarchyIterator::iterate_and_write();
+ update_archive_bounding_box();
+}
+
+void ABCHierarchyIterator::update_archive_bounding_box()
+{
+ Imath::Box3d bounds;
+ update_bounding_box_recursive(bounds, HierarchyContext::root());
+ abc_archive_->update_bounding_box(bounds);
+}
+
+void ABCHierarchyIterator::update_bounding_box_recursive(Imath::Box3d &bounds,
+ const HierarchyContext *context)
+{
+ if (context != nullptr) {
+ AbstractHierarchyWriter *abstract_writer = writers_[context->export_path];
+ ABCAbstractWriter *abc_writer = static_cast<ABCAbstractWriter *>(abstract_writer);
+
+ if (abc_writer != nullptr) {
+ bounds.extendBy(abc_writer->bounding_box());
+ }
+ }
+
+ for (HierarchyContext *child_context : graph_children(context)) {
+ update_bounding_box_recursive(bounds, child_context);
+ }
+}
+
+bool ABCHierarchyIterator::mark_as_weak_export(const Object *object) const
+{
+ if (params_.selected_only && (object->base_flag & BASE_SELECTED) == 0) {
+ return true;
+ }
+ /* TODO(Sybren): handle other flags too? */
+ return false;
+}
+
+void ABCHierarchyIterator::delete_object_writer(AbstractHierarchyWriter *writer)
+{
+ delete writer;
+}
+
+std::string ABCHierarchyIterator::make_valid_name(const std::string &name) const
+{
+ std::string abc_name(name);
+ std::replace(abc_name.begin(), abc_name.end(), ' ', '_');
+ std::replace(abc_name.begin(), abc_name.end(), '.', '_');
+ std::replace(abc_name.begin(), abc_name.end(), ':', '_');
+ return abc_name;
+}
+
+AbstractHierarchyIterator::ExportGraph::key_type ABCHierarchyIterator::
+ determine_graph_index_object(const HierarchyContext *context)
+{
+ if (params_.flatten_hierarchy) {
+ return std::make_pair(nullptr, nullptr);
+ }
+
+ return AbstractHierarchyIterator::determine_graph_index_object(context);
+}
+
+AbstractHierarchyIterator::ExportGraph::key_type ABCHierarchyIterator::determine_graph_index_dupli(
+ const HierarchyContext *context, const std::set<Object *> &dupli_set)
+{
+ if (params_.flatten_hierarchy) {
+ return std::make_pair(nullptr, nullptr);
+ }
+
+ return AbstractHierarchyIterator::determine_graph_index_dupli(context, dupli_set);
+}
+
+Alembic::Abc::OObject ABCHierarchyIterator::get_alembic_parent(
+ const HierarchyContext *context) const
+{
+ Alembic::Abc::OObject parent;
+
+ if (!context->higher_up_export_path.empty()) {
+ AbstractHierarchyWriter *writer = get_writer(context->higher_up_export_path);
+ ABCAbstractWriter *abc_writer = static_cast<ABCAbstractWriter *>(writer);
+ parent = abc_writer->get_alembic_object();
+ }
+
+ if (!parent.valid()) {
+ /* An invalid parent object means "no parent", which should be translated to Alembic's top
+ * archive object. */
+ return abc_archive_->archive->getTop();
+ }
+
+ return parent;
+}
+
+ABCWriterConstructorArgs ABCHierarchyIterator::writer_constructor_args(
+ const HierarchyContext *context) const
+{
+ ABCWriterConstructorArgs constructor_args;
+ constructor_args.depsgraph = depsgraph_;
+ constructor_args.abc_archive = abc_archive_;
+ constructor_args.abc_parent = get_alembic_parent(context);
+ constructor_args.abc_name = context->export_name;
+ constructor_args.abc_path = context->export_path;
+ constructor_args.hierarchy_iterator = this;
+ constructor_args.export_params = &params_;
+ return constructor_args;
+}
+
+AbstractHierarchyWriter *ABCHierarchyIterator::create_transform_writer(
+ const HierarchyContext *context)
+{
+ ABCAbstractWriter *transform_writer = new ABCTransformWriter(writer_constructor_args(context));
+ transform_writer->create_alembic_objects(context);
+ return transform_writer;
+}
+
+AbstractHierarchyWriter *ABCHierarchyIterator::create_data_writer(const HierarchyContext *context)
+{
+ const ABCWriterConstructorArgs writer_args = writer_constructor_args(context);
+ ABCAbstractWriter *data_writer = nullptr;
+
+ switch (context->object->type) {
+ case OB_MESH:
+ data_writer = new ABCMeshWriter(writer_args);
+ break;
+ case OB_CAMERA:
+ data_writer = new ABCCameraWriter(writer_args);
+ break;
+ case OB_CURVE:
+ if (params_.curves_as_mesh) {
+ data_writer = new ABCCurveMeshWriter(writer_args);
+ }
+ else {
+ data_writer = new ABCCurveWriter(writer_args);
+ }
+ break;
+ case OB_SURF:
+ if (params_.curves_as_mesh) {
+ data_writer = new ABCCurveMeshWriter(writer_args);
+ }
+ else {
+ data_writer = new ABCNurbsWriter(writer_args);
+ }
+ break;
+ case OB_MBALL:
+ data_writer = new ABCMetaballWriter(writer_args);
+ break;
+
+ case OB_EMPTY:
+ case OB_LAMP:
+ case OB_FONT:
+ case OB_SPEAKER:
+ case OB_LIGHTPROBE:
+ case OB_LATTICE:
+ case OB_ARMATURE:
+ case OB_GPENCIL:
+ return nullptr;
+ case OB_TYPE_MAX:
+ BLI_assert(!"OB_TYPE_MAX should not be used");
+ return nullptr;
+ }
+
+ if (!data_writer->is_supported(context)) {
+ delete data_writer;
+ return nullptr;
+ }
+
+ data_writer->create_alembic_objects(context);
+ return data_writer;
+}
+
+AbstractHierarchyWriter *ABCHierarchyIterator::create_hair_writer(const HierarchyContext *context)
+{
+ if (!params_.export_hair) {
+ return nullptr;
+ }
+
+ const ABCWriterConstructorArgs writer_args = writer_constructor_args(context);
+ ABCAbstractWriter *hair_writer = new ABCHairWriter(writer_args);
+
+ if (!hair_writer->is_supported(context)) {
+ delete hair_writer;
+ return nullptr;
+ }
+
+ hair_writer->create_alembic_objects(context);
+ return hair_writer;
+}
+
+AbstractHierarchyWriter *ABCHierarchyIterator::create_particle_writer(
+ const HierarchyContext *context)
+{
+ if (!params_.export_particles) {
+ return nullptr;
+ }
+
+ const ABCWriterConstructorArgs writer_args = writer_constructor_args(context);
+ std::unique_ptr<ABCPointsWriter> particle_writer(std::make_unique<ABCPointsWriter>(writer_args));
+
+ if (!particle_writer->is_supported(context)) {
+ return nullptr;
+ }
+
+ particle_writer->create_alembic_objects(context);
+ return particle_writer.release();
+}
+
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/alembic/exporter/abc_hierarchy_iterator.h b/source/blender/io/alembic/exporter/abc_hierarchy_iterator.h
new file mode 100644
index 00000000000..edcb31806ba
--- /dev/null
+++ b/source/blender/io/alembic/exporter/abc_hierarchy_iterator.h
@@ -0,0 +1,90 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+#pragma once
+
+#include "ABC_alembic.h"
+#include "abc_archive.h"
+
+#include "IO_abstract_hierarchy_iterator.h"
+
+#include <string>
+
+#include <Alembic/Abc/OArchive.h>
+#include <Alembic/Abc/OObject.h>
+
+struct Depsgraph;
+struct ID;
+struct Object;
+
+namespace blender {
+namespace io {
+namespace alembic {
+
+class ABCHierarchyIterator;
+
+struct ABCWriterConstructorArgs {
+ Depsgraph *depsgraph;
+ ABCArchive *abc_archive;
+ Alembic::Abc::OObject abc_parent;
+ std::string abc_name;
+ std::string abc_path;
+ const ABCHierarchyIterator *hierarchy_iterator;
+ const AlembicExportParams *export_params;
+};
+
+class ABCHierarchyIterator : public AbstractHierarchyIterator {
+ private:
+ ABCArchive *abc_archive_;
+ const AlembicExportParams &params_;
+
+ public:
+ ABCHierarchyIterator(Depsgraph *depsgraph,
+ ABCArchive *abc_archive_,
+ const AlembicExportParams &params);
+
+ virtual void iterate_and_write() override;
+ virtual std::string make_valid_name(const std::string &name) const override;
+
+ protected:
+ virtual bool mark_as_weak_export(const Object *object) const override;
+
+ virtual ExportGraph::key_type determine_graph_index_object(
+ const HierarchyContext *context) override;
+ virtual AbstractHierarchyIterator::ExportGraph::key_type determine_graph_index_dupli(
+ const HierarchyContext *context, const std::set<Object *> &dupli_set) override;
+
+ virtual AbstractHierarchyWriter *create_transform_writer(
+ const HierarchyContext *context) override;
+ virtual AbstractHierarchyWriter *create_data_writer(const HierarchyContext *context) override;
+ virtual AbstractHierarchyWriter *create_hair_writer(const HierarchyContext *context) override;
+ virtual AbstractHierarchyWriter *create_particle_writer(
+ const HierarchyContext *context) override;
+
+ virtual void delete_object_writer(AbstractHierarchyWriter *writer) override;
+
+ private:
+ Alembic::Abc::OObject get_alembic_parent(const HierarchyContext *context) const;
+ ABCWriterConstructorArgs writer_constructor_args(const HierarchyContext *context) const;
+ void update_archive_bounding_box();
+ void update_bounding_box_recursive(Imath::Box3d &bounds, const HierarchyContext *context);
+};
+
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/alembic/exporter/abc_subdiv_disabler.cc b/source/blender/io/alembic/exporter/abc_subdiv_disabler.cc
new file mode 100644
index 00000000000..7c147076975
--- /dev/null
+++ b/source/blender/io/alembic/exporter/abc_subdiv_disabler.cc
@@ -0,0 +1,107 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+#include "abc_subdiv_disabler.h"
+
+#include <stdio.h>
+
+#include "BLI_listbase.h"
+
+#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_query.h"
+
+#include "DNA_layer_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_object_types.h"
+
+#include "BKE_modifier.h"
+
+namespace blender {
+namespace io {
+namespace alembic {
+
+SubdivModifierDisabler::SubdivModifierDisabler(Depsgraph *depsgraph) : depsgraph_(depsgraph)
+{
+}
+
+SubdivModifierDisabler::~SubdivModifierDisabler()
+{
+ for (ModifierData *modifier : disabled_modifiers_) {
+ modifier->mode &= ~eModifierMode_DisableTemporary;
+ }
+}
+
+void SubdivModifierDisabler::disable_modifiers()
+{
+ Scene *scene = DEG_get_input_scene(depsgraph_);
+ ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph_);
+
+ LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ Object *object = base->object;
+
+ if (object->type != OB_MESH) {
+ continue;
+ }
+
+ ModifierData *subdiv = get_subdiv_modifier(scene, object);
+ if (subdiv == nullptr) {
+ continue;
+ }
+
+ /* This disables more modifiers than necessary, as it doesn't take restrictions like
+ * "export selected objects only" into account. However, with the subsurfs disabled,
+ * moving to a different frame is also going to be faster, so in the end this is probably
+ * a good thing to do. */
+ subdiv->mode |= eModifierMode_DisableTemporary;
+ disabled_modifiers_.insert(subdiv);
+ DEG_id_tag_update(&object->id, ID_RECALC_GEOMETRY);
+ }
+}
+
+/* Check if the mesh is a subsurf, ignoring disabled modifiers and
+ * displace if it's after subsurf. */
+ModifierData *SubdivModifierDisabler::get_subdiv_modifier(Scene *scene, Object *ob)
+{
+ ModifierData *md = static_cast<ModifierData *>(ob->modifiers.last);
+
+ for (; md; md = md->prev) {
+ if (!BKE_modifier_is_enabled(scene, md, eModifierMode_Render)) {
+ continue;
+ }
+
+ if (md->type == eModifierType_Subsurf) {
+ SubsurfModifierData *smd = reinterpret_cast<SubsurfModifierData *>(md);
+
+ if (smd->subdivType == ME_CC_SUBSURF) {
+ return md;
+ }
+ }
+
+ /* mesh is not a subsurf. break */
+ if ((md->type != eModifierType_Displace) && (md->type != eModifierType_ParticleSystem)) {
+ return nullptr;
+ }
+ }
+
+ return nullptr;
+}
+
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/alembic/exporter/abc_writer_archive.h b/source/blender/io/alembic/exporter/abc_subdiv_disabler.h
index db13dc0ec92..677847f3f63 100644
--- a/source/blender/io/alembic/exporter/abc_writer_archive.h
+++ b/source/blender/io/alembic/exporter/abc_subdiv_disabler.h
@@ -13,40 +13,41 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
- * The Original Code is Copyright (C) 2016 Kévin Dietrich.
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
* All rights reserved.
*/
#pragma once
-/** \file
- * \ingroup balembic
- */
-
-#include <Alembic/Abc/All.h>
-#include <Alembic/AbcCoreOgawa/All.h>
-
-#include <fstream>
+#include <set>
-struct Main;
+struct Depsgraph;
+struct ModifierData;
+struct Object;
struct Scene;
namespace blender {
namespace io {
namespace alembic {
-/* Wrappers around input and output archives. The goal is to be able to use
- * streams so that unicode paths work on Windows (T49112), and to make sure that
- * the stream objects remain valid as long as the archives are open.
+/**
+ * Temporarily all subdivision modifiers on mesh objects.
+ * The destructor restores all disabled modifiers.
+ *
+ * This is used to export unsubdivided meshes to Alembic. It is done in a separate step before the
+ * exporter starts iterating over all the frames, so that it only has to happen once per export.
*/
-
-class ArchiveWriter {
- std::ofstream m_outfile;
- Alembic::Abc::OArchive m_archive;
+class SubdivModifierDisabler final {
+ private:
+ Depsgraph *depsgraph_;
+ std::set<ModifierData *> disabled_modifiers_;
public:
- ArchiveWriter(const char *filename, const std::string &abc_scene_name, const Scene *scene);
+ explicit SubdivModifierDisabler(Depsgraph *depsgraph);
+ ~SubdivModifierDisabler();
+
+ void disable_modifiers();
- Alembic::Abc::OArchive &archive();
+ static ModifierData *get_subdiv_modifier(Scene *scene, Object *ob);
};
} // namespace alembic
diff --git a/source/blender/io/alembic/exporter/abc_writer_abstract.cc b/source/blender/io/alembic/exporter/abc_writer_abstract.cc
new file mode 100644
index 00000000000..e43b394e27f
--- /dev/null
+++ b/source/blender/io/alembic/exporter/abc_writer_abstract.cc
@@ -0,0 +1,101 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+#include "abc_writer_abstract.h"
+#include "abc_hierarchy_iterator.h"
+
+#include "BKE_animsys.h"
+#include "BKE_key.h"
+#include "BKE_object.h"
+
+#include "DNA_modifier_types.h"
+
+#include "CLG_log.h"
+static CLG_LogRef LOG = {"io.alembic"};
+
+namespace blender {
+namespace io {
+namespace alembic {
+
+using Alembic::Abc::OObject;
+using Alembic::Abc::TimeSamplingPtr;
+
+ABCAbstractWriter::ABCAbstractWriter(const ABCWriterConstructorArgs &args)
+ : args_(args),
+ frame_has_been_written_(false),
+ is_animated_(false),
+ timesample_index_(args_.abc_archive->time_sampling_index_shapes())
+{
+}
+
+ABCAbstractWriter::~ABCAbstractWriter()
+{
+}
+
+bool ABCAbstractWriter::is_supported(const HierarchyContext * /*context*/) const
+{
+ return true;
+}
+
+void ABCAbstractWriter::write(HierarchyContext &context)
+{
+ if (!frame_has_been_written_) {
+ is_animated_ = (args_.export_params->frame_start != args_.export_params->frame_end) &&
+ check_is_animated(context);
+ }
+ else if (!is_animated_) {
+ /* A frame has already been written, and without animation one frame is enough. */
+ return;
+ }
+
+ do_write(context);
+
+ frame_has_been_written_ = true;
+}
+
+const Imath::Box3d &ABCAbstractWriter::bounding_box() const
+{
+ return bounding_box_;
+}
+
+void ABCAbstractWriter::update_bounding_box(Object *object)
+{
+ BoundBox *bb = BKE_object_boundbox_get(object);
+
+ if (!bb) {
+ if (object->type != OB_CAMERA) {
+ CLOG_WARN(&LOG, "Bounding box is null!\n");
+ }
+ bounding_box_.min.x = bounding_box_.min.y = bounding_box_.min.z = 0;
+ bounding_box_.max.x = bounding_box_.max.y = bounding_box_.max.z = 0;
+ return;
+ }
+
+ /* Convert Z-up to Y-up. This also changes which vector goes into which min/max property. */
+ bounding_box_.min.x = bb->vec[0][0];
+ bounding_box_.min.y = bb->vec[0][2];
+ bounding_box_.min.z = -bb->vec[6][1];
+
+ bounding_box_.max.x = bb->vec[6][0];
+ bounding_box_.max.y = bb->vec[6][2];
+ bounding_box_.max.z = -bb->vec[0][1];
+}
+
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/alembic/exporter/abc_writer_abstract.h b/source/blender/io/alembic/exporter/abc_writer_abstract.h
new file mode 100644
index 00000000000..d6fd692775e
--- /dev/null
+++ b/source/blender/io/alembic/exporter/abc_writer_abstract.h
@@ -0,0 +1,77 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+#pragma once
+
+#include "IO_abstract_hierarchy_iterator.h"
+#include "abc_hierarchy_iterator.h"
+
+#include <Alembic/Abc/OObject.h>
+#include <vector>
+
+#include "DEG_depsgraph_query.h"
+#include "DNA_material_types.h"
+
+struct Material;
+struct Object;
+
+namespace blender {
+namespace io {
+namespace alembic {
+
+class ABCAbstractWriter : public AbstractHierarchyWriter {
+ protected:
+ const ABCWriterConstructorArgs args_;
+
+ bool frame_has_been_written_;
+ bool is_animated_;
+ uint32_t timesample_index_;
+ Imath::Box3d bounding_box_;
+
+ public:
+ explicit ABCAbstractWriter(const ABCWriterConstructorArgs &args);
+ virtual ~ABCAbstractWriter();
+
+ virtual void write(HierarchyContext &context) override;
+
+ /* Returns true if the data to be written is actually supported. This would, for example, allow a
+ * hypothetical camera writer accept a perspective camera but reject an orthogonal one.
+ *
+ * Returning false from a transform writer will prevent the object and all its decendants from
+ * being exported. Returning false from a data writer (object data, hair, or particles) will
+ * only prevent that data from being written (and thus cause the object to be exported as an
+ * Empty). */
+ virtual bool is_supported(const HierarchyContext *context) const;
+
+ const Imath::Box3d &bounding_box() const;
+
+ /* Called by AlembicHierarchyCreator after checking that the data is supported via
+ * is_supported(). */
+ virtual void create_alembic_objects(const HierarchyContext *context) = 0;
+
+ virtual const Alembic::Abc::OObject get_alembic_object() const = 0;
+
+ protected:
+ virtual void do_write(HierarchyContext &context) = 0;
+
+ virtual void update_bounding_box(Object *object);
+};
+
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/source/blender/io/alembic/exporter/abc_writer_archive.cc b/source/blender/io/alembic/exporter/abc_writer_archive.cc
deleted file mode 100644
index 741a3697b38..00000000000
--- a/source/blender/io/alembic/exporter/abc_writer_archive.cc
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * The Original Code is Copyright (C) 2016 Kévin Dietrich.
- * All rights reserved.
- */
-
-/** \file
- * \ingroup balembic
- */
-
-#include "abc_writer_archive.h"
-
-#include "BKE_blender_version.h"
-
-#include "BLI_path_util.h"
-#include "BLI_string.h"
-
-#include "DNA_scene_types.h"
-
-#ifdef WIN32
-# include "utfconv.h"
-#endif
-
-#include <fstream>
-
-using Alembic::Abc::ErrorHandler;
-using Alembic::Abc::kWrapExisting;
-using Alembic::Abc::OArchive;
-
-namespace blender {
-namespace io {
-namespace alembic {
-
-/* This kinda duplicates CreateArchiveWithInfo, but Alembic does not seem to
- * have a version supporting streams. */
-static OArchive create_archive(std::ostream *ostream,
- const std::string &scene_name,
- double scene_fps)
-{
- Alembic::Abc::MetaData abc_metadata;
-
- abc_metadata.set(Alembic::Abc::kApplicationNameKey, "Blender");
- abc_metadata.set(Alembic::Abc::kUserDescriptionKey, scene_name);
- abc_metadata.set("blender_version", std::string("v") + BKE_blender_version_string());
- abc_metadata.set("FramesPerTimeUnit", std::to_string(scene_fps));
-
- time_t raw_time;
- time(&raw_time);
- char buffer[128];
-
-#if defined _WIN32 || defined _WIN64
- ctime_s(buffer, 128, &raw_time);
-#else
- ctime_r(&raw_time, buffer);
-#endif
-
- const std::size_t buffer_len = strlen(buffer);
- if (buffer_len > 0 && buffer[buffer_len - 1] == '\n') {
- buffer[buffer_len - 1] = '\0';
- }
-
- abc_metadata.set(Alembic::Abc::kDateWrittenKey, buffer);
-
- ErrorHandler::Policy policy = ErrorHandler::kThrowPolicy;
- Alembic::AbcCoreOgawa::WriteArchive archive_writer;
- return OArchive(archive_writer(ostream, abc_metadata), kWrapExisting, policy);
-}
-
-ArchiveWriter::ArchiveWriter(const char *filename,
- const std::string &abc_scene_name,
- const Scene *scene)
-{
- /* Use stream to support unicode character paths on Windows. */
-#ifdef WIN32
- UTF16_ENCODE(filename);
- std::wstring wstr(filename_16);
- m_outfile.open(wstr.c_str(), std::ios::out | std::ios::binary);
- UTF16_UN_ENCODE(filename);
-#else
- m_outfile.open(filename, std::ios::out | std::ios::binary);
-#endif
-
- m_archive = create_archive(&m_outfile, abc_scene_name, FPS);
-}
-
-OArchive &ArchiveWriter::archive()
-{
- return m_archive;
-}
-
-} // namespace alembic
-} // namespace io
-} // namespace blender
diff --git a/source/blender/io/alembic/exporter/abc_writer_camera.cc b/source/blender/io/alembic/exporter/abc_writer_camera.cc
index ef6d1512a96..7e7277cb4ea 100644
--- a/source/blender/io/alembic/exporter/abc_writer_camera.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_camera.cc
@@ -19,67 +19,90 @@
*/
#include "abc_writer_camera.h"
-#include "abc_writer_transform.h"
+#include "abc_hierarchy_iterator.h"
+
+#include "BKE_camera.h"
+
+#include "BLI_assert.h"
#include "DNA_camera_types.h"
-#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
-using Alembic::AbcGeom::OCamera;
-using Alembic::AbcGeom::OFloatProperty;
+#include "CLG_log.h"
+static CLG_LogRef LOG = {"io.alembic"};
namespace blender {
namespace io {
namespace alembic {
-AbcCameraWriter::AbcCameraWriter(Object *ob,
- AbcTransformWriter *parent,
- uint32_t time_sampling,
- ExportSettings &settings)
- : AbcObjectWriter(ob, time_sampling, settings, parent)
+using Alembic::AbcGeom::CameraSample;
+using Alembic::AbcGeom::OCamera;
+using Alembic::AbcGeom::OFloatProperty;
+
+ABCCameraWriter::ABCCameraWriter(const ABCWriterConstructorArgs &args) : ABCAbstractWriter(args)
+{
+}
+
+bool ABCCameraWriter::is_supported(const HierarchyContext *context) const
+{
+ Camera *camera = static_cast<Camera *>(context->object->data);
+ return camera->type == CAM_PERSP;
+}
+
+void ABCCameraWriter::create_alembic_objects(const HierarchyContext * /*context*/)
{
- OCamera camera(parent->alembicXform(), m_name, m_time_sampling);
- m_camera_schema = camera.getSchema();
+ CLOG_INFO(&LOG, 2, "exporting %s", args_.abc_path.c_str());
+ abc_camera_ = OCamera(args_.abc_parent, args_.abc_name, timesample_index_);
+ abc_camera_schema_ = abc_camera_.getSchema();
+
+ abc_custom_data_container_ = abc_camera_schema_.getUserProperties();
+ abc_stereo_distance_ = OFloatProperty(
+ abc_custom_data_container_, "stereoDistance", timesample_index_);
+ abc_eye_separation_ = OFloatProperty(
+ abc_custom_data_container_, "eyeSeparation", timesample_index_);
+}
- m_custom_data_container = m_camera_schema.getUserProperties();
- m_stereo_distance = OFloatProperty(m_custom_data_container, "stereoDistance", m_time_sampling);
- m_eye_separation = OFloatProperty(m_custom_data_container, "eyeSeparation", m_time_sampling);
+const Alembic::Abc::OObject ABCCameraWriter::get_alembic_object() const
+{
+ return abc_camera_;
}
-void AbcCameraWriter::do_write()
+void ABCCameraWriter::do_write(HierarchyContext &context)
{
- Camera *cam = static_cast<Camera *>(m_object->data);
+ Camera *cam = static_cast<Camera *>(context.object->data);
- m_stereo_distance.set(cam->stereo.convergence_distance);
- m_eye_separation.set(cam->stereo.interocular_distance);
+ abc_stereo_distance_.set(cam->stereo.convergence_distance);
+ abc_eye_separation_.set(cam->stereo.interocular_distance);
const double apperture_x = cam->sensor_x / 10.0;
const double apperture_y = cam->sensor_y / 10.0;
const double film_aspect = apperture_x / apperture_y;
- m_camera_sample.setFocalLength(cam->lens);
- m_camera_sample.setHorizontalAperture(apperture_x);
- m_camera_sample.setVerticalAperture(apperture_y);
- m_camera_sample.setHorizontalFilmOffset(apperture_x * cam->shiftx);
- m_camera_sample.setVerticalFilmOffset(apperture_y * cam->shifty * film_aspect);
- m_camera_sample.setNearClippingPlane(cam->clip_start);
- m_camera_sample.setFarClippingPlane(cam->clip_end);
+ CameraSample camera_sample;
+ camera_sample.setFocalLength(cam->lens);
+ camera_sample.setHorizontalAperture(apperture_x);
+ camera_sample.setVerticalAperture(apperture_y);
+ camera_sample.setHorizontalFilmOffset(apperture_x * cam->shiftx);
+ camera_sample.setVerticalFilmOffset(apperture_y * cam->shifty * film_aspect);
+ camera_sample.setNearClippingPlane(cam->clip_start);
+ camera_sample.setFarClippingPlane(cam->clip_end);
if (cam->dof.focus_object) {
- Imath::V3f v(m_object->loc[0] - cam->dof.focus_object->loc[0],
- m_object->loc[1] - cam->dof.focus_object->loc[1],
- m_object->loc[2] - cam->dof.focus_object->loc[2]);
- m_camera_sample.setFocusDistance(v.length());
+ Imath::V3f v(context.object->loc[0] - cam->dof.focus_object->loc[0],
+ context.object->loc[1] - cam->dof.focus_object->loc[1],
+ context.object->loc[2] - cam->dof.focus_object->loc[2]);
+ camera_sample.setFocusDistance(v.length());
}
else {
- m_camera_sample.setFocusDistance(cam->dof.focus_distance);
+ camera_sample.setFocusDistance(cam->dof.focus_distance);
}
/* Blender camera does not have an fstop param, so try to find a custom prop
* instead. */
- m_camera_sample.setFStop(cam->dof.aperture_fstop);
+ camera_sample.setFStop(cam->dof.aperture_fstop);
- m_camera_sample.setLensSqueezeRatio(1.0);
- m_camera_schema.set(m_camera_sample);
+ camera_sample.setLensSqueezeRatio(1.0);
+ abc_camera_schema_.set(camera_sample);
}
} // namespace alembic
diff --git a/source/blender/io/alembic/exporter/abc_writer_camera.h b/source/blender/io/alembic/exporter/abc_writer_camera.h
index befa1e24551..a72cfa2f357 100644
--- a/source/blender/io/alembic/exporter/abc_writer_camera.h
+++ b/source/blender/io/alembic/exporter/abc_writer_camera.h
@@ -19,29 +19,32 @@
* \ingroup balembic
*/
-#include "abc_writer_object.h"
+#include "abc_writer_abstract.h"
-/* ************************************************************************** */
+#include <Alembic/AbcGeom/OCamera.h>
namespace blender {
namespace io {
namespace alembic {
-class AbcCameraWriter : public AbcObjectWriter {
- Alembic::AbcGeom::OCameraSchema m_camera_schema;
- Alembic::AbcGeom::CameraSample m_camera_sample;
- Alembic::AbcGeom::OCompoundProperty m_custom_data_container;
- Alembic::AbcGeom::OFloatProperty m_stereo_distance;
- Alembic::AbcGeom::OFloatProperty m_eye_separation;
+class ABCCameraWriter : public ABCAbstractWriter {
+ private:
+ Alembic::AbcGeom::OCamera abc_camera_;
+ Alembic::AbcGeom::OCameraSchema abc_camera_schema_;
+
+ Alembic::AbcGeom::OCompoundProperty abc_custom_data_container_;
+ Alembic::AbcGeom::OFloatProperty abc_stereo_distance_;
+ Alembic::AbcGeom::OFloatProperty abc_eye_separation_;
public:
- AbcCameraWriter(Object *ob,
- AbcTransformWriter *parent,
- uint32_t time_sampling,
- ExportSettings &settings);
+ explicit ABCCameraWriter(const ABCWriterConstructorArgs &args);
- private:
- virtual void do_write();
+ virtual void create_alembic_objects(const HierarchyContext *context) override;
+ virtual const Alembic::Abc::OObject get_alembic_object() const override;
+
+ protected:
+ virtual bool is_supported(const HierarchyContext *context) const override;
+ virtual void do_write(HierarchyContext &context) override;
};
} // namespace alembic
diff --git a/source/blender/io/alembic/exporter/abc_writer_curves.cc b/source/blender/io/alembic/exporter/abc_writer_curves.cc
index 7c2acb9e0c9..f2a46c5e4fe 100644
--- a/source/blender/io/alembic/exporter/abc_writer_curves.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_curves.cc
@@ -22,9 +22,7 @@
*/
#include "abc_writer_curves.h"
-#include "abc_writer_transform.h"
#include "intern/abc_axis_conversion.h"
-#include "intern/abc_reader_curves.h"
#include "DNA_curve_types.h"
#include "DNA_object_types.h"
@@ -33,6 +31,9 @@
#include "BKE_mesh.h"
#include "BKE_object.h"
+#include "CLG_log.h"
+static CLG_LogRef LOG = {"io.alembic"};
+
using Alembic::AbcGeom::OCompoundProperty;
using Alembic::AbcGeom::OCurves;
using Alembic::AbcGeom::OCurvesSchema;
@@ -44,24 +45,32 @@ namespace blender {
namespace io {
namespace alembic {
-AbcCurveWriter::AbcCurveWriter(Object *ob,
- AbcTransformWriter *parent,
- uint32_t time_sampling,
- ExportSettings &settings)
- : AbcObjectWriter(ob, time_sampling, settings, parent)
+const std::string ABC_CURVE_RESOLUTION_U_PROPNAME("blender:resolution");
+
+ABCCurveWriter::ABCCurveWriter(const ABCWriterConstructorArgs &args) : ABCAbstractWriter(args)
+{
+}
+
+void ABCCurveWriter::create_alembic_objects(const HierarchyContext *context)
{
- OCurves curves(parent->alembicXform(), m_name, m_time_sampling);
- m_schema = curves.getSchema();
+ CLOG_INFO(&LOG, 2, "exporting %s", args_.abc_path.c_str());
+ abc_curve_ = OCurves(args_.abc_parent, args_.abc_name, timesample_index_);
+ abc_curve_schema_ = abc_curve_.getSchema();
- Curve *cu = static_cast<Curve *>(m_object->data);
- OCompoundProperty user_props = m_schema.getUserProperties();
+ Curve *cu = static_cast<Curve *>(context->object->data);
+ OCompoundProperty user_props = abc_curve_schema_.getUserProperties();
OInt16Property user_prop_resolu(user_props, ABC_CURVE_RESOLUTION_U_PROPNAME);
user_prop_resolu.set(cu->resolu);
}
-void AbcCurveWriter::do_write()
+const Alembic::Abc::OObject ABCCurveWriter::get_alembic_object() const
+{
+ return abc_curve_;
+}
+
+void ABCCurveWriter::do_write(HierarchyContext &context)
{
- Curve *curve = static_cast<Curve *>(m_object->data);
+ Curve *curve = static_cast<Curve *>(context.object->data);
std::vector<Imath::V3f> verts;
std::vector<int32_t> vert_counts;
@@ -152,35 +161,31 @@ void AbcCurveWriter::do_write()
Alembic::AbcGeom::OFloatGeomParam::Sample width_sample;
width_sample.setVals(widths);
- m_sample = OCurvesSchema::Sample(verts,
- vert_counts,
- curve_type,
- periodicity,
- width_sample,
- OV2fGeomParam::Sample(), /* UVs */
- ON3fGeomParam::Sample(), /* normals */
- curve_basis,
- weights,
- orders,
- knots);
-
- m_sample.setSelfBounds(bounds());
- m_schema.set(m_sample);
+ OCurvesSchema::Sample sample(verts,
+ vert_counts,
+ curve_type,
+ periodicity,
+ width_sample,
+ OV2fGeomParam::Sample(), /* UVs */
+ ON3fGeomParam::Sample(), /* normals */
+ curve_basis,
+ weights,
+ orders,
+ knots);
+
+ update_bounding_box(context.object);
+ sample.setSelfBounds(bounding_box_);
+ abc_curve_schema_.set(sample);
}
-AbcCurveMeshWriter::AbcCurveMeshWriter(Object *ob,
- AbcTransformWriter *parent,
- uint32_t time_sampling,
- ExportSettings &settings)
- : AbcGenericMeshWriter(ob, parent, time_sampling, settings)
+ABCCurveMeshWriter::ABCCurveMeshWriter(const ABCWriterConstructorArgs &args)
+ : ABCGenericMeshWriter(args)
{
}
-Mesh *AbcCurveMeshWriter::getEvaluatedMesh(Scene * /*scene_eval*/,
- Object *ob_eval,
- bool &r_needsfree)
+Mesh *ABCCurveMeshWriter::get_export_mesh(Object *object_eval, bool &r_needsfree)
{
- Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob_eval);
+ Mesh *mesh_eval = BKE_object_get_evaluated_mesh(object_eval);
if (mesh_eval != NULL) {
/* Mesh_eval only exists when generative modifiers are in use. */
r_needsfree = false;
@@ -188,7 +193,7 @@ Mesh *AbcCurveMeshWriter::getEvaluatedMesh(Scene * /*scene_eval*/,
}
r_needsfree = true;
- return BKE_mesh_new_nomain_from_curve(ob_eval);
+ return BKE_mesh_new_nomain_from_curve(object_eval);
}
} // namespace alembic
diff --git a/source/blender/io/alembic/exporter/abc_writer_curves.h b/source/blender/io/alembic/exporter/abc_writer_curves.h
index d12ebc46a22..12a909761f5 100644
--- a/source/blender/io/alembic/exporter/abc_writer_curves.h
+++ b/source/blender/io/alembic/exporter/abc_writer_curves.h
@@ -22,36 +22,38 @@
* \ingroup balembic
*/
+#include "abc_writer_abstract.h"
#include "abc_writer_mesh.h"
-#include "abc_writer_object.h"
+
+#include <Alembic/AbcGeom/OCurves.h>
namespace blender {
namespace io {
namespace alembic {
-class AbcCurveWriter : public AbcObjectWriter {
- Alembic::AbcGeom::OCurvesSchema m_schema;
- Alembic::AbcGeom::OCurvesSchema::Sample m_sample;
+extern const std::string ABC_CURVE_RESOLUTION_U_PROPNAME;
+
+class ABCCurveWriter : public ABCAbstractWriter {
+ private:
+ Alembic::AbcGeom::OCurves abc_curve_;
+ Alembic::AbcGeom::OCurvesSchema abc_curve_schema_;
public:
- AbcCurveWriter(Object *ob,
- AbcTransformWriter *parent,
- uint32_t time_sampling,
- ExportSettings &settings);
+ explicit ABCCurveWriter(const ABCWriterConstructorArgs &args);
+
+ virtual void create_alembic_objects(const HierarchyContext *context) override;
+ virtual const Alembic::Abc::OObject get_alembic_object() const override;
protected:
- void do_write();
+ virtual void do_write(HierarchyContext &context) override;
};
-class AbcCurveMeshWriter : public AbcGenericMeshWriter {
+class ABCCurveMeshWriter : public ABCGenericMeshWriter {
public:
- AbcCurveMeshWriter(Object *ob,
- AbcTransformWriter *parent,
- uint32_t time_sampling,
- ExportSettings &settings);
+ ABCCurveMeshWriter(const ABCWriterConstructorArgs &args);
protected:
- Mesh *getEvaluatedMesh(Scene *scene_eval, Object *ob_eval, bool &r_needsfree);
+ virtual Mesh *get_export_mesh(Object *object_eval, bool &r_needsfree) override;
};
} // namespace alembic
diff --git a/source/blender/io/alembic/exporter/abc_writer_hair.cc b/source/blender/io/alembic/exporter/abc_writer_hair.cc
index 304aa7a5ff8..ac4deddd9b4 100644
--- a/source/blender/io/alembic/exporter/abc_writer_hair.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_hair.cc
@@ -19,7 +19,6 @@
*/
#include "abc_writer_hair.h"
-#include "abc_writer_transform.h"
#include "intern/abc_axis_conversion.h"
#include <cstdio>
@@ -35,40 +34,46 @@
#include "BKE_mesh_runtime.h"
#include "BKE_particle.h"
-using Alembic::Abc::P3fArraySamplePtr;
+#include "CLG_log.h"
+static CLG_LogRef LOG = {"io.alembic"};
+using Alembic::Abc::P3fArraySamplePtr;
using Alembic::AbcGeom::OCurves;
using Alembic::AbcGeom::OCurvesSchema;
using Alembic::AbcGeom::ON3fGeomParam;
using Alembic::AbcGeom::OV2fGeomParam;
-/* ************************************************************************** */
-
namespace blender {
namespace io {
namespace alembic {
-AbcHairWriter::AbcHairWriter(Object *ob,
- AbcTransformWriter *parent,
- uint32_t time_sampling,
- ExportSettings &settings,
- ParticleSystem *psys)
- : AbcObjectWriter(ob, time_sampling, settings, parent), m_uv_warning_shown(false)
+ABCHairWriter::ABCHairWriter(const ABCWriterConstructorArgs &args)
+ : ABCAbstractWriter(args), uv_warning_shown_(false)
{
- m_psys = psys;
+}
- std::string psys_name = get_valid_abc_name(psys->name);
- OCurves curves(parent->alembicXform(), psys_name, m_time_sampling);
- m_schema = curves.getSchema();
+void ABCHairWriter::create_alembic_objects(const HierarchyContext * /*context*/)
+{
+ CLOG_INFO(&LOG, 2, "exporting %s", args_.abc_path.c_str());
+ abc_curves_ = OCurves(args_.abc_parent, args_.abc_name, timesample_index_);
+ abc_curves_schema_ = abc_curves_.getSchema();
}
-void AbcHairWriter::do_write()
+const Alembic::Abc::OObject ABCHairWriter::get_alembic_object() const
{
- if (!m_psys) {
- return;
- }
- Mesh *mesh = mesh_get_eval_final(
- m_settings.depsgraph, m_settings.scene, m_object, &CD_MASK_MESH);
+ return abc_curves_;
+}
+
+bool ABCHairWriter::check_is_animated(const HierarchyContext & /*context*/) const
+{
+ /* We assume that hair particles are always animated. */
+ return true;
+}
+
+void ABCHairWriter::do_write(HierarchyContext &context)
+{
+ Scene *scene_eval = DEG_get_evaluated_scene(args_.depsgraph);
+ Mesh *mesh = mesh_get_eval_final(args_.depsgraph, scene_eval, context.object, &CD_MASK_MESH);
BKE_mesh_tessface_ensure(mesh);
std::vector<Imath::V3f> verts;
@@ -76,44 +81,45 @@ void AbcHairWriter::do_write()
std::vector<Imath::V2f> uv_values;
std::vector<Imath::V3f> norm_values;
- if (m_psys->pathcache) {
- ParticleSettings *part = m_psys->part;
- bool export_children = m_settings.export_child_hairs && m_psys->childcache &&
- part->childtype != 0;
+ ParticleSystem *psys = context.particle_system;
+ if (psys->pathcache) {
+ ParticleSettings *part = psys->part;
+ bool export_children = psys->childcache && part->childtype != 0;
if (!export_children || part->draw & PART_DRAW_PARENT) {
- write_hair_sample(mesh, part, verts, norm_values, uv_values, hvertices);
+ write_hair_sample(context, mesh, verts, norm_values, uv_values, hvertices);
}
if (export_children) {
- write_hair_child_sample(mesh, part, verts, norm_values, uv_values, hvertices);
+ write_hair_child_sample(context, mesh, verts, norm_values, uv_values, hvertices);
}
}
Alembic::Abc::P3fArraySample iPos(verts);
- m_sample = OCurvesSchema::Sample(iPos, hvertices);
- m_sample.setBasis(Alembic::AbcGeom::kNoBasis);
- m_sample.setType(Alembic::AbcGeom::kLinear);
- m_sample.setWrap(Alembic::AbcGeom::kNonPeriodic);
+ OCurvesSchema::Sample sample(iPos, hvertices);
+ sample.setBasis(Alembic::AbcGeom::kNoBasis);
+ sample.setType(Alembic::AbcGeom::kLinear);
+ sample.setWrap(Alembic::AbcGeom::kNonPeriodic);
if (!uv_values.empty()) {
OV2fGeomParam::Sample uv_smp;
uv_smp.setVals(uv_values);
- m_sample.setUVs(uv_smp);
+ sample.setUVs(uv_smp);
}
if (!norm_values.empty()) {
ON3fGeomParam::Sample norm_smp;
norm_smp.setVals(norm_values);
- m_sample.setNormals(norm_smp);
+ sample.setNormals(norm_smp);
}
- m_sample.setSelfBounds(bounds());
- m_schema.set(m_sample);
+ update_bounding_box(context.object);
+ sample.setSelfBounds(bounding_box_);
+ abc_curves_schema_.set(sample);
}
-void AbcHairWriter::write_hair_sample(Mesh *mesh,
- ParticleSettings *part,
+void ABCHairWriter::write_hair_sample(const HierarchyContext &context,
+ Mesh *mesh,
std::vector<Imath::V3f> &verts,
std::vector<Imath::V3f> &norm_values,
std::vector<Imath::V2f> &uv_values,
@@ -121,28 +127,30 @@ void AbcHairWriter::write_hair_sample(Mesh *mesh,
{
/* Get untransformed vertices, there's a xform under the hair. */
float inv_mat[4][4];
- invert_m4_m4_safe(inv_mat, m_object->obmat);
+ invert_m4_m4_safe(inv_mat, context.object->obmat);
MTFace *mtface = mesh->mtface;
MFace *mface = mesh->mface;
MVert *mverts = mesh->mvert;
- if ((!mtface || !mface) && !m_uv_warning_shown) {
+ if ((!mtface || !mface) && !uv_warning_shown_) {
std::fprintf(stderr,
"Warning, no UV set found for underlying geometry of %s.\n",
- m_object->id.name + 2);
- m_uv_warning_shown = true;
+ context.object->id.name + 2);
+ uv_warning_shown_ = true;
}
- ParticleData *pa = m_psys->particles;
+ ParticleSystem *psys = context.particle_system;
+ ParticleSettings *part = psys->part;
+ ParticleData *pa = psys->particles;
int k;
- ParticleCacheKey **cache = m_psys->pathcache;
+ ParticleCacheKey **cache = psys->pathcache;
ParticleCacheKey *path;
float normal[3];
Imath::V3f tmp_nor;
- for (int p = 0; p < m_psys->totpart; p++, pa++) {
+ for (int p = 0; p < psys->totpart; p++, pa++) {
/* underlying info for faces-only emission */
path = cache[p];
@@ -224,8 +232,8 @@ void AbcHairWriter::write_hair_sample(Mesh *mesh,
}
}
-void AbcHairWriter::write_hair_child_sample(Mesh *mesh,
- ParticleSettings *part,
+void ABCHairWriter::write_hair_child_sample(const HierarchyContext &context,
+ Mesh *mesh,
std::vector<Imath::V3f> &verts,
std::vector<Imath::V3f> &norm_values,
std::vector<Imath::V2f> &uv_values,
@@ -233,26 +241,30 @@ void AbcHairWriter::write_hair_child_sample(Mesh *mesh,
{
/* Get untransformed vertices, there's a xform under the hair. */
float inv_mat[4][4];
- invert_m4_m4_safe(inv_mat, m_object->obmat);
+ invert_m4_m4_safe(inv_mat, context.object->obmat);
MTFace *mtface = mesh->mtface;
MVert *mverts = mesh->mvert;
- ParticleCacheKey **cache = m_psys->childcache;
+ ParticleSystem *psys = context.particle_system;
+ ParticleSettings *part = psys->part;
+ ParticleCacheKey **cache = psys->childcache;
ParticleCacheKey *path;
- ChildParticle *pc = m_psys->child;
+ ChildParticle *pc = psys->child;
- for (int p = 0; p < m_psys->totchild; p++, pc++) {
+ for (int p = 0; p < psys->totchild; p++, pc++) {
path = cache[p];
if (part->from == PART_FROM_FACE && part->childtype != PART_CHILD_PARTICLES && mtface) {
const int num = pc->num;
if (num < 0) {
- ABC_LOG(m_settings.logger)
- << "Warning, child particle of hair system " << m_psys->name
- << " has unknown face index of geometry of " << (m_object->id.name + 2)
- << ", skipping child hair." << std::endl;
+ CLOG_WARN(
+ &LOG,
+ "Child particle of hair system %s has unknown face index of geometry of %s, skipping "
+ "child hair.",
+ psys->name,
+ context.object->id.name + 2);
continue;
}
diff --git a/source/blender/io/alembic/exporter/abc_writer_hair.h b/source/blender/io/alembic/exporter/abc_writer_hair.h
index d7831684a12..af1372a08f3 100644
--- a/source/blender/io/alembic/exporter/abc_writer_hair.h
+++ b/source/blender/io/alembic/exporter/abc_writer_hair.h
@@ -19,9 +19,10 @@
* \ingroup balembic
*/
-#include "abc_writer_object.h"
+#include "abc_writer_abstract.h"
+#include <Alembic/AbcGeom/OCurves.h>
+#include <vector>
-struct Mesh;
struct ParticleSettings;
struct ParticleSystem;
@@ -29,33 +30,33 @@ namespace blender {
namespace io {
namespace alembic {
-class AbcHairWriter : public AbcObjectWriter {
- ParticleSystem *m_psys;
-
- Alembic::AbcGeom::OCurvesSchema m_schema;
- Alembic::AbcGeom::OCurvesSchema::Sample m_sample;
+class ABCHairWriter : public ABCAbstractWriter {
+ private:
+ Alembic::AbcGeom::OCurves abc_curves_;
+ Alembic::AbcGeom::OCurvesSchema abc_curves_schema_;
- bool m_uv_warning_shown;
+ bool uv_warning_shown_;
public:
- AbcHairWriter(Object *ob,
- AbcTransformWriter *parent,
- uint32_t time_sampling,
- ExportSettings &settings,
- ParticleSystem *psys);
+ explicit ABCHairWriter(const ABCWriterConstructorArgs &args);
- private:
- virtual void do_write();
+ virtual void create_alembic_objects(const HierarchyContext *context) override;
+ virtual const Alembic::Abc::OObject get_alembic_object() const override;
- void write_hair_sample(struct Mesh *mesh,
- ParticleSettings *part,
+ protected:
+ virtual void do_write(HierarchyContext &context) override;
+ virtual bool check_is_animated(const HierarchyContext &context) const override;
+
+ private:
+ void write_hair_sample(const HierarchyContext &context,
+ struct Mesh *mesh,
std::vector<Imath::V3f> &verts,
std::vector<Imath::V3f> &norm_values,
std::vector<Imath::V2f> &uv_values,
std::vector<int32_t> &hvertices);
- void write_hair_child_sample(struct Mesh *mesh,
- ParticleSettings *part,
+ void write_hair_child_sample(const HierarchyContext &context,
+ struct Mesh *mesh,
std::vector<Imath::V3f> &verts,
std::vector<Imath::V3f> &norm_values,
std::vector<Imath::V2f> &uv_values,
diff --git a/source/blender/io/alembic/exporter/abc_writer_mball.cc b/source/blender/io/alembic/exporter/abc_writer_mball.cc
index 8ae3bb64390..167e392eb96 100644
--- a/source/blender/io/alembic/exporter/abc_writer_mball.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_mball.cc
@@ -19,11 +19,9 @@
*/
#include "abc_writer_mball.h"
-#include "abc_writer_mesh.h"
+#include "abc_hierarchy_iterator.h"
-#include "DNA_mesh_types.h"
-#include "DNA_meta_types.h"
-#include "DNA_object_types.h"
+#include "BLI_assert.h"
#include "BKE_displist.h"
#include "BKE_lib_id.h"
@@ -31,68 +29,57 @@
#include "BKE_mesh.h"
#include "BKE_object.h"
-#include "BLI_utildefines.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meta_types.h"
namespace blender {
namespace io {
namespace alembic {
-AbcMBallWriter::AbcMBallWriter(Main *bmain,
- Object *ob,
- AbcTransformWriter *parent,
- uint32_t time_sampling,
- ExportSettings &settings)
- : AbcGenericMeshWriter(ob, parent, time_sampling, settings), m_bmain(bmain)
+ABCMetaballWriter::ABCMetaballWriter(const ABCWriterConstructorArgs &args)
+ : ABCGenericMeshWriter(args)
{
- m_is_animated = isAnimated();
}
-AbcMBallWriter::~AbcMBallWriter()
+bool ABCMetaballWriter::is_supported(const HierarchyContext *context) const
{
+ Scene *scene = DEG_get_input_scene(args_.depsgraph);
+ bool supported = is_basis_ball(scene, context->object) &&
+ ABCGenericMeshWriter::is_supported(context);
+ return supported;
}
-bool AbcMBallWriter::isAnimated() const
+bool ABCMetaballWriter::check_is_animated(const HierarchyContext & /*context*/) const
{
+ /* We assume that metaballs are always animated, as the current object may
+ * not be animated but another ball in the same group may be. */
return true;
}
-Mesh *AbcMBallWriter::getEvaluatedMesh(Scene * /*scene_eval*/, Object *ob_eval, bool &r_needsfree)
+bool ABCMetaballWriter::export_as_subdivision_surface(Object * /*ob_eval*/) const
{
- Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob_eval);
- if (mesh_eval != NULL) {
+ /* Metaballs should be exported to subdivision surfaces, if the export options allow. */
+ return true;
+}
+
+Mesh *ABCMetaballWriter::get_export_mesh(Object *object_eval, bool &r_needsfree)
+{
+ Mesh *mesh_eval = BKE_object_get_evaluated_mesh(object_eval);
+ if (mesh_eval != nullptr) {
/* Mesh_eval only exists when generative modifiers are in use. */
r_needsfree = false;
return mesh_eval;
}
r_needsfree = true;
-
- /* The approach below is copied from BKE_mesh_new_from_object() */
- Mesh *tmpmesh = BKE_mesh_add(m_bmain, ((ID *)m_object->data)->name + 2);
- BLI_assert(tmpmesh != NULL);
-
- /* BKE_mesh_add gives us a user count we don't need */
- id_us_min(&tmpmesh->id);
-
- ListBase disp = {NULL, NULL};
- /* TODO(sergey): This is gonna to work for until Depsgraph
- * only contains for_render flag. As soon as CoW is
- * implemented, this is to be rethought.
- */
- BKE_displist_make_mball_forRender(m_settings.depsgraph, m_settings.scene, m_object, &disp);
- BKE_mesh_from_metaball(&disp, tmpmesh);
- BKE_displist_free(&disp);
-
- BKE_mesh_texspace_copy_from_object(tmpmesh, m_object);
-
- return tmpmesh;
+ return BKE_mesh_new_from_object(args_.depsgraph, object_eval, false);
}
-void AbcMBallWriter::freeEvaluatedMesh(struct Mesh *mesh)
+void ABCMetaballWriter::free_export_mesh(Mesh *mesh)
{
- BKE_id_free(m_bmain, mesh);
+ BKE_id_free(nullptr, mesh);
}
-bool AbcMBallWriter::isBasisBall(Scene *scene, Object *ob)
+bool ABCMetaballWriter::is_basis_ball(Scene *scene, Object *ob) const
{
Object *basis_ob = BKE_mball_basis_find(scene, ob);
return ob == basis_ob;
diff --git a/source/blender/io/alembic/exporter/abc_writer_mball.h b/source/blender/io/alembic/exporter/abc_writer_mball.h
index d632f0bd410..90d8c4d4b15 100644
--- a/source/blender/io/alembic/exporter/abc_writer_mball.h
+++ b/source/blender/io/alembic/exporter/abc_writer_mball.h
@@ -20,39 +20,24 @@
*/
#include "abc_writer_mesh.h"
-#include "abc_writer_object.h"
-
-struct Main;
-struct Object;
namespace blender {
namespace io {
namespace alembic {
-/* AbcMBallWriter converts the metaballs to meshes at every frame,
- * and defers to AbcGenericMeshWriter to perform the writing
- * to the Alembic file. Only the basis balls are exported, as this
- * results in the entire shape as one mesh. */
-class AbcMBallWriter : public AbcGenericMeshWriter {
- Main *m_bmain;
-
+class ABCMetaballWriter : public ABCGenericMeshWriter {
public:
- explicit AbcMBallWriter(Main *bmain,
- Object *ob,
- AbcTransformWriter *parent,
- uint32_t time_sampling,
- ExportSettings &settings);
-
- ~AbcMBallWriter();
-
- static bool isBasisBall(Scene *scene, Object *ob);
+ explicit ABCMetaballWriter(const ABCWriterConstructorArgs &args);
protected:
- Mesh *getEvaluatedMesh(Scene *scene_eval, Object *ob_eval, bool &r_needsfree) override;
- void freeEvaluatedMesh(struct Mesh *mesh) override;
+ virtual Mesh *get_export_mesh(Object *object_eval, bool &r_needsfree) override;
+ virtual void free_export_mesh(Mesh *mesh) override;
+ virtual bool is_supported(const HierarchyContext *context) const override;
+ virtual bool check_is_animated(const HierarchyContext &context) const override;
+ virtual bool export_as_subdivision_surface(Object *ob_eval) const override;
private:
- bool isAnimated() const override;
+ bool is_basis_ball(Scene *scene, Object *ob) const;
};
} // namespace alembic
diff --git a/source/blender/io/alembic/exporter/abc_writer_mesh.cc b/source/blender/io/alembic/exporter/abc_writer_mesh.cc
index 512768bcd98..07196f2b81f 100644
--- a/source/blender/io/alembic/exporter/abc_writer_mesh.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_mesh.cc
@@ -19,29 +19,37 @@
*/
#include "abc_writer_mesh.h"
-#include "abc_writer_transform.h"
+#include "abc_hierarchy_iterator.h"
#include "intern/abc_axis_conversion.h"
-#include "DNA_material_types.h"
-#include "DNA_mesh_types.h"
-#include "DNA_meshdata_types.h"
-#include "DNA_object_fluidsim_types.h"
+#include "BLI_assert.h"
+#include "BLI_math_vector.h"
-#include "BKE_anim_data.h"
-#include "BKE_key.h"
+#include "BKE_customdata.h"
#include "BKE_lib_id.h"
#include "BKE_material.h"
#include "BKE_mesh.h"
-#include "BKE_mesh_runtime.h"
#include "BKE_modifier.h"
+#include "BKE_object.h"
#include "bmesh.h"
#include "bmesh_tools.h"
-#include "DEG_depsgraph_query.h"
+#include "DEG_depsgraph.h"
+
+#include "DNA_layer_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_object_fluidsim_types.h"
+#include "DNA_particle_types.h"
+
+#include "CLG_log.h"
+static CLG_LogRef LOG = {"io.alembic"};
using Alembic::Abc::FloatArraySample;
using Alembic::Abc::Int32ArraySample;
+using Alembic::Abc::OObject;
using Alembic::Abc::V2fArraySample;
using Alembic::Abc::V3fArraySample;
@@ -64,136 +72,77 @@ namespace alembic {
/* NOTE: Alembic's polygon winding order is clockwise, to match with Renderman. */
-static void get_vertices(struct Mesh *mesh, std::vector<Imath::V3f> &points)
-{
- points.clear();
- points.resize(mesh->totvert);
-
- MVert *verts = mesh->mvert;
-
- for (int i = 0, e = mesh->totvert; i < e; i++) {
- copy_yup_from_zup(points[i].getValue(), verts[i].co);
- }
-}
-
+static void get_vertices(struct Mesh *mesh, std::vector<Imath::V3f> &points);
static void get_topology(struct Mesh *mesh,
std::vector<int32_t> &poly_verts,
std::vector<int32_t> &loop_counts,
- bool &r_has_flat_shaded_poly)
-{
- const int num_poly = mesh->totpoly;
- const int num_loops = mesh->totloop;
- MLoop *mloop = mesh->mloop;
- MPoly *mpoly = mesh->mpoly;
- r_has_flat_shaded_poly = false;
-
- poly_verts.clear();
- loop_counts.clear();
- poly_verts.reserve(num_loops);
- loop_counts.reserve(num_poly);
-
- /* NOTE: data needs to be written in the reverse order. */
- for (int i = 0; i < num_poly; i++) {
- MPoly &poly = mpoly[i];
- loop_counts.push_back(poly.totloop);
-
- r_has_flat_shaded_poly |= (poly.flag & ME_SMOOTH) == 0;
-
- MLoop *loop = mloop + poly.loopstart + (poly.totloop - 1);
-
- for (int j = 0; j < poly.totloop; j++, loop--) {
- poly_verts.push_back(loop->v);
- }
- }
-}
-
+ bool &r_has_flat_shaded_poly);
static void get_creases(struct Mesh *mesh,
std::vector<int32_t> &indices,
std::vector<int32_t> &lengths,
- std::vector<float> &sharpnesses)
-{
- const float factor = 1.0f / 255.0f;
+ std::vector<float> &sharpnesses);
+static void get_loop_normals(struct Mesh *mesh,
+ std::vector<Imath::V3f> &normals,
+ bool has_flat_shaded_poly);
- indices.clear();
- lengths.clear();
- sharpnesses.clear();
+ABCGenericMeshWriter::ABCGenericMeshWriter(const ABCWriterConstructorArgs &args)
+ : ABCAbstractWriter(args), is_subd_(false)
+{
+}
- MEdge *edge = mesh->medge;
+void ABCGenericMeshWriter::create_alembic_objects(const HierarchyContext *context)
+{
+ if (!args_.export_params->apply_subdiv && export_as_subdivision_surface(context->object)) {
+ is_subd_ = args_.export_params->use_subdiv_schema;
+ }
- for (int i = 0, e = mesh->totedge; i < e; i++) {
- const float sharpness = static_cast<float>(edge[i].crease) * factor;
+ if (is_subd_) {
+ CLOG_INFO(&LOG, 2, "exporting OSubD %s", args_.abc_path.c_str());
+ abc_subdiv_ = OSubD(args_.abc_parent, args_.abc_name, timesample_index_);
+ abc_subdiv_schema_ = abc_subdiv_.getSchema();
+ }
+ else {
+ CLOG_INFO(&LOG, 2, "exporting OPolyMesh %s", args_.abc_path.c_str());
+ abc_poly_mesh_ = OPolyMesh(args_.abc_parent, args_.abc_name, timesample_index_);
+ abc_poly_mesh_schema_ = abc_poly_mesh_.getSchema();
- if (sharpness != 0.0f) {
- indices.push_back(edge[i].v1);
- indices.push_back(edge[i].v2);
- sharpnesses.push_back(sharpness);
- }
+ OCompoundProperty typeContainer = abc_poly_mesh_.getSchema().getUserProperties();
+ OBoolProperty type(typeContainer, "meshtype");
+ type.set(subsurf_modifier_ == nullptr);
}
- lengths.resize(sharpnesses.size(), 2);
+ Scene *scene_eval = DEG_get_evaluated_scene(args_.depsgraph);
+ liquid_sim_modifier_ = get_liquid_sim_modifier(scene_eval, context->object);
}
-static void get_loop_normals(struct Mesh *mesh,
- std::vector<Imath::V3f> &normals,
- bool has_flat_shaded_poly)
+ABCGenericMeshWriter::~ABCGenericMeshWriter()
{
- normals.clear();
-
- /* If all polygons are smooth shaded, and there are no custom normals, we don't need to export
- * normals at all. This is also done by other software, see T71246. */
- if (!has_flat_shaded_poly && !CustomData_has_layer(&mesh->ldata, CD_CUSTOMLOOPNORMAL) &&
- (mesh->flag & ME_AUTOSMOOTH) == 0) {
- return;
- }
-
- BKE_mesh_calc_normals_split(mesh);
- const float(*lnors)[3] = static_cast<float(*)[3]>(CustomData_get_layer(&mesh->ldata, CD_NORMAL));
- BLI_assert(lnors != NULL || !"BKE_mesh_calc_normals_split() should have computed CD_NORMAL");
-
- normals.resize(mesh->totloop);
+}
- /* NOTE: data needs to be written in the reverse order. */
- int abc_index = 0;
- MPoly *mp = mesh->mpoly;
- for (int i = 0, e = mesh->totpoly; i < e; i++, mp++) {
- for (int j = mp->totloop - 1; j >= 0; j--, abc_index++) {
- int blender_index = mp->loopstart + j;
- copy_yup_from_zup(normals[abc_index].getValue(), lnors[blender_index]);
- }
+const Alembic::Abc::OObject ABCGenericMeshWriter::get_alembic_object() const
+{
+ if (is_subd_) {
+ return abc_subdiv_;
}
+ return abc_poly_mesh_;
}
-/* *************** Modifiers *************** */
-
-/* check if the mesh is a subsurf, ignoring disabled modifiers and
- * displace if it's after subsurf. */
-static ModifierData *get_subsurf_modifier(Scene *scene, Object *ob)
+bool ABCGenericMeshWriter::export_as_subdivision_surface(Object *ob_eval) const
{
- ModifierData *md = static_cast<ModifierData *>(ob->modifiers.last);
+ ModifierData *md = static_cast<ModifierData *>(ob_eval->modifiers.last);
for (; md; md = md->prev) {
- if (!BKE_modifier_is_enabled(scene, md, eModifierMode_Render)) {
- continue;
- }
-
- if (md->type == eModifierType_Subsurf) {
- SubsurfModifierData *smd = reinterpret_cast<SubsurfModifierData *>(md);
-
- if (smd->subdivType == ME_CC_SUBSURF) {
- return md;
- }
- }
-
- /* mesh is not a subsurf. break */
- if ((md->type != eModifierType_Displace) && (md->type != eModifierType_ParticleSystem)) {
- return NULL;
+ /* This modifier has been temporarily disabled by SubdivModifierDisabler,
+ * so this indicates this is to be exported as subdivision surface. */
+ if (md->type == eModifierType_Subsurf && (md->mode & eModifierMode_DisableTemporary)) {
+ return true;
}
}
- return NULL;
+ return false;
}
-static ModifierData *get_liquid_sim_modifier(Scene *scene, Object *ob)
+ModifierData *ABCGenericMeshWriter::get_liquid_sim_modifier(Scene *scene, Object *ob)
{
ModifierData *md = BKE_modifiers_findby_type(ob, eModifierType_Fluidsim);
@@ -205,122 +154,99 @@ static ModifierData *get_liquid_sim_modifier(Scene *scene, Object *ob)
}
}
- return NULL;
+ return nullptr;
}
-/* ************************************************************************** */
-
-AbcGenericMeshWriter::AbcGenericMeshWriter(Object *ob,
- AbcTransformWriter *parent,
- uint32_t time_sampling,
- ExportSettings &settings)
- : AbcObjectWriter(ob, time_sampling, settings, parent)
+bool ABCGenericMeshWriter::is_supported(const HierarchyContext *context) const
{
- m_is_animated = isAnimated();
- m_subsurf_mod = NULL;
- m_is_subd = false;
-
- /* If the object is static, use the default static time sampling. */
- if (!m_is_animated) {
- time_sampling = 0;
- }
+ Object *object = context->object;
+ bool is_dupli = context->duplicator != nullptr;
+ int base_flag;
- if (!m_settings.apply_subdiv) {
- m_subsurf_mod = get_subsurf_modifier(m_settings.scene, m_object);
- m_is_subd = (m_subsurf_mod != NULL);
+ if (is_dupli) {
+ /* Construct the object's base flags from its dupli-parent, just like is done in
+ * deg_objects_dupli_iterator_next(). Without this, the visibility check below will fail. Doing
+ * this here, instead of a more suitable location in AbstractHierarchyIterator, prevents
+ * copying the Object for every dupli. */
+ base_flag = object->base_flag;
+ object->base_flag = context->duplicator->base_flag | BASE_FROM_DUPLI;
}
- m_is_liquid = (get_liquid_sim_modifier(m_settings.scene, m_object) != NULL);
+ int visibility = BKE_object_visibility(
+ object, DAG_EVAL_RENDER /* TODO(Sybren): add evaluation mode to export options? */);
- while (parent->alembicXform().getChildHeader(m_name)) {
- m_name.append("_");
+ if (is_dupli) {
+ object->base_flag = base_flag;
}
- if (m_settings.use_subdiv_schema && m_is_subd) {
- OSubD subd(parent->alembicXform(), m_name, m_time_sampling);
- m_subdiv_schema = subd.getSchema();
- }
- else {
- OPolyMesh mesh(parent->alembicXform(), m_name, m_time_sampling);
- m_mesh_schema = mesh.getSchema();
-
- OCompoundProperty typeContainer = m_mesh_schema.getUserProperties();
- OBoolProperty type(typeContainer, "meshtype");
- type.set(m_is_subd);
- }
+ return (visibility & OB_VISIBLE_SELF) != 0;
}
-AbcGenericMeshWriter::~AbcGenericMeshWriter()
+void ABCGenericMeshWriter::do_write(HierarchyContext &context)
{
- if (m_subsurf_mod) {
- m_subsurf_mod->mode &= ~eModifierMode_DisableTemporary;
- }
-}
+ Object *object = context.object;
+ bool needsfree = false;
-bool AbcGenericMeshWriter::isAnimated() const
-{
- if (BKE_animdata_id_is_animated(static_cast<ID *>(m_object->data))) {
- return true;
- }
- if (BKE_key_from_object(m_object) != NULL) {
- return true;
- }
+ Mesh *mesh = get_export_mesh(object, needsfree);
- /* Test modifiers. */
- ModifierData *md = static_cast<ModifierData *>(m_object->modifiers.first);
- while (md) {
+ if (mesh == nullptr) {
+ return;
+ }
- if (md->type != eModifierType_Subsurf) {
- return true;
- }
+ if (args_.export_params->triangulate) {
+ const bool tag_only = false;
+ const int quad_method = args_.export_params->quad_method;
+ const int ngon_method = args_.export_params->ngon_method;
- md = md->next;
- }
+ struct BMeshCreateParams bmcp = {false};
+ struct BMeshFromMeshParams bmfmp = {true, false, false, 0};
+ BMesh *bm = BKE_mesh_to_bmesh_ex(mesh, &bmcp, &bmfmp);
- return false;
-}
+ BM_mesh_triangulate(bm, quad_method, ngon_method, 4, tag_only, nullptr, nullptr, nullptr);
-void AbcGenericMeshWriter::setIsAnimated(bool is_animated)
-{
- m_is_animated = is_animated;
-}
+ Mesh *triangulated_mesh = BKE_mesh_from_bmesh_for_eval_nomain(bm, nullptr, mesh);
+ BM_mesh_free(bm);
-void AbcGenericMeshWriter::do_write()
-{
- /* We have already stored a sample for this object. */
- if (!m_first_frame && !m_is_animated) {
- return;
+ if (needsfree) {
+ free_export_mesh(mesh);
+ }
+ mesh = triangulated_mesh;
+ needsfree = true;
}
- bool needsfree;
- struct Mesh *mesh = getFinalMesh(needsfree);
+ m_custom_data_config.pack_uvs = args_.export_params->packuv;
+ m_custom_data_config.mpoly = mesh->mpoly;
+ m_custom_data_config.mloop = mesh->mloop;
+ m_custom_data_config.totpoly = mesh->totpoly;
+ m_custom_data_config.totloop = mesh->totloop;
+ m_custom_data_config.totvert = mesh->totvert;
try {
- if (m_settings.use_subdiv_schema && m_subdiv_schema.valid()) {
- writeSubD(mesh);
+ if (is_subd_) {
+ write_subd(context, mesh);
}
else {
- writeMesh(mesh);
+ write_mesh(context, mesh);
}
if (needsfree) {
- freeEvaluatedMesh(mesh);
+ free_export_mesh(mesh);
}
}
catch (...) {
if (needsfree) {
- freeEvaluatedMesh(mesh);
+ free_export_mesh(mesh);
}
throw;
}
}
-void AbcGenericMeshWriter::freeEvaluatedMesh(struct Mesh *mesh)
+void ABCGenericMeshWriter::free_export_mesh(Mesh *mesh)
{
- BKE_id_free(NULL, mesh);
+ BKE_id_free(nullptr, mesh);
}
-void AbcGenericMeshWriter::writeMesh(struct Mesh *mesh)
+void ABCGenericMeshWriter::write_mesh(HierarchyContext &context, Mesh *mesh)
{
std::vector<Imath::V3f> points, normals;
std::vector<int32_t> poly_verts, loop_counts;
@@ -330,32 +256,33 @@ void AbcGenericMeshWriter::writeMesh(struct Mesh *mesh)
get_vertices(mesh, points);
get_topology(mesh, poly_verts, loop_counts, has_flat_shaded_poly);
- if (m_first_frame && m_settings.export_face_sets) {
- writeFaceSets(mesh, m_mesh_schema);
+ if (!frame_has_been_written_ && args_.export_params->face_sets) {
+ write_face_sets(context.object, mesh, abc_poly_mesh_schema_);
}
- m_mesh_sample = OPolyMeshSchema::Sample(
+ OPolyMeshSchema::Sample mesh_sample = OPolyMeshSchema::Sample(
V3fArraySample(points), Int32ArraySample(poly_verts), Int32ArraySample(loop_counts));
- UVSample sample;
- if (m_settings.export_uvs) {
- const char *name = get_uv_sample(sample, m_custom_data_config, &mesh->ldata);
+ UVSample uvs_and_indices;
- if (!sample.indices.empty() && !sample.uvs.empty()) {
+ if (!frame_has_been_written_ && args_.export_params->uvs) {
+ const char *name = get_uv_sample(uvs_and_indices, m_custom_data_config, &mesh->ldata);
+
+ if (!uvs_and_indices.indices.empty() && !uvs_and_indices.uvs.empty()) {
OV2fGeomParam::Sample uv_sample;
- uv_sample.setVals(V2fArraySample(sample.uvs));
- uv_sample.setIndices(UInt32ArraySample(sample.indices));
+ uv_sample.setVals(V2fArraySample(uvs_and_indices.uvs));
+ uv_sample.setIndices(UInt32ArraySample(uvs_and_indices.indices));
uv_sample.setScope(kFacevaryingScope);
- m_mesh_schema.setUVSourceName(name);
- m_mesh_sample.setUVs(uv_sample);
+ abc_poly_mesh_schema_.setUVSourceName(name);
+ mesh_sample.setUVs(uv_sample);
}
write_custom_data(
- m_mesh_schema.getArbGeomParams(), m_custom_data_config, &mesh->ldata, CD_MLOOPUV);
+ abc_poly_mesh_schema_.getArbGeomParams(), m_custom_data_config, &mesh->ldata, CD_MLOOPUV);
}
- if (m_settings.export_normals) {
+ if (args_.export_params->normals) {
get_loop_normals(mesh, normals, has_flat_shaded_poly);
ON3fGeomParam::Sample normals_sample;
@@ -364,22 +291,23 @@ void AbcGenericMeshWriter::writeMesh(struct Mesh *mesh)
normals_sample.setVals(V3fArraySample(normals));
}
- m_mesh_sample.setNormals(normals_sample);
+ mesh_sample.setNormals(normals_sample);
}
- if (m_is_liquid) {
- getVelocities(mesh, velocities);
- m_mesh_sample.setVelocities(V3fArraySample(velocities));
+ if (liquid_sim_modifier_ != nullptr) {
+ get_velocities(mesh, velocities);
+ mesh_sample.setVelocities(V3fArraySample(velocities));
}
- m_mesh_sample.setSelfBounds(bounds());
+ update_bounding_box(context.object);
+ mesh_sample.setSelfBounds(bounding_box_);
- m_mesh_schema.set(m_mesh_sample);
+ abc_poly_mesh_schema_.set(mesh_sample);
- writeArbGeoParams(mesh);
+ write_arb_geo_params(mesh);
}
-void AbcGenericMeshWriter::writeSubD(struct Mesh *mesh)
+void ABCGenericMeshWriter::write_subd(HierarchyContext &context, struct Mesh *mesh)
{
std::vector<float> crease_sharpness;
std::vector<Imath::V3f> points;
@@ -391,15 +319,15 @@ void AbcGenericMeshWriter::writeSubD(struct Mesh *mesh)
get_topology(mesh, poly_verts, loop_counts, has_flat_poly);
get_creases(mesh, crease_indices, crease_lengths, crease_sharpness);
- if (m_first_frame && m_settings.export_face_sets) {
- writeFaceSets(mesh, m_subdiv_schema);
+ if (!frame_has_been_written_ && args_.export_params->face_sets) {
+ write_face_sets(context.object, mesh, abc_subdiv_schema_);
}
- m_subdiv_sample = OSubDSchema::Sample(
+ OSubDSchema::Sample subdiv_sample = OSubDSchema::Sample(
V3fArraySample(points), Int32ArraySample(poly_verts), Int32ArraySample(loop_counts));
UVSample sample;
- if (m_first_frame && m_settings.export_uvs) {
+ if (!frame_has_been_written_ && args_.export_params->uvs) {
const char *name = get_uv_sample(sample, m_custom_data_config, &mesh->ldata);
if (!sample.indices.empty() && !sample.uvs.empty()) {
@@ -408,30 +336,32 @@ void AbcGenericMeshWriter::writeSubD(struct Mesh *mesh)
uv_sample.setIndices(UInt32ArraySample(sample.indices));
uv_sample.setScope(kFacevaryingScope);
- m_subdiv_schema.setUVSourceName(name);
- m_subdiv_sample.setUVs(uv_sample);
+ abc_subdiv_schema_.setUVSourceName(name);
+ subdiv_sample.setUVs(uv_sample);
}
write_custom_data(
- m_subdiv_schema.getArbGeomParams(), m_custom_data_config, &mesh->ldata, CD_MLOOPUV);
+ abc_subdiv_schema_.getArbGeomParams(), m_custom_data_config, &mesh->ldata, CD_MLOOPUV);
}
if (!crease_indices.empty()) {
- m_subdiv_sample.setCreaseIndices(Int32ArraySample(crease_indices));
- m_subdiv_sample.setCreaseLengths(Int32ArraySample(crease_lengths));
- m_subdiv_sample.setCreaseSharpnesses(FloatArraySample(crease_sharpness));
+ subdiv_sample.setCreaseIndices(Int32ArraySample(crease_indices));
+ subdiv_sample.setCreaseLengths(Int32ArraySample(crease_lengths));
+ subdiv_sample.setCreaseSharpnesses(FloatArraySample(crease_sharpness));
}
- m_subdiv_sample.setSelfBounds(bounds());
- m_subdiv_schema.set(m_subdiv_sample);
+ update_bounding_box(context.object);
+ subdiv_sample.setSelfBounds(bounding_box_);
+ abc_subdiv_schema_.set(subdiv_sample);
- writeArbGeoParams(mesh);
+ write_arb_geo_params(mesh);
}
-template<typename Schema> void AbcGenericMeshWriter::writeFaceSets(struct Mesh *me, Schema &schema)
+template<typename Schema>
+void ABCGenericMeshWriter::write_face_sets(Object *object, struct Mesh *mesh, Schema &schema)
{
std::map<std::string, std::vector<int32_t>> geo_groups;
- getGeoGroups(me, geo_groups);
+ get_geo_groups(object, mesh, geo_groups);
std::map<std::string, std::vector<int32_t>>::iterator it;
for (it = geo_groups.begin(); it != geo_groups.end(); ++it) {
@@ -442,83 +372,35 @@ template<typename Schema> void AbcGenericMeshWriter::writeFaceSets(struct Mesh *
}
}
-Mesh *AbcGenericMeshWriter::getFinalMesh(bool &r_needsfree)
+void ABCGenericMeshWriter::write_arb_geo_params(struct Mesh *me)
{
- /* We don't want subdivided mesh data */
- if (m_subsurf_mod) {
- m_subsurf_mod->mode |= eModifierMode_DisableTemporary;
- }
-
- r_needsfree = false;
-
- Scene *scene = DEG_get_evaluated_scene(m_settings.depsgraph);
- Object *ob_eval = DEG_get_evaluated_object(m_settings.depsgraph, m_object);
- struct Mesh *mesh = getEvaluatedMesh(scene, ob_eval, r_needsfree);
-
- if (m_subsurf_mod) {
- m_subsurf_mod->mode &= ~eModifierMode_DisableTemporary;
- }
-
- if (m_settings.triangulate) {
- const bool tag_only = false;
- const int quad_method = m_settings.quad_method;
- const int ngon_method = m_settings.ngon_method;
-
- struct BMeshCreateParams bmcp = {false};
- struct BMeshFromMeshParams bmfmp = {true, false, false, 0};
- BMesh *bm = BKE_mesh_to_bmesh_ex(mesh, &bmcp, &bmfmp);
-
- BM_mesh_triangulate(bm, quad_method, ngon_method, 4, tag_only, NULL, NULL, NULL);
-
- Mesh *result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, mesh);
- BM_mesh_free(bm);
-
- if (r_needsfree) {
- BKE_id_free(NULL, mesh);
- }
-
- mesh = result;
- r_needsfree = true;
+ if (liquid_sim_modifier_ != nullptr) {
+ /* We don't need anything more for liquid meshes. */
+ return;
}
- m_custom_data_config.pack_uvs = m_settings.pack_uv;
- m_custom_data_config.mpoly = mesh->mpoly;
- m_custom_data_config.mloop = mesh->mloop;
- m_custom_data_config.totpoly = mesh->totpoly;
- m_custom_data_config.totloop = mesh->totloop;
- m_custom_data_config.totvert = mesh->totvert;
-
- return mesh;
-}
-
-void AbcGenericMeshWriter::writeArbGeoParams(struct Mesh *me)
-{
- if (m_is_liquid) {
- /* We don't need anything more for liquid meshes. */
+ if (frame_has_been_written_ || !args_.export_params->vcolors) {
return;
}
- if (m_first_frame && m_settings.export_vcols) {
- if (m_subdiv_schema.valid()) {
- write_custom_data(
- m_subdiv_schema.getArbGeomParams(), m_custom_data_config, &me->ldata, CD_MLOOPCOL);
- }
- else {
- write_custom_data(
- m_mesh_schema.getArbGeomParams(), m_custom_data_config, &me->ldata, CD_MLOOPCOL);
- }
+ OCompoundProperty arb_geom_params;
+ if (is_subd_) {
+ arb_geom_params = abc_subdiv_.getSchema().getArbGeomParams();
+ }
+ else {
+ arb_geom_params = abc_poly_mesh_.getSchema().getArbGeomParams();
}
+ write_custom_data(arb_geom_params, m_custom_data_config, &me->ldata, CD_MLOOPCOL);
}
-void AbcGenericMeshWriter::getVelocities(struct Mesh *mesh, std::vector<Imath::V3f> &vels)
+void ABCGenericMeshWriter::get_velocities(struct Mesh *mesh, std::vector<Imath::V3f> &vels)
{
const int totverts = mesh->totvert;
vels.clear();
vels.resize(totverts);
- ModifierData *md = get_liquid_sim_modifier(m_settings.scene, m_object);
- FluidsimModifierData *fmd = reinterpret_cast<FluidsimModifierData *>(md);
+ FluidsimModifierData *fmd = reinterpret_cast<FluidsimModifierData *>(liquid_sim_modifier_);
FluidsimSettings *fss = fmd->fss;
if (fss->meshVelocities) {
@@ -534,8 +416,9 @@ void AbcGenericMeshWriter::getVelocities(struct Mesh *mesh, std::vector<Imath::V
}
}
-void AbcGenericMeshWriter::getGeoGroups(struct Mesh *mesh,
- std::map<std::string, std::vector<int32_t>> &geo_groups)
+void ABCGenericMeshWriter::get_geo_groups(Object *object,
+ struct Mesh *mesh,
+ std::map<std::string, std::vector<int32_t>> &geo_groups)
{
const int num_poly = mesh->totpoly;
MPoly *polygons = mesh->mpoly;
@@ -544,13 +427,13 @@ void AbcGenericMeshWriter::getGeoGroups(struct Mesh *mesh,
MPoly &current_poly = polygons[i];
short mnr = current_poly.mat_nr;
- Material *mat = BKE_object_material_get(m_object, mnr + 1);
+ Material *mat = BKE_object_material_get(object, mnr + 1);
if (!mat) {
continue;
}
- std::string name = get_id_name(&mat->id);
+ std::string name = args_.hierarchy_iterator->get_id_name(&mat->id);
if (geo_groups.find(name) == geo_groups.end()) {
std::vector<int32_t> faceArray;
@@ -561,9 +444,9 @@ void AbcGenericMeshWriter::getGeoGroups(struct Mesh *mesh,
}
if (geo_groups.size() == 0) {
- Material *mat = BKE_object_material_get(m_object, 1);
+ Material *mat = BKE_object_material_get(object, 1);
- std::string name = (mat) ? get_id_name(&mat->id) : "default";
+ std::string name = (mat) ? args_.hierarchy_iterator->get_id_name(&mat->id) : "default";
std::vector<int32_t> faceArray;
@@ -575,23 +458,114 @@ void AbcGenericMeshWriter::getGeoGroups(struct Mesh *mesh,
}
}
-AbcMeshWriter::AbcMeshWriter(Object *ob,
- AbcTransformWriter *parent,
- uint32_t time_sampling,
- ExportSettings &settings)
- : AbcGenericMeshWriter(ob, parent, time_sampling, settings)
+/* NOTE: Alembic's polygon winding order is clockwise, to match with Renderman. */
+
+static void get_vertices(struct Mesh *mesh, std::vector<Imath::V3f> &points)
+{
+ points.clear();
+ points.resize(mesh->totvert);
+
+ MVert *verts = mesh->mvert;
+
+ for (int i = 0, e = mesh->totvert; i < e; i++) {
+ copy_yup_from_zup(points[i].getValue(), verts[i].co);
+ }
+}
+
+static void get_topology(struct Mesh *mesh,
+ std::vector<int32_t> &poly_verts,
+ std::vector<int32_t> &loop_counts,
+ bool &r_has_flat_shaded_poly)
+{
+ const int num_poly = mesh->totpoly;
+ const int num_loops = mesh->totloop;
+ MLoop *mloop = mesh->mloop;
+ MPoly *mpoly = mesh->mpoly;
+ r_has_flat_shaded_poly = false;
+
+ poly_verts.clear();
+ loop_counts.clear();
+ poly_verts.reserve(num_loops);
+ loop_counts.reserve(num_poly);
+
+ /* NOTE: data needs to be written in the reverse order. */
+ for (int i = 0; i < num_poly; i++) {
+ MPoly &poly = mpoly[i];
+ loop_counts.push_back(poly.totloop);
+
+ r_has_flat_shaded_poly |= (poly.flag & ME_SMOOTH) == 0;
+
+ MLoop *loop = mloop + poly.loopstart + (poly.totloop - 1);
+
+ for (int j = 0; j < poly.totloop; j++, loop--) {
+ poly_verts.push_back(loop->v);
+ }
+ }
+}
+
+static void get_creases(struct Mesh *mesh,
+ std::vector<int32_t> &indices,
+ std::vector<int32_t> &lengths,
+ std::vector<float> &sharpnesses)
+{
+ const float factor = 1.0f / 255.0f;
+
+ indices.clear();
+ lengths.clear();
+ sharpnesses.clear();
+
+ MEdge *edge = mesh->medge;
+
+ for (int i = 0, e = mesh->totedge; i < e; i++) {
+ const float sharpness = static_cast<float>(edge[i].crease) * factor;
+
+ if (sharpness != 0.0f) {
+ indices.push_back(edge[i].v1);
+ indices.push_back(edge[i].v2);
+ sharpnesses.push_back(sharpness);
+ }
+ }
+
+ lengths.resize(sharpnesses.size(), 2);
+}
+
+static void get_loop_normals(struct Mesh *mesh,
+ std::vector<Imath::V3f> &normals,
+ bool has_flat_shaded_poly)
{
+ normals.clear();
+
+ /* If all polygons are smooth shaded, and there are no custom normals, we don't need to export
+ * normals at all. This is also done by other software, see T71246. */
+ if (!has_flat_shaded_poly && !CustomData_has_layer(&mesh->ldata, CD_CUSTOMLOOPNORMAL) &&
+ (mesh->flag & ME_AUTOSMOOTH) == 0) {
+ return;
+ }
+
+ BKE_mesh_calc_normals_split(mesh);
+ const float(*lnors)[3] = static_cast<float(*)[3]>(CustomData_get_layer(&mesh->ldata, CD_NORMAL));
+ BLI_assert(lnors != nullptr || !"BKE_mesh_calc_normals_split() should have computed CD_NORMAL");
+
+ normals.resize(mesh->totloop);
+
+ /* NOTE: data needs to be written in the reverse order. */
+ int abc_index = 0;
+ MPoly *mp = mesh->mpoly;
+ for (int i = 0, e = mesh->totpoly; i < e; i++, mp++) {
+ for (int j = mp->totloop - 1; j >= 0; j--, abc_index++) {
+ int blender_index = mp->loopstart + j;
+ copy_yup_from_zup(normals[abc_index].getValue(), lnors[blender_index]);
+ }
+ }
}
-AbcMeshWriter::~AbcMeshWriter()
+ABCMeshWriter::ABCMeshWriter(const ABCWriterConstructorArgs &args) : ABCGenericMeshWriter(args)
{
}
-Mesh *AbcMeshWriter::getEvaluatedMesh(Scene *scene_eval,
- Object *ob_eval,
- bool &UNUSED(r_needsfree))
+Mesh *ABCMeshWriter::get_export_mesh(Object *object_eval, bool & /*r_needsfree*/)
{
- return mesh_get_eval_final(m_settings.depsgraph, scene_eval, ob_eval, &CD_MASK_MESH);
+ return BKE_object_get_evaluated_mesh(object_eval);
}
} // namespace alembic
diff --git a/source/blender/io/alembic/exporter/abc_writer_mesh.h b/source/blender/io/alembic/exporter/abc_writer_mesh.h
index 412c5530b32..3a592255a70 100644
--- a/source/blender/io/alembic/exporter/abc_writer_mesh.h
+++ b/source/blender/io/alembic/exporter/abc_writer_mesh.h
@@ -19,75 +19,75 @@
* \ingroup balembic
*/
-#include "abc_writer_object.h"
+#include "abc_writer_abstract.h"
#include "intern/abc_customdata.h"
-struct Mesh;
+#include <Alembic/AbcGeom/OPolyMesh.h>
+#include <Alembic/AbcGeom/OSubD.h>
+
struct ModifierData;
namespace blender {
namespace io {
namespace alembic {
-/* Writer for Alembic meshes. Does not assume the object is a mesh object. */
-class AbcGenericMeshWriter : public AbcObjectWriter {
- protected:
- Alembic::AbcGeom::OPolyMeshSchema m_mesh_schema;
- Alembic::AbcGeom::OPolyMeshSchema::Sample m_mesh_sample;
-
- Alembic::AbcGeom::OSubDSchema m_subdiv_schema;
- Alembic::AbcGeom::OSubDSchema::Sample m_subdiv_sample;
+/* Writer for Alembic geometry. Does not assume the object is a mesh object. */
+class ABCGenericMeshWriter : public ABCAbstractWriter {
+ private:
+ /* Either polymesh or subd is used, depending on is_subd_.
+ * References to the schema must be kept, or Alembic will not properly write. */
+ Alembic::AbcGeom::OPolyMesh abc_poly_mesh_;
+ Alembic::AbcGeom::OPolyMeshSchema abc_poly_mesh_schema_;
- Alembic::Abc::OArrayProperty m_mat_indices;
+ Alembic::AbcGeom::OSubD abc_subdiv_;
+ Alembic::AbcGeom::OSubDSchema abc_subdiv_schema_;
- bool m_is_animated;
- ModifierData *m_subsurf_mod;
+ /* Determines whether a poly mesh or a subdivision surface is exported.
+ * The value is set by an export option but only true if there is a subsdivision modifier on the
+ * exported object. */
+ bool is_subd_;
+ ModifierData *subsurf_modifier_;
+ ModifierData *liquid_sim_modifier_;
CDStreamConfig m_custom_data_config;
- bool m_is_liquid;
- bool m_is_subd;
-
public:
- AbcGenericMeshWriter(Object *ob,
- AbcTransformWriter *parent,
- uint32_t time_sampling,
- ExportSettings &settings);
+ explicit ABCGenericMeshWriter(const ABCWriterConstructorArgs &args);
+ virtual ~ABCGenericMeshWriter();
- ~AbcGenericMeshWriter();
- void setIsAnimated(bool is_animated);
+ virtual void create_alembic_objects(const HierarchyContext *context) override;
+ virtual const Alembic::Abc::OObject get_alembic_object() const;
protected:
- virtual void do_write();
- virtual bool isAnimated() const;
- virtual Mesh *getEvaluatedMesh(Scene *scene_eval, Object *ob_eval, bool &r_needsfree) = 0;
- virtual void freeEvaluatedMesh(struct Mesh *mesh);
+ virtual bool is_supported(const HierarchyContext *context) const override;
+ virtual void do_write(HierarchyContext &context) override;
- Mesh *getFinalMesh(bool &r_needsfree);
+ virtual Mesh *get_export_mesh(Object *object_eval, bool &r_needsfree) = 0;
+ virtual void free_export_mesh(Mesh *mesh);
- void writeMesh(struct Mesh *mesh);
- void writeSubD(struct Mesh *mesh);
+ virtual bool export_as_subdivision_surface(Object *ob_eval) const;
- void writeArbGeoParams(struct Mesh *mesh);
- void getGeoGroups(struct Mesh *mesh, std::map<std::string, std::vector<int32_t>> &geoGroups);
+ private:
+ void write_mesh(HierarchyContext &context, Mesh *mesh);
+ void write_subd(HierarchyContext &context, Mesh *mesh);
+ template<typename Schema> void write_face_sets(Object *object, Mesh *mesh, Schema &schema);
- /* fluid surfaces support */
- void getVelocities(struct Mesh *mesh, std::vector<Imath::V3f> &vels);
+ ModifierData *get_liquid_sim_modifier(Scene *scene_eval, Object *ob_eval);
- template<typename Schema> void writeFaceSets(struct Mesh *mesh, Schema &schema);
+ void write_arb_geo_params(Mesh *me);
+ void get_velocities(Mesh *mesh, std::vector<Imath::V3f> &vels);
+ void get_geo_groups(Object *object,
+ Mesh *mesh,
+ std::map<std::string, std::vector<int32_t>> &geo_groups);
};
-class AbcMeshWriter : public AbcGenericMeshWriter {
+/* Writer for Alembic geometry of Blender Mesh objects. */
+class ABCMeshWriter : public ABCGenericMeshWriter {
public:
- AbcMeshWriter(Object *ob,
- AbcTransformWriter *parent,
- uint32_t time_sampling,
- ExportSettings &settings);
-
- ~AbcMeshWriter();
+ ABCMeshWriter(const ABCWriterConstructorArgs &args);
protected:
- virtual Mesh *getEvaluatedMesh(Scene *scene_eval, Object *ob_eval, bool &r_needsfree) override;
+ virtual Mesh *get_export_mesh(Object *object_eval, bool &r_needsfree) override;
};
} // namespace alembic
diff --git a/source/blender/io/alembic/exporter/abc_writer_nurbs.cc b/source/blender/io/alembic/exporter/abc_writer_nurbs.cc
index fa37c1eac59..1fd382214a6 100644
--- a/source/blender/io/alembic/exporter/abc_writer_nurbs.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_nurbs.cc
@@ -19,7 +19,6 @@
*/
#include "abc_writer_nurbs.h"
-#include "abc_writer_transform.h"
#include "intern/abc_axis_conversion.h"
#include "DNA_curve_types.h"
@@ -29,52 +28,70 @@
#include "BKE_curve.h"
+#include "CLG_log.h"
+static CLG_LogRef LOG = {"io.alembic"};
+
+namespace blender {
+namespace io {
+namespace alembic {
+
+using Alembic::Abc::OObject;
using Alembic::AbcGeom::FloatArraySample;
using Alembic::AbcGeom::OBoolProperty;
using Alembic::AbcGeom::OCompoundProperty;
using Alembic::AbcGeom::ONuPatch;
using Alembic::AbcGeom::ONuPatchSchema;
-namespace blender {
-namespace io {
-namespace alembic {
+ABCNurbsWriter::ABCNurbsWriter(const ABCWriterConstructorArgs &args) : ABCAbstractWriter(args)
+{
+}
-AbcNurbsWriter::AbcNurbsWriter(Object *ob,
- AbcTransformWriter *parent,
- uint32_t time_sampling,
- ExportSettings &settings)
- : AbcObjectWriter(ob, time_sampling, settings, parent)
+void ABCNurbsWriter::create_alembic_objects(const HierarchyContext *context)
{
- m_is_animated = isAnimated();
+ Curve *curve = static_cast<Curve *>(context->object->data);
+ size_t num_nurbs = BLI_listbase_count(&curve->nurb);
+ OObject abc_parent = args_.abc_parent;
+ const char *abc_parent_path = abc_parent.getFullName().c_str();
- /* if the object is static, use the default static time sampling */
- if (!m_is_animated) {
- m_time_sampling = 0;
- }
+ for (size_t i = 0; i < num_nurbs; i++) {
+ std::stringstream patch_name_stream;
+ patch_name_stream << args_.abc_name << '_' << i;
- Curve *curve = static_cast<Curve *>(m_object->data);
- size_t numNurbs = BLI_listbase_count(&curve->nurb);
+ while (abc_parent.getChildHeader(patch_name_stream.str())) {
+ patch_name_stream << "_";
+ }
- for (size_t i = 0; i < numNurbs; i++) {
- std::stringstream str;
- str << m_name << '_' << i;
+ std::string patch_name = patch_name_stream.str();
+ CLOG_INFO(&LOG, 2, "exporting %s/%s", abc_parent_path, patch_name.c_str());
- while (parent->alembicXform().getChildHeader(str.str())) {
- str << "_";
- }
+ ONuPatch nurbs(abc_parent, patch_name.c_str(), timesample_index_);
+ abc_nurbs_.push_back(nurbs);
+ abc_nurbs_schemas_.push_back(nurbs.getSchema());
+ }
+}
- ONuPatch nurbs(parent->alembicXform(), str.str().c_str(), m_time_sampling);
- m_nurbs_schema.push_back(nurbs.getSchema());
+const OObject ABCNurbsWriter::get_alembic_object() const
+{
+ if (abc_nurbs_.empty()) {
+ return OObject();
}
+ /* For parenting purposes within the Alembic file, all NURBS patches are equal, so just use the
+ * first one. */
+ return abc_nurbs_[0];
}
-bool AbcNurbsWriter::isAnimated() const
+bool ABCNurbsWriter::check_is_animated(const HierarchyContext &context) const
{
- /* check if object has shape keys */
- Curve *cu = static_cast<Curve *>(m_object->data);
+ /* Check if object has shape keys. */
+ Curve *cu = static_cast<Curve *>(context.object->data);
return (cu->key != NULL);
}
+bool ABCNurbsWriter::is_supported(const HierarchyContext *context) const
+{
+ return ELEM(context->object->type, OB_SURF, OB_CURVE);
+}
+
static void get_knots(std::vector<float> &knots, const int num_knots, float *nu_knots)
{
if (num_knots <= 1) {
@@ -95,22 +112,13 @@ static void get_knots(std::vector<float> &knots, const int num_knots, float *nu_
knots.push_back(2.0f * knots[num_knots] - knots[num_knots - 1]);
}
-void AbcNurbsWriter::do_write()
+void ABCNurbsWriter::do_write(HierarchyContext &context)
{
- /* we have already stored a sample for this object. */
- if (!m_first_frame && !m_is_animated) {
- return;
- }
-
- if (!ELEM(m_object->type, OB_SURF, OB_CURVE)) {
- return;
- }
-
- Curve *curve = static_cast<Curve *>(m_object->data);
+ Curve *curve = static_cast<Curve *>(context.object->data);
ListBase *nulb;
- if (m_object->runtime.curve_cache->deformed_nurbs.first != NULL) {
- nulb = &m_object->runtime.curve_cache->deformed_nurbs;
+ if (context.object->runtime.curve_cache->deformed_nurbs.first != NULL) {
+ nulb = &context.object->runtime.curve_cache->deformed_nurbs;
}
else {
nulb = BKE_curve_nurbs_get(curve);
@@ -147,7 +155,7 @@ void AbcNurbsWriter::do_write()
/* TODO(kevin): to accommodate other software we should duplicate control
* points to indicate that a NURBS is cyclic. */
- OCompoundProperty user_props = m_nurbs_schema[count].getUserProperties();
+ OCompoundProperty user_props = abc_nurbs_schemas_[count].getUserProperties();
if ((nu->flagu & CU_NURB_ENDPOINT) != 0) {
OBoolProperty prop(user_props, "endpoint_u");
@@ -169,7 +177,7 @@ void AbcNurbsWriter::do_write()
prop.set(true);
}
- m_nurbs_schema[count].set(sample);
+ abc_nurbs_schemas_[count].set(sample);
}
}
diff --git a/source/blender/io/alembic/exporter/abc_writer_nurbs.h b/source/blender/io/alembic/exporter/abc_writer_nurbs.h
index b2a9cf1b786..23af4c40556 100644
--- a/source/blender/io/alembic/exporter/abc_writer_nurbs.h
+++ b/source/blender/io/alembic/exporter/abc_writer_nurbs.h
@@ -19,26 +19,37 @@
* \ingroup balembic
*/
-#include "abc_writer_object.h"
+#include "abc_writer_abstract.h"
+#include "abc_writer_mesh.h"
+#include <vector>
namespace blender {
namespace io {
namespace alembic {
-class AbcNurbsWriter : public AbcObjectWriter {
- std::vector<Alembic::AbcGeom::ONuPatchSchema> m_nurbs_schema;
- bool m_is_animated;
+class ABCNurbsWriter : public ABCAbstractWriter {
+ private:
+ std::vector<Alembic::AbcGeom::ONuPatch> abc_nurbs_;
+ std::vector<Alembic::AbcGeom::ONuPatchSchema> abc_nurbs_schemas_;
public:
- AbcNurbsWriter(Object *ob,
- AbcTransformWriter *parent,
- uint32_t time_sampling,
- ExportSettings &settings);
+ explicit ABCNurbsWriter(const ABCWriterConstructorArgs &args);
- private:
- virtual void do_write();
+ virtual void create_alembic_objects(const HierarchyContext *context) override;
+ virtual const Alembic::Abc::OObject get_alembic_object() const override;
+
+ protected:
+ virtual bool is_supported(const HierarchyContext *context) const override;
+ virtual void do_write(HierarchyContext &context) override;
+ virtual bool check_is_animated(const HierarchyContext &context) const override;
+};
+
+class ABCNurbsMeshWriter : public ABCGenericMeshWriter {
+ public:
+ explicit ABCNurbsMeshWriter(const ABCWriterConstructorArgs &args);
- bool isAnimated() const;
+ protected:
+ virtual Mesh *get_export_mesh(Object *object_eval, bool &r_needsfree) override;
};
} // namespace alembic
diff --git a/source/blender/io/alembic/exporter/abc_writer_object.cc b/source/blender/io/alembic/exporter/abc_writer_object.cc
deleted file mode 100644
index 97cac3a1af9..00000000000
--- a/source/blender/io/alembic/exporter/abc_writer_object.cc
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-/** \file
- * \ingroup balembic
- */
-
-#include "abc_writer_object.h"
-
-#include "DNA_object_types.h"
-
-#include "BKE_object.h"
-
-namespace blender {
-namespace io {
-namespace alembic {
-
-AbcObjectWriter::AbcObjectWriter(Object *ob,
- uint32_t time_sampling,
- ExportSettings &settings,
- AbcObjectWriter *parent)
- : m_object(ob), m_settings(settings), m_time_sampling(time_sampling), m_first_frame(true)
-{
- /* This class is used as superclass for objects themselves (i.e. transforms) and for object
- * data (meshes, curves, cameras, etc.). However, when writing transforms, the m_name field is
- * ignored. This is a temporary tweak to get the exporter to write object data with the data
- * name instead of the object name in a safe way. */
- if (m_object->data == nullptr) {
- m_name = get_id_name(m_object);
- }
- else {
- ID *ob_data = static_cast<ID *>(m_object->data);
- m_name = get_id_name(ob_data);
- }
-
- if (parent) {
- parent->addChild(this);
- }
-}
-
-AbcObjectWriter::~AbcObjectWriter()
-{
-}
-
-void AbcObjectWriter::addChild(AbcObjectWriter *child)
-{
- m_children.push_back(child);
-}
-
-Imath::Box3d AbcObjectWriter::bounds()
-{
- BoundBox *bb = BKE_object_boundbox_get(this->m_object);
-
- if (!bb) {
- if (this->m_object->type != OB_CAMERA) {
- ABC_LOG(m_settings.logger) << "Bounding box is null!\n";
- }
-
- return Imath::Box3d();
- }
-
- /* Convert Z-up to Y-up. This also changes which vector goes into which min/max property. */
- this->m_bounds.min.x = bb->vec[0][0];
- this->m_bounds.min.y = bb->vec[0][2];
- this->m_bounds.min.z = -bb->vec[6][1];
-
- this->m_bounds.max.x = bb->vec[6][0];
- this->m_bounds.max.y = bb->vec[6][2];
- this->m_bounds.max.z = -bb->vec[0][1];
-
- return this->m_bounds;
-}
-
-void AbcObjectWriter::write()
-{
- do_write();
- m_first_frame = false;
-}
-
-} // namespace alembic
-} // namespace io
-} // namespace blender
diff --git a/source/blender/io/alembic/exporter/abc_writer_object.h b/source/blender/io/alembic/exporter/abc_writer_object.h
deleted file mode 100644
index 7f8e8735812..00000000000
--- a/source/blender/io/alembic/exporter/abc_writer_object.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-#pragma once
-
-/** \file
- * \ingroup balembic
- */
-
-#include <Alembic/Abc/All.h>
-#include <Alembic/AbcGeom/All.h>
-
-#include "abc_exporter.h"
-
-#include "DNA_ID.h"
-
-struct Main;
-struct Object;
-
-namespace blender {
-namespace io {
-namespace alembic {
-
-class AbcTransformWriter;
-
-class AbcObjectWriter {
- protected:
- Object *m_object;
- ExportSettings &m_settings;
-
- uint32_t m_time_sampling;
-
- Imath::Box3d m_bounds;
- std::vector<AbcObjectWriter *> m_children;
-
- std::vector<std::pair<std::string, IDProperty *>> m_props;
-
- bool m_first_frame;
- std::string m_name;
-
- public:
- AbcObjectWriter(Object *ob,
- uint32_t time_sampling,
- ExportSettings &settings,
- AbcObjectWriter *parent = NULL);
-
- virtual ~AbcObjectWriter();
-
- void addChild(AbcObjectWriter *child);
-
- virtual Imath::Box3d bounds();
-
- void write();
-
- private:
- virtual void do_write() = 0;
-};
-
-} // namespace alembic
-} // namespace io
-} // namespace blender
diff --git a/source/blender/io/alembic/exporter/abc_writer_points.cc b/source/blender/io/alembic/exporter/abc_writer_points.cc
index 4d7f5d11c36..19870e39a90 100644
--- a/source/blender/io/alembic/exporter/abc_writer_points.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_points.cc
@@ -22,9 +22,6 @@
*/
#include "abc_writer_points.h"
-#include "abc_writer_mesh.h"
-#include "abc_writer_transform.h"
-#include "intern/abc_util.h"
#include "DNA_object_types.h"
#include "DNA_particle_types.h"
@@ -36,81 +33,102 @@
#include "DEG_depsgraph_query.h"
+#include "CLG_log.h"
+static CLG_LogRef LOG = {"io.alembic"};
+
+namespace blender {
+namespace io {
+namespace alembic {
+
using Alembic::AbcGeom::kVertexScope;
using Alembic::AbcGeom::OPoints;
using Alembic::AbcGeom::OPointsSchema;
-/* ************************************************************************** */
+ABCPointsWriter::ABCPointsWriter(const ABCWriterConstructorArgs &args) : ABCAbstractWriter(args)
+{
+}
-namespace blender {
-namespace io {
-namespace alembic {
+void ABCPointsWriter::create_alembic_objects(const HierarchyContext * /*context*/)
+{
+ CLOG_INFO(&LOG, 2, "exporting OPoints %s", args_.abc_path.c_str());
+ abc_points_ = OPoints(args_.abc_parent, args_.abc_name, timesample_index_);
+ abc_points_schema_ = abc_points_.getSchema();
+}
-AbcPointsWriter::AbcPointsWriter(Object *ob,
- AbcTransformWriter *parent,
- uint32_t time_sampling,
- ExportSettings &settings,
- ParticleSystem *psys)
- : AbcObjectWriter(ob, time_sampling, settings, parent)
+const Alembic::Abc::OObject ABCPointsWriter::get_alembic_object() const
{
- m_psys = psys;
+ return abc_points_;
+}
- std::string psys_name = get_valid_abc_name(psys->name);
- OPoints points(parent->alembicXform(), psys_name, m_time_sampling);
- m_schema = points.getSchema();
+bool ABCPointsWriter::is_supported(const HierarchyContext *context) const
+{
+ return ELEM(context->particle_system->part->type,
+ PART_EMITTER,
+ PART_FLUID_FLIP,
+ PART_FLUID_SPRAY,
+ PART_FLUID_BUBBLE,
+ PART_FLUID_FOAM,
+ PART_FLUID_TRACER,
+ PART_FLUID_SPRAYFOAM,
+ PART_FLUID_SPRAYBUBBLE,
+ PART_FLUID_FOAMBUBBLE,
+ PART_FLUID_SPRAYFOAMBUBBLE);
}
-void AbcPointsWriter::do_write()
+bool ABCPointsWriter::check_is_animated(const HierarchyContext & /*context*/) const
{
- if (!m_psys) {
- return;
- }
+ /* We assume that particles are always animated. */
+ return true;
+}
+
+void ABCPointsWriter::do_write(HierarchyContext &context)
+{
+ BLI_assert(context.particle_system != nullptr);
std::vector<Imath::V3f> points;
std::vector<Imath::V3f> velocities;
std::vector<float> widths;
std::vector<uint64_t> ids;
+ ParticleSystem *psys = context.particle_system;
ParticleKey state;
-
ParticleSimulationData sim;
- sim.depsgraph = m_settings.depsgraph;
- sim.scene = m_settings.scene;
- sim.ob = m_object;
- sim.psys = m_psys;
+ sim.depsgraph = args_.depsgraph;
+ sim.scene = DEG_get_evaluated_scene(args_.depsgraph);
+ sim.ob = context.object;
+ sim.psys = psys;
- m_psys->lattice_deform_data = psys_create_lattice_deform_data(&sim);
+ psys->lattice_deform_data = psys_create_lattice_deform_data(&sim);
uint64_t index = 0;
- for (int p = 0; p < m_psys->totpart; p++) {
+ for (int p = 0; p < psys->totpart; p++) {
float pos[3], vel[3];
- if (m_psys->particles[p].flag & (PARS_NO_DISP | PARS_UNEXIST)) {
+ if (psys->particles[p].flag & (PARS_NO_DISP | PARS_UNEXIST)) {
continue;
}
- state.time = DEG_get_ctime(m_settings.depsgraph);
-
+ state.time = DEG_get_ctime(args_.depsgraph);
if (psys_get_particle_state(&sim, p, &state, 0) == 0) {
continue;
}
/* location */
- mul_v3_m4v3(pos, m_object->imat, state.co);
+ mul_v3_m4v3(pos, context.object->imat, state.co);
/* velocity */
- sub_v3_v3v3(vel, state.co, m_psys->particles[p].prev_state.co);
+ sub_v3_v3v3(vel, state.co, psys->particles[p].prev_state.co);
/* Convert Z-up to Y-up. */
points.push_back(Imath::V3f(pos[0], pos[2], -pos[1]));
velocities.push_back(Imath::V3f(vel[0], vel[2], -vel[1]));
- widths.push_back(m_psys->particles[p].size);
+ widths.push_back(psys->particles[p].size);
ids.push_back(index++);
}
- if (m_psys->lattice_deform_data) {
- BKE_lattice_deform_data_destroy(m_psys->lattice_deform_data);
- m_psys->lattice_deform_data = NULL;
+ if (psys->lattice_deform_data) {
+ BKE_lattice_deform_data_destroy(psys->lattice_deform_data);
+ psys->lattice_deform_data = NULL;
}
Alembic::Abc::P3fArraySample psample(points);
@@ -119,10 +137,10 @@ void AbcPointsWriter::do_write()
Alembic::Abc::FloatArraySample wsample_array(widths);
Alembic::AbcGeom::OFloatGeomParam::Sample wsample(wsample_array, kVertexScope);
- m_sample = OPointsSchema::Sample(psample, idsample, vsample, wsample);
- m_sample.setSelfBounds(bounds());
-
- m_schema.set(m_sample);
+ OPointsSchema::Sample sample(psample, idsample, vsample, wsample);
+ update_bounding_box(context.object);
+ sample.setSelfBounds(bounding_box_);
+ abc_points_schema_.set(sample);
}
} // namespace alembic
diff --git a/source/blender/io/alembic/exporter/abc_writer_points.h b/source/blender/io/alembic/exporter/abc_writer_points.h
index 7e8a3eaadcd..03800b80acf 100644
--- a/source/blender/io/alembic/exporter/abc_writer_points.h
+++ b/source/blender/io/alembic/exporter/abc_writer_points.h
@@ -22,30 +22,29 @@
* \ingroup balembic
*/
-#include "abc_writer_object.h"
-#include "intern/abc_customdata.h"
+#include "abc_writer_abstract.h"
-struct ParticleSystem;
-
-/* ************************************************************************** */
+#include <Alembic/AbcGeom/OPoints.h>
namespace blender {
namespace io {
namespace alembic {
-class AbcPointsWriter : public AbcObjectWriter {
- Alembic::AbcGeom::OPointsSchema m_schema;
- Alembic::AbcGeom::OPointsSchema::Sample m_sample;
- ParticleSystem *m_psys;
+class ABCPointsWriter : public ABCAbstractWriter {
+ Alembic::AbcGeom::OPoints abc_points_;
+ Alembic::AbcGeom::OPointsSchema abc_points_schema_;
public:
- AbcPointsWriter(Object *ob,
- AbcTransformWriter *parent,
- uint32_t time_sampling,
- ExportSettings &settings,
- ParticleSystem *psys);
+ explicit ABCPointsWriter(const ABCWriterConstructorArgs &args);
+
+ virtual void create_alembic_objects(const HierarchyContext *context) override;
+ virtual const Alembic::Abc::OObject get_alembic_object() const override;
+
+ virtual bool is_supported(const HierarchyContext *context) const override;
- void do_write();
+ protected:
+ virtual bool check_is_animated(const HierarchyContext &context) const override;
+ virtual void do_write(HierarchyContext &context) override;
};
} // namespace alembic
diff --git a/source/blender/io/alembic/exporter/abc_writer_transform.cc b/source/blender/io/alembic/exporter/abc_writer_transform.cc
index fa808c1b75b..65d6b7c5b41 100644
--- a/source/blender/io/alembic/exporter/abc_writer_transform.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_transform.cc
@@ -19,112 +19,95 @@
*/
#include "abc_writer_transform.h"
+#include "abc_hierarchy_iterator.h"
#include "intern/abc_axis_conversion.h"
+#include "intern/abc_util.h"
-#include <OpenEXR/ImathBoxAlgo.h>
+#include "BKE_object.h"
-#include "DNA_object_types.h"
+#include "BLI_math_matrix.h"
+#include "BLI_math_rotation.h"
-#include "BLI_math.h"
+#include "DNA_layer_types.h"
-#include "DEG_depsgraph_query.h"
-
-using Alembic::AbcGeom::OObject;
-using Alembic::AbcGeom::OXform;
+#include "CLG_log.h"
+static CLG_LogRef LOG = {"io.alembic"};
namespace blender {
namespace io {
namespace alembic {
-AbcTransformWriter::AbcTransformWriter(Object *ob,
- const OObject &abc_parent,
- AbcTransformWriter *parent,
- unsigned int time_sampling,
- ExportSettings &settings)
- : AbcObjectWriter(ob, time_sampling, settings, parent), m_proxy_from(NULL)
-{
- m_is_animated = hasAnimation(m_object);
-
- if (!m_is_animated) {
- time_sampling = 0;
- }
-
- m_xform = OXform(abc_parent, get_id_name(m_object), time_sampling);
- m_schema = m_xform.getSchema();
+using Alembic::Abc::OObject;
+using Alembic::AbcGeom::OXform;
+using Alembic::AbcGeom::OXformSchema;
+using Alembic::AbcGeom::XformSample;
- /* Blender objects can't have a parent without inheriting the transform. */
- m_inherits_xform = parent != NULL;
+ABCTransformWriter::ABCTransformWriter(const ABCWriterConstructorArgs &args)
+ : ABCAbstractWriter(args)
+{
+ timesample_index_ = args_.abc_archive->time_sampling_index_transforms();
}
-void AbcTransformWriter::do_write()
+void ABCTransformWriter::create_alembic_objects(const HierarchyContext * /*context*/)
{
- Object *ob_eval = DEG_get_evaluated_object(m_settings.depsgraph, m_object);
-
- if (m_first_frame) {
- m_visibility = Alembic::AbcGeom::CreateVisibilityProperty(
- m_xform, m_xform.getSchema().getTimeSampling());
- }
-
- m_visibility.set(!(ob_eval->restrictflag & OB_RESTRICT_VIEWPORT));
+ CLOG_INFO(&LOG, 2, "exporting %s", args_.abc_path.c_str());
+ abc_xform_ = OXform(args_.abc_parent, args_.abc_name, timesample_index_);
+ abc_xform_schema_ = abc_xform_.getSchema();
+}
- if (!m_first_frame && !m_is_animated) {
- return;
- }
+void ABCTransformWriter::do_write(HierarchyContext &context)
+{
+ float parent_relative_matrix[4][4]; // The object matrix relative to the parent.
+ mul_m4_m4m4(parent_relative_matrix, context.parent_matrix_inv_world, context.matrix_world);
- float yup_mat[4][4];
- create_transform_matrix(
- ob_eval, yup_mat, m_inherits_xform ? ABC_MATRIX_LOCAL : ABC_MATRIX_WORLD, m_proxy_from);
+ // After this, parent_relative_matrix uses Y=up.
+ copy_m44_axis_swap(parent_relative_matrix, parent_relative_matrix, ABC_YUP_FROM_ZUP);
/* If the parent is a camera, undo its to-Maya rotation (see below). */
- bool is_root_object = !m_inherits_xform || ob_eval->parent == nullptr;
- if (!is_root_object && ob_eval->parent->type == OB_CAMERA) {
+ bool is_root_object = context.export_parent == nullptr;
+ if (!is_root_object && context.export_parent->type == OB_CAMERA) {
float rot_mat[4][4];
axis_angle_to_mat4_single(rot_mat, 'X', M_PI_2);
- mul_m4_m4m4(yup_mat, rot_mat, yup_mat);
+ mul_m4_m4m4(parent_relative_matrix, rot_mat, parent_relative_matrix);
}
/* If the object is a camera, apply an extra rotation to Maya camera orientation. */
- if (ob_eval->type == OB_CAMERA) {
+ if (context.object->type == OB_CAMERA) {
float rot_mat[4][4];
axis_angle_to_mat4_single(rot_mat, 'X', -M_PI_2);
- mul_m4_m4m4(yup_mat, yup_mat, rot_mat);
+ mul_m4_m4m4(parent_relative_matrix, parent_relative_matrix, rot_mat);
}
if (is_root_object) {
/* Only apply scaling to root objects, parenting will propagate it. */
float scale_mat[4][4];
- scale_m4_fl(scale_mat, m_settings.global_scale);
- scale_mat[3][3] = m_settings.global_scale; /* also scale translation */
- mul_m4_m4m4(yup_mat, yup_mat, scale_mat);
- yup_mat[3][3] /= m_settings.global_scale; /* normalise the homogeneous component */
+ scale_m4_fl(scale_mat, args_.export_params->global_scale);
+ scale_mat[3][3] = args_.export_params->global_scale; /* also scale translation */
+ mul_m4_m4m4(parent_relative_matrix, parent_relative_matrix, scale_mat);
+ parent_relative_matrix[3][3] /=
+ args_.export_params->global_scale; /* normalise the homogeneous component */
}
- m_matrix = convert_matrix_datatype(yup_mat);
- m_sample.setMatrix(m_matrix);
-
- /* Always export as "inherits transform", as this is the only way in which Blender works. The
- * above code has already taken care of writing the correct matrix so that this option is not
- * necessary. However, certain packages (for example the USD Alembic exporter) are incompatible
- * with non-inheriting transforms and will completely ignore the transform if that is used. */
- m_sample.setInheritsXforms(true);
- m_schema.set(m_sample);
+ XformSample xform_sample;
+ xform_sample.setMatrix(convert_matrix_datatype(parent_relative_matrix));
+ xform_sample.setInheritsXforms(true);
+ abc_xform_schema_.set(xform_sample);
}
-Imath::Box3d AbcTransformWriter::bounds()
+const OObject ABCTransformWriter::get_alembic_object() const
{
- Imath::Box3d bounds;
-
- for (int i = 0; i < m_children.size(); i++) {
- Imath::Box3d box(m_children[i]->bounds());
- bounds.extendBy(box);
- }
-
- return Imath::transform(bounds, m_matrix);
+ return abc_xform_;
}
-bool AbcTransformWriter::hasAnimation(Object * /*ob*/) const
+bool ABCTransformWriter::check_is_animated(const HierarchyContext &context) const
{
- return true;
+ if (context.duplicator != NULL) {
+ /* This object is being duplicated, so could be emitted by a particle system and thus
+ * influenced by forces. TODO(Sybren): Make this more strict. Probably better to get from the
+ * depsgraph whether this object instance has a time source. */
+ return true;
+ }
+ return BKE_object_moves_in_time(context.object, context.animation_check_include_parent);
}
} // namespace alembic
diff --git a/source/blender/io/alembic/exporter/abc_writer_transform.h b/source/blender/io/alembic/exporter/abc_writer_transform.h
index fc997d77f4c..950bff39c29 100644
--- a/source/blender/io/alembic/exporter/abc_writer_transform.h
+++ b/source/blender/io/alembic/exporter/abc_writer_transform.h
@@ -19,44 +19,27 @@
* \ingroup balembic
*/
-#include "abc_writer_object.h"
+#include "abc_writer_abstract.h"
-#include <Alembic/AbcGeom/All.h>
+#include <Alembic/AbcGeom/OXform.h>
namespace blender {
namespace io {
namespace alembic {
-class AbcTransformWriter : public AbcObjectWriter {
- Alembic::AbcGeom::OXform m_xform;
- Alembic::AbcGeom::OXformSchema m_schema;
- Alembic::AbcGeom::XformSample m_sample;
- Alembic::AbcGeom::OVisibilityProperty m_visibility;
- Alembic::Abc::M44d m_matrix;
-
- bool m_is_animated;
- bool m_inherits_xform;
-
- public:
- Object *m_proxy_from;
+class ABCTransformWriter : public ABCAbstractWriter {
+ private:
+ Alembic::AbcGeom::OXform abc_xform_;
+ Alembic::AbcGeom::OXformSchema abc_xform_schema_;
public:
- AbcTransformWriter(Object *ob,
- const Alembic::AbcGeom::OObject &abc_parent,
- AbcTransformWriter *parent,
- unsigned int time_sampling,
- ExportSettings &settings);
-
- Alembic::AbcGeom::OXform &alembicXform()
- {
- return m_xform;
- }
- virtual Imath::Box3d bounds();
-
- private:
- virtual void do_write();
+ explicit ABCTransformWriter(const ABCWriterConstructorArgs &args);
+ virtual void create_alembic_objects(const HierarchyContext *context) override;
- bool hasAnimation(Object *ob) const;
+ protected:
+ virtual void do_write(HierarchyContext &context) override;
+ virtual bool check_is_animated(const HierarchyContext &context) const override;
+ virtual const Alembic::Abc::OObject get_alembic_object() const override;
};
} // namespace alembic
diff --git a/source/blender/io/alembic/intern/alembic_capi.cc b/source/blender/io/alembic/intern/alembic_capi.cc
index bca84b97749..5932791ecf4 100644
--- a/source/blender/io/alembic/intern/alembic_capi.cc
+++ b/source/blender/io/alembic/intern/alembic_capi.cc
@@ -70,36 +70,19 @@
#include "WM_api.h"
#include "WM_types.h"
-using Alembic::Abc::Int32ArraySamplePtr;
using Alembic::Abc::ObjectHeader;
-
-using Alembic::AbcGeom::kWrapExisting;
-using Alembic::AbcGeom::MetaData;
-using Alembic::AbcGeom::P3fArraySamplePtr;
-
using Alembic::AbcGeom::ICamera;
-using Alembic::AbcGeom::ICompoundProperty;
using Alembic::AbcGeom::ICurves;
-using Alembic::AbcGeom::ICurvesSchema;
using Alembic::AbcGeom::IFaceSet;
using Alembic::AbcGeom::ILight;
-using Alembic::AbcGeom::IN3fArrayProperty;
-using Alembic::AbcGeom::IN3fGeomParam;
using Alembic::AbcGeom::INuPatch;
using Alembic::AbcGeom::IObject;
using Alembic::AbcGeom::IPoints;
-using Alembic::AbcGeom::IPointsSchema;
using Alembic::AbcGeom::IPolyMesh;
-using Alembic::AbcGeom::IPolyMeshSchema;
using Alembic::AbcGeom::ISampleSelector;
using Alembic::AbcGeom::ISubD;
-using Alembic::AbcGeom::IV2fGeomParam;
using Alembic::AbcGeom::IXform;
-using Alembic::AbcGeom::IXformSchema;
-using Alembic::AbcGeom::N3fArraySamplePtr;
-using Alembic::AbcGeom::V3fArraySamplePtr;
-using Alembic::AbcGeom::XformSample;
-
+using Alembic::AbcGeom::MetaData;
using Alembic::AbcMaterial::IMaterial;
using namespace blender::io::alembic;
diff --git a/source/blender/io/common/IO_abstract_hierarchy_iterator.h b/source/blender/io/common/IO_abstract_hierarchy_iterator.h
index 2f0c1b270f1..5f84fd48b71 100644
--- a/source/blender/io/common/IO_abstract_hierarchy_iterator.h
+++ b/source/blender/io/common/IO_abstract_hierarchy_iterator.h
@@ -197,8 +197,8 @@ class AbstractHierarchyIterator {
/* Iterate over the depsgraph, create writers, and tell the writers to write.
* Main entry point for the AbstractHierarchyIterator, must be called for every to-be-exported
- * frame. */
- void iterate_and_write();
+ * (sub)frame. */
+ virtual void iterate_and_write();
/* Release all writers. Call after all frames have been exported. */
void release_writers();
diff --git a/tests/gtests/alembic/CMakeLists.txt b/tests/gtests/alembic/CMakeLists.txt
index 6ba1c4465d9..90f1829fc77 100644
--- a/tests/gtests/alembic/CMakeLists.txt
+++ b/tests/gtests/alembic/CMakeLists.txt
@@ -24,6 +24,8 @@ set(INC
../../../source/blender/blenlib
../../../source/blender/blenkernel
../../../source/blender/io/alembic
+ ../../../source/blender/io/common
+ ../../../source/blender/io/usd/intern
../../../source/blender/makesdna
../../../source/blender/depsgraph
${ALEMBIC_INCLUDE_DIRS}
diff --git a/tests/gtests/alembic/abc_export_test.cc b/tests/gtests/alembic/abc_export_test.cc
index 2926809a1af..cdc98178550 100644
--- a/tests/gtests/alembic/abc_export_test.cc
+++ b/tests/gtests/alembic/abc_export_test.cc
@@ -1,11 +1,12 @@
#include "testing/testing.h"
// Keep first since utildefines defines AT which conflicts with STL
-#include "exporter/abc_exporter.h"
+#include "exporter/abc_archive.h"
#include "intern/abc_util.h"
extern "C" {
#include "BKE_main.h"
+#include "BLI_fileops.h"
#include "BLI_math.h"
#include "BLI_utildefines.h"
#include "DNA_scene_types.h"
@@ -13,140 +14,150 @@ extern "C" {
#include "DEG_depsgraph.h"
-using namespace blender::io::alembic;
-
-class TestableAbcExporter : public AbcExporter {
- public:
- TestableAbcExporter(Main *bmain, const char *filename, ExportSettings &settings)
- : AbcExporter(bmain, filename, settings)
- {
- }
-
- void getShutterSamples(unsigned int nr_of_samples,
- bool time_relative,
- std::vector<double> &samples)
- {
- AbcExporter::getShutterSamples(nr_of_samples, time_relative, samples);
- }
-
- void getFrameSet(unsigned int nr_of_samples, std::set<double> &frames)
- {
- AbcExporter::getFrameSet(nr_of_samples, frames);
- }
-};
+namespace blender {
+namespace io {
+namespace alembic {
class AlembicExportTest : public testing::Test {
protected:
- ExportSettings settings;
+ ABCArchive *abc_archive;
+
+ AlembicExportParams params;
Scene scene;
Depsgraph *depsgraph;
- TestableAbcExporter *exporter;
Main *bmain;
virtual void SetUp()
{
- settings.frame_start = 31.0;
- settings.frame_end = 223.0;
+ abc_archive = nullptr;
/* Fake a 25 FPS scene with a nonzero base (because that's sometimes forgotten) */
scene.r.frs_sec = 50;
scene.r.frs_sec_base = 2;
+ strcpy(scene.id.name, "SCTestScene");
bmain = BKE_main_new();
/* TODO(sergey): Pass scene layer somehow? */
ViewLayer *view_layer = (ViewLayer *)scene.view_layers.first;
- settings.depsgraph = depsgraph = DEG_graph_new(bmain, &scene, view_layer, DAG_EVAL_VIEWPORT);
-
- settings.scene = &scene;
- settings.view_layer = view_layer;
-
- exporter = NULL;
+ depsgraph = DEG_graph_new(bmain, &scene, view_layer, DAG_EVAL_RENDER);
}
virtual void TearDown()
{
BKE_main_free(bmain);
DEG_graph_free(depsgraph);
- delete exporter;
+ deleteArchive();
+ }
+
+ // Call after setting up the parameters.
+ void createArchive()
+ {
+ if (abc_archive != nullptr) {
+ deleteArchive();
+ }
+ abc_archive = new ABCArchive(bmain, &scene, params, "somefile.abc");
}
- // Call after setting up the settings.
- void createExporter()
+ void deleteArchive()
{
- exporter = new TestableAbcExporter(bmain, "somefile.abc", settings);
+ delete abc_archive;
+ if (BLI_exists("somefile.abc")) {
+ BLI_delete("somefile.abc", false, false);
+ }
+ abc_archive = nullptr;
}
};
-TEST_F(AlembicExportTest, TimeSamplesFullShutter)
+TEST_F(AlembicExportTest, TimeSamplesFullShutterUniform)
{
- settings.shutter_open = 0.0;
- settings.shutter_close = 1.0;
-
- createExporter();
- std::vector<double> samples;
-
- /* test 5 samples per frame */
- exporter->getShutterSamples(5, true, samples);
- EXPECT_EQ(5, samples.size());
- EXPECT_NEAR(1.240, samples[0], 1e-5f);
- EXPECT_NEAR(1.248, samples[1], 1e-5f);
- EXPECT_NEAR(1.256, samples[2], 1e-5f);
- EXPECT_NEAR(1.264, samples[3], 1e-5f);
- EXPECT_NEAR(1.272, samples[4], 1e-5f);
-
- /* test same, but using frame number offset instead of time */
- exporter->getShutterSamples(5, false, samples);
- EXPECT_EQ(5, samples.size());
- EXPECT_NEAR(0.0, samples[0], 1e-5f);
- EXPECT_NEAR(0.2, samples[1], 1e-5f);
- EXPECT_NEAR(0.4, samples[2], 1e-5f);
- EXPECT_NEAR(0.6, samples[3], 1e-5f);
- EXPECT_NEAR(0.8, samples[4], 1e-5f);
-
- /* use the same setup to test getFrameSet() */
- std::set<double> frames;
- exporter->getFrameSet(5, frames);
- EXPECT_EQ(965, frames.size());
- EXPECT_EQ(1, frames.count(31.0));
- EXPECT_EQ(1, frames.count(31.2));
- EXPECT_EQ(1, frames.count(31.4));
- EXPECT_EQ(1, frames.count(31.6));
- EXPECT_EQ(1, frames.count(31.8));
+ /* Test 5 samples per frame, for 2 frames. */
+ params.shutter_open = 0.0;
+ params.shutter_close = 1.0;
+ params.frame_start = 31.0;
+ params.frame_end = 32.0;
+ params.frame_samples_xform = params.frame_samples_shape = 5;
+ createArchive();
+ std::vector<double> frames(abc_archive->frames_begin(), abc_archive->frames_end());
+ EXPECT_EQ(10, frames.size());
+ EXPECT_NEAR(31.0, frames[0], 1e-5);
+ EXPECT_NEAR(31.2, frames[1], 1e-5);
+ EXPECT_NEAR(31.4, frames[2], 1e-5);
+ EXPECT_NEAR(31.6, frames[3], 1e-5);
+ EXPECT_NEAR(31.8, frames[4], 1e-5);
+ EXPECT_NEAR(32.0, frames[5], 1e-5);
+ EXPECT_NEAR(32.2, frames[6], 1e-5);
+ EXPECT_NEAR(32.4, frames[7], 1e-5);
+ EXPECT_NEAR(32.6, frames[8], 1e-5);
+ EXPECT_NEAR(32.8, frames[9], 1e-5);
+
+ for (double frame : frames) {
+ EXPECT_TRUE(abc_archive->is_xform_frame(frame));
+ EXPECT_TRUE(abc_archive->is_shape_frame(frame));
+ }
+}
+
+TEST_F(AlembicExportTest, TimeSamplesFullShutterDifferent)
+{
+ /* Test 3 samples per frame for transforms, and 2 per frame for shapes, for 2 frames. */
+ params.shutter_open = 0.0;
+ params.shutter_close = 1.0;
+ params.frame_start = 31.0;
+ params.frame_end = 32.0;
+ params.frame_samples_xform = 3;
+ params.frame_samples_shape = 2;
+ createArchive();
+ std::vector<double> frames(abc_archive->frames_begin(), abc_archive->frames_end());
+ EXPECT_EQ(8, frames.size());
+ EXPECT_NEAR(31.0, frames[0], 1e-5); // transform + shape
+ EXPECT_TRUE(abc_archive->is_xform_frame(frames[0]));
+ EXPECT_TRUE(abc_archive->is_shape_frame(frames[0]));
+ EXPECT_NEAR(31.33333, frames[1], 1e-5); // transform
+ EXPECT_TRUE(abc_archive->is_xform_frame(frames[1]));
+ EXPECT_FALSE(abc_archive->is_shape_frame(frames[1]));
+ EXPECT_NEAR(31.5, frames[2], 1e-5); // shape
+ EXPECT_FALSE(abc_archive->is_xform_frame(frames[2]));
+ EXPECT_TRUE(abc_archive->is_shape_frame(frames[2]));
+ EXPECT_NEAR(31.66666, frames[3], 1e-5); // transform
+ EXPECT_TRUE(abc_archive->is_xform_frame(frames[3]));
+ EXPECT_FALSE(abc_archive->is_shape_frame(frames[3]));
+ EXPECT_NEAR(32.0, frames[4], 1e-5); // transform + shape
+ EXPECT_TRUE(abc_archive->is_xform_frame(frames[4]));
+ EXPECT_TRUE(abc_archive->is_shape_frame(frames[4]));
+ EXPECT_NEAR(32.33333, frames[5], 1e-5); // transform
+ EXPECT_TRUE(abc_archive->is_xform_frame(frames[5]));
+ EXPECT_FALSE(abc_archive->is_shape_frame(frames[5]));
+ EXPECT_NEAR(32.5, frames[6], 1e-5); // shape
+ EXPECT_FALSE(abc_archive->is_xform_frame(frames[6]));
+ EXPECT_TRUE(abc_archive->is_shape_frame(frames[6]));
+ EXPECT_NEAR(32.66666, frames[7], 1e-5); // transform
+ EXPECT_TRUE(abc_archive->is_xform_frame(frames[7]));
+ EXPECT_FALSE(abc_archive->is_shape_frame(frames[7]));
}
TEST_F(AlembicExportTest, TimeSamples180degShutter)
{
- settings.shutter_open = -0.25;
- settings.shutter_close = 0.25;
-
- createExporter();
- std::vector<double> samples;
-
- /* test 5 samples per frame */
- exporter->getShutterSamples(5, true, samples);
- EXPECT_EQ(5, samples.size());
- EXPECT_NEAR(1.230, samples[0], 1e-5f);
- EXPECT_NEAR(1.234, samples[1], 1e-5f);
- EXPECT_NEAR(1.238, samples[2], 1e-5f);
- EXPECT_NEAR(1.242, samples[3], 1e-5f);
- EXPECT_NEAR(1.246, samples[4], 1e-5f);
-
- /* test same, but using frame number offset instead of time */
- exporter->getShutterSamples(5, false, samples);
- EXPECT_EQ(5, samples.size());
- EXPECT_NEAR(-0.25, samples[0], 1e-5f);
- EXPECT_NEAR(-0.15, samples[1], 1e-5f);
- EXPECT_NEAR(-0.05, samples[2], 1e-5f);
- EXPECT_NEAR(0.05, samples[3], 1e-5f);
- EXPECT_NEAR(0.15, samples[4], 1e-5f);
-
- /* Use the same setup to test getFrameSet().
- * Here only a few numbers are tested, due to rounding issues. */
- std::set<double> frames;
- exporter->getFrameSet(5, frames);
- EXPECT_EQ(965, frames.size());
- EXPECT_EQ(1, frames.count(30.75));
- EXPECT_EQ(1, frames.count(30.95));
- EXPECT_EQ(1, frames.count(31.15));
+ /* Test 5 samples per frame, for 2 frames. */
+ params.shutter_open = -0.25;
+ params.shutter_close = 0.25;
+ params.frame_start = 31.0;
+ params.frame_end = 32.0;
+ params.frame_samples_xform = params.frame_samples_shape = 5;
+ createArchive();
+ std::vector<double> frames(abc_archive->frames_begin(), abc_archive->frames_end());
+ EXPECT_EQ(10, frames.size());
+ EXPECT_NEAR(31 - 0.25, frames[0], 1e-5);
+ EXPECT_NEAR(31 - 0.15, frames[1], 1e-5);
+ EXPECT_NEAR(31 - 0.05, frames[2], 1e-5);
+ EXPECT_NEAR(31 + 0.05, frames[3], 1e-5);
+ EXPECT_NEAR(31 + 0.15, frames[4], 1e-5);
+ EXPECT_NEAR(32 - 0.25, frames[5], 1e-5);
+ EXPECT_NEAR(32 - 0.15, frames[6], 1e-5);
+ EXPECT_NEAR(32 - 0.05, frames[7], 1e-5);
+ EXPECT_NEAR(32 + 0.05, frames[8], 1e-5);
+ EXPECT_NEAR(32 + 0.15, frames[9], 1e-5);
}
+
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/tests/gtests/alembic/abc_matrix_test.cc b/tests/gtests/alembic/abc_matrix_test.cc
index 60dfd4e30cf..fe0635ea7ab 100644
--- a/tests/gtests/alembic/abc_matrix_test.cc
+++ b/tests/gtests/alembic/abc_matrix_test.cc
@@ -8,7 +8,9 @@ extern "C" {
#include "BLI_utildefines.h"
}
-using namespace blender::io::alembic;
+namespace blender {
+namespace io {
+namespace alembic {
TEST(abc_matrix, CreateRotationMatrixY_YfromZ)
{
@@ -286,3 +288,7 @@ TEST(abc_matrix, CopyM44AxisSwapWithScale_gimbal_ZfromY)
EXPECT_M4_NEAR(expect, result, 1e-5f);
}
+
+} // namespace alembic
+} // namespace io
+} // namespace blender
diff --git a/tests/python/alembic_tests.py b/tests/python/alembic_tests.py
index 705bb98a060..5f8113729c3 100644
--- a/tests/python/alembic_tests.py
+++ b/tests/python/alembic_tests.py
@@ -214,7 +214,7 @@ class DupliGroupExportTest(AbstractAlembicTest):
self.run_blender('dupligroup-scene.blend', script)
# Now check the resulting Alembic file.
- xform = self.abcprop(abc, '/Real_Cube/Linked_Suzanne/Cylinder/Suzanne/.xform')
+ xform = self.abcprop(abc, '/Real_Cube/Linked_Suzanne/Cylinder-0/Suzanne-1/.xform')
self.assertEqual(1, xform['.inherits'])
self.assertAlmostEqualFloatArray(
xform['.vals'],
@@ -232,7 +232,7 @@ class DupliGroupExportTest(AbstractAlembicTest):
self.run_blender('dupligroup-scene.blend', script)
# Now check the resulting Alembic file.
- xform = self.abcprop(abc, '/Suzanne/.xform')
+ xform = self.abcprop(abc, '/Suzanne-1/.xform')
self.assertEqual(1, xform['.inherits'])
self.assertAlmostEqualFloatArray(